@camstack/system 1.2.347 → 1.2.348

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.
Files changed (80) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/calendars/calendar-color.d.ts +5 -0
  14. package/dist/builtins/calendars/calendar-prefs-store.d.ts +13 -0
  15. package/dist/builtins/calendars/calendars.addon.d.ts +4 -1
  16. package/dist/builtins/calendars/calendars.addon.js +143 -23
  17. package/dist/builtins/calendars/calendars.addon.mjs +143 -23
  18. package/dist/builtins/camera-grid/index.js +1 -1
  19. package/dist/builtins/camera-grid/index.mjs +1 -1
  20. package/dist/builtins/composer/composer.addon.js +2 -2
  21. package/dist/builtins/composer/composer.addon.mjs +2 -2
  22. package/dist/builtins/console-logging/index.js +1 -1
  23. package/dist/builtins/console-logging/index.mjs +1 -1
  24. package/dist/builtins/core-blocks/block-package.d.ts +12 -1
  25. package/dist/builtins/core-blocks/block-settings-store.d.ts +25 -0
  26. package/dist/builtins/core-blocks/block-supervisor.d.ts +6 -1
  27. package/dist/builtins/core-blocks/compile-block.d.ts +33 -2
  28. package/dist/builtins/core-blocks/core-block-store.d.ts +5 -0
  29. package/dist/builtins/core-blocks/core-blocks.addon.d.ts +10 -1
  30. package/dist/builtins/core-blocks/core-blocks.addon.js +363 -20
  31. package/dist/builtins/core-blocks/core-blocks.addon.mjs +360 -21
  32. package/dist/builtins/core-blocks/extract-settings-schema.d.ts +6 -0
  33. package/dist/builtins/core-blocks/index.d.ts +3 -1
  34. package/dist/builtins/core-blocks/index.js +3 -0
  35. package/dist/builtins/core-blocks/index.mjs +2 -2
  36. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  37. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  38. package/dist/builtins/hub-forwarder/index.js +1 -1
  39. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  40. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  41. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  42. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  43. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  44. package/dist/builtins/local-network/local-network.addon.js +3 -3
  45. package/dist/builtins/local-network/local-network.addon.mjs +3 -3
  46. package/dist/builtins/loki-logging/index.js +1 -1
  47. package/dist/builtins/loki-logging/index.mjs +1 -1
  48. package/dist/builtins/native-metrics/native-metrics.addon.js +4 -4
  49. package/dist/builtins/native-metrics/native-metrics.addon.mjs +4 -4
  50. package/dist/builtins/platform-probe/index.js +3 -3
  51. package/dist/builtins/platform-probe/index.mjs +2 -2
  52. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  53. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  54. package/dist/builtins/snapshot/index.js +1 -1
  55. package/dist/builtins/snapshot/index.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +2 -2
  57. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +2 -2
  58. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  60. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  61. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  62. package/dist/builtins/system-config/system-config.addon.js +1 -1
  63. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  64. package/dist/builtins/winston-logging/index.js +1 -1
  65. package/dist/builtins/winston-logging/index.mjs +1 -1
  66. package/dist/{child-cap-dispatch-OLFN-F85.mjs → child-cap-dispatch-B2N_gWV-.mjs} +3 -3
  67. package/dist/{child-cap-dispatch-CVOa1BZI.js → child-cap-dispatch-k3dEOCnu.js} +3 -3
  68. package/dist/{composition-sources-CeZ3tyH8.js → composition-sources-C42s3_1h.js} +1 -1
  69. package/dist/{composition-sources-pQsmMdpR.mjs → composition-sources-D7cxA4Yv.mjs} +1 -1
  70. package/dist/{dist-DTgd2gOR.js → dist-BgV_QADz.js} +5608 -5248
  71. package/dist/{dist-BUFcHq7H.mjs → dist-nUn3vto_.mjs} +5586 -5250
  72. package/dist/index.js +7 -7
  73. package/dist/index.mjs +6 -6
  74. package/dist/{retired-settings-keys-Ddb5cCNR.js → retired-settings-keys-D980hwnw.js} +1 -1
  75. package/dist/{retired-settings-keys-ltKefX3N.mjs → retired-settings-keys-pKNWq-CL.mjs} +1 -1
  76. package/package.json +1 -1
  77. package/dist/{resource-monitor-B1v6E07R.mjs → resource-monitor-B2WZ8_q9.mjs} +1 -1
  78. package/dist/{resource-monitor-DzNn_IZK.js → resource-monitor-BgWGRxN5.js} +1 -1
  79. package/dist/{tls-DAtaKVUT.mjs → tls-DTlVWwAI.mjs} +1 -1
  80. package/dist/{tls-DEjOOARt.js → tls-ue_Xefmv.js} +1 -1
@@ -1,13 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DTgd2gOR.js");
4
- const require_composition_sources = require("../../composition-sources-CeZ3tyH8.js");
3
+ const require_dist = require("../../dist-BgV_QADz.js");
4
+ const require_composition_sources = require("../../composition-sources-C42s3_1h.js");
5
5
  let node_crypto = require("node:crypto");
6
6
  let _camstack_types_node = require("@camstack/types/node");
7
7
  let node_fs_promises = require("node:fs/promises");
8
8
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
9
9
  let node_path = require("node:path");
10
10
  node_path = require_chunk.__toESM(node_path);
11
+ let node_os = require("node:os");
12
+ node_os = require_chunk.__toESM(node_os);
13
+ let node_url = require("node:url");
11
14
  //#region src/builtins/core-blocks/block-package.ts
12
15
  /**
13
16
  * Turning a stored block into something the addon runner can fork.
@@ -133,11 +136,27 @@ function renderBlockManifest(blockId, blockName) {
133
136
  * rewrite: the author's code is the thing that changes, and a partial update
134
137
  * that left a stale `block.js` behind would run yesterday's code while the
135
138
  * editor showed today's.
139
+ *
140
+ * Layout:
141
+ * `src/<path>` — author TypeScript when `files` is provided
142
+ * `dist/block.js` — bundled / transpiled entry (what the wrapper imports)
143
+ * `dist/index.js` — generated BaseAddon wrapper
136
144
  */
137
145
  async function materializeBlock(input) {
138
146
  const addonDir = node_path.join(input.root, input.blockId);
139
147
  const distDir = node_path.join(addonDir, "dist");
148
+ const srcDir = node_path.join(addonDir, "src");
149
+ await node_fs_promises.rm(srcDir, {
150
+ recursive: true,
151
+ force: true
152
+ });
140
153
  await node_fs_promises.mkdir(distDir, { recursive: true });
154
+ if (input.files !== void 0) for (const [rel, content] of Object.entries(input.files)) {
155
+ const abs = node_path.resolve(srcDir, rel);
156
+ if (!abs.startsWith(srcDir + node_path.sep) && abs !== srcDir) throw new Error(`refusing path outside the block package: ${rel}`);
157
+ await node_fs_promises.mkdir(node_path.dirname(abs), { recursive: true });
158
+ await node_fs_promises.writeFile(abs, content, "utf-8");
159
+ }
141
160
  await Promise.all([
142
161
  node_fs_promises.writeFile(node_path.join(addonDir, "package.json"), renderBlockManifest(input.blockId, input.blockName), "utf-8"),
143
162
  node_fs_promises.writeFile(node_path.join(distDir, "block.js"), input.js, "utf-8"),
@@ -388,7 +407,12 @@ var BlockSupervisor = class {
388
407
  * — the author asked for this to run, so they are owed the reason it does
389
408
  * not. */
390
409
  async launch(block) {
391
- const compiled = await this.deps.compile(block.code);
410
+ const { files, entry } = require_dist.resolveCoreBlockFiles(block);
411
+ const compiled = await this.deps.compile({
412
+ code: block.code,
413
+ files,
414
+ entry
415
+ });
392
416
  if (!compiled.ok || compiled.js === void 0) {
393
417
  await this.report(block.id, "failed", `the block does not compile: ${compiled.error ?? "unknown"}`);
394
418
  return;
@@ -398,7 +422,8 @@ var BlockSupervisor = class {
398
422
  root: this.deps.root,
399
423
  blockId: block.id,
400
424
  blockName: block.name,
401
- js: compiled.js
425
+ js: compiled.js,
426
+ files
402
427
  });
403
428
  await this.deps.host.spawn({
404
429
  runnerId: pkg.addonId,
@@ -464,6 +489,73 @@ var BlockSupervisor = class {
464
489
  }
465
490
  };
466
491
  //#endregion
492
+ //#region src/builtins/core-blocks/block-settings-store.ts
493
+ var CORE_BLOCK_SETTINGS_COLLECTION = "core-blocks:settings";
494
+ var CORE_BLOCK_SETTINGS_COLUMNS = [{
495
+ name: "id",
496
+ type: "TEXT",
497
+ primaryKey: true,
498
+ notNull: true
499
+ }, {
500
+ name: "values",
501
+ type: "JSON",
502
+ notNull: true,
503
+ defaultValue: "{}"
504
+ }];
505
+ var BlockSettingsStore = class {
506
+ store;
507
+ constructor(deps) {
508
+ this.store = deps.store;
509
+ }
510
+ static async declare(store) {
511
+ await store.declareCollection.mutate({
512
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
513
+ columns: [...CORE_BLOCK_SETTINGS_COLUMNS]
514
+ });
515
+ }
516
+ async getValues(blockId) {
517
+ const row = await this.store.get.query({
518
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
519
+ key: blockId
520
+ });
521
+ if (row === null || typeof row !== "object") return {};
522
+ const values = row.values;
523
+ if (values !== null && typeof values === "object" && !Array.isArray(values)) return { ...values };
524
+ if (!("values" in row)) return { ...row };
525
+ return {};
526
+ }
527
+ async mergeValues(blockId, patch) {
528
+ const next = {
529
+ ...await this.getValues(blockId),
530
+ ...patch
531
+ };
532
+ await this.store.set.mutate({
533
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
534
+ key: blockId,
535
+ value: { values: next }
536
+ });
537
+ await this.mirrorToBlockAddon(blockId, next);
538
+ return next;
539
+ }
540
+ async delete(blockId) {
541
+ await this.store.delete.mutate({
542
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
543
+ key: blockId
544
+ });
545
+ }
546
+ async mirrorToBlockAddon(blockId, values) {
547
+ const namespace = require_dist.coreBlockAddonId(blockId);
548
+ try {
549
+ await this.store.set.mutate({
550
+ namespace,
551
+ collection: "addon-settings",
552
+ key: "root",
553
+ value: values
554
+ });
555
+ } catch {}
556
+ }
557
+ };
558
+ //#endregion
467
559
  //#region src/builtins/core-blocks/block-type-defs.ts
468
560
  /**
469
561
  * The declaration files a block is authored against.
@@ -537,6 +629,24 @@ async function walk(root, dir, out) {
537
629
  }
538
630
  //#endregion
539
631
  //#region src/builtins/core-blocks/compile-block.ts
632
+ /**
633
+ * Compiling a core block's TypeScript before it is stored or run.
634
+ *
635
+ * Two callers, one implementation on purpose: the editor asks as the author
636
+ * types, and the store asks before it writes. **The verdict has to be the same
637
+ * one**, or the editor green-lights code the server later refuses — the failure
638
+ * shape where a person is told everything is fine by the thing they are looking
639
+ * at.
640
+ *
641
+ * This is a TRANSPILE / BUNDLE, not a typecheck: esbuild strips types and
642
+ * reports syntax errors, which is what stops unrunnable source from reaching
643
+ * disk. Type errors are the editor's job (Monaco runs the real compiler against
644
+ * the real `.d.ts` graph); doing it again here would mean shipping tsc into the
645
+ * hub for an answer the author already has.
646
+ *
647
+ * Single-file blocks use `transform`. Multi-file projects write sources to a
648
+ * temp dir and `build` from `entry` so relative imports resolve.
649
+ */
540
650
  /** An esbuild error carries the position; the shape is narrow enough to read
541
651
  * defensively rather than to type against the package. */
542
652
  function positionOf(err) {
@@ -560,11 +670,18 @@ function messageOf(err) {
560
670
  }
561
671
  return err instanceof Error ? err.message : String(err);
562
672
  }
673
+ function isFilesInput(source) {
674
+ return typeof source === "object" && source !== null && "files" in source;
675
+ }
563
676
  /**
564
677
  * Never throws. A compile failure is an ANSWER — the author needs to read it,
565
678
  * and a thrown error at this layer becomes a 500 with the useful part buried.
566
679
  */
567
- async function compileBlock(transpiler, code) {
680
+ async function compileBlock(transpiler, source) {
681
+ if (isFilesInput(source)) return compileFiles(transpiler, source.files, source.entry);
682
+ return compileSingle(transpiler, source);
683
+ }
684
+ async function compileSingle(transpiler, code) {
568
685
  if (code.trim().length === 0) return {
569
686
  ok: false,
570
687
  error: "the block is empty — it must export a default function"
@@ -586,6 +703,79 @@ async function compileBlock(transpiler, code) {
586
703
  };
587
704
  }
588
705
  }
706
+ async function compileFiles(transpiler, files, entryPath) {
707
+ const entry = require_dist.resolveCoreBlockEntry(entryPath);
708
+ const entrySource = files[entry];
709
+ if (entrySource === void 0) return {
710
+ ok: false,
711
+ error: `files must include the entry "${entry}"`
712
+ };
713
+ if (entrySource.trim().length === 0 && Object.keys(files).length === 1) return {
714
+ ok: false,
715
+ error: "the block is empty — it must export a default function"
716
+ };
717
+ if (Object.keys(files).length === 1 && !/\bfrom\s+['"]\.\.?\//.test(entrySource)) return compileSingle(transpiler, entrySource);
718
+ if (typeof transpiler.build !== "function") return {
719
+ ok: false,
720
+ error: "the multi-file bundler is unavailable on this node"
721
+ };
722
+ const work = await node_fs_promises.mkdtemp(node_path.join(node_os.tmpdir(), "core-block-compile-"));
723
+ try {
724
+ for (const [rel, content] of Object.entries(files)) {
725
+ const abs = node_path.resolve(work, rel);
726
+ if (!abs.startsWith(work + node_path.sep) && abs !== work) return {
727
+ ok: false,
728
+ error: `refusing path outside the block package: ${rel}`
729
+ };
730
+ await node_fs_promises.mkdir(node_path.dirname(abs), { recursive: true });
731
+ await node_fs_promises.writeFile(abs, content, "utf-8");
732
+ }
733
+ const outfile = node_path.join(work, ".bundle", "block.js");
734
+ await node_fs_promises.mkdir(node_path.dirname(outfile), { recursive: true });
735
+ const result = await transpiler.build({
736
+ absWorkingDir: work,
737
+ entryPoints: [entry],
738
+ outfile,
739
+ bundle: true,
740
+ format: "esm",
741
+ platform: "node",
742
+ target: "node22",
743
+ write: true,
744
+ logLevel: "silent"
745
+ });
746
+ if (result.errors.length > 0) {
747
+ const first = result.errors[0];
748
+ return {
749
+ ok: false,
750
+ error: first.text,
751
+ ...first.location ? {
752
+ line: first.location.line,
753
+ column: first.location.column
754
+ } : {}
755
+ };
756
+ }
757
+ const js = await node_fs_promises.readFile(outfile, "utf-8");
758
+ if (js.trim().length === 0) return {
759
+ ok: false,
760
+ error: "the block is empty — it must export a default function"
761
+ };
762
+ return {
763
+ ok: true,
764
+ js
765
+ };
766
+ } catch (err) {
767
+ return {
768
+ ok: false,
769
+ error: messageOf(err),
770
+ ...positionOf(err)
771
+ };
772
+ } finally {
773
+ await node_fs_promises.rm(work, {
774
+ recursive: true,
775
+ force: true
776
+ }).catch(() => void 0);
777
+ }
778
+ }
589
779
  //#endregion
590
780
  //#region src/builtins/core-blocks/core-block-store.ts
591
781
  /**
@@ -781,8 +971,10 @@ var CoreBlockStore = class {
781
971
  async create(input, createdBy) {
782
972
  this.requireLoaded();
783
973
  const now = this.now();
974
+ const normalized = require_dist.normalizeCoreBlockWrite(input);
784
975
  const block = require_dist.CoreBlockSchema.parse({
785
976
  ...input,
977
+ ...normalized,
786
978
  id: this.newId(),
787
979
  createdBy,
788
980
  createdAt: now,
@@ -798,11 +990,23 @@ var CoreBlockStore = class {
798
990
  async update(blockId, patch) {
799
991
  const existing = this.byId.get(blockId);
800
992
  if (!existing) throw new Error(`core block not found: ${blockId}`);
801
- if ((patch.code !== void 0 && patch.code !== existing.code || patch.placement !== void 0 && patch.placement !== existing.placement || patch.integrationId !== void 0 && patch.integrationId !== existing.integrationId) && require_dist.coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — its code, placement and integration are fixed; edit it with updateComposition`);
993
+ const sourcePatch = this.normalizeSourcePatch(existing, patch);
994
+ const nextCode = sourcePatch?.code ?? existing.code;
995
+ const nextFiles = sourcePatch !== null ? sourcePatch.files : existing.files;
996
+ const nextEntry = sourcePatch !== null ? sourcePatch.entry : existing.entry;
997
+ const codeChanged = nextCode !== existing.code;
998
+ const filesChanged = JSON.stringify(nextFiles ?? null) !== JSON.stringify(existing.files ?? null);
999
+ const entryChanged = (nextEntry ?? void 0) !== (existing.entry ?? void 0);
1000
+ if ((codeChanged || filesChanged || entryChanged || patch.placement !== void 0 && patch.placement !== existing.placement || patch.integrationId !== void 0 && patch.integrationId !== existing.integrationId) && require_dist.coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — its code, placement and integration are fixed; edit it with updateComposition`);
802
1001
  if (patch.name !== void 0 && patch.name !== existing.name && require_dist.coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — rename it with updateComposition, which checks expectedUpdatedAt`);
803
1002
  const updated = require_dist.CoreBlockSchema.parse({
804
1003
  ...existing,
805
1004
  ...patch,
1005
+ ...sourcePatch !== null ? {
1006
+ code: nextCode,
1007
+ files: nextFiles,
1008
+ entry: nextEntry
1009
+ } : {},
806
1010
  id: existing.id,
807
1011
  createdBy: existing.createdBy,
808
1012
  createdAt: existing.createdAt,
@@ -907,6 +1111,32 @@ var CoreBlockStore = class {
907
1111
  }
908
1112
  });
909
1113
  }
1114
+ /**
1115
+ * Resolve a source-affecting patch into the normalized `{ code, files?, entry? }`
1116
+ * that will be stored. `null` when the patch does not touch source.
1117
+ */
1118
+ normalizeSourcePatch(existing, patch) {
1119
+ if (patch.files !== void 0) return require_dist.normalizeCoreBlockWrite({
1120
+ code: patch.code,
1121
+ files: patch.files,
1122
+ entry: patch.entry ?? existing.entry
1123
+ });
1124
+ if (patch.code === void 0 && patch.entry === void 0) return null;
1125
+ if (existing.files !== void 0) {
1126
+ const entry = require_dist.resolveCoreBlockEntry(patch.entry ?? existing.entry);
1127
+ return require_dist.normalizeCoreBlockWrite({
1128
+ files: {
1129
+ ...existing.files,
1130
+ [entry]: patch.code ?? existing.files[entry] ?? existing.code
1131
+ },
1132
+ entry
1133
+ });
1134
+ }
1135
+ return require_dist.normalizeCoreBlockWrite({
1136
+ code: patch.code ?? existing.code,
1137
+ entry: patch.entry
1138
+ });
1139
+ }
910
1140
  };
911
1141
  //#endregion
912
1142
  //#region src/builtins/core-blocks/blocks-integration.ts
@@ -1412,6 +1642,47 @@ function buildCompositionApi(deps) {
1412
1642
  };
1413
1643
  }
1414
1644
  //#endregion
1645
+ //#region src/builtins/core-blocks/extract-settings-schema.ts
1646
+ /**
1647
+ * Read an optional `settingsSchema` named export from a compiled block bundle.
1648
+ *
1649
+ * The author declares the form next to their default export:
1650
+ *
1651
+ * export const settingsSchema = { sections: [...] }
1652
+ * export default async function (ctx) { … }
1653
+ *
1654
+ * Until the first successful extract, `getBlockSettings` returns null. A
1655
+ * failed import is NOT a compile failure — the block can still run; the
1656
+ * settings UI simply stays absent.
1657
+ */
1658
+ function looksLikeSchema(value) {
1659
+ if (value === null || typeof value !== "object") return false;
1660
+ const sections = value.sections;
1661
+ return Array.isArray(sections);
1662
+ }
1663
+ /**
1664
+ * Import the bundled module and return its `settingsSchema` export, or null.
1665
+ * Never throws.
1666
+ */
1667
+ async function extractSettingsSchema(js) {
1668
+ const dir = await node_fs_promises.mkdtemp(node_path.join(node_os.tmpdir(), "core-block-schema-"));
1669
+ const file = node_path.join(dir, "block.mjs");
1670
+ try {
1671
+ await node_fs_promises.writeFile(file, js, "utf-8");
1672
+ const mod = await import((0, node_url.pathToFileURL)(file).href);
1673
+ if (mod === null || typeof mod !== "object") return null;
1674
+ const schema = mod.settingsSchema;
1675
+ return looksLikeSchema(schema) ? schema : null;
1676
+ } catch {
1677
+ return null;
1678
+ } finally {
1679
+ await node_fs_promises.rm(dir, {
1680
+ recursive: true,
1681
+ force: true
1682
+ }).catch(() => void 0);
1683
+ }
1684
+ }
1685
+ //#endregion
1415
1686
  //#region src/builtins/core-blocks/core-blocks.addon.ts
1416
1687
  /**
1417
1688
  * The `core-blocks` provider — CRUD over user-authored TypeScript, with the
@@ -1452,6 +1723,7 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1452
1723
  super({});
1453
1724
  }
1454
1725
  store = null;
1726
+ prefs = null;
1455
1727
  transpiler = null;
1456
1728
  typeDefs = null;
1457
1729
  supervisor = null;
@@ -1461,10 +1733,12 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1461
1733
  const settingsStore = this.ctx.api?.settingsStore;
1462
1734
  if (!settingsStore) throw new Error("core-blocks: settings-store API not available — refusing to register the cap");
1463
1735
  await CoreBlockStore.declare(settingsStore);
1736
+ await BlockSettingsStore.declare(settingsStore);
1464
1737
  this.store = new CoreBlockStore({
1465
1738
  store: settingsStore,
1466
1739
  logger: this.ctx.logger.child("store")
1467
1740
  });
1741
+ this.prefs = new BlockSettingsStore({ store: settingsStore });
1468
1742
  await this.store.load();
1469
1743
  this.transpiler = await loadTranspiler(this.ctx.logger);
1470
1744
  armBlocksIntegration({
@@ -1491,6 +1765,7 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1491
1765
  this.supervisor?.stop();
1492
1766
  this.supervisor = null;
1493
1767
  this.store = null;
1768
+ this.prefs = null;
1494
1769
  this.transpiler = null;
1495
1770
  this.typeDefs = null;
1496
1771
  }
@@ -1527,12 +1802,16 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1527
1802
  setStatus: (blockId, status, error) => store.setStatus(blockId, status, error)
1528
1803
  },
1529
1804
  host: createBrokerProcessHost(broker),
1530
- compile: async (code) => {
1805
+ compile: async (source) => {
1531
1806
  if (this.transpiler === null) return {
1532
1807
  ok: false,
1533
1808
  error: "the compiler is unavailable on this node"
1534
1809
  };
1535
- return compileBlock(this.transpiler, code);
1810
+ const input = source.files !== void 0 ? {
1811
+ files: { ...source.files },
1812
+ entry: source.entry
1813
+ } : source.code;
1814
+ return compileBlock(this.transpiler, input);
1536
1815
  },
1537
1816
  logger: this.ctx.logger.child("supervisor"),
1538
1817
  composer: { apply: async (input) => {
@@ -1594,27 +1873,51 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1594
1873
  return this.store;
1595
1874
  }
1596
1875
  /**
1597
- * Compile, or explain why not.
1876
+ * Compile, or explain why not. Returns the emitted JS so a successful gate
1877
+ * can also extract `settingsSchema` without compiling twice.
1598
1878
  *
1599
1879
  * A missing transpiler REFUSES the write rather than waving it through: code
1600
1880
  * that reached disk uncompiled is code the runner will fail on, far from the
1601
1881
  * person who wrote it.
1602
1882
  */
1603
- async gate(code) {
1883
+ async gate(input) {
1604
1884
  if (this.transpiler === null) throw new Error("core-blocks: the compiler is unavailable — refusing to store unchecked code");
1605
- const result = await compileBlock(this.transpiler, code);
1606
- if (!result.ok) {
1885
+ const normalized = require_dist.normalizeCoreBlockWrite(input);
1886
+ const source = normalized.files !== void 0 ? {
1887
+ files: normalized.files,
1888
+ entry: normalized.entry
1889
+ } : normalized.code;
1890
+ const result = await compileBlock(this.transpiler, source);
1891
+ if (!result.ok || result.js === void 0) {
1607
1892
  const where = result.line !== void 0 ? ` (line ${result.line})` : "";
1608
1893
  throw new Error(`core-blocks: the code does not compile${where}: ${result.error ?? "unknown"}`);
1609
1894
  }
1895
+ return result.js;
1896
+ }
1897
+ requirePrefs() {
1898
+ if (this.prefs === null) throw new Error("core-blocks: not initialized");
1899
+ return this.prefs;
1900
+ }
1901
+ /**
1902
+ * After a successful compile, cache the entry's `settingsSchema` export on
1903
+ * the block when the author did not set one by hand. Never overwrites an
1904
+ * explicit schema the editor already stored.
1905
+ */
1906
+ async maybeCacheSettingsSchema(blockId, js, existingSchema) {
1907
+ if (existingSchema !== void 0) return null;
1908
+ const schema = await extractSettingsSchema(js);
1909
+ if (schema === null) return null;
1910
+ return this.requireStore().update(blockId, { settingsSchema: schema });
1610
1911
  }
1611
1912
  buildProvider() {
1612
1913
  return {
1613
1914
  list: async () => ({ blocks: [...this.requireStore().list()] }),
1614
1915
  get: async ({ blockId }) => ({ block: this.requireStore().get(blockId) }),
1615
1916
  create: async ({ block, caller }) => {
1616
- await this.gate(block.code);
1617
- const created = await this.requireStore().create(block, caller.userId);
1917
+ const js = await this.gate(block);
1918
+ let created = await this.requireStore().create(block, caller.userId);
1919
+ const withSchema = await this.maybeCacheSettingsSchema(created.id, js, created.settingsSchema);
1920
+ if (withSchema !== null) created = withSchema;
1618
1921
  this.ctx.logger.info("core block created", { meta: {
1619
1922
  blockId: created.id,
1620
1923
  name: created.name,
@@ -1624,8 +1927,21 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1624
1927
  return { block: created };
1625
1928
  },
1626
1929
  update: async ({ blockId, block, caller }) => {
1627
- if (block.code !== void 0) await this.gate(block.code);
1628
- const updated = await this.requireStore().update(blockId, block);
1930
+ const sourceChanged = block.code !== void 0 || block.files !== void 0 || block.entry !== void 0;
1931
+ let js = null;
1932
+ if (sourceChanged) {
1933
+ const existing = this.requireStore().get(blockId);
1934
+ js = await this.gate({
1935
+ code: block.code ?? existing?.code,
1936
+ files: block.files ?? existing?.files,
1937
+ entry: block.entry ?? existing?.entry
1938
+ });
1939
+ }
1940
+ let updated = await this.requireStore().update(blockId, block);
1941
+ if (js !== null && block.settingsSchema === void 0) {
1942
+ const withSchema = await this.maybeCacheSettingsSchema(blockId, js, void 0);
1943
+ if (withSchema !== null) updated = withSchema;
1944
+ }
1629
1945
  this.ctx.logger.info("core block updated", { meta: {
1630
1946
  blockId,
1631
1947
  by: caller.userId
@@ -1635,6 +1951,7 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1635
1951
  },
1636
1952
  delete: async ({ blockId }) => {
1637
1953
  await this.requireStore().delete(blockId);
1954
+ await this.requirePrefs().delete(blockId);
1638
1955
  this.convergeSoon();
1639
1956
  return { success: true };
1640
1957
  },
@@ -1664,14 +1981,30 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
1664
1981
  await this.supervisor.restart(blockId);
1665
1982
  return { block: store.get(blockId) ?? block };
1666
1983
  },
1667
- compile: async ({ code }) => {
1984
+ compile: async (input) => {
1668
1985
  if (this.transpiler === null) return {
1669
1986
  ok: false,
1670
1987
  error: "the compiler is unavailable on this node"
1671
1988
  };
1672
- const { js: _js, ...verdict } = await compileBlock(this.transpiler, code);
1989
+ const source = input.files !== void 0 ? {
1990
+ files: input.files,
1991
+ entry: input.entry
1992
+ } : input.code ?? "";
1993
+ const { js: _js, ...verdict } = await compileBlock(this.transpiler, source);
1673
1994
  return verdict;
1674
1995
  },
1996
+ getBlockSettings: async ({ blockId }) => {
1997
+ const block = this.requireStore().get(blockId);
1998
+ if (block === null) return null;
1999
+ const schema = block.settingsSchema;
2000
+ if (schema === void 0) return null;
2001
+ return require_dist.hydrateSchema(schema, await this.requirePrefs().getValues(blockId));
2002
+ },
2003
+ updateBlockSettings: async ({ blockId, patch }) => {
2004
+ if (this.requireStore().get(blockId) === null) throw new Error(`core-blocks: no block "${blockId}"`);
2005
+ await this.requirePrefs().mergeValues(blockId, patch);
2006
+ return { success: true };
2007
+ },
1675
2008
  getTypeDefs: async () => {
1676
2009
  this.typeDefs ??= await loadBlockTypeDefs(this.ctx.logger);
1677
2010
  return { libs: this.typeDefs.map((l) => ({
@@ -1758,9 +2091,16 @@ function createBrokerProcessHost(broker) {
1758
2091
  */
1759
2092
  async function loadTranspiler(logger) {
1760
2093
  try {
1761
- const transform = (await import("esbuild")).transform;
2094
+ const mod = await import("esbuild");
2095
+ const transform = mod.transform;
2096
+ const build = mod.build;
1762
2097
  if (typeof transform !== "function") return null;
1763
- const transpiler = { transform };
2098
+ const transpiler = {
2099
+ transform,
2100
+ ...typeof build === "function" ? { build: async (options) => {
2101
+ return { errors: (await build(options)).errors };
2102
+ } } : {}
2103
+ };
1764
2104
  await transpiler.transform("export default 0", {
1765
2105
  loader: "ts",
1766
2106
  format: "esm",
@@ -1774,13 +2114,16 @@ async function loadTranspiler(logger) {
1774
2114
  }
1775
2115
  //#endregion
1776
2116
  exports.BLOCK_ADDON_PREFIX = BLOCK_ADDON_PREFIX;
2117
+ exports.BlockSettingsStore = BlockSettingsStore;
1777
2118
  exports.BlockSupervisor = BlockSupervisor;
1778
2119
  exports.CORE_BLOCKS_COLLECTION = CORE_BLOCKS_COLLECTION;
2120
+ exports.CORE_BLOCK_SETTINGS_COLLECTION = CORE_BLOCK_SETTINGS_COLLECTION;
1779
2121
  exports.CoreBlockStore = CoreBlockStore;
1780
2122
  exports.CoreBlocksAddon = CoreBlocksAddon;
1781
2123
  exports.blockAddonId = blockAddonId;
1782
2124
  exports.blockIdFromAddonId = blockIdFromAddonId;
1783
2125
  exports.compileBlock = compileBlock;
2126
+ exports.extractSettingsSchema = extractSettingsSchema;
1784
2127
  exports.loadBlockTypeDefs = loadBlockTypeDefs;
1785
2128
  exports.materializeBlock = materializeBlock;
1786
2129
  exports.removeBlockPackage = removeBlockPackage;