PageSourceSearch

https://comunica.dev/js/comunica-worker.js

js comunica.dev collected 2026-10-02 14:40:16 UTC 4,405 bytes, 155 lines download raw bytes

1/* Runs the Comunica query engine off the main thread, for the demo on the home page. */
2
3const ENGINE_URL = 'https://rdf.js.org/comunica-browser/versions/v4/engines/query-sparql/comunica-browser.js';
4const PROGRESS_INTERVAL = 250;
5
6let engine;
7let active;
8let requests = 0;
9
10// Wrap fetch before the engine is loaded, so that all of its requests are counted.
11const originalFetch = self.fetch.bind(self);
12self.fetch = function (...args) {
13  requests++;
14  return originalFetch(...args);
15};
16
17self.onmessage = function (event) {
18  if (event.data.type === 'query') {
19    runQuery(event.data);
20  } else if (event.data.type === 'stop') {
21    stopQuery(true);
22  } else if (event.data.type === 'preload') {
23    preloadEngine();
24  }
25};
26
27// Fetch the engine ahead of the first query, without contacting any source.
28function preloadEngine() {
29  try {
30    loadEngine();
31    postMessage({ type: 'ready' });
32  } catch (error) {
33    postMessage({ type: 'engineError', message: String((error && error.message) || error) });
34  }
35}
36
37// Load the pre-built engine on first use, and keep it for later queries.
38function loadEngine() {
39  if (!engine) {
40    importScripts(ENGINE_URL);
41    engine = new self.Comunica.QueryEngine();
42  }
43  return engine;
44}
45
46function termToJson(term) {
47  return term ? { termType: term.termType, value: term.value } : null;
48}
49
50// Finish the active query, ignoring any later events of the same run.
51function finish(run, truncated) {
52  if (run.finished) {
53    return;
54  }
55  run.finished = true;
56  clearInterval(run.timer);
57  if (run.iterator) {
58    run.iterator.destroy();
59  }
60  if (active === run) {
61    active = undefined;
62  }
63  postMessage({ type: 'end', token: run.token, requests, truncated });
64}
65
66function fail(run, error) {
67  if (run.finished) {
68    return;
69  }
70  run.finished = true;
71  clearInterval(run.timer);
72  if (active === run) {
73    active = undefined;
74  }
75  postMessage({ type: 'error', token: run.token, message: String((error && error.message) || error), requests });
76}
77
78function stopQuery(notify) {
79  if (active) {
80    const run = active;
81    if (notify) {
82      finish(run, false);
83    } else {
84      run.finished = true;
85      clearInterval(run.timer);
86      if (run.iterator) {
87        run.iterator.destroy();
88      }
89      active = undefined;
90    }
91  }
92}
93
94async function runQuery({ token, query, sources, limit }) {
95  stopQuery(false);
96  requests = 0;
97
98  const run = { token, finished: false, iterator: undefined, timer: undefined };
99  active = run;
100  run.timer = setInterval(() => postMessage({ type: 'progress', token, requests }), PROGRESS_INTERVAL);
101
102  try {
103    const result = await loadEngine().query(query, { sources });
104    if (run.finished) {
105      return;
106    }
107
108    // Queries that produce a single answer instead of a stream of rows.
109    if (result.resultType === 'boolean' || result.resultType === 'void') {
110      const value = await result.execute();
111      postMessage({ type: 'variables', token, variables: ['result'] });
112      postMessage({
113        type: 'result',
114        token,
115        row: [{ termType: 'Literal', value: result.resultType === 'boolean' ? String(value) : 'Done' }],
116        requests,
117      });
118      finish(run, false);
119      return;
120    }
121
122    const quads = result.resultType === 'quads';
123    let variables;
124    if (quads) {
125      variables = ['subject', 'predicate', 'object'];
126    } else {
127      const metadata = await result.metadata();
128      // Comunica reports variables either directly or wrapped, depending on the version.
129      variables = (metadata.variables || []).map(entry => (entry.variable ? entry.variable.value : entry.value));
130    }
131    if (run.finished) {
132      return;
133    }
134    postMessage({ type: 'variables', token, variables });
135
136    run.iterator = await result.execute();
137    let count = 0;
138    run.iterator.on('data', (item) => {
139      count++;
140      // One result past the limit proves that more exist, without rendering them.
141      if (count > limit) {
142        finish(run, true);
143        return;
144      }
145      const row = quads
146        ? [termToJson(item.subject), termToJson(item.predicate), termToJson(item.object)]
147        : variables.map(variable => termToJson(item.get(variable)));
148      postMessage({ type: 'result', token, row, requests });
149    });
150    run.iterator.on('end', () => finish(run, false));
151    run.iterator.on('error', error => fail(run, error));
152  } catch (error) {
153    fail(run, error);
154  }
155}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.