@econ-v1/app-sdk 7.4.0 → 7.4.2

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.
@@ -14,8 +14,26 @@ export declare const ALIASES: ReadonlyMap<string, BurgerModule>;
14
14
  * (`ws`, `undici`) that apps legitimately depend on.
15
15
  */
16
16
  export declare const NODE_BUILTINS: ReadonlySet<string>;
17
- /** Named exports of each `burger:*` module (C4, C6). Used to bridge CommonJS `require()`. */
17
+ /**
18
+ * Named exports of each `burger:*` module (C4, C6). Used to bridge CommonJS
19
+ * `require()`.
20
+ *
21
+ * DATA-APP PACK: `realpathSync`, `copyFileSync`, `symlinkSync`, `openSync`,
22
+ * `writeSync`, `fsyncSync`, `fdatasyncSync`, `closeSync`, `Dirent` and
23
+ * `constants` on `burger:fs` ship with the data-app pack
24
+ * (econ-v1/node-app-burger#12), not Phase 1. They are listed here so the
25
+ * build-time gate accepts them ahead of a runtime that provides them; an app
26
+ * using them must depend on that node-app-burger release. `readSync`,
27
+ * `fstatSync` and `utimesSync` arrive with the second data-app pack (asked
28
+ * for by the node-app-memory flip, econ-v1/node-app-memory#40).
29
+ */
18
30
  export declare const BURGER_EXPORTS: Readonly<Record<BurgerModule, readonly string[]>>;
31
+ /**
32
+ * `burger:*` modules with a `default` export (C6): every module except
33
+ * `burger:host` and `burger:test` (verified against the runtime JS in
34
+ * node-app-burger). `import x from "burger:host"` fails to link.
35
+ */
36
+ export declare const BURGER_DEFAULT_EXPORT: Readonly<Partial<Record<BurgerModule, true>>>;
19
37
  export type SpecifierClass = {
20
38
  kind: "burger";
21
39
  target: BurgerModule;
@@ -20,134 +20,6 @@ function assertSupportedBun(version = Bun.version) {
20
20
  // ../burger-build/src/gate.ts
21
21
  import { readFileSync } from "fs";
22
22
  import { extname, isAbsolute, relative, resolve } from "path";
23
- var FS_SPECIFIERS = new Set([
24
- "fs",
25
- "node:fs",
26
- "fs/promises",
27
- "node:fs/promises",
28
- "burger:fs",
29
- "burger:fs/promises"
30
- ]);
31
- var BUN_GLOBAL = /(?<![\w$.'"`])Bun\s*\.\s*[A-Za-z_$]|\bglobalThis\s*\.\s*Bun\b/;
32
- var IMPORT_META_PATH = /\bimport\.meta\.(?:url|dir|dirname|filename|path)\b/;
33
- var NATIVE_LOADER_PACKAGE = /[\\/]node_modules[\\/](?:bindings|node-gyp-build)[\\/]/;
34
- function metafileKeyToPath(key, cwd) {
35
- if (isAbsolute(key))
36
- return key;
37
- const colon = key.indexOf(":");
38
- const slash = key.indexOf("/");
39
- if (colon > 0 && (slash === -1 || colon < slash))
40
- return key;
41
- return resolve(cwd, key);
42
- }
43
- function importerGraph(metafile, cwd) {
44
- const parents = new Map;
45
- for (const [key, input] of Object.entries(metafile.inputs)) {
46
- const importer = metafileKeyToPath(key, cwd);
47
- for (const record of input.imports) {
48
- if (record.external)
49
- continue;
50
- const child = metafileKeyToPath(record.path, cwd);
51
- if (child !== importer && !parents.has(child)) {
52
- parents.set(child, importer);
53
- }
54
- }
55
- }
56
- return parents;
57
- }
58
- function importerChain(file, parents) {
59
- const chain = [file];
60
- const seen = new Set(chain);
61
- let current = parents.get(file);
62
- while (current !== undefined && !seen.has(current)) {
63
- chain.unshift(current);
64
- seen.add(current);
65
- current = parents.get(current);
66
- }
67
- return chain;
68
- }
69
- function loaderFor(file) {
70
- switch (extname(file)) {
71
- case ".ts":
72
- case ".mts":
73
- case ".cts":
74
- return "ts";
75
- case ".tsx":
76
- return "tsx";
77
- case ".jsx":
78
- return "jsx";
79
- case ".js":
80
- case ".mjs":
81
- case ".cjs":
82
- return "js";
83
- default:
84
- return;
85
- }
86
- }
87
- function codeOf(file, source) {
88
- const loader = loaderFor(file);
89
- if (!loader)
90
- return;
91
- try {
92
- return new Bun.Transpiler({ loader }).transformSync(source);
93
- } catch {
94
- return source;
95
- }
96
- }
97
- function scanModule(file, code, importedSpecifiers) {
98
- const messages = [];
99
- if (BUN_GLOBAL.test(code)) {
100
- messages.push("uses the Bun global (Bun.*), which does not exist in Burger");
101
- }
102
- if (IMPORT_META_PATH.test(code) && importedSpecifiers.some((specifier) => FS_SPECIFIERS.has(specifier))) {
103
- messages.push("reads files relative to import.meta; bundling moves this module, so the files are not found at runtime \u2014 import the data statically instead");
104
- }
105
- if (NATIVE_LOADER_PACKAGE.test(file)) {
106
- messages.push("loads a native addon (bindings/node-gyp-build), which Burger cannot run");
107
- }
108
- return messages;
109
- }
110
- function collectProblems(metafile, violations, cwd) {
111
- const parents = importerGraph(metafile, cwd);
112
- const problems = [];
113
- for (const violation of violations) {
114
- problems.push({
115
- message: `import "${violation.specifier}": ${violation.reason}`,
116
- chain: importerChain(violation.importer, parents)
117
- });
118
- }
119
- for (const [key, input] of Object.entries(metafile.inputs)) {
120
- const file = metafileKeyToPath(key, cwd);
121
- if (!isAbsolute(file))
122
- continue;
123
- let source;
124
- try {
125
- source = readFileSync(file, "utf8");
126
- } catch {
127
- continue;
128
- }
129
- const code = codeOf(file, source);
130
- if (code === undefined)
131
- continue;
132
- const specifiers = input.imports.flatMap((record) => record.original === undefined ? [record.path] : [record.path, record.original]);
133
- for (const message of scanModule(file, code, specifiers)) {
134
- problems.push({ message, chain: importerChain(file, parents) });
135
- }
136
- }
137
- return problems;
138
- }
139
- function formatProblems(problems, cwd) {
140
- const lines = [`burger-build: ${problems.length} problem(s) \u2014 this code cannot run on Burger:`];
141
- for (const problem of problems) {
142
- lines.push(` \u2717 ${problem.message}`);
143
- problem.chain.forEach((file, index) => {
144
- const shown = isAbsolute(file) ? relative(cwd, file) : file;
145
- lines.push(index === 0 ? ` ${shown}` : ` \u2192 ${shown}`);
146
- });
147
- }
148
- return lines.join(`
149
- `);
150
- }
151
23
 
152
24
  // ../burger-build/src/aliases.ts
153
25
  var BURGER_MODULES = [
@@ -265,7 +137,20 @@ var BURGER_EXPORTS = {
265
137
  "chmodSync",
266
138
  "unlinkSync",
267
139
  "mkdtempSync",
268
- "promises"
140
+ "promises",
141
+ "realpathSync",
142
+ "copyFileSync",
143
+ "symlinkSync",
144
+ "openSync",
145
+ "writeSync",
146
+ "fsyncSync",
147
+ "fdatasyncSync",
148
+ "closeSync",
149
+ "Dirent",
150
+ "constants",
151
+ "readSync",
152
+ "fstatSync",
153
+ "utimesSync"
269
154
  ],
270
155
  "burger:fs/promises": [
271
156
  "exists",
@@ -313,6 +198,16 @@ var BURGER_EXPORTS = {
313
198
  "setDefaultTimeout"
314
199
  ]
315
200
  };
201
+ var BURGER_DEFAULT_EXPORT = {
202
+ "burger:fs": true,
203
+ "burger:fs/promises": true,
204
+ "burger:path": true,
205
+ "burger:os": true,
206
+ "burger:worker_threads": true,
207
+ "burger:crypto": true,
208
+ "burger:buffer": true,
209
+ "burger:sqlite": true
210
+ };
316
211
  var BURGER_MODULE_SET = new Set(BURGER_MODULES);
317
212
  function classifySpecifier(specifier) {
318
213
  if (BURGER_MODULE_SET.has(specifier)) {
@@ -340,6 +235,191 @@ function classifySpecifier(specifier) {
340
235
  return { kind: "other" };
341
236
  }
342
237
 
238
+ // ../burger-build/src/gate.ts
239
+ var FS_SPECIFIERS = new Set([
240
+ "fs",
241
+ "node:fs",
242
+ "fs/promises",
243
+ "node:fs/promises",
244
+ "burger:fs",
245
+ "burger:fs/promises"
246
+ ]);
247
+ var BUN_GLOBAL = /(?<![\w$.'"`])Bun\s*\.\s*[A-Za-z_$]|\bglobalThis\s*\.\s*Bun\b/;
248
+ var IMPORT_META_PATH = /\bimport\.meta\.(?:url|dir|dirname|filename|path)\b/;
249
+ var IMPORT_OR_REEXPORT = /\b(?:import|export)\s+([^;]+?)\s+from\s*["']([^"']+)["']/g;
250
+ function parseNamedList(braced, into) {
251
+ for (const rawItem of braced.replace(/^\{|\}$/g, "").split(",")) {
252
+ const item = rawItem.trim();
253
+ if (item === "" || item.startsWith("type "))
254
+ continue;
255
+ const asMatch = /^([\w$]+|default)\s+as\s+[\w$]+$/.exec(item);
256
+ if (asMatch)
257
+ into.push(asMatch[1]);
258
+ else if (/^[\w$]+$/.test(item) || item === "default")
259
+ into.push(item);
260
+ }
261
+ }
262
+ function parseClause(raw) {
263
+ const clause = raw.trim();
264
+ const result = { named: [] };
265
+ if (clause.startsWith("type ") || clause.startsWith("*"))
266
+ return result;
267
+ if (clause.startsWith("{")) {
268
+ parseNamedList(clause, result.named);
269
+ return result;
270
+ }
271
+ const match = /^([\w$]+)\s*(?:,\s*([\s\S]+))?$/.exec(clause);
272
+ if (!match)
273
+ return result;
274
+ result.named.push("default");
275
+ const trailing = match[2]?.trim();
276
+ if (trailing?.startsWith("{"))
277
+ parseNamedList(trailing, result.named);
278
+ return result;
279
+ }
280
+ function missingExportMessage(target, name, specifier) {
281
+ return `"${target}" has no export "${name}" (imported as "${specifier}"); Burger provides: ${BURGER_EXPORTS[target].join(", ")}`;
282
+ }
283
+ function scanBurgerImports(code) {
284
+ const messages = [];
285
+ const reported = new Set;
286
+ for (const match of code.matchAll(IMPORT_OR_REEXPORT)) {
287
+ const specifier = match[2];
288
+ const specifierClass = classifySpecifier(specifier);
289
+ if (specifierClass.kind !== "burger")
290
+ continue;
291
+ const target = specifierClass.target;
292
+ for (const name of parseClause(match[1]).named) {
293
+ const provided = name === "default" ? target in BURGER_DEFAULT_EXPORT : BURGER_EXPORTS[target].includes(name);
294
+ const key = `${target}\x00${name}\x00${specifier}`;
295
+ if (provided || reported.has(key))
296
+ continue;
297
+ reported.add(key);
298
+ messages.push(missingExportMessage(target, name, specifier));
299
+ }
300
+ }
301
+ return messages;
302
+ }
303
+ var NATIVE_LOADER_PACKAGE = /[\\/]node_modules[\\/](?:bindings|node-gyp-build)[\\/]/;
304
+ function metafileKeyToPath(key, cwd) {
305
+ if (isAbsolute(key))
306
+ return key;
307
+ const colon = key.indexOf(":");
308
+ const slash = key.indexOf("/");
309
+ if (colon > 0 && (slash === -1 || colon < slash))
310
+ return key;
311
+ return resolve(cwd, key);
312
+ }
313
+ function importerGraph(metafile, cwd) {
314
+ const parents = new Map;
315
+ for (const [key, input] of Object.entries(metafile.inputs)) {
316
+ const importer = metafileKeyToPath(key, cwd);
317
+ for (const record of input.imports) {
318
+ if (record.external)
319
+ continue;
320
+ const child = metafileKeyToPath(record.path, cwd);
321
+ if (child !== importer && !parents.has(child)) {
322
+ parents.set(child, importer);
323
+ }
324
+ }
325
+ }
326
+ return parents;
327
+ }
328
+ function importerChain(file, parents) {
329
+ const chain = [file];
330
+ const seen = new Set(chain);
331
+ let current = parents.get(file);
332
+ while (current !== undefined && !seen.has(current)) {
333
+ chain.unshift(current);
334
+ seen.add(current);
335
+ current = parents.get(current);
336
+ }
337
+ return chain;
338
+ }
339
+ function loaderFor(file) {
340
+ switch (extname(file)) {
341
+ case ".ts":
342
+ case ".mts":
343
+ case ".cts":
344
+ return "ts";
345
+ case ".tsx":
346
+ return "tsx";
347
+ case ".jsx":
348
+ return "jsx";
349
+ case ".js":
350
+ case ".mjs":
351
+ case ".cjs":
352
+ return "js";
353
+ default:
354
+ return;
355
+ }
356
+ }
357
+ function codeOf(file, source) {
358
+ const loader = loaderFor(file);
359
+ if (!loader)
360
+ return;
361
+ try {
362
+ return new Bun.Transpiler({ loader }).transformSync(source);
363
+ } catch {
364
+ return source;
365
+ }
366
+ }
367
+ function scanModule(file, code, importedSpecifiers) {
368
+ const messages = [];
369
+ if (BUN_GLOBAL.test(code)) {
370
+ messages.push("uses the Bun global (Bun.*), which does not exist in Burger");
371
+ }
372
+ if (IMPORT_META_PATH.test(code) && importedSpecifiers.some((specifier) => FS_SPECIFIERS.has(specifier))) {
373
+ messages.push("reads files relative to import.meta; bundling moves this module, so the files are not found at runtime \u2014 import the data statically instead");
374
+ }
375
+ if (NATIVE_LOADER_PACKAGE.test(file)) {
376
+ messages.push("loads a native addon (bindings/node-gyp-build), which Burger cannot run");
377
+ }
378
+ messages.push(...scanBurgerImports(code));
379
+ return messages;
380
+ }
381
+ function collectProblems(metafile, violations, cwd) {
382
+ const parents = importerGraph(metafile, cwd);
383
+ const problems = [];
384
+ for (const violation of violations) {
385
+ problems.push({
386
+ message: `import "${violation.specifier}": ${violation.reason}`,
387
+ chain: importerChain(violation.importer, parents)
388
+ });
389
+ }
390
+ for (const [key, input] of Object.entries(metafile.inputs)) {
391
+ const file = metafileKeyToPath(key, cwd);
392
+ if (!isAbsolute(file))
393
+ continue;
394
+ let source;
395
+ try {
396
+ source = readFileSync(file, "utf8");
397
+ } catch {
398
+ continue;
399
+ }
400
+ const code = codeOf(file, source);
401
+ if (code === undefined)
402
+ continue;
403
+ const specifiers = input.imports.flatMap((record) => record.original === undefined ? [record.path] : [record.path, record.original]);
404
+ for (const message of scanModule(file, code, specifiers)) {
405
+ problems.push({ message, chain: importerChain(file, parents) });
406
+ }
407
+ }
408
+ return problems;
409
+ }
410
+ function formatProblems(problems, cwd) {
411
+ const lines = [`burger-build: ${problems.length} problem(s) \u2014 this code cannot run on Burger:`];
412
+ for (const problem of problems) {
413
+ lines.push(` \u2717 ${problem.message}`);
414
+ problem.chain.forEach((file, index) => {
415
+ const shown = isAbsolute(file) ? relative(cwd, file) : file;
416
+ lines.push(index === 0 ? ` ${shown}` : ` \u2192 ${shown}`);
417
+ });
418
+ }
419
+ return lines.join(`
420
+ `);
421
+ }
422
+
343
423
  // ../burger-build/src/plugin.ts
344
424
  var BRIDGE_NAMESPACE = "burger-require";
345
425
  var BRIDGE_PREFIX = "cjs-bridge/";
@@ -515,22 +595,36 @@ function relativeSpecifier(fromDir, to) {
515
595
  const rel = relative2(fromDir, to).split(sep).join("/");
516
596
  return rel.startsWith(".") ? rel : `./${rel}`;
517
597
  }
598
+ var JS_TO_TS_SUFFIXES = {
599
+ ".js": [".ts", ".tsx"],
600
+ ".jsx": [".tsx"],
601
+ ".mjs": [".mts"],
602
+ ".cjs": []
603
+ };
518
604
  function resolveRelativeFile(importerDir, specifier) {
519
605
  const base = resolve2(importerDir, specifier);
606
+ const isFile = (candidate) => existsSync(candidate) && statSync(candidate).isFile();
607
+ if (isFile(base))
608
+ return base;
609
+ const mapped = JS_TO_TS_SUFFIXES[extname2(specifier)];
610
+ if (mapped !== undefined) {
611
+ const stem = base.slice(0, base.length - extname2(specifier).length);
612
+ return mapped.map((suffix) => stem + suffix).find(isFile);
613
+ }
520
614
  const candidates = [
521
- base,
522
615
  ...RESOLVE_SUFFIXES.map((suffix) => base + suffix),
523
616
  ...RESOLVE_SUFFIXES.map((suffix) => join2(base, `index${suffix}`))
524
617
  ];
525
- return candidates.find((candidate) => existsSync(candidate) && statSync(candidate).isFile());
618
+ return candidates.find(isFile);
526
619
  }
527
620
  function dependencyFileName(specifier) {
528
621
  return specifier.replace(/^@/, "").replace(/[\\/]/g, "__") + ".js";
529
622
  }
623
+ var TEXT_NAMESPACE = "burger-text";
530
624
  function rewriteSpecifier(ws, importer, specifier) {
531
625
  const specifierClass = classifySpecifier(specifier);
532
626
  if (specifierClass.kind === "burger")
533
- return specifierClass.target;
627
+ return { external: specifierClass.target };
534
628
  if (specifierClass.kind === "forbidden") {
535
629
  ws.problems.push({ message: `import "${specifier}": ${specifierClass.reason}`, chain: [importer] });
536
630
  return;
@@ -547,7 +641,14 @@ function rewriteSpecifier(ws, importer, specifier) {
547
641
  return;
548
642
  }
549
643
  ws.jsonFiles.add(target);
550
- return `${relativeSpecifier(dirname(importer), target)}.js`;
644
+ return { external: `${relativeSpecifier(dirname(importer), target)}.js` };
645
+ }
646
+ if (!isCodeFile(target)) {
647
+ if (!isInside(target, ws.root)) {
648
+ ws.problems.push({ message: `"${specifier}" is outside ${ws.root}`, chain: [importer] });
649
+ return;
650
+ }
651
+ return { inline: target };
551
652
  }
552
653
  if (!ws.sources.has(target)) {
553
654
  ws.problems.push({
@@ -556,7 +657,7 @@ function rewriteSpecifier(ws, importer, specifier) {
556
657
  });
557
658
  return;
558
659
  }
559
- return preparedPath(relativeSpecifier(dirname(importer), target));
660
+ return { external: preparedPath(relativeSpecifier(dirname(importer), target)) };
560
661
  }
561
662
  if (specifier.startsWith("#")) {
562
663
  ws.problems.push({
@@ -566,7 +667,7 @@ function rewriteSpecifier(ws, importer, specifier) {
566
667
  return;
567
668
  }
568
669
  ws.dependencies.add(specifier);
569
- return specifier;
670
+ return { external: specifier };
570
671
  }
571
672
  function moduleIdentity(importer, specifier) {
572
673
  const specifierClass = classifySpecifier(specifier);
@@ -610,8 +711,17 @@ function sourcesPlugin(ws) {
610
711
  });
611
712
  return { path: args.path, external: true };
612
713
  }
613
- return { path: rewriteSpecifier(ws, args.importer, args.path) ?? args.path, external: true };
714
+ const resolved = rewriteSpecifier(ws, args.importer, args.path);
715
+ if (resolved === undefined)
716
+ return { path: args.path, external: true };
717
+ if ("inline" in resolved)
718
+ return { path: resolved.inline, namespace: TEXT_NAMESPACE, external: false };
719
+ return { path: resolved.external, external: true };
614
720
  });
721
+ build.onLoad({ filter: /.*/, namespace: TEXT_NAMESPACE }, (args) => ({
722
+ contents: readFileSync2(args.path, "utf8"),
723
+ loader: "text"
724
+ }));
615
725
  }
616
726
  };
617
727
  }
@@ -736,7 +846,8 @@ async function prepareTests(options) {
736
846
  const output = join2(out, preparedPath(relative2(root, file)));
737
847
  const prepared = readFileSync2(output, "utf8").replace(MOCK_MODULE_CALL, (call, quote, specifier) => {
738
848
  const rewritten = rewriteSpecifier(ws, file, specifier);
739
- return rewritten === undefined ? call : call.replace(`${quote}${specifier}${quote}`, JSON.stringify(rewritten));
849
+ const replacement = rewritten === undefined ? undefined : ("external" in rewritten) ? rewritten.external : undefined;
850
+ return replacement === undefined ? call : call.replace(`${quote}${specifier}${quote}`, JSON.stringify(replacement));
740
851
  });
741
852
  writeFileSync2(output, prepared);
742
853
  const source = readFileSync2(file, "utf8");
@@ -15,6 +15,8 @@ export interface Problem {
15
15
  /** Entry point first, offending module last; absolute paths. */
16
16
  chain: string[];
17
17
  }
18
+ /** Problems from importing a name a `burger:*` module does not export. */
19
+ export declare function scanBurgerImports(code: string): string[];
18
20
  /** Metafile keys are relative to the process cwd; namespaced keys (`ns:path`) are left as-is. */
19
21
  export declare function metafileKeyToPath(key: string, cwd: string): string;
20
22
  /** child → first importer, over resolved (non-external) imports. */
@@ -15,134 +15,6 @@ function assertSupportedBun(version = Bun.version) {
15
15
  // ../burger-build/src/gate.ts
16
16
  import { readFileSync } from "fs";
17
17
  import { extname, isAbsolute, relative, resolve } from "path";
18
- var FS_SPECIFIERS = new Set([
19
- "fs",
20
- "node:fs",
21
- "fs/promises",
22
- "node:fs/promises",
23
- "burger:fs",
24
- "burger:fs/promises"
25
- ]);
26
- var BUN_GLOBAL = /(?<![\w$.'"`])Bun\s*\.\s*[A-Za-z_$]|\bglobalThis\s*\.\s*Bun\b/;
27
- var IMPORT_META_PATH = /\bimport\.meta\.(?:url|dir|dirname|filename|path)\b/;
28
- var NATIVE_LOADER_PACKAGE = /[\\/]node_modules[\\/](?:bindings|node-gyp-build)[\\/]/;
29
- function metafileKeyToPath(key, cwd) {
30
- if (isAbsolute(key))
31
- return key;
32
- const colon = key.indexOf(":");
33
- const slash = key.indexOf("/");
34
- if (colon > 0 && (slash === -1 || colon < slash))
35
- return key;
36
- return resolve(cwd, key);
37
- }
38
- function importerGraph(metafile, cwd) {
39
- const parents = new Map;
40
- for (const [key, input] of Object.entries(metafile.inputs)) {
41
- const importer = metafileKeyToPath(key, cwd);
42
- for (const record of input.imports) {
43
- if (record.external)
44
- continue;
45
- const child = metafileKeyToPath(record.path, cwd);
46
- if (child !== importer && !parents.has(child)) {
47
- parents.set(child, importer);
48
- }
49
- }
50
- }
51
- return parents;
52
- }
53
- function importerChain(file, parents) {
54
- const chain = [file];
55
- const seen = new Set(chain);
56
- let current = parents.get(file);
57
- while (current !== undefined && !seen.has(current)) {
58
- chain.unshift(current);
59
- seen.add(current);
60
- current = parents.get(current);
61
- }
62
- return chain;
63
- }
64
- function loaderFor(file) {
65
- switch (extname(file)) {
66
- case ".ts":
67
- case ".mts":
68
- case ".cts":
69
- return "ts";
70
- case ".tsx":
71
- return "tsx";
72
- case ".jsx":
73
- return "jsx";
74
- case ".js":
75
- case ".mjs":
76
- case ".cjs":
77
- return "js";
78
- default:
79
- return;
80
- }
81
- }
82
- function codeOf(file, source) {
83
- const loader = loaderFor(file);
84
- if (!loader)
85
- return;
86
- try {
87
- return new Bun.Transpiler({ loader }).transformSync(source);
88
- } catch {
89
- return source;
90
- }
91
- }
92
- function scanModule(file, code, importedSpecifiers) {
93
- const messages = [];
94
- if (BUN_GLOBAL.test(code)) {
95
- messages.push("uses the Bun global (Bun.*), which does not exist in Burger");
96
- }
97
- if (IMPORT_META_PATH.test(code) && importedSpecifiers.some((specifier) => FS_SPECIFIERS.has(specifier))) {
98
- messages.push("reads files relative to import.meta; bundling moves this module, so the files are not found at runtime \u2014 import the data statically instead");
99
- }
100
- if (NATIVE_LOADER_PACKAGE.test(file)) {
101
- messages.push("loads a native addon (bindings/node-gyp-build), which Burger cannot run");
102
- }
103
- return messages;
104
- }
105
- function collectProblems(metafile, violations, cwd) {
106
- const parents = importerGraph(metafile, cwd);
107
- const problems = [];
108
- for (const violation of violations) {
109
- problems.push({
110
- message: `import "${violation.specifier}": ${violation.reason}`,
111
- chain: importerChain(violation.importer, parents)
112
- });
113
- }
114
- for (const [key, input] of Object.entries(metafile.inputs)) {
115
- const file = metafileKeyToPath(key, cwd);
116
- if (!isAbsolute(file))
117
- continue;
118
- let source;
119
- try {
120
- source = readFileSync(file, "utf8");
121
- } catch {
122
- continue;
123
- }
124
- const code = codeOf(file, source);
125
- if (code === undefined)
126
- continue;
127
- const specifiers = input.imports.flatMap((record) => record.original === undefined ? [record.path] : [record.path, record.original]);
128
- for (const message of scanModule(file, code, specifiers)) {
129
- problems.push({ message, chain: importerChain(file, parents) });
130
- }
131
- }
132
- return problems;
133
- }
134
- function formatProblems(problems, cwd) {
135
- const lines = [`burger-build: ${problems.length} problem(s) \u2014 this code cannot run on Burger:`];
136
- for (const problem of problems) {
137
- lines.push(` \u2717 ${problem.message}`);
138
- problem.chain.forEach((file, index) => {
139
- const shown = isAbsolute(file) ? relative(cwd, file) : file;
140
- lines.push(index === 0 ? ` ${shown}` : ` \u2192 ${shown}`);
141
- });
142
- }
143
- return lines.join(`
144
- `);
145
- }
146
18
 
147
19
  // ../burger-build/src/aliases.ts
148
20
  var BURGER_MODULES = [
@@ -260,7 +132,20 @@ var BURGER_EXPORTS = {
260
132
  "chmodSync",
261
133
  "unlinkSync",
262
134
  "mkdtempSync",
263
- "promises"
135
+ "promises",
136
+ "realpathSync",
137
+ "copyFileSync",
138
+ "symlinkSync",
139
+ "openSync",
140
+ "writeSync",
141
+ "fsyncSync",
142
+ "fdatasyncSync",
143
+ "closeSync",
144
+ "Dirent",
145
+ "constants",
146
+ "readSync",
147
+ "fstatSync",
148
+ "utimesSync"
264
149
  ],
265
150
  "burger:fs/promises": [
266
151
  "exists",
@@ -308,6 +193,16 @@ var BURGER_EXPORTS = {
308
193
  "setDefaultTimeout"
309
194
  ]
310
195
  };
196
+ var BURGER_DEFAULT_EXPORT = {
197
+ "burger:fs": true,
198
+ "burger:fs/promises": true,
199
+ "burger:path": true,
200
+ "burger:os": true,
201
+ "burger:worker_threads": true,
202
+ "burger:crypto": true,
203
+ "burger:buffer": true,
204
+ "burger:sqlite": true
205
+ };
311
206
  var BURGER_MODULE_SET = new Set(BURGER_MODULES);
312
207
  function classifySpecifier(specifier) {
313
208
  if (BURGER_MODULE_SET.has(specifier)) {
@@ -335,6 +230,191 @@ function classifySpecifier(specifier) {
335
230
  return { kind: "other" };
336
231
  }
337
232
 
233
+ // ../burger-build/src/gate.ts
234
+ var FS_SPECIFIERS = new Set([
235
+ "fs",
236
+ "node:fs",
237
+ "fs/promises",
238
+ "node:fs/promises",
239
+ "burger:fs",
240
+ "burger:fs/promises"
241
+ ]);
242
+ var BUN_GLOBAL = /(?<![\w$.'"`])Bun\s*\.\s*[A-Za-z_$]|\bglobalThis\s*\.\s*Bun\b/;
243
+ var IMPORT_META_PATH = /\bimport\.meta\.(?:url|dir|dirname|filename|path)\b/;
244
+ var IMPORT_OR_REEXPORT = /\b(?:import|export)\s+([^;]+?)\s+from\s*["']([^"']+)["']/g;
245
+ function parseNamedList(braced, into) {
246
+ for (const rawItem of braced.replace(/^\{|\}$/g, "").split(",")) {
247
+ const item = rawItem.trim();
248
+ if (item === "" || item.startsWith("type "))
249
+ continue;
250
+ const asMatch = /^([\w$]+|default)\s+as\s+[\w$]+$/.exec(item);
251
+ if (asMatch)
252
+ into.push(asMatch[1]);
253
+ else if (/^[\w$]+$/.test(item) || item === "default")
254
+ into.push(item);
255
+ }
256
+ }
257
+ function parseClause(raw) {
258
+ const clause = raw.trim();
259
+ const result = { named: [] };
260
+ if (clause.startsWith("type ") || clause.startsWith("*"))
261
+ return result;
262
+ if (clause.startsWith("{")) {
263
+ parseNamedList(clause, result.named);
264
+ return result;
265
+ }
266
+ const match = /^([\w$]+)\s*(?:,\s*([\s\S]+))?$/.exec(clause);
267
+ if (!match)
268
+ return result;
269
+ result.named.push("default");
270
+ const trailing = match[2]?.trim();
271
+ if (trailing?.startsWith("{"))
272
+ parseNamedList(trailing, result.named);
273
+ return result;
274
+ }
275
+ function missingExportMessage(target, name, specifier) {
276
+ return `"${target}" has no export "${name}" (imported as "${specifier}"); Burger provides: ${BURGER_EXPORTS[target].join(", ")}`;
277
+ }
278
+ function scanBurgerImports(code) {
279
+ const messages = [];
280
+ const reported = new Set;
281
+ for (const match of code.matchAll(IMPORT_OR_REEXPORT)) {
282
+ const specifier = match[2];
283
+ const specifierClass = classifySpecifier(specifier);
284
+ if (specifierClass.kind !== "burger")
285
+ continue;
286
+ const target = specifierClass.target;
287
+ for (const name of parseClause(match[1]).named) {
288
+ const provided = name === "default" ? target in BURGER_DEFAULT_EXPORT : BURGER_EXPORTS[target].includes(name);
289
+ const key = `${target}\x00${name}\x00${specifier}`;
290
+ if (provided || reported.has(key))
291
+ continue;
292
+ reported.add(key);
293
+ messages.push(missingExportMessage(target, name, specifier));
294
+ }
295
+ }
296
+ return messages;
297
+ }
298
+ var NATIVE_LOADER_PACKAGE = /[\\/]node_modules[\\/](?:bindings|node-gyp-build)[\\/]/;
299
+ function metafileKeyToPath(key, cwd) {
300
+ if (isAbsolute(key))
301
+ return key;
302
+ const colon = key.indexOf(":");
303
+ const slash = key.indexOf("/");
304
+ if (colon > 0 && (slash === -1 || colon < slash))
305
+ return key;
306
+ return resolve(cwd, key);
307
+ }
308
+ function importerGraph(metafile, cwd) {
309
+ const parents = new Map;
310
+ for (const [key, input] of Object.entries(metafile.inputs)) {
311
+ const importer = metafileKeyToPath(key, cwd);
312
+ for (const record of input.imports) {
313
+ if (record.external)
314
+ continue;
315
+ const child = metafileKeyToPath(record.path, cwd);
316
+ if (child !== importer && !parents.has(child)) {
317
+ parents.set(child, importer);
318
+ }
319
+ }
320
+ }
321
+ return parents;
322
+ }
323
+ function importerChain(file, parents) {
324
+ const chain = [file];
325
+ const seen = new Set(chain);
326
+ let current = parents.get(file);
327
+ while (current !== undefined && !seen.has(current)) {
328
+ chain.unshift(current);
329
+ seen.add(current);
330
+ current = parents.get(current);
331
+ }
332
+ return chain;
333
+ }
334
+ function loaderFor(file) {
335
+ switch (extname(file)) {
336
+ case ".ts":
337
+ case ".mts":
338
+ case ".cts":
339
+ return "ts";
340
+ case ".tsx":
341
+ return "tsx";
342
+ case ".jsx":
343
+ return "jsx";
344
+ case ".js":
345
+ case ".mjs":
346
+ case ".cjs":
347
+ return "js";
348
+ default:
349
+ return;
350
+ }
351
+ }
352
+ function codeOf(file, source) {
353
+ const loader = loaderFor(file);
354
+ if (!loader)
355
+ return;
356
+ try {
357
+ return new Bun.Transpiler({ loader }).transformSync(source);
358
+ } catch {
359
+ return source;
360
+ }
361
+ }
362
+ function scanModule(file, code, importedSpecifiers) {
363
+ const messages = [];
364
+ if (BUN_GLOBAL.test(code)) {
365
+ messages.push("uses the Bun global (Bun.*), which does not exist in Burger");
366
+ }
367
+ if (IMPORT_META_PATH.test(code) && importedSpecifiers.some((specifier) => FS_SPECIFIERS.has(specifier))) {
368
+ messages.push("reads files relative to import.meta; bundling moves this module, so the files are not found at runtime \u2014 import the data statically instead");
369
+ }
370
+ if (NATIVE_LOADER_PACKAGE.test(file)) {
371
+ messages.push("loads a native addon (bindings/node-gyp-build), which Burger cannot run");
372
+ }
373
+ messages.push(...scanBurgerImports(code));
374
+ return messages;
375
+ }
376
+ function collectProblems(metafile, violations, cwd) {
377
+ const parents = importerGraph(metafile, cwd);
378
+ const problems = [];
379
+ for (const violation of violations) {
380
+ problems.push({
381
+ message: `import "${violation.specifier}": ${violation.reason}`,
382
+ chain: importerChain(violation.importer, parents)
383
+ });
384
+ }
385
+ for (const [key, input] of Object.entries(metafile.inputs)) {
386
+ const file = metafileKeyToPath(key, cwd);
387
+ if (!isAbsolute(file))
388
+ continue;
389
+ let source;
390
+ try {
391
+ source = readFileSync(file, "utf8");
392
+ } catch {
393
+ continue;
394
+ }
395
+ const code = codeOf(file, source);
396
+ if (code === undefined)
397
+ continue;
398
+ const specifiers = input.imports.flatMap((record) => record.original === undefined ? [record.path] : [record.path, record.original]);
399
+ for (const message of scanModule(file, code, specifiers)) {
400
+ problems.push({ message, chain: importerChain(file, parents) });
401
+ }
402
+ }
403
+ return problems;
404
+ }
405
+ function formatProblems(problems, cwd) {
406
+ const lines = [`burger-build: ${problems.length} problem(s) \u2014 this code cannot run on Burger:`];
407
+ for (const problem of problems) {
408
+ lines.push(` \u2717 ${problem.message}`);
409
+ problem.chain.forEach((file, index) => {
410
+ const shown = isAbsolute(file) ? relative(cwd, file) : file;
411
+ lines.push(index === 0 ? ` ${shown}` : ` \u2192 ${shown}`);
412
+ });
413
+ }
414
+ return lines.join(`
415
+ `);
416
+ }
417
+
338
418
  // ../burger-build/src/plugin.ts
339
419
  var BRIDGE_NAMESPACE = "burger-require";
340
420
  var BRIDGE_PREFIX = "cjs-bridge/";
@@ -509,22 +589,36 @@ function relativeSpecifier(fromDir, to) {
509
589
  const rel = relative2(fromDir, to).split(sep).join("/");
510
590
  return rel.startsWith(".") ? rel : `./${rel}`;
511
591
  }
592
+ var JS_TO_TS_SUFFIXES = {
593
+ ".js": [".ts", ".tsx"],
594
+ ".jsx": [".tsx"],
595
+ ".mjs": [".mts"],
596
+ ".cjs": []
597
+ };
512
598
  function resolveRelativeFile(importerDir, specifier) {
513
599
  const base = resolve2(importerDir, specifier);
600
+ const isFile = (candidate) => existsSync(candidate) && statSync(candidate).isFile();
601
+ if (isFile(base))
602
+ return base;
603
+ const mapped = JS_TO_TS_SUFFIXES[extname2(specifier)];
604
+ if (mapped !== undefined) {
605
+ const stem = base.slice(0, base.length - extname2(specifier).length);
606
+ return mapped.map((suffix) => stem + suffix).find(isFile);
607
+ }
514
608
  const candidates = [
515
- base,
516
609
  ...RESOLVE_SUFFIXES.map((suffix) => base + suffix),
517
610
  ...RESOLVE_SUFFIXES.map((suffix) => join2(base, `index${suffix}`))
518
611
  ];
519
- return candidates.find((candidate) => existsSync(candidate) && statSync(candidate).isFile());
612
+ return candidates.find(isFile);
520
613
  }
521
614
  function dependencyFileName(specifier) {
522
615
  return specifier.replace(/^@/, "").replace(/[\\/]/g, "__") + ".js";
523
616
  }
617
+ var TEXT_NAMESPACE = "burger-text";
524
618
  function rewriteSpecifier(ws, importer, specifier) {
525
619
  const specifierClass = classifySpecifier(specifier);
526
620
  if (specifierClass.kind === "burger")
527
- return specifierClass.target;
621
+ return { external: specifierClass.target };
528
622
  if (specifierClass.kind === "forbidden") {
529
623
  ws.problems.push({ message: `import "${specifier}": ${specifierClass.reason}`, chain: [importer] });
530
624
  return;
@@ -541,7 +635,14 @@ function rewriteSpecifier(ws, importer, specifier) {
541
635
  return;
542
636
  }
543
637
  ws.jsonFiles.add(target);
544
- return `${relativeSpecifier(dirname(importer), target)}.js`;
638
+ return { external: `${relativeSpecifier(dirname(importer), target)}.js` };
639
+ }
640
+ if (!isCodeFile(target)) {
641
+ if (!isInside(target, ws.root)) {
642
+ ws.problems.push({ message: `"${specifier}" is outside ${ws.root}`, chain: [importer] });
643
+ return;
644
+ }
645
+ return { inline: target };
545
646
  }
546
647
  if (!ws.sources.has(target)) {
547
648
  ws.problems.push({
@@ -550,7 +651,7 @@ function rewriteSpecifier(ws, importer, specifier) {
550
651
  });
551
652
  return;
552
653
  }
553
- return preparedPath(relativeSpecifier(dirname(importer), target));
654
+ return { external: preparedPath(relativeSpecifier(dirname(importer), target)) };
554
655
  }
555
656
  if (specifier.startsWith("#")) {
556
657
  ws.problems.push({
@@ -560,7 +661,7 @@ function rewriteSpecifier(ws, importer, specifier) {
560
661
  return;
561
662
  }
562
663
  ws.dependencies.add(specifier);
563
- return specifier;
664
+ return { external: specifier };
564
665
  }
565
666
  function moduleIdentity(importer, specifier) {
566
667
  const specifierClass = classifySpecifier(specifier);
@@ -604,8 +705,17 @@ function sourcesPlugin(ws) {
604
705
  });
605
706
  return { path: args.path, external: true };
606
707
  }
607
- return { path: rewriteSpecifier(ws, args.importer, args.path) ?? args.path, external: true };
708
+ const resolved = rewriteSpecifier(ws, args.importer, args.path);
709
+ if (resolved === undefined)
710
+ return { path: args.path, external: true };
711
+ if ("inline" in resolved)
712
+ return { path: resolved.inline, namespace: TEXT_NAMESPACE, external: false };
713
+ return { path: resolved.external, external: true };
608
714
  });
715
+ build.onLoad({ filter: /.*/, namespace: TEXT_NAMESPACE }, (args) => ({
716
+ contents: readFileSync2(args.path, "utf8"),
717
+ loader: "text"
718
+ }));
609
719
  }
610
720
  };
611
721
  }
@@ -730,7 +840,8 @@ async function prepareTests(options) {
730
840
  const output = join2(out, preparedPath(relative2(root, file)));
731
841
  const prepared = readFileSync2(output, "utf8").replace(MOCK_MODULE_CALL, (call, quote, specifier) => {
732
842
  const rewritten = rewriteSpecifier(ws, file, specifier);
733
- return rewritten === undefined ? call : call.replace(`${quote}${specifier}${quote}`, JSON.stringify(rewritten));
843
+ const replacement = rewritten === undefined ? undefined : ("external" in rewritten) ? rewritten.external : undefined;
844
+ return replacement === undefined ? call : call.replace(`${quote}${specifier}${quote}`, JSON.stringify(replacement));
734
845
  });
735
846
  writeFileSync2(output, prepared);
736
847
  const source = readFileSync2(file, "utf8");
@@ -42,12 +42,19 @@ interface Workspace {
42
42
  dependencies: Set<string>;
43
43
  problems: Problem[];
44
44
  }
45
+ /** Namespace `sourcesPlugin` uses for a non-code, non-JSON file inlined as text (matching Bun's own `build`). */
46
+ export declare const TEXT_NAMESPACE = "burger-text";
47
+ export type ResolvedImport = {
48
+ external: string;
49
+ } | {
50
+ inline: string;
51
+ };
45
52
  /**
46
53
  * What a prepared module imports instead of `specifier` (written in
47
54
  * `importer`). Returns undefined — with a recorded problem — when the import
48
55
  * cannot work under Burger.
49
56
  */
50
- export declare function rewriteSpecifier(ws: Workspace, importer: string, specifier: string): string | undefined;
57
+ export declare function rewriteSpecifier(ws: Workspace, importer: string, specifier: string): ResolvedImport | undefined;
51
58
  /**
52
59
  * Problems for a module that statically imports a module it also replaces
53
60
  * with `mock.module`. Bun patches the already-linked bindings, so the test
@@ -64,3 +64,48 @@ interface BurgerProcess {
64
64
  memoryUsage(): BurgerProcessMemoryUsage;
65
65
  }
66
66
  declare var process: BurgerProcess;
67
+
68
+ /** A message delivered to a `Worker` or to a worker's own scope. Data is copied as JSON. */
69
+ interface BurgerWorkerMessageEvent {
70
+ readonly type: "message";
71
+ readonly data: unknown;
72
+ readonly target: unknown;
73
+ }
74
+
75
+ /** An error a worker's handler threw, reported to its parent. */
76
+ interface BurgerWorkerErrorEvent {
77
+ readonly type: "error";
78
+ readonly message: string;
79
+ readonly error: Error;
80
+ readonly target: unknown;
81
+ }
82
+
83
+ /**
84
+ * The Worker-equivalent (data-app pack, econ-v1/node-app-burger#12): the Web
85
+ * `Worker` as Bun provides it, limited to running work an app can cancel.
86
+ * `specifier` is a path, a `file:` URL string, or an object with a `file:`
87
+ * `href`.
88
+ */
89
+ declare class Worker {
90
+ constructor(specifier: string | { readonly href: string }, options?: { type?: "module" });
91
+ postMessage(message: unknown): void;
92
+ terminate(): void;
93
+ onmessage: ((event: BurgerWorkerMessageEvent) => void) | null;
94
+ onerror: ((event: BurgerWorkerErrorEvent) => void) | null;
95
+ addEventListener(type: "message", listener: (event: BurgerWorkerMessageEvent) => void): void;
96
+ addEventListener(type: "error", listener: (event: BurgerWorkerErrorEvent) => void): void;
97
+ removeEventListener(type: "message" | "error", listener: (...args: never[]) => void): void;
98
+ }
99
+
100
+ /**
101
+ * Inside a worker module (data-app pack): the worker's own scope, where
102
+ * `self` is `globalThis`. A handler that throws is reported to the parent's
103
+ * `onerror` and the worker keeps running.
104
+ */
105
+ declare function postMessage(message: unknown): void;
106
+ /** Ends this worker from inside. */
107
+ declare function close(): void;
108
+ declare var onmessage: ((event: BurgerWorkerMessageEvent) => void) | null;
109
+ declare function addEventListener(type: "message", listener: (event: BurgerWorkerMessageEvent) => void): void;
110
+ declare function removeEventListener(type: "message", listener: (event: BurgerWorkerMessageEvent) => void): void;
111
+ declare var self: typeof globalThis;
@@ -47,13 +47,40 @@ declare module "burger:fs" {
47
47
  readonly mtimeMs: number;
48
48
  }
49
49
 
50
+ /**
51
+ * A `readdirSync(path, { withFileTypes: true })` entry, Node 22 shape (data-app
52
+ * pack). The type is the entry's own, symlinks not followed.
53
+ */
54
+ export class Dirent {
55
+ private constructor();
56
+ readonly name: string;
57
+ /** The directory it was listed from, as the caller spelled it. */
58
+ readonly parentPath: string;
59
+ /** Same as `parentPath`; Node's older name for it, kept by the runtime for code that reads it. */
60
+ readonly path: string;
61
+ isFile(): boolean;
62
+ isDirectory(): boolean;
63
+ isSymbolicLink(): boolean;
64
+ isFIFO(): boolean;
65
+ isSocket(): boolean;
66
+ isBlockDevice(): boolean;
67
+ isCharacterDevice(): boolean;
68
+ }
69
+
70
+ export interface ReaddirOptions {
71
+ encoding?: Utf8Encoding;
72
+ /** Names (or Dirents) of every entry below, as paths relative to the listed directory. */
73
+ recursive?: boolean;
74
+ }
75
+
50
76
  export function existsSync(path: PathLike): boolean;
51
77
  export function readFileSync(path: PathLike): Uint8Array;
52
78
  export function readFileSync(path: PathLike, encoding: Utf8Encoding | { encoding: Utf8Encoding }): string;
53
79
  export function writeFileSync(path: PathLike, data: string | Uint8Array): void;
54
80
  export function appendFileSync(path: PathLike, data: string | Uint8Array): void;
55
81
  export function mkdirSync(path: PathLike, options?: { recursive?: boolean }): void;
56
- export function readdirSync(path: PathLike): string[];
82
+ export function readdirSync(path: PathLike, options?: Utf8Encoding | (ReaddirOptions & { withFileTypes?: false })): string[];
83
+ export function readdirSync(path: PathLike, options: ReaddirOptions & { withFileTypes: true }): Dirent[];
57
84
  export function statSync(path: PathLike): Stats;
58
85
  export function rmSync(path: PathLike, options?: { recursive?: boolean; force?: boolean }): void;
59
86
  export function renameSync(oldPath: PathLike, newPath: PathLike): void;
@@ -61,6 +88,39 @@ declare module "burger:fs" {
61
88
  export function unlinkSync(path: PathLike): void;
62
89
  /** Create a unique directory whose name starts with `prefix`; returns its path. */
63
90
  export function mkdtempSync(prefix: string): string;
91
+ // data-app pack (econ-v1/node-app-burger#12):
92
+ export interface RealpathSync {
93
+ (path: PathLike, options?: Utf8Encoding | { encoding: Utf8Encoding }): string;
94
+ /** The same resolver (Node's name for it). */
95
+ native(path: PathLike, options?: Utf8Encoding | { encoding: Utf8Encoding }): string;
96
+ }
97
+ export const realpathSync: RealpathSync;
98
+ /** `copyFileSync` modes and `access`-style mode bits. `COPYFILE_FICLONE` falls back to a plain copy. */
99
+ export const constants: {
100
+ readonly COPYFILE_EXCL: 1;
101
+ readonly COPYFILE_FICLONE: 2;
102
+ readonly COPYFILE_FICLONE_FORCE: 4;
103
+ readonly F_OK: 0;
104
+ readonly R_OK: 4;
105
+ readonly W_OK: 2;
106
+ readonly X_OK: 1;
107
+ };
108
+ export function copyFileSync(src: PathLike, dest: PathLike, mode?: number): void;
109
+ /** `type` ("file"/"dir"/"junction") matters only on Windows and is ignored. */
110
+ export function symlinkSync(target: PathLike, path: PathLike, type?: "file" | "dir" | "junction"): void;
111
+ export type OpenFlags = "r" | "r+" | "rs+" | "w" | "wx" | "w+" | "wx+" | "a" | "ax" | "a+" | "ax+";
112
+ export function openSync(path: PathLike, flags?: OpenFlags, mode?: number | string): number;
113
+ export function writeSync(fd: number, buffer: Uint8Array, offset?: number, length?: number, position?: number | null): number;
114
+ export function writeSync(fd: number, text: string, position?: number | null, encoding?: Utf8Encoding): number;
115
+ export function fsyncSync(fd: number): void;
116
+ export function fdatasyncSync(fd: number): void;
117
+ export function closeSync(fd: number): void;
118
+ // second data-app pack (econ-v1/node-app-memory#40):
119
+ /** Read into `buffer` at `offset`; `position` null reads from the current position. Returns bytes read. */
120
+ export function readSync(fd: number, buffer: Uint8Array, offset?: number, length?: number, position?: number | null): number;
121
+ export function fstatSync(fd: number): Stats;
122
+ /** Set access and modification times (seconds since the epoch, or Dates). */
123
+ export function utimesSync(path: PathLike, atime: number | Date, mtime: number | Date): void;
64
124
 
65
125
  export interface FsPromises {
66
126
  exists(path: PathLike): Promise<boolean>;
@@ -69,7 +129,8 @@ declare module "burger:fs" {
69
129
  writeFile(path: PathLike, data: string | Uint8Array): Promise<void>;
70
130
  appendFile(path: PathLike, data: string | Uint8Array): Promise<void>;
71
131
  mkdir(path: PathLike, options?: { recursive?: boolean }): Promise<void>;
72
- readdir(path: PathLike): Promise<string[]>;
132
+ readdir(path: PathLike, options?: Utf8Encoding | (ReaddirOptions & { withFileTypes?: false })): Promise<string[]>;
133
+ readdir(path: PathLike, options: ReaddirOptions & { withFileTypes: true }): Promise<Dirent[]>;
73
134
  stat(path: PathLike): Promise<Stats>;
74
135
  rm(path: PathLike, options?: { recursive?: boolean; force?: boolean }): Promise<void>;
75
136
  rename(oldPath: PathLike, newPath: PathLike): Promise<void>;
@@ -93,6 +154,19 @@ declare module "burger:fs" {
93
154
  chmodSync: typeof chmodSync;
94
155
  unlinkSync: typeof unlinkSync;
95
156
  mkdtempSync: typeof mkdtempSync;
157
+ realpathSync: typeof realpathSync;
158
+ constants: typeof constants;
159
+ copyFileSync: typeof copyFileSync;
160
+ symlinkSync: typeof symlinkSync;
161
+ openSync: typeof openSync;
162
+ writeSync: typeof writeSync;
163
+ fsyncSync: typeof fsyncSync;
164
+ fdatasyncSync: typeof fdatasyncSync;
165
+ closeSync: typeof closeSync;
166
+ readSync: typeof readSync;
167
+ fstatSync: typeof fstatSync;
168
+ utimesSync: typeof utimesSync;
169
+ Dirent: typeof Dirent;
96
170
  promises: FsPromises;
97
171
  };
98
172
  export default fs;
@@ -222,7 +296,7 @@ declare module "burger:crypto" {
222
296
  }
223
297
 
224
298
  declare module "burger:buffer" {
225
- export type BufferEncoding = "utf8" | "utf-8" | "hex" | "base64" | "base64url";
299
+ export type BufferEncoding = "utf8" | "utf-8" | "hex" | "base64" | "base64url" | "latin1" | "binary" | "ascii";
226
300
 
227
301
  /** C5 subset of Node's Buffer. Instances are `Uint8Array`s. */
228
302
  export interface Buffer extends Uint8Array<ArrayBuffer> {
@@ -265,21 +339,43 @@ declare module "burger:sqlite" {
265
339
  get(...params: SQLParams): Row | null;
266
340
  run(...params: SQLParams): Changes;
267
341
  values(...params: SQLParams): SQLValue[][];
342
+ /** data-app pack: rows one at a time; the deadline bounds the whole iteration. */
343
+ iterate(...params: SQLParams): IterableIterator<Row>;
344
+ [Symbol.iterator](): IterableIterator<Row>;
345
+ /** data-app pack. */
346
+ readonly columnNames: string[];
347
+ /**
348
+ * data-app pack (Burger extension): wall-clock budget in ms for each
349
+ * execution of this statement. Past it the call throws an Error whose
350
+ * `code` is "SQLITE_INTERRUPT"; unlike the runtime watchdog it is not a
351
+ * strike. `undefined` inherits the Database's `deadlineMs`; 0 or `null`
352
+ * means none.
353
+ */
354
+ deadlineMs: number | null | undefined;
268
355
  }
269
356
 
270
357
  export interface DatabaseOptions {
271
358
  create?: boolean;
272
359
  readonly?: boolean;
360
+ readwrite?: boolean;
361
+ /** data-app pack: the default `deadlineMs` of this database's statements. */
362
+ deadlineMs?: number;
273
363
  }
274
364
 
275
365
  export class Database {
276
366
  constructor(filename?: string, options?: DatabaseOptions);
367
+ /** data-app pack: the default statement deadline in ms (`undefined`: none). */
368
+ deadlineMs: number | null | undefined;
277
369
  query<Row = unknown>(sql: string): Statement<Row>;
278
370
  prepare<Row = unknown>(sql: string): Statement<Row>;
279
371
  exec(sql: string, ...params: SQLParams): Changes;
280
372
  run(sql: string, ...params: SQLParams): Changes;
281
373
  transaction<Args extends unknown[], Result>(fn: (...args: Args) => Result): (...args: Args) => Result;
282
374
  close(): void;
375
+ /** data-app pack: the database image (`sqlite3_serialize`) of schema `name` (default "main"). */
376
+ serialize(name?: string): Uint8Array;
377
+ /** second data-app pack: open an in-memory database from `serialize()` bytes. */
378
+ static deserialize(serialized: Uint8Array, isReadOnly?: boolean): Database;
283
379
  }
284
380
 
285
381
  /** Default export is the Database class itself, which also carries `.Database` (C6). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@econ-v1/app-sdk",
3
- "version": "7.4.0",
3
+ "version": "7.4.2",
4
4
  "description": "TypeScript SDK for building Node mini-app plugins (Bun runtime) on the Lightning-powered marketplace daemon",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "Node contributors",