@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,9 +1,11 @@
1
- import { Gn as BaseAddon, Ln as compositionSourceKey, Mn as composedDeviceRef, Wn as errMsg, Y as buildCapabilityLookup, at as coreBlockIdFromAddonId, d as CORE_BLOCK_ADDON_PREFIX, f as CoreBlockSchema, ht as describeComposableCaps, it as coreBlockComposition, l as COMPOSER_RUNNER_ID, nt as compositionSourceRefs, on as replacedFieldKeys, ot as coreBlockSourceKind, rt as coreBlockAddonId, st as coreBlocksCapability, t as ALL_CAPABILITY_DEFINITIONS, tn as planComposition, u as CORE_BLOCKS_ADDON_ID, v as DeclaredDevices, wn as validateComposition } from "../../dist-BUFcHq7H.mjs";
2
- import { t as describeCompositionSources } from "../../composition-sources-pQsmMdpR.mjs";
1
+ import { In as composedDeviceRef, Jn as errMsg, On as validateComposition, Qt as normalizeCoreBlockWrite, Vn as compositionSourceKey, X as buildCapabilityLookup, Yn as BaseAddon, at as coreBlockComposition, cn as replacedFieldKeys, ct as coreBlocksCapability, d as CORE_BLOCK_ADDON_PREFIX, dn as resolveCoreBlockFiles, gt as describeComposableCaps, it as coreBlockAddonId, l as COMPOSER_RUNNER_ID, lr as hydrateSchema, ot as coreBlockIdFromAddonId, p as CoreBlockSchema, rn as planComposition, rt as compositionSourceRefs, st as coreBlockSourceKind, t as ALL_CAPABILITY_DEFINITIONS, u as CORE_BLOCKS_ADDON_ID, un as resolveCoreBlockEntry, y as DeclaredDevices } from "../../dist-nUn3vto_.mjs";
2
+ import { t as describeCompositionSources } from "../../composition-sources-D7cxA4Yv.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { customizationBlockName } from "@camstack/types/node";
5
5
  import * as fs from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
7
+ import * as os from "node:os";
8
+ import { pathToFileURL } from "node:url";
7
9
  //#region src/builtins/core-blocks/block-package.ts
8
10
  /**
9
11
  * Turning a stored block into something the addon runner can fork.
@@ -129,11 +131,27 @@ function renderBlockManifest(blockId, blockName) {
129
131
  * rewrite: the author's code is the thing that changes, and a partial update
130
132
  * that left a stale `block.js` behind would run yesterday's code while the
131
133
  * editor showed today's.
134
+ *
135
+ * Layout:
136
+ * `src/<path>` — author TypeScript when `files` is provided
137
+ * `dist/block.js` — bundled / transpiled entry (what the wrapper imports)
138
+ * `dist/index.js` — generated BaseAddon wrapper
132
139
  */
133
140
  async function materializeBlock(input) {
134
141
  const addonDir = path$1.join(input.root, input.blockId);
135
142
  const distDir = path$1.join(addonDir, "dist");
143
+ const srcDir = path$1.join(addonDir, "src");
144
+ await fs.rm(srcDir, {
145
+ recursive: true,
146
+ force: true
147
+ });
136
148
  await fs.mkdir(distDir, { recursive: true });
149
+ if (input.files !== void 0) for (const [rel, content] of Object.entries(input.files)) {
150
+ const abs = path$1.resolve(srcDir, rel);
151
+ if (!abs.startsWith(srcDir + path$1.sep) && abs !== srcDir) throw new Error(`refusing path outside the block package: ${rel}`);
152
+ await fs.mkdir(path$1.dirname(abs), { recursive: true });
153
+ await fs.writeFile(abs, content, "utf-8");
154
+ }
137
155
  await Promise.all([
138
156
  fs.writeFile(path$1.join(addonDir, "package.json"), renderBlockManifest(input.blockId, input.blockName), "utf-8"),
139
157
  fs.writeFile(path$1.join(distDir, "block.js"), input.js, "utf-8"),
@@ -384,7 +402,12 @@ var BlockSupervisor = class {
384
402
  * — the author asked for this to run, so they are owed the reason it does
385
403
  * not. */
386
404
  async launch(block) {
387
- const compiled = await this.deps.compile(block.code);
405
+ const { files, entry } = resolveCoreBlockFiles(block);
406
+ const compiled = await this.deps.compile({
407
+ code: block.code,
408
+ files,
409
+ entry
410
+ });
388
411
  if (!compiled.ok || compiled.js === void 0) {
389
412
  await this.report(block.id, "failed", `the block does not compile: ${compiled.error ?? "unknown"}`);
390
413
  return;
@@ -394,7 +417,8 @@ var BlockSupervisor = class {
394
417
  root: this.deps.root,
395
418
  blockId: block.id,
396
419
  blockName: block.name,
397
- js: compiled.js
420
+ js: compiled.js,
421
+ files
398
422
  });
399
423
  await this.deps.host.spawn({
400
424
  runnerId: pkg.addonId,
@@ -460,6 +484,73 @@ var BlockSupervisor = class {
460
484
  }
461
485
  };
462
486
  //#endregion
487
+ //#region src/builtins/core-blocks/block-settings-store.ts
488
+ var CORE_BLOCK_SETTINGS_COLLECTION = "core-blocks:settings";
489
+ var CORE_BLOCK_SETTINGS_COLUMNS = [{
490
+ name: "id",
491
+ type: "TEXT",
492
+ primaryKey: true,
493
+ notNull: true
494
+ }, {
495
+ name: "values",
496
+ type: "JSON",
497
+ notNull: true,
498
+ defaultValue: "{}"
499
+ }];
500
+ var BlockSettingsStore = class {
501
+ store;
502
+ constructor(deps) {
503
+ this.store = deps.store;
504
+ }
505
+ static async declare(store) {
506
+ await store.declareCollection.mutate({
507
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
508
+ columns: [...CORE_BLOCK_SETTINGS_COLUMNS]
509
+ });
510
+ }
511
+ async getValues(blockId) {
512
+ const row = await this.store.get.query({
513
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
514
+ key: blockId
515
+ });
516
+ if (row === null || typeof row !== "object") return {};
517
+ const values = row.values;
518
+ if (values !== null && typeof values === "object" && !Array.isArray(values)) return { ...values };
519
+ if (!("values" in row)) return { ...row };
520
+ return {};
521
+ }
522
+ async mergeValues(blockId, patch) {
523
+ const next = {
524
+ ...await this.getValues(blockId),
525
+ ...patch
526
+ };
527
+ await this.store.set.mutate({
528
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
529
+ key: blockId,
530
+ value: { values: next }
531
+ });
532
+ await this.mirrorToBlockAddon(blockId, next);
533
+ return next;
534
+ }
535
+ async delete(blockId) {
536
+ await this.store.delete.mutate({
537
+ collection: CORE_BLOCK_SETTINGS_COLLECTION,
538
+ key: blockId
539
+ });
540
+ }
541
+ async mirrorToBlockAddon(blockId, values) {
542
+ const namespace = coreBlockAddonId(blockId);
543
+ try {
544
+ await this.store.set.mutate({
545
+ namespace,
546
+ collection: "addon-settings",
547
+ key: "root",
548
+ value: values
549
+ });
550
+ } catch {}
551
+ }
552
+ };
553
+ //#endregion
463
554
  //#region src/builtins/core-blocks/block-type-defs.ts
464
555
  /**
465
556
  * The declaration files a block is authored against.
@@ -533,6 +624,24 @@ async function walk(root, dir, out) {
533
624
  }
534
625
  //#endregion
535
626
  //#region src/builtins/core-blocks/compile-block.ts
627
+ /**
628
+ * Compiling a core block's TypeScript before it is stored or run.
629
+ *
630
+ * Two callers, one implementation on purpose: the editor asks as the author
631
+ * types, and the store asks before it writes. **The verdict has to be the same
632
+ * one**, or the editor green-lights code the server later refuses — the failure
633
+ * shape where a person is told everything is fine by the thing they are looking
634
+ * at.
635
+ *
636
+ * This is a TRANSPILE / BUNDLE, not a typecheck: esbuild strips types and
637
+ * reports syntax errors, which is what stops unrunnable source from reaching
638
+ * disk. Type errors are the editor's job (Monaco runs the real compiler against
639
+ * the real `.d.ts` graph); doing it again here would mean shipping tsc into the
640
+ * hub for an answer the author already has.
641
+ *
642
+ * Single-file blocks use `transform`. Multi-file projects write sources to a
643
+ * temp dir and `build` from `entry` so relative imports resolve.
644
+ */
536
645
  /** An esbuild error carries the position; the shape is narrow enough to read
537
646
  * defensively rather than to type against the package. */
538
647
  function positionOf(err) {
@@ -556,11 +665,18 @@ function messageOf(err) {
556
665
  }
557
666
  return err instanceof Error ? err.message : String(err);
558
667
  }
668
+ function isFilesInput(source) {
669
+ return typeof source === "object" && source !== null && "files" in source;
670
+ }
559
671
  /**
560
672
  * Never throws. A compile failure is an ANSWER — the author needs to read it,
561
673
  * and a thrown error at this layer becomes a 500 with the useful part buried.
562
674
  */
563
- async function compileBlock(transpiler, code) {
675
+ async function compileBlock(transpiler, source) {
676
+ if (isFilesInput(source)) return compileFiles(transpiler, source.files, source.entry);
677
+ return compileSingle(transpiler, source);
678
+ }
679
+ async function compileSingle(transpiler, code) {
564
680
  if (code.trim().length === 0) return {
565
681
  ok: false,
566
682
  error: "the block is empty — it must export a default function"
@@ -582,6 +698,79 @@ async function compileBlock(transpiler, code) {
582
698
  };
583
699
  }
584
700
  }
701
+ async function compileFiles(transpiler, files, entryPath) {
702
+ const entry = resolveCoreBlockEntry(entryPath);
703
+ const entrySource = files[entry];
704
+ if (entrySource === void 0) return {
705
+ ok: false,
706
+ error: `files must include the entry "${entry}"`
707
+ };
708
+ if (entrySource.trim().length === 0 && Object.keys(files).length === 1) return {
709
+ ok: false,
710
+ error: "the block is empty — it must export a default function"
711
+ };
712
+ if (Object.keys(files).length === 1 && !/\bfrom\s+['"]\.\.?\//.test(entrySource)) return compileSingle(transpiler, entrySource);
713
+ if (typeof transpiler.build !== "function") return {
714
+ ok: false,
715
+ error: "the multi-file bundler is unavailable on this node"
716
+ };
717
+ const work = await fs.mkdtemp(path$1.join(os.tmpdir(), "core-block-compile-"));
718
+ try {
719
+ for (const [rel, content] of Object.entries(files)) {
720
+ const abs = path$1.resolve(work, rel);
721
+ if (!abs.startsWith(work + path$1.sep) && abs !== work) return {
722
+ ok: false,
723
+ error: `refusing path outside the block package: ${rel}`
724
+ };
725
+ await fs.mkdir(path$1.dirname(abs), { recursive: true });
726
+ await fs.writeFile(abs, content, "utf-8");
727
+ }
728
+ const outfile = path$1.join(work, ".bundle", "block.js");
729
+ await fs.mkdir(path$1.dirname(outfile), { recursive: true });
730
+ const result = await transpiler.build({
731
+ absWorkingDir: work,
732
+ entryPoints: [entry],
733
+ outfile,
734
+ bundle: true,
735
+ format: "esm",
736
+ platform: "node",
737
+ target: "node22",
738
+ write: true,
739
+ logLevel: "silent"
740
+ });
741
+ if (result.errors.length > 0) {
742
+ const first = result.errors[0];
743
+ return {
744
+ ok: false,
745
+ error: first.text,
746
+ ...first.location ? {
747
+ line: first.location.line,
748
+ column: first.location.column
749
+ } : {}
750
+ };
751
+ }
752
+ const js = await fs.readFile(outfile, "utf-8");
753
+ if (js.trim().length === 0) return {
754
+ ok: false,
755
+ error: "the block is empty — it must export a default function"
756
+ };
757
+ return {
758
+ ok: true,
759
+ js
760
+ };
761
+ } catch (err) {
762
+ return {
763
+ ok: false,
764
+ error: messageOf(err),
765
+ ...positionOf(err)
766
+ };
767
+ } finally {
768
+ await fs.rm(work, {
769
+ recursive: true,
770
+ force: true
771
+ }).catch(() => void 0);
772
+ }
773
+ }
585
774
  //#endregion
586
775
  //#region src/builtins/core-blocks/core-block-store.ts
587
776
  /**
@@ -777,8 +966,10 @@ var CoreBlockStore = class {
777
966
  async create(input, createdBy) {
778
967
  this.requireLoaded();
779
968
  const now = this.now();
969
+ const normalized = normalizeCoreBlockWrite(input);
780
970
  const block = CoreBlockSchema.parse({
781
971
  ...input,
972
+ ...normalized,
782
973
  id: this.newId(),
783
974
  createdBy,
784
975
  createdAt: now,
@@ -794,11 +985,23 @@ var CoreBlockStore = class {
794
985
  async update(blockId, patch) {
795
986
  const existing = this.byId.get(blockId);
796
987
  if (!existing) throw new Error(`core block not found: ${blockId}`);
797
- 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) && coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — its code, placement and integration are fixed; edit it with updateComposition`);
988
+ const sourcePatch = this.normalizeSourcePatch(existing, patch);
989
+ const nextCode = sourcePatch?.code ?? existing.code;
990
+ const nextFiles = sourcePatch !== null ? sourcePatch.files : existing.files;
991
+ const nextEntry = sourcePatch !== null ? sourcePatch.entry : existing.entry;
992
+ const codeChanged = nextCode !== existing.code;
993
+ const filesChanged = JSON.stringify(nextFiles ?? null) !== JSON.stringify(existing.files ?? null);
994
+ const entryChanged = (nextEntry ?? void 0) !== (existing.entry ?? void 0);
995
+ if ((codeChanged || filesChanged || entryChanged || patch.placement !== void 0 && patch.placement !== existing.placement || patch.integrationId !== void 0 && patch.integrationId !== existing.integrationId) && coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — its code, placement and integration are fixed; edit it with updateComposition`);
798
996
  if (patch.name !== void 0 && patch.name !== existing.name && coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — rename it with updateComposition, which checks expectedUpdatedAt`);
799
997
  const updated = CoreBlockSchema.parse({
800
998
  ...existing,
801
999
  ...patch,
1000
+ ...sourcePatch !== null ? {
1001
+ code: nextCode,
1002
+ files: nextFiles,
1003
+ entry: nextEntry
1004
+ } : {},
802
1005
  id: existing.id,
803
1006
  createdBy: existing.createdBy,
804
1007
  createdAt: existing.createdAt,
@@ -903,6 +1106,32 @@ var CoreBlockStore = class {
903
1106
  }
904
1107
  });
905
1108
  }
1109
+ /**
1110
+ * Resolve a source-affecting patch into the normalized `{ code, files?, entry? }`
1111
+ * that will be stored. `null` when the patch does not touch source.
1112
+ */
1113
+ normalizeSourcePatch(existing, patch) {
1114
+ if (patch.files !== void 0) return normalizeCoreBlockWrite({
1115
+ code: patch.code,
1116
+ files: patch.files,
1117
+ entry: patch.entry ?? existing.entry
1118
+ });
1119
+ if (patch.code === void 0 && patch.entry === void 0) return null;
1120
+ if (existing.files !== void 0) {
1121
+ const entry = resolveCoreBlockEntry(patch.entry ?? existing.entry);
1122
+ return normalizeCoreBlockWrite({
1123
+ files: {
1124
+ ...existing.files,
1125
+ [entry]: patch.code ?? existing.files[entry] ?? existing.code
1126
+ },
1127
+ entry
1128
+ });
1129
+ }
1130
+ return normalizeCoreBlockWrite({
1131
+ code: patch.code ?? existing.code,
1132
+ entry: patch.entry
1133
+ });
1134
+ }
906
1135
  };
907
1136
  //#endregion
908
1137
  //#region src/builtins/core-blocks/blocks-integration.ts
@@ -1408,6 +1637,47 @@ function buildCompositionApi(deps) {
1408
1637
  };
1409
1638
  }
1410
1639
  //#endregion
1640
+ //#region src/builtins/core-blocks/extract-settings-schema.ts
1641
+ /**
1642
+ * Read an optional `settingsSchema` named export from a compiled block bundle.
1643
+ *
1644
+ * The author declares the form next to their default export:
1645
+ *
1646
+ * export const settingsSchema = { sections: [...] }
1647
+ * export default async function (ctx) { … }
1648
+ *
1649
+ * Until the first successful extract, `getBlockSettings` returns null. A
1650
+ * failed import is NOT a compile failure — the block can still run; the
1651
+ * settings UI simply stays absent.
1652
+ */
1653
+ function looksLikeSchema(value) {
1654
+ if (value === null || typeof value !== "object") return false;
1655
+ const sections = value.sections;
1656
+ return Array.isArray(sections);
1657
+ }
1658
+ /**
1659
+ * Import the bundled module and return its `settingsSchema` export, or null.
1660
+ * Never throws.
1661
+ */
1662
+ async function extractSettingsSchema(js) {
1663
+ const dir = await fs.mkdtemp(path$1.join(os.tmpdir(), "core-block-schema-"));
1664
+ const file = path$1.join(dir, "block.mjs");
1665
+ try {
1666
+ await fs.writeFile(file, js, "utf-8");
1667
+ const mod = await import(pathToFileURL(file).href);
1668
+ if (mod === null || typeof mod !== "object") return null;
1669
+ const schema = mod.settingsSchema;
1670
+ return looksLikeSchema(schema) ? schema : null;
1671
+ } catch {
1672
+ return null;
1673
+ } finally {
1674
+ await fs.rm(dir, {
1675
+ recursive: true,
1676
+ force: true
1677
+ }).catch(() => void 0);
1678
+ }
1679
+ }
1680
+ //#endregion
1411
1681
  //#region src/builtins/core-blocks/core-blocks.addon.ts
1412
1682
  /**
1413
1683
  * The `core-blocks` provider — CRUD over user-authored TypeScript, with the
@@ -1448,6 +1718,7 @@ var CoreBlocksAddon = class extends BaseAddon {
1448
1718
  super({});
1449
1719
  }
1450
1720
  store = null;
1721
+ prefs = null;
1451
1722
  transpiler = null;
1452
1723
  typeDefs = null;
1453
1724
  supervisor = null;
@@ -1457,10 +1728,12 @@ var CoreBlocksAddon = class extends BaseAddon {
1457
1728
  const settingsStore = this.ctx.api?.settingsStore;
1458
1729
  if (!settingsStore) throw new Error("core-blocks: settings-store API not available — refusing to register the cap");
1459
1730
  await CoreBlockStore.declare(settingsStore);
1731
+ await BlockSettingsStore.declare(settingsStore);
1460
1732
  this.store = new CoreBlockStore({
1461
1733
  store: settingsStore,
1462
1734
  logger: this.ctx.logger.child("store")
1463
1735
  });
1736
+ this.prefs = new BlockSettingsStore({ store: settingsStore });
1464
1737
  await this.store.load();
1465
1738
  this.transpiler = await loadTranspiler(this.ctx.logger);
1466
1739
  armBlocksIntegration({
@@ -1487,6 +1760,7 @@ var CoreBlocksAddon = class extends BaseAddon {
1487
1760
  this.supervisor?.stop();
1488
1761
  this.supervisor = null;
1489
1762
  this.store = null;
1763
+ this.prefs = null;
1490
1764
  this.transpiler = null;
1491
1765
  this.typeDefs = null;
1492
1766
  }
@@ -1523,12 +1797,16 @@ var CoreBlocksAddon = class extends BaseAddon {
1523
1797
  setStatus: (blockId, status, error) => store.setStatus(blockId, status, error)
1524
1798
  },
1525
1799
  host: createBrokerProcessHost(broker),
1526
- compile: async (code) => {
1800
+ compile: async (source) => {
1527
1801
  if (this.transpiler === null) return {
1528
1802
  ok: false,
1529
1803
  error: "the compiler is unavailable on this node"
1530
1804
  };
1531
- return compileBlock(this.transpiler, code);
1805
+ const input = source.files !== void 0 ? {
1806
+ files: { ...source.files },
1807
+ entry: source.entry
1808
+ } : source.code;
1809
+ return compileBlock(this.transpiler, input);
1532
1810
  },
1533
1811
  logger: this.ctx.logger.child("supervisor"),
1534
1812
  composer: { apply: async (input) => {
@@ -1590,27 +1868,51 @@ var CoreBlocksAddon = class extends BaseAddon {
1590
1868
  return this.store;
1591
1869
  }
1592
1870
  /**
1593
- * Compile, or explain why not.
1871
+ * Compile, or explain why not. Returns the emitted JS so a successful gate
1872
+ * can also extract `settingsSchema` without compiling twice.
1594
1873
  *
1595
1874
  * A missing transpiler REFUSES the write rather than waving it through: code
1596
1875
  * that reached disk uncompiled is code the runner will fail on, far from the
1597
1876
  * person who wrote it.
1598
1877
  */
1599
- async gate(code) {
1878
+ async gate(input) {
1600
1879
  if (this.transpiler === null) throw new Error("core-blocks: the compiler is unavailable — refusing to store unchecked code");
1601
- const result = await compileBlock(this.transpiler, code);
1602
- if (!result.ok) {
1880
+ const normalized = normalizeCoreBlockWrite(input);
1881
+ const source = normalized.files !== void 0 ? {
1882
+ files: normalized.files,
1883
+ entry: normalized.entry
1884
+ } : normalized.code;
1885
+ const result = await compileBlock(this.transpiler, source);
1886
+ if (!result.ok || result.js === void 0) {
1603
1887
  const where = result.line !== void 0 ? ` (line ${result.line})` : "";
1604
1888
  throw new Error(`core-blocks: the code does not compile${where}: ${result.error ?? "unknown"}`);
1605
1889
  }
1890
+ return result.js;
1891
+ }
1892
+ requirePrefs() {
1893
+ if (this.prefs === null) throw new Error("core-blocks: not initialized");
1894
+ return this.prefs;
1895
+ }
1896
+ /**
1897
+ * After a successful compile, cache the entry's `settingsSchema` export on
1898
+ * the block when the author did not set one by hand. Never overwrites an
1899
+ * explicit schema the editor already stored.
1900
+ */
1901
+ async maybeCacheSettingsSchema(blockId, js, existingSchema) {
1902
+ if (existingSchema !== void 0) return null;
1903
+ const schema = await extractSettingsSchema(js);
1904
+ if (schema === null) return null;
1905
+ return this.requireStore().update(blockId, { settingsSchema: schema });
1606
1906
  }
1607
1907
  buildProvider() {
1608
1908
  return {
1609
1909
  list: async () => ({ blocks: [...this.requireStore().list()] }),
1610
1910
  get: async ({ blockId }) => ({ block: this.requireStore().get(blockId) }),
1611
1911
  create: async ({ block, caller }) => {
1612
- await this.gate(block.code);
1613
- const created = await this.requireStore().create(block, caller.userId);
1912
+ const js = await this.gate(block);
1913
+ let created = await this.requireStore().create(block, caller.userId);
1914
+ const withSchema = await this.maybeCacheSettingsSchema(created.id, js, created.settingsSchema);
1915
+ if (withSchema !== null) created = withSchema;
1614
1916
  this.ctx.logger.info("core block created", { meta: {
1615
1917
  blockId: created.id,
1616
1918
  name: created.name,
@@ -1620,8 +1922,21 @@ var CoreBlocksAddon = class extends BaseAddon {
1620
1922
  return { block: created };
1621
1923
  },
1622
1924
  update: async ({ blockId, block, caller }) => {
1623
- if (block.code !== void 0) await this.gate(block.code);
1624
- const updated = await this.requireStore().update(blockId, block);
1925
+ const sourceChanged = block.code !== void 0 || block.files !== void 0 || block.entry !== void 0;
1926
+ let js = null;
1927
+ if (sourceChanged) {
1928
+ const existing = this.requireStore().get(blockId);
1929
+ js = await this.gate({
1930
+ code: block.code ?? existing?.code,
1931
+ files: block.files ?? existing?.files,
1932
+ entry: block.entry ?? existing?.entry
1933
+ });
1934
+ }
1935
+ let updated = await this.requireStore().update(blockId, block);
1936
+ if (js !== null && block.settingsSchema === void 0) {
1937
+ const withSchema = await this.maybeCacheSettingsSchema(blockId, js, void 0);
1938
+ if (withSchema !== null) updated = withSchema;
1939
+ }
1625
1940
  this.ctx.logger.info("core block updated", { meta: {
1626
1941
  blockId,
1627
1942
  by: caller.userId
@@ -1631,6 +1946,7 @@ var CoreBlocksAddon = class extends BaseAddon {
1631
1946
  },
1632
1947
  delete: async ({ blockId }) => {
1633
1948
  await this.requireStore().delete(blockId);
1949
+ await this.requirePrefs().delete(blockId);
1634
1950
  this.convergeSoon();
1635
1951
  return { success: true };
1636
1952
  },
@@ -1660,14 +1976,30 @@ var CoreBlocksAddon = class extends BaseAddon {
1660
1976
  await this.supervisor.restart(blockId);
1661
1977
  return { block: store.get(blockId) ?? block };
1662
1978
  },
1663
- compile: async ({ code }) => {
1979
+ compile: async (input) => {
1664
1980
  if (this.transpiler === null) return {
1665
1981
  ok: false,
1666
1982
  error: "the compiler is unavailable on this node"
1667
1983
  };
1668
- const { js: _js, ...verdict } = await compileBlock(this.transpiler, code);
1984
+ const source = input.files !== void 0 ? {
1985
+ files: input.files,
1986
+ entry: input.entry
1987
+ } : input.code ?? "";
1988
+ const { js: _js, ...verdict } = await compileBlock(this.transpiler, source);
1669
1989
  return verdict;
1670
1990
  },
1991
+ getBlockSettings: async ({ blockId }) => {
1992
+ const block = this.requireStore().get(blockId);
1993
+ if (block === null) return null;
1994
+ const schema = block.settingsSchema;
1995
+ if (schema === void 0) return null;
1996
+ return hydrateSchema(schema, await this.requirePrefs().getValues(blockId));
1997
+ },
1998
+ updateBlockSettings: async ({ blockId, patch }) => {
1999
+ if (this.requireStore().get(blockId) === null) throw new Error(`core-blocks: no block "${blockId}"`);
2000
+ await this.requirePrefs().mergeValues(blockId, patch);
2001
+ return { success: true };
2002
+ },
1671
2003
  getTypeDefs: async () => {
1672
2004
  this.typeDefs ??= await loadBlockTypeDefs(this.ctx.logger);
1673
2005
  return { libs: this.typeDefs.map((l) => ({
@@ -1754,9 +2086,16 @@ function createBrokerProcessHost(broker) {
1754
2086
  */
1755
2087
  async function loadTranspiler(logger) {
1756
2088
  try {
1757
- const transform = (await import("esbuild")).transform;
2089
+ const mod = await import("esbuild");
2090
+ const transform = mod.transform;
2091
+ const build = mod.build;
1758
2092
  if (typeof transform !== "function") return null;
1759
- const transpiler = { transform };
2093
+ const transpiler = {
2094
+ transform,
2095
+ ...typeof build === "function" ? { build: async (options) => {
2096
+ return { errors: (await build(options)).errors };
2097
+ } } : {}
2098
+ };
1760
2099
  await transpiler.transform("export default 0", {
1761
2100
  loader: "ts",
1762
2101
  format: "esm",
@@ -1769,4 +2108,4 @@ async function loadTranspiler(logger) {
1769
2108
  }
1770
2109
  }
1771
2110
  //#endregion
1772
- export { CoreBlocksAddon, BlockSupervisor as a, blockIdFromAddonId as c, loadBlockTypeDefs as i, materializeBlock as l, CoreBlockStore as n, BLOCK_ADDON_PREFIX as o, compileBlock as r, blockAddonId as s, CORE_BLOCKS_COLLECTION as t, removeBlockPackage as u };
2111
+ export { CoreBlocksAddon, loadBlockTypeDefs as a, BlockSupervisor as c, blockIdFromAddonId as d, materializeBlock as f, compileBlock as i, BLOCK_ADDON_PREFIX as l, CORE_BLOCKS_COLLECTION as n, BlockSettingsStore as o, removeBlockPackage as p, CoreBlockStore as r, CORE_BLOCK_SETTINGS_COLLECTION as s, extractSettingsSchema as t, blockAddonId as u };
@@ -0,0 +1,6 @@
1
+ import { ConfigUISchema } from '@camstack/types';
2
+ /**
3
+ * Import the bundled module and return its `settingsSchema` export, or null.
4
+ * Never throws.
5
+ */
6
+ export declare function extractSettingsSchema(js: string): Promise<ConfigUISchema | null>;
@@ -1,6 +1,8 @@
1
1
  export { BLOCK_ADDON_PREFIX, blockAddonId, blockIdFromAddonId, materializeBlock, removeBlockPackage, } from './block-package.js';
2
2
  export { type BlockProcessHost, type BlockRunnerSnapshot, type BlockRunnerState, BlockSupervisor, } from './block-supervisor.js';
3
+ export { BlockSettingsStore, CORE_BLOCK_SETTINGS_COLLECTION, } from './block-settings-store.js';
3
4
  export { type BlockTypeLib, loadBlockTypeDefs } from './block-type-defs.js';
4
- export { type BlockTranspiler, compileBlock } from './compile-block.js';
5
+ export { type BlockTranspiler, type CompileBlockSource, compileBlock } from './compile-block.js';
5
6
  export { CORE_BLOCKS_COLLECTION, CoreBlockStore } from './core-block-store.js';
6
7
  export { CoreBlocksAddon as default, CoreBlocksAddon } from './core-blocks.addon.js';
8
+ export { extractSettingsSchema } from './extract-settings-schema.js';
@@ -5,14 +5,17 @@ Object.defineProperties(exports, {
5
5
  require("../../chunk-Cek0wNdY.js");
6
6
  const require_builtins_core_blocks_core_blocks_addon = require("./core-blocks.addon.js");
7
7
  exports.BLOCK_ADDON_PREFIX = require_builtins_core_blocks_core_blocks_addon.BLOCK_ADDON_PREFIX;
8
+ exports.BlockSettingsStore = require_builtins_core_blocks_core_blocks_addon.BlockSettingsStore;
8
9
  exports.BlockSupervisor = require_builtins_core_blocks_core_blocks_addon.BlockSupervisor;
9
10
  exports.CORE_BLOCKS_COLLECTION = require_builtins_core_blocks_core_blocks_addon.CORE_BLOCKS_COLLECTION;
11
+ exports.CORE_BLOCK_SETTINGS_COLLECTION = require_builtins_core_blocks_core_blocks_addon.CORE_BLOCK_SETTINGS_COLLECTION;
10
12
  exports.CoreBlockStore = require_builtins_core_blocks_core_blocks_addon.CoreBlockStore;
11
13
  exports.CoreBlocksAddon = require_builtins_core_blocks_core_blocks_addon.CoreBlocksAddon;
12
14
  exports.blockAddonId = require_builtins_core_blocks_core_blocks_addon.blockAddonId;
13
15
  exports.blockIdFromAddonId = require_builtins_core_blocks_core_blocks_addon.blockIdFromAddonId;
14
16
  exports.compileBlock = require_builtins_core_blocks_core_blocks_addon.compileBlock;
15
17
  exports.default = require_builtins_core_blocks_core_blocks_addon.CoreBlocksAddon;
18
+ exports.extractSettingsSchema = require_builtins_core_blocks_core_blocks_addon.extractSettingsSchema;
16
19
  exports.loadBlockTypeDefs = require_builtins_core_blocks_core_blocks_addon.loadBlockTypeDefs;
17
20
  exports.materializeBlock = require_builtins_core_blocks_core_blocks_addon.materializeBlock;
18
21
  exports.removeBlockPackage = require_builtins_core_blocks_core_blocks_addon.removeBlockPackage;
@@ -1,2 +1,2 @@
1
- import { CoreBlocksAddon, a as BlockSupervisor, c as blockIdFromAddonId, i as loadBlockTypeDefs, l as materializeBlock, n as CoreBlockStore, o as BLOCK_ADDON_PREFIX, r as compileBlock, s as blockAddonId, t as CORE_BLOCKS_COLLECTION, u as removeBlockPackage } from "./core-blocks.addon.mjs";
2
- export { BLOCK_ADDON_PREFIX, BlockSupervisor, CORE_BLOCKS_COLLECTION, CoreBlockStore, CoreBlocksAddon, CoreBlocksAddon as default, blockAddonId, blockIdFromAddonId, compileBlock, loadBlockTypeDefs, materializeBlock, removeBlockPackage };
1
+ import { CoreBlocksAddon, a as loadBlockTypeDefs, c as BlockSupervisor, d as blockIdFromAddonId, f as materializeBlock, i as compileBlock, l as BLOCK_ADDON_PREFIX, n as CORE_BLOCKS_COLLECTION, o as BlockSettingsStore, p as removeBlockPackage, r as CoreBlockStore, s as CORE_BLOCK_SETTINGS_COLLECTION, t as extractSettingsSchema, u as blockAddonId } from "./core-blocks.addon.mjs";
2
+ export { BLOCK_ADDON_PREFIX, BlockSettingsStore, BlockSupervisor, CORE_BLOCKS_COLLECTION, CORE_BLOCK_SETTINGS_COLLECTION, CoreBlockStore, CoreBlocksAddon, CoreBlocksAddon as default, blockAddonId, blockIdFromAddonId, compileBlock, extractSettingsSchema, loadBlockTypeDefs, materializeBlock, removeBlockPackage };
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DTgd2gOR.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-Ddb5cCNR.js");
6
+ const require_dist = require("../../dist-BgV_QADz.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-D980hwnw.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
10
10
  let _camstack_types_node = require("@camstack/types/node");
@@ -1,5 +1,5 @@
1
- import { D as MINUTES_PER_DAY, Gn as BaseAddon, Hn as DeviceRole, Lt as legacyAutoLinkIds, P as STREAM_PROFILE_META, S as FieldClaimSchema, St as enumerateSchemaFields, T as LocationIconIdSchema, Tt as expandContainer, Un as DeviceType, Vn as DeviceFeature, Vt as locationIconKey, Wn as errMsg, X as buildStreamParamsConfigSchema, Zn as WELL_KNOWN_TAB_MAP, Zt as normalizeUnit, _t as deviceManagerCapability, ct as createDeviceExtensionProvider, en as parseStreamParamsFormPatch, g as DAY_NAMES, gr as EventCategory, gt as deviceExtensionCapability, hn as siteZoneKey, kt as formatMinutes, mr as sleep$1, o as CAMERA_SWITCH_ORDER, or as isDeviceConfigCap, pt as defineExtensionSlotProvider, s as CAP_NAMES_WITH_STATUS, t as ALL_CAPABILITY_DEFINITIONS, un as runtimeStatePolicyFor, vt as deviceStateCapability, x as FieldClaimModeSchema, xt as enumerateItemArrayFields, y as DeviceStatusSchema, yt as deviceStatusCapability } from "../../dist-BUFcHq7H.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-ltKefX3N.mjs";
1
+ import { $t as normalizeUnit, At as formatMinutes, C as FieldClaimSchema, Ct as enumerateSchemaFields, E as LocationIconIdSchema, Et as expandContainer, F as STREAM_PROFILE_META, Gn as DeviceFeature, Ht as locationIconKey, Jn as errMsg, Kn as DeviceRole, O as MINUTES_PER_DAY, Rt as legacyAutoLinkIds, S as FieldClaimModeSchema, St as enumerateItemArrayFields, Yn as BaseAddon, Z as buildStreamParamsConfigSchema, _ as DAY_NAMES, _t as deviceExtensionCapability, b as DeviceStatusSchema, br as EventCategory, bt as deviceStatusCapability, lt as createDeviceExtensionProvider, mn as runtimeStatePolicyFor, mt as defineExtensionSlotProvider, nn as parseStreamParamsFormPatch, o as CAMERA_SWITCH_ORDER, qn as DeviceType, s as CAP_NAMES_WITH_STATUS, t as ALL_CAPABILITY_DEFINITIONS, tr as WELL_KNOWN_TAB_MAP, ur as isDeviceConfigCap, vr as sleep$1, vt as deviceManagerCapability, yn as siteZoneKey, yt as deviceStateCapability } from "../../dist-nUn3vto_.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-pKNWq-CL.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DTgd2gOR.js");
6
+ const require_dist = require("../../dist-BgV_QADz.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, Ht as logDestinationCapability } from "../../dist-BUFcHq7H.mjs";
1
+ import { Ut as logDestinationCapability, Yn as BaseAddon } from "../../dist-nUn3vto_.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DTgd2gOR.js");
3
+ const require_dist = require("../../dist-BgV_QADz.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";