@mulmoclaude/core 3.7.0 → 3.11.0

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 (75) hide show
  1. package/dist/collection/core/itemId.d.ts +28 -0
  2. package/dist/collection/firestore.cjs +9 -1
  3. package/dist/collection/firestore.cjs.map +1 -1
  4. package/dist/collection/firestore.js +10 -2
  5. package/dist/collection/firestore.js.map +1 -1
  6. package/dist/collection/index.cjs +55 -66
  7. package/dist/collection/index.cjs.map +1 -1
  8. package/dist/collection/index.d.ts +1 -1
  9. package/dist/collection/index.js +3 -15
  10. package/dist/collection/index.js.map +1 -1
  11. package/dist/collection/registry/server/index.cjs +19 -19
  12. package/dist/collection/registry/server/index.cjs.map +1 -1
  13. package/dist/collection/registry/server/index.js +2 -2
  14. package/dist/collection/server/firestoreDocs.d.ts +18 -0
  15. package/dist/collection/server/host.d.ts +29 -0
  16. package/dist/collection/server/index.cjs +70 -62
  17. package/dist/collection/server/index.d.ts +2 -3
  18. package/dist/collection/server/index.js +4 -4
  19. package/dist/collection/server/io.d.ts +5 -3
  20. package/dist/collection/server/manageTool.d.ts +0 -4
  21. package/dist/collection/server/publishManifest.d.ts +1 -0
  22. package/dist/collection/server/publishProject.d.ts +171 -2
  23. package/dist/collection-watchers/index.cjs +82 -38
  24. package/dist/collection-watchers/index.cjs.map +1 -1
  25. package/dist/collection-watchers/index.js +67 -23
  26. package/dist/collection-watchers/index.js.map +1 -1
  27. package/dist/{store-_61sO8K8.js → discovery-B9zdNkgW.js} +2525 -2354
  28. package/dist/discovery-B9zdNkgW.js.map +1 -0
  29. package/dist/{store-5_P_NsGa.cjs → discovery-BsXiDZBR.cjs} +2545 -2362
  30. package/dist/discovery-BsXiDZBR.cjs.map +1 -0
  31. package/dist/feeds/index.cjs +4 -4
  32. package/dist/feeds/index.js +2 -2
  33. package/dist/feeds/server/index.cjs +14 -14
  34. package/dist/feeds/server/index.cjs.map +1 -1
  35. package/dist/feeds/server/index.js +4 -4
  36. package/dist/firestore/listen.d.ts +4 -0
  37. package/dist/google/index.cjs +16 -16
  38. package/dist/google/index.cjs.map +1 -1
  39. package/dist/google/index.js +2 -2
  40. package/dist/{ingestTypes-CEi-Ot7F.js → ingestTypes-B312BwXg.js} +2 -2
  41. package/dist/{ingestTypes-CEi-Ot7F.js.map → ingestTypes-B312BwXg.js.map} +1 -1
  42. package/dist/{ingestTypes-iaiq33Ph.cjs → ingestTypes-B4XfrUoq.cjs} +3 -3
  43. package/dist/{ingestTypes-iaiq33Ph.cjs.map → ingestTypes-B4XfrUoq.cjs.map} +1 -1
  44. package/dist/{calendarGrid-CQ8MVSRb.js → itemId-DfTm08jm.js} +34 -2
  45. package/dist/itemId-DfTm08jm.js.map +1 -0
  46. package/dist/{calendarGrid-DGILaVxI.cjs → itemId-Dzrr3vs5.cjs} +45 -1
  47. package/dist/itemId-Dzrr3vs5.cjs.map +1 -0
  48. package/dist/listen-D5av7GzB.js +22 -0
  49. package/dist/listen-D5av7GzB.js.map +1 -0
  50. package/dist/listen-UZA68Upt.cjs +45 -0
  51. package/dist/listen-UZA68Upt.cjs.map +1 -0
  52. package/dist/{promptSafety-CpiME8pj.js → promptSafety-CSdyp7RF.js} +2 -2
  53. package/dist/{promptSafety-CpiME8pj.js.map → promptSafety-CSdyp7RF.js.map} +1 -1
  54. package/dist/{promptSafety-NNGViiCr.cjs → promptSafety-CWqYq-NS.cjs} +8 -8
  55. package/dist/{promptSafety-NNGViiCr.cjs.map → promptSafety-CWqYq-NS.cjs.map} +1 -1
  56. package/dist/remote-host/server/hostRunner.d.ts +2 -4
  57. package/dist/remote-host/server/index.cjs +4 -19
  58. package/dist/remote-host/server/index.cjs.map +1 -1
  59. package/dist/remote-host/server/index.js +1 -16
  60. package/dist/remote-host/server/index.js.map +1 -1
  61. package/dist/{server-CWZyg8fn.cjs → server-X_magqUa.cjs} +620 -734
  62. package/dist/server-X_magqUa.cjs.map +1 -0
  63. package/dist/{server-B48Jyxcj.js → server-zcM1ViCE.js} +418 -568
  64. package/dist/server-zcM1ViCE.js.map +1 -0
  65. package/dist/whisper/index.cjs +1 -1
  66. package/dist/whisper/index.js +1 -1
  67. package/package.json +1 -1
  68. package/dist/calendarGrid-CQ8MVSRb.js.map +0 -1
  69. package/dist/calendarGrid-DGILaVxI.cjs.map +0 -1
  70. package/dist/collection/core/shortHexId.d.ts +0 -8
  71. package/dist/collection/server/publish.d.ts +0 -56
  72. package/dist/server-B48Jyxcj.js.map +0 -1
  73. package/dist/server-CWZyg8fn.cjs.map +0 -1
  74. package/dist/store-5_P_NsGa.cjs.map +0 -1
  75. package/dist/store-_61sO8K8.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_root = require("../root-rPH6FGDT.cjs");
3
- const require_calendarGrid = require("../calendarGrid-DGILaVxI.cjs");
4
- const require_promptSafety = require("../promptSafety-NNGViiCr.cjs");
3
+ const require_itemId = require("../itemId-Dzrr3vs5.cjs");
4
+ const require_promptSafety = require("../promptSafety-CWqYq-NS.cjs");
5
5
  require("../collection/index.cjs");
6
- const require_store = require("../store-5_P_NsGa.cjs");
7
- const require_server = require("../server-CWZyg8fn.cjs");
6
+ const require_discovery = require("../discovery-BsXiDZBR.cjs");
7
+ const require_server = require("../server-X_magqUa.cjs");
8
8
  const require_notifier = require("../notifier-DZJj0sOh.cjs");
9
9
  let node_fs_promises = require("node:fs/promises");
10
10
  //#region src/collection-watchers/config.ts
@@ -86,8 +86,8 @@ var SHARED_MARK = "#";
86
86
  * Deliberately NOT on the package barrel: the id is a disk format, not an API. */
87
87
  function completionLegacyId(slug, itemId, root, aid) {
88
88
  if (root !== void 0 && aid !== void 0) throw new Error(`completionLegacyId: "${slug}" carries both a root (${root}) and an app (${aid})`);
89
- if (!require_calendarGrid.isValidCollectionName(slug)) throw new Error(`completionLegacyId: collection name "${slug}" is not a valid collection name`);
90
- if (aid !== void 0 && !require_calendarGrid.isValidCollectionName(aid)) throw new Error(`completionLegacyId: app id "${aid}" is not a valid collection name`);
89
+ if (!require_itemId.isValidCollectionName(slug)) throw new Error(`completionLegacyId: collection name "${slug}" is not a valid collection name`);
90
+ if (aid !== void 0 && !require_itemId.isValidCollectionName(aid)) throw new Error(`completionLegacyId: app id "${aid}" is not a valid collection name`);
91
91
  return `${LEGACY_ID_PREFIX}${aid !== void 0 ? `${SHARED_MARK}${aid}${ROOT_SEP}` : root === void 0 ? "" : `${ROOT_MARK}${require_root.canonicalRoot(root)}${ROOT_SEP}`}${slug}:${itemId}`;
92
92
  }
93
93
  /** Decode a key back into its (root, slug, itemId), or null if the string
@@ -130,7 +130,7 @@ function parseCompletionLegacyId(legacyId) {
130
130
  function resolveDisplayLabel(schema, item, itemId) {
131
131
  const { displayField } = schema;
132
132
  if (!displayField) return itemId;
133
- const label = require_calendarGrid.fieldText(item[displayField]).trim();
133
+ const label = require_itemId.fieldText(item[displayField]).trim();
134
134
  return label.length > 0 ? label : itemId;
135
135
  }
136
136
  /** The scope a collection's bells belong to.
@@ -285,7 +285,7 @@ async function reconcileItem(collection, itemId, ioOpts = {}, now = evalNow()) {
285
285
  await clearScoped(slug, itemId, scope);
286
286
  return;
287
287
  }
288
- const item = await require_store.storeFor(collection, ioOpts).read(itemId);
288
+ const item = await require_discovery.storeFor(collection, ioOpts).read(itemId);
289
289
  if (item === null) {
290
290
  await clearScoped(slug, itemId, scope);
291
291
  return;
@@ -324,7 +324,7 @@ async function reconcileAllItems(collection, ioOpts = {}, now = evalNow()) {
324
324
  if (!schema.completionField) return;
325
325
  let items;
326
326
  try {
327
- items = await require_store.storeFor(collection, ioOpts).list();
327
+ items = await require_discovery.storeFor(collection, ioOpts).list();
328
328
  } catch (err) {
329
329
  log().warn("reconcile list failed", {
330
330
  slug,
@@ -369,7 +369,7 @@ function sweepVerdict(entry, ownRoot) {
369
369
  * whose record the forward pass can never walk to (a delete leaves a bell that
370
370
  * a walk over the SURVIVING records cannot clear). */
371
371
  async function isStaleEntry(slug, itemId, opts, aid) {
372
- const collection = await require_store.loadCollection(slug, opts);
372
+ const collection = await require_discovery.loadCollection(slug, opts);
373
373
  if (aid !== void 0 && collection?.appId !== aid) return false;
374
374
  if (!collection || !collection.schema.completionField) return true;
375
375
  return recordNoLongerBells(collection, itemId, opts);
@@ -378,7 +378,7 @@ async function isStaleEntry(slug, itemId, opts, aid) {
378
378
  * `notifyWhen`. Split out so the caller stays one decision — WHOSE entry this
379
379
  * is — and this one stays the other. */
380
380
  async function recordNoLongerBells(collection, itemId, opts) {
381
- const item = await require_store.storeFor(collection, opts).read(itemId);
381
+ const item = await require_discovery.storeFor(collection, opts).read(itemId);
382
382
  return item === null || require_promptSafety.itemIsDone(collection.schema, item) || !require_promptSafety.whenMatches(collection.schema.notifyWhen, item);
383
383
  }
384
384
  /** One entry's fate: whose it is (`sweepVerdict`), then — when it is this
@@ -451,7 +451,7 @@ var WATCHER_ROOT_CONFLICT = "WATCHER_ROOT_CONFLICT";
451
451
  * is a comparison, and under an explicit-root binding there is no default to
452
452
  * read, which is not an error here. */
453
453
  function effectiveRoot(opts) {
454
- const root = opts?.workspaceRoot ?? require_store.peekWorkspaceRoot() ?? void 0;
454
+ const root = opts?.workspaceRoot ?? require_discovery.peekWorkspaceRoot() ?? void 0;
455
455
  return root === void 0 ? void 0 : require_root.canonicalRoot(root);
456
456
  }
457
457
  /** The discovery options a generation runs under, with an explicit root put in
@@ -639,7 +639,7 @@ async function tickTimeTriggers(gen, now = evalNow()) {
639
639
  * shared collection's store grows an `onSnapshot` watch, it stops being
640
640
  * special here with no edit to this file. */
641
641
  function cannotReportChanges(gen, collection) {
642
- return require_store.storeFor(collection, gen.discoveryOpts).watch === void 0;
642
+ return require_discovery.storeFor(collection, gen.discoveryOpts).watch === void 0;
643
643
  }
644
644
  /** True when a schema declares behaviour that only a reconcile pass can
645
645
  * produce: bells (`completionField`), date-triggered bells (`triggerField`),
@@ -674,7 +674,7 @@ async function reconcileUnwatched(gen, collection, now) {
674
674
  * already sweeps on a changed schema and on a vanished slug, and the sweep is
675
675
  * what clears bells the declaration no longer justifies. */
676
676
  async function tickUnwatchedCollections(gen, now) {
677
- if (require_store.firestoreHandle() === null) return;
677
+ if (require_discovery.firestoreHandle() === null) return;
678
678
  const pending = [...gen.watchers.values()].map((entry) => entry.collection).filter((collection) => cannotReportChanges(gen, collection) && needsReconcilePass(collection.schema));
679
679
  if (pending.length === 0) return;
680
680
  for (const collection of pending) await reconcileUnwatched(gen, collection, now);
@@ -688,7 +688,7 @@ async function tickUnwatchedCollections(gen, now) {
688
688
  async function syncWatchers(gen) {
689
689
  let collections;
690
690
  try {
691
- collections = await require_store.discoverCollections(gen.discoveryOpts);
691
+ collections = await require_discovery.discoverCollections(gen.discoveryOpts);
692
692
  } catch (err) {
693
693
  log().warn("watcher discover failed", { error: errMsg(err) });
694
694
  return false;
@@ -714,10 +714,22 @@ function stopVanishedWatchers(gen, liveSlugs) {
714
714
  }
715
715
  return mutated;
716
716
  }
717
+ /** True when the collection's records moved — a different `dataSource.path`, a
718
+ * different `dataPath`, a flip between the two modes, or a different APP. The
719
+ * mount (an fs.watch, or a Firestore listener) is bound to the OLD location,
720
+ * so it must be remounted, not just re-reconciled.
721
+ *
722
+ * `appId` belongs here even though it is not in the schema: it is resolved
723
+ * separately, from the repository's `app.json`. Point that file at another app
724
+ * and every read and write follows immediately, while a listener armed on the
725
+ * old one keeps running — so the collection would serve the NEW app's records
726
+ * and be woken by the OLD app's changes, forever. */
727
+ function backendMoved(previous, next) {
728
+ return previous.appId !== next.appId || storagePathChanged(previous.schema, next.schema);
729
+ }
717
730
  /** True when a schema edit moved the collection's storage — a different
718
731
  * `dataSource.path`, a different `dataPath`, or a flip between the two
719
- * modes. The mounted fs.watch is bound to the OLD location, so it must
720
- * be remounted, not just re-reconciled. */
732
+ * modes. */
721
733
  function storagePathChanged(previous, next) {
722
734
  return previous.dataSource?.path !== next.dataSource?.path || previous.dataPath !== next.dataPath || previous.storage?.type !== next.storage?.type || storageFilePath(previous.storage) !== storageFilePath(next.storage);
723
735
  }
@@ -729,23 +741,30 @@ function storageFilePath(storage) {
729
741
  }
730
742
  /** Re-reconcile already-watched collections whose schema changed since
731
743
  * the last tick. New collections fall through to `startNewWatchers`. */
744
+ /** Unmount a collection whose records moved. `startNewWatchers` (right after
745
+ * this pass in syncWatchers) remounts it on the new location. A dataSource
746
+ * collection also gets a change ping so open views refetch immediately. */
747
+ function dropStaleMount(gen, existing, collection) {
748
+ log().info("watcher backend moved, remounting", { slug: collection.slug });
749
+ try {
750
+ existing.unsubscribe();
751
+ } catch {}
752
+ gen.watchers.delete(collection.slug);
753
+ if (collection.schema.dataSource !== void 0) safePublish(gen, collection, {
754
+ slug: collection.slug,
755
+ op: "upsert"
756
+ });
757
+ }
732
758
  async function reconcileChangedSchemas(gen, collections) {
733
759
  let mutated = false;
734
760
  for (const collection of collections) {
735
761
  const existing = gen.watchers.get(collection.slug);
736
762
  if (!existing) continue;
737
763
  const nextJson = JSON.stringify(collection.schema);
738
- if (existing.schemaJson === nextJson) continue;
739
- if (storagePathChanged(existing.collection.schema, collection.schema)) {
740
- log().info("watcher storage path changed, remounting", { slug: collection.slug });
741
- try {
742
- existing.unsubscribe();
743
- } catch {}
744
- gen.watchers.delete(collection.slug);
745
- if (collection.schema.dataSource !== void 0) safePublish(gen, {
746
- slug: collection.slug,
747
- op: "upsert"
748
- });
764
+ const moved = backendMoved(existing.collection, collection);
765
+ if (existing.schemaJson === nextJson && !moved) continue;
766
+ if (moved) {
767
+ dropStaleMount(gen, existing, collection);
749
768
  mutated = true;
750
769
  continue;
751
770
  }
@@ -753,7 +772,7 @@ async function reconcileChangedSchemas(gen, collections) {
753
772
  existing.collection = collection;
754
773
  log().info("watcher schema changed, re-reconciling", { slug: collection.slug });
755
774
  await reconcileAllItems(collection, gen.discoveryOpts);
756
- if (collection.schema.dataSource !== void 0) safePublish(gen, {
775
+ if (collection.schema.dataSource !== void 0) safePublish(gen, collection, {
757
776
  slug: collection.slug,
758
777
  op: "upsert"
759
778
  });
@@ -788,7 +807,7 @@ async function startWatcherFor(gen, collection) {
788
807
  const { slug } = collection;
789
808
  try {
790
809
  await reconcileAllItems(collection, gen.discoveryOpts);
791
- const store = require_store.storeFor(collection, gen.discoveryOpts);
810
+ const store = require_discovery.storeFor(collection, gen.discoveryOpts);
792
811
  const unsubscribe = store.watch ? await store.watch((change) => {
793
812
  handleStoreChange(gen, slug, change).catch((err) => {
794
813
  log().warn("store change handling failed", {
@@ -841,13 +860,15 @@ async function handleStoreChange(gen, slug, change) {
841
860
  * per slug so a burst of writes collapses into one pass plus one trailing
842
861
  * re-run. */
843
862
  function scheduleCollectionReconcile(gen, slug) {
863
+ let target = gen.watchers.get(slug)?.collection ?? { appId: void 0 };
844
864
  return runSingleFlight(gen.collectionSlots, slug, async () => {
845
- const collection = await require_store.loadCollection(slug, gen.discoveryOpts);
865
+ const collection = await require_discovery.loadCollection(slug, gen.discoveryOpts);
846
866
  if (!collection) return;
867
+ target = collection;
847
868
  await reconcileAllItems(collection, gen.discoveryOpts);
848
869
  await sweepStaleActiveEntries(gen.discoveryOpts);
849
870
  }, () => {
850
- safePublish(gen, {
871
+ safePublish(gen, target, {
851
872
  slug,
852
873
  op: "upsert"
853
874
  });
@@ -911,16 +932,32 @@ function runSingleFlight(slots, key, pass, onSettled) {
911
932
  * file-backed collections reach here; a `storage` (db) collection has no
912
933
  * per-record file and publishes wholesale in `scheduleStorageReconcile`. */
913
934
  async function publishItemChange(gen, collection, itemId) {
914
- const changeOp = await itemFileExists(collection.dataDir, itemId) ? "upsert" : "delete";
915
- safePublish(gen, {
935
+ const changeOp = await recordStillExists(gen, collection, itemId) ? "upsert" : "delete";
936
+ safePublish(gen, collection, {
916
937
  slug: collection.slug,
917
938
  ids: [itemId],
918
939
  op: changeOp
919
940
  });
920
941
  }
942
+ /** Upsert or delete? Answered by whatever actually holds the record.
943
+ *
944
+ * A shared collection's records are Firestore documents — there is no item
945
+ * file, so the file check answers "gone" for every one of them and each live
946
+ * change would be published as a DELETE. Asking the store is the question the
947
+ * file check was standing in for; the file path keeps it because it is
948
+ * cheaper and its semantics (an unreadable dir, an id that is not a safe
949
+ * filename) are already settled there. */
950
+ async function recordStillExists(gen, collection, itemId) {
951
+ if (collection.appId === void 0) return itemFileExists(collection.dataDir, itemId);
952
+ try {
953
+ return await require_discovery.storeFor(collection, gen.discoveryOpts).read(itemId) !== null;
954
+ } catch {
955
+ return true;
956
+ }
957
+ }
921
958
  async function itemFileExists(dataDir, itemId) {
922
959
  try {
923
- await (0, node_fs_promises.access)(require_store.itemFilePath(dataDir, itemId));
960
+ await (0, node_fs_promises.access)(require_discovery.itemFilePath(dataDir, itemId));
924
961
  return true;
925
962
  } catch {
926
963
  return false;
@@ -934,10 +971,17 @@ async function itemFileExists(dataDir, itemId) {
934
971
  * carries only absolute paths, and a root reconstructed by string surgery
935
972
  * would be a guess. Undefined in a single-workspace host, which is exactly
936
973
  * what the payload contract means by "the host's configured root". */
937
- function safePublish(gen, payload) {
938
- const enriched = require_store.collectionChangePayload(payload, gen.discoveryOpts.workspaceRoot);
974
+ /** Publish a change on the channel the collection actually lives on.
975
+ *
976
+ * A SHARED collection is not keyed by this checkout: its identity is
977
+ * `(aid, cid)`, and the same collection is open in every clone. Publishing it
978
+ * as `(root, slug)` sends the refresh to a channel no shared subscriber is
979
+ * listening on — the live update simply never arrives, and nothing errors.
980
+ * That is why this takes the collection rather than a bare slug. */
981
+ function safePublish(gen, collection, base) {
982
+ const enriched = collection.appId === void 0 ? require_discovery.collectionChangePayload(base, gen.discoveryOpts.workspaceRoot) : require_discovery.sharedCollectionChangePayload(base, collection.appId);
939
983
  try {
940
- require_store.publishCollectionChange(enriched);
984
+ require_discovery.publishCollectionChange(enriched);
941
985
  } catch (err) {
942
986
  log().warn("collection change publish failed", {
943
987
  slug: enriched.slug,