@malloy-publisher/server 0.0.198-dev2 → 0.0.198-dev4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build.ts CHANGED
@@ -5,14 +5,14 @@ fs.rmSync("./dist", { recursive: true, force: true });
5
5
  fs.mkdirSync("./dist");
6
6
 
7
7
  await build({
8
+ // compile_worker.ts is bundled as a SEPARATE entrypoint so it can
9
+ // be loaded by `new Worker(...)` at runtime. It must NOT be
10
+ // inlined into server.mjs (workers can't share module state with
11
+ // the parent process — they get their own JS realm).
8
12
  entrypoints: [
9
13
  "./src/server.ts",
10
14
  "./src/instrumentation.ts",
11
- // Schema-introspection worker. Loaded at runtime via
12
- // `new Worker(new URL("./schema_worker.mjs", import.meta.url))`
13
- // from `schema_worker_pool.ts`, so it has to be a separate
14
- // bundle output sitting next to server.mjs in dist/.
15
- "./src/service/schema_worker.ts",
15
+ "./src/compile/compile_worker.ts",
16
16
  ],
17
17
  outdir: "./dist",
18
18
  target: "node",
@@ -56,13 +56,23 @@ fs.copyFileSync(
56
56
  // Rename ESM outputs to .mjs so both Node and Bun can execute them
57
57
  fs.renameSync("./dist/server.js", "./dist/server.mjs");
58
58
  fs.renameSync("./dist/instrumentation.js", "./dist/instrumentation.mjs");
59
- // Bun nests outputs by their path relative to the common entrypoint
60
- // root (./src). schema_worker.ts is under src/service/, so its output
61
- // lands at dist/service/schema_worker.jsnot dist/.
62
- fs.renameSync(
63
- "./dist/service/schema_worker.js",
64
- "./dist/service/schema_worker.mjs",
65
- );
59
+ // Bun emits compile_worker into its source-relative subdir; flatten
60
+ // so compile_pool.ts's `resolveWorkerScript()` finds it as a sibling
61
+ // of server.mjs. The path layout match is intentional keep these
62
+ // two in sync or the worker pool falls back to in-process compile.
63
+ if (fs.existsSync("./dist/compile/compile_worker.js")) {
64
+ fs.renameSync(
65
+ "./dist/compile/compile_worker.js",
66
+ "./dist/compile_worker.mjs",
67
+ );
68
+ try {
69
+ fs.rmdirSync("./dist/compile");
70
+ } catch {
71
+ /* directory may be non-empty if Bun produced sourcemaps; leave it */
72
+ }
73
+ } else if (fs.existsSync("./dist/compile_worker.js")) {
74
+ fs.renameSync("./dist/compile_worker.js", "./dist/compile_worker.mjs");
75
+ }
66
76
 
67
77
  // Add shebang to server.mjs for npx/bunx compatibility
68
78
  const serverJsPath = "./dist/server.mjs";
@@ -0,0 +1,633 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
37
+ var __export = (target, all) => {
38
+ for (var name in all)
39
+ __defProp(target, name, {
40
+ get: all[name],
41
+ enumerable: true,
42
+ configurable: true,
43
+ set: __exportSetter.bind(all, name)
44
+ });
45
+ };
46
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
48
+
49
+ // src/compile/compile_worker.ts
50
+ import {
51
+ contextOverlay,
52
+ MalloyConfig,
53
+ MalloyError,
54
+ Runtime,
55
+ isSourceDef,
56
+ modelDefToModelInfo
57
+ } from "@malloydata/malloy";
58
+ import * as fs from "fs";
59
+ import { parentPort, threadId } from "node:worker_threads";
60
+ import { fileURLToPath } from "url";
61
+
62
+ // src/service/filter.ts
63
+ var VALID_FILTER_TYPES = new Set([
64
+ "equal",
65
+ "in",
66
+ "like",
67
+ "greater_than",
68
+ "less_than"
69
+ ]);
70
+ var ANNOTATION_PREFIX = "#(filter)";
71
+ function parseFilterAnnotation(annotation) {
72
+ const trimmed = annotation.trim();
73
+ if (!trimmed.startsWith(ANNOTATION_PREFIX)) {
74
+ return null;
75
+ }
76
+ const body = trimmed.slice(ANNOTATION_PREFIX.length).trim();
77
+ const tokens = tokenize(body);
78
+ let name;
79
+ let dimension;
80
+ let type;
81
+ let implicit = false;
82
+ let required = false;
83
+ for (const token of tokens) {
84
+ if (token.includes("=")) {
85
+ const eqIndex = token.indexOf("=");
86
+ const key = token.slice(0, eqIndex).toLowerCase();
87
+ const value = token.slice(eqIndex + 1);
88
+ switch (key) {
89
+ case "name":
90
+ name = value;
91
+ break;
92
+ case "dimension":
93
+ dimension = value;
94
+ break;
95
+ case "type":
96
+ if (!VALID_FILTER_TYPES.has(value)) {
97
+ throw new Error(`Invalid filter type "${value}". Must be one of: ${[...VALID_FILTER_TYPES].join(", ")}`);
98
+ }
99
+ type = value;
100
+ break;
101
+ default:
102
+ throw new Error(`Unknown filter parameter "${key}"`);
103
+ }
104
+ } else {
105
+ const flag = token.toLowerCase();
106
+ if (flag === "implicit") {
107
+ implicit = true;
108
+ } else if (flag === "required") {
109
+ required = true;
110
+ } else {
111
+ throw new Error(`Unknown filter flag "${token}"`);
112
+ }
113
+ }
114
+ }
115
+ if (!dimension) {
116
+ throw new Error("filter annotation missing required 'dimension' parameter");
117
+ }
118
+ if (!type) {
119
+ throw new Error("filter annotation missing required 'type' parameter");
120
+ }
121
+ return {
122
+ name: name ?? dimension,
123
+ dimension,
124
+ type,
125
+ implicit,
126
+ required
127
+ };
128
+ }
129
+ function parseFilters(annotations) {
130
+ const byName = new Map;
131
+ for (const annotation of annotations) {
132
+ const parsed = parseFilterAnnotation(annotation);
133
+ if (parsed) {
134
+ byName.set(parsed.name, parsed);
135
+ }
136
+ }
137
+ return [...byName.values()];
138
+ }
139
+ function escapeMalloyString(value) {
140
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
141
+ }
142
+ function isBooleanLiteral(v) {
143
+ const lower = v.toLowerCase();
144
+ return lower === "true" || lower === "false";
145
+ }
146
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
147
+ var ISO_TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/;
148
+ function isDateLiteral(v) {
149
+ return ISO_DATE_RE.test(v) || ISO_TIMESTAMP_RE.test(v);
150
+ }
151
+ function malloyLiteral(v) {
152
+ if (isBooleanLiteral(v)) {
153
+ return v.toLowerCase();
154
+ }
155
+ if (isDateLiteral(v)) {
156
+ return `@${v.slice(0, 10)}`;
157
+ }
158
+ return `'${escapeMalloyString(v)}'`;
159
+ }
160
+ function buildPredicate(filter, value) {
161
+ const dim = `\`${filter.dimension}\``;
162
+ switch (filter.type) {
163
+ case "equal": {
164
+ const v = Array.isArray(value) ? value[0] : value;
165
+ return `${dim} = ${malloyLiteral(v)}`;
166
+ }
167
+ case "in": {
168
+ const values = Array.isArray(value) ? value : [value];
169
+ if (values.length === 1) {
170
+ return `${dim} = ${malloyLiteral(values[0])}`;
171
+ }
172
+ const conditions = values.map((v) => `${dim} = ${malloyLiteral(v)}`);
173
+ return `(${conditions.join(" or ")})`;
174
+ }
175
+ case "like": {
176
+ const v = Array.isArray(value) ? value[0] : value;
177
+ const escaped = escapeMalloyString(v.toLowerCase());
178
+ const pattern = escaped.startsWith("%") || escaped.endsWith("%") ? escaped : `%${escaped}%`;
179
+ return `lower(${dim}) ~ '${pattern}'`;
180
+ }
181
+ case "greater_than": {
182
+ const v = Array.isArray(value) ? value[0] : value;
183
+ return `${dim} > ${malloyLiteral(v)}`;
184
+ }
185
+ case "less_than": {
186
+ const v = Array.isArray(value) ? value[0] : value;
187
+ return `${dim} < ${malloyLiteral(v)}`;
188
+ }
189
+ }
190
+ }
191
+ function buildFilterClause(filters, params) {
192
+ const predicates = [];
193
+ for (const filter of filters) {
194
+ const value = params[filter.name];
195
+ const hasValue = value !== undefined && value !== null && (Array.isArray(value) ? value.length > 0 : value !== "");
196
+ if (!hasValue) {
197
+ if (filter.required) {
198
+ throw new FilterValidationError(`Required filter "${filter.name}" (dimension: ${filter.dimension}) was not provided`);
199
+ }
200
+ continue;
201
+ }
202
+ predicates.push(buildPredicate(filter, value));
203
+ }
204
+ if (predicates.length === 0) {
205
+ return "";
206
+ }
207
+ return predicates.join(" and ");
208
+ }
209
+ function injectFilterRefinement(query, filterClause) {
210
+ if (!filterClause) {
211
+ return query;
212
+ }
213
+ return `${query.trimEnd()} + {where: ${filterClause}}`;
214
+ }
215
+
216
+ class FilterValidationError extends Error {
217
+ constructor(message) {
218
+ super(message);
219
+ this.name = "FilterValidationError";
220
+ }
221
+ }
222
+ function tokenize(input) {
223
+ const tokens = [];
224
+ let current = "";
225
+ let inQuote = false;
226
+ let quoteChar = "";
227
+ for (const ch of input) {
228
+ if (inQuote) {
229
+ if (ch === quoteChar) {
230
+ inQuote = false;
231
+ } else {
232
+ current += ch;
233
+ }
234
+ } else if (ch === '"' || ch === "'") {
235
+ inQuote = true;
236
+ quoteChar = ch;
237
+ } else if (ch === " " || ch === "\t") {
238
+ if (current) {
239
+ tokens.push(current);
240
+ current = "";
241
+ }
242
+ } else {
243
+ current += ch;
244
+ }
245
+ }
246
+ if (current) {
247
+ tokens.push(current);
248
+ }
249
+ return tokens;
250
+ }
251
+
252
+ // src/compile/compile_worker.ts
253
+ if (!parentPort) {
254
+ throw new Error("compile_worker.ts must be loaded inside a worker_threads Worker");
255
+ }
256
+ var port = parentPort;
257
+ var nextRpcId = 0;
258
+ var pendingRpc = new Map;
259
+ function newRpcId() {
260
+ nextRpcId += 1;
261
+ return `w${threadId}-rpc-${nextRpcId}`;
262
+ }
263
+ function callMain(send) {
264
+ const requestId = newRpcId();
265
+ return new Promise((resolve, reject) => {
266
+ pendingRpc.set(requestId, {
267
+ resolve: (value) => resolve(value),
268
+ reject
269
+ });
270
+ send(requestId);
271
+ });
272
+ }
273
+ function dispatchMainResponse(message) {
274
+ if (message.type === "schema-for-tables-response" || message.type === "schema-for-sql-response" || message.type === "read-url-response" || message.type === "connection-metadata-response") {
275
+ const pending = pendingRpc.get(message.requestId);
276
+ if (!pending)
277
+ return;
278
+ pendingRpc.delete(message.requestId);
279
+ pending.resolve(message);
280
+ return;
281
+ }
282
+ if (message.type === "rpc-error") {
283
+ const pending = pendingRpc.get(message.requestId);
284
+ if (!pending)
285
+ return;
286
+ pendingRpc.delete(message.requestId);
287
+ pending.reject(deserializeError(message.error));
288
+ return;
289
+ }
290
+ }
291
+
292
+ class ProxyConnection {
293
+ name;
294
+ dialectName;
295
+ digest;
296
+ jobId;
297
+ constructor(metadata, jobId) {
298
+ this.name = metadata.name;
299
+ this.dialectName = metadata.dialectName;
300
+ this.digest = metadata.digest;
301
+ this.jobId = jobId;
302
+ }
303
+ getDigest() {
304
+ return this.digest;
305
+ }
306
+ async fetchSchemaForTables(tables, options) {
307
+ const response = await callMain((requestId) => {
308
+ const req = {
309
+ type: "schema-for-tables",
310
+ requestId,
311
+ jobId: this.jobId,
312
+ connectionName: this.name,
313
+ tables,
314
+ options: serializeFetchOptions(options)
315
+ };
316
+ port.postMessage(req);
317
+ });
318
+ return { schemas: response.schemas, errors: response.errors };
319
+ }
320
+ async fetchSchemaForSQLStruct(sentence, options) {
321
+ const response = await callMain((requestId) => {
322
+ const req = {
323
+ type: "schema-for-sql",
324
+ requestId,
325
+ jobId: this.jobId,
326
+ connectionName: this.name,
327
+ sentence,
328
+ options: serializeFetchOptions(options)
329
+ };
330
+ port.postMessage(req);
331
+ });
332
+ if (response.error !== undefined) {
333
+ return { error: response.error };
334
+ }
335
+ if (response.structDef === undefined) {
336
+ return { error: "Empty SQL schema response from main thread" };
337
+ }
338
+ return { structDef: response.structDef };
339
+ }
340
+ async runSQL() {
341
+ throw new Error(`ProxyConnection(${this.name}): runSQL is not available in compile workers`);
342
+ }
343
+ isPool() {
344
+ return false;
345
+ }
346
+ canPersist() {
347
+ return false;
348
+ }
349
+ canStream() {
350
+ return false;
351
+ }
352
+ async close() {}
353
+ async idle() {}
354
+ async estimateQueryCost() {
355
+ throw new Error(`ProxyConnection(${this.name}): estimateQueryCost not available in compile workers`);
356
+ }
357
+ async fetchMetadata() {
358
+ return {};
359
+ }
360
+ async fetchTableMetadata() {
361
+ return {};
362
+ }
363
+ }
364
+ function serializeFetchOptions(options) {
365
+ const out = {};
366
+ if (options.refreshTimestamp !== undefined) {
367
+ out.refreshTimestamp = options.refreshTimestamp;
368
+ }
369
+ if (options.modelAnnotation !== undefined) {
370
+ out.modelAnnotation = options.modelAnnotation;
371
+ }
372
+ return out;
373
+ }
374
+ function makeWorkerUrlReader(jobId) {
375
+ return {
376
+ readURL: async (url) => {
377
+ if (url.protocol === "file:") {
378
+ const filePath = fileURLToPath(url);
379
+ return fs.promises.readFile(filePath, "utf8");
380
+ }
381
+ const response = await callMain((requestId) => {
382
+ const req = {
383
+ type: "read-url",
384
+ requestId,
385
+ jobId,
386
+ url: url.toString()
387
+ };
388
+ port.postMessage(req);
389
+ });
390
+ return response.contents;
391
+ }
392
+ };
393
+ }
394
+ function buildWorkerMalloyConfig(job) {
395
+ const proxies = new Map;
396
+ const inflight = new Map;
397
+ const config = new MalloyConfig({ connections: {} }, {
398
+ config: contextOverlay({ rootDirectory: job.packagePath })
399
+ });
400
+ config.wrapConnections((_base) => ({
401
+ lookupConnection: async (name) => {
402
+ const effectiveName = name ?? job.defaultConnectionName ?? "duckdb";
403
+ const cached = proxies.get(effectiveName);
404
+ if (cached)
405
+ return cached;
406
+ let pending = inflight.get(effectiveName);
407
+ if (!pending) {
408
+ pending = (async () => {
409
+ const response = await callMain((requestId) => {
410
+ const req = {
411
+ type: "connection-metadata",
412
+ requestId,
413
+ jobId: job.requestId,
414
+ connectionName: effectiveName
415
+ };
416
+ port.postMessage(req);
417
+ });
418
+ const proxy2 = new ProxyConnection(response.metadata, job.requestId);
419
+ proxies.set(effectiveName, proxy2);
420
+ inflight.delete(effectiveName);
421
+ return proxy2;
422
+ })();
423
+ inflight.set(effectiveName, pending);
424
+ }
425
+ const proxy = await pending;
426
+ return proxy;
427
+ }
428
+ }));
429
+ return config;
430
+ }
431
+ async function compile(job) {
432
+ const compileStart = performance.now();
433
+ const malloyConfig = buildWorkerMalloyConfig(job);
434
+ const urlReader = makeWorkerUrlReader(job.requestId);
435
+ const runtime = new Runtime({
436
+ urlReader,
437
+ config: malloyConfig,
438
+ buildManifest: job.buildManifest !== undefined && job.buildManifest !== null ? {
439
+ entries: job.buildManifest,
440
+ strict: false
441
+ } : undefined
442
+ });
443
+ const isInline = typeof job.inlineSource === "string";
444
+ if (!isInline && typeof job.modelPath !== "string") {
445
+ throw new Error("CompileJobRequest must supply either inlineSource or modelPath");
446
+ }
447
+ const importBaseURL = isInline ? new URL(job.importBaseURL ?? `file://${job.packagePath}/`) : new URL(".", new URL(`file://${job.packagePath}/${job.modelPath}`));
448
+ const mm = isInline ? runtime.loadModel(job.inlineSource, { importBaseURL }) : runtime.loadModel(new URL(`file://${job.packagePath}/${job.modelPath}`), { importBaseURL });
449
+ const compiledModel = await mm.getModel();
450
+ const modelDef = compiledModel._modelDef;
451
+ const malloyGivens = Array.from(compiledModel.givens.values());
452
+ const givens = malloyGivens.length > 0 ? malloyGivens.map((g) => malloyGivenToWire(g)) : undefined;
453
+ const sourceInfos = [];
454
+ const importedSourceNames = new Set;
455
+ const imports = modelDef.imports ?? [];
456
+ for (const importLocation of imports) {
457
+ try {
458
+ const modelString = await urlReader.readURL(new URL(importLocation.importURL));
459
+ const importedModelDef = (await runtime.loadModel(modelString, { importBaseURL }).getModel())._modelDef;
460
+ const importedInfo = modelDefToModelInfo(importedModelDef);
461
+ const importedSources = importedInfo.entries.filter((entry) => entry.kind === "source");
462
+ for (const source of importedSources) {
463
+ if (!importedSourceNames.has(source.name)) {
464
+ sourceInfos.push(source);
465
+ importedSourceNames.add(source.name);
466
+ }
467
+ }
468
+ } catch {}
469
+ }
470
+ const localInfo = modelDefToModelInfo(modelDef);
471
+ const localSources = localInfo.entries.filter((entry) => entry.kind === "source");
472
+ for (const source of localSources) {
473
+ if (!importedSourceNames.has(source.name)) {
474
+ sourceInfos.push(source);
475
+ }
476
+ }
477
+ const modelPathForAnnotations = job.modelPath ?? "";
478
+ const { sources, filterMap } = extractSources(modelPathForAnnotations, modelDef);
479
+ const queries = extractQueries(modelPathForAnnotations, modelDef);
480
+ const filterMapEntries = Array.from(filterMap.entries());
481
+ return {
482
+ type: "compile-result",
483
+ requestId: job.requestId,
484
+ modelDef,
485
+ sourceInfos,
486
+ sources,
487
+ queries,
488
+ filterMap: filterMapEntries,
489
+ givens,
490
+ dataStyles: {},
491
+ compileDurationMs: performance.now() - compileStart
492
+ };
493
+ }
494
+ function malloyGivenToWire(given) {
495
+ const t = given.type;
496
+ const renderedType = t.type === "filter expression" ? `filter<${t.filterType}>` : t.type;
497
+ return {
498
+ name: given.name,
499
+ type: renderedType,
500
+ annotations: given.getTaglines(/^#\(/)
501
+ };
502
+ }
503
+ function extractSources(modelPath, modelDef) {
504
+ const filterMap = new Map;
505
+ const sources = Object.values(modelDef.contents).filter((obj) => isSourceDef(obj)).map((sourceObj) => {
506
+ const sourceName = sourceObj.as || sourceObj.name;
507
+ const annotations = sourceObj.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text);
508
+ const collected = [];
509
+ let cur = sourceObj.annotation;
510
+ while (cur) {
511
+ if (cur.blockNotes) {
512
+ collected.push(cur.blockNotes.map((note) => note.text));
513
+ }
514
+ cur = cur.inherits;
515
+ }
516
+ const allAnnotations = collected.reverse().flat();
517
+ let filters;
518
+ if (allAnnotations.length > 0) {
519
+ try {
520
+ const parsed = parseFilters(allAnnotations);
521
+ if (parsed.length > 0) {
522
+ filterMap.set(sourceName, parsed);
523
+ const fields = sourceObj.fields;
524
+ filters = parsed.map((f) => {
525
+ const field = fields.find((fd) => (fd.as || fd.name) === f.dimension);
526
+ return {
527
+ name: f.name,
528
+ dimension: f.dimension,
529
+ type: f.type,
530
+ implicit: f.implicit,
531
+ required: f.required,
532
+ dimensionType: field?.type
533
+ };
534
+ });
535
+ }
536
+ } catch {}
537
+ }
538
+ const views = sourceObj.fields.filter((f) => f.type === "turtle").filter((turtle) => turtle.pipeline.map((stage) => stage.type).every((type) => type === "reduce")).map((turtle) => ({
539
+ name: turtle.as || turtle.name,
540
+ annotations: turtle?.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text)
541
+ }));
542
+ return {
543
+ name: sourceName,
544
+ annotations,
545
+ views,
546
+ filters
547
+ };
548
+ });
549
+ return { sources, filterMap };
550
+ }
551
+ function extractQueries(modelPath, modelDef) {
552
+ const isNamedQuery = (obj) => obj.type === "query";
553
+ return Object.values(modelDef.contents).filter(isNamedQuery).map((q) => ({
554
+ name: q.as || q.name,
555
+ sourceName: typeof q.structRef === "string" ? q.structRef : undefined,
556
+ annotations: q?.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text)
557
+ }));
558
+ }
559
+ function serializeError(error) {
560
+ if (error instanceof MalloyError) {
561
+ return {
562
+ name: error.name,
563
+ message: error.message,
564
+ stack: error.stack,
565
+ malloyProblems: error.problems,
566
+ isCompilationError: true
567
+ };
568
+ }
569
+ if (error instanceof Error) {
570
+ return {
571
+ name: error.name,
572
+ message: error.message,
573
+ stack: error.stack
574
+ };
575
+ }
576
+ return { name: "Error", message: String(error) };
577
+ }
578
+ function deserializeError(serialized) {
579
+ const err = new Error(serialized.message);
580
+ err.name = serialized.name;
581
+ if (serialized.stack)
582
+ err.stack = serialized.stack;
583
+ return err;
584
+ }
585
+ var shuttingDown = false;
586
+ var inFlightJobs = new Set;
587
+ port.on("message", (message) => {
588
+ if (message.type === "shutdown") {
589
+ shuttingDown = true;
590
+ maybeExit();
591
+ return;
592
+ }
593
+ if (message.type === "compile") {
594
+ if (shuttingDown) {
595
+ const errMsg = {
596
+ type: "compile-error",
597
+ requestId: message.requestId,
598
+ error: {
599
+ name: "ShuttingDown",
600
+ message: "Compile worker is shutting down"
601
+ }
602
+ };
603
+ port.postMessage(errMsg);
604
+ return;
605
+ }
606
+ inFlightJobs.add(message.requestId);
607
+ runJob(message);
608
+ return;
609
+ }
610
+ dispatchMainResponse(message);
611
+ });
612
+ async function runJob(job) {
613
+ try {
614
+ const result = await compile(job);
615
+ port.postMessage(result);
616
+ } catch (error) {
617
+ const errMsg = {
618
+ type: "compile-error",
619
+ requestId: job.requestId,
620
+ error: serializeError(error)
621
+ };
622
+ port.postMessage(errMsg);
623
+ } finally {
624
+ inFlightJobs.delete(job.requestId);
625
+ maybeExit();
626
+ }
627
+ }
628
+ function maybeExit() {
629
+ if (shuttingDown && inFlightJobs.size === 0 && pendingRpc.size === 0) {
630
+ setImmediate(() => process.exit(0));
631
+ }
632
+ }
633
+ port.postMessage({ type: "ready" });