@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.
- package/dist/collection/core/itemId.d.ts +28 -0
- package/dist/collection/firestore.cjs +9 -1
- package/dist/collection/firestore.cjs.map +1 -1
- package/dist/collection/firestore.js +10 -2
- package/dist/collection/firestore.js.map +1 -1
- package/dist/collection/index.cjs +55 -66
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.d.ts +1 -1
- package/dist/collection/index.js +3 -15
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/registry/server/index.cjs +19 -19
- package/dist/collection/registry/server/index.cjs.map +1 -1
- package/dist/collection/registry/server/index.js +2 -2
- package/dist/collection/server/firestoreDocs.d.ts +18 -0
- package/dist/collection/server/host.d.ts +29 -0
- package/dist/collection/server/index.cjs +70 -62
- package/dist/collection/server/index.d.ts +2 -3
- package/dist/collection/server/index.js +4 -4
- package/dist/collection/server/io.d.ts +5 -3
- package/dist/collection/server/manageTool.d.ts +0 -4
- package/dist/collection/server/publishManifest.d.ts +1 -0
- package/dist/collection/server/publishProject.d.ts +171 -2
- package/dist/collection-watchers/index.cjs +82 -38
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.js +67 -23
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/{store-_61sO8K8.js → discovery-B9zdNkgW.js} +2525 -2354
- package/dist/discovery-B9zdNkgW.js.map +1 -0
- package/dist/{store-5_P_NsGa.cjs → discovery-BsXiDZBR.cjs} +2545 -2362
- package/dist/discovery-BsXiDZBR.cjs.map +1 -0
- package/dist/feeds/index.cjs +4 -4
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/server/index.cjs +14 -14
- package/dist/feeds/server/index.cjs.map +1 -1
- package/dist/feeds/server/index.js +4 -4
- package/dist/firestore/listen.d.ts +4 -0
- package/dist/google/index.cjs +16 -16
- package/dist/google/index.cjs.map +1 -1
- package/dist/google/index.js +2 -2
- package/dist/{ingestTypes-CEi-Ot7F.js → ingestTypes-B312BwXg.js} +2 -2
- package/dist/{ingestTypes-CEi-Ot7F.js.map → ingestTypes-B312BwXg.js.map} +1 -1
- package/dist/{ingestTypes-iaiq33Ph.cjs → ingestTypes-B4XfrUoq.cjs} +3 -3
- package/dist/{ingestTypes-iaiq33Ph.cjs.map → ingestTypes-B4XfrUoq.cjs.map} +1 -1
- package/dist/{calendarGrid-CQ8MVSRb.js → itemId-DfTm08jm.js} +34 -2
- package/dist/itemId-DfTm08jm.js.map +1 -0
- package/dist/{calendarGrid-DGILaVxI.cjs → itemId-Dzrr3vs5.cjs} +45 -1
- package/dist/itemId-Dzrr3vs5.cjs.map +1 -0
- package/dist/listen-D5av7GzB.js +22 -0
- package/dist/listen-D5av7GzB.js.map +1 -0
- package/dist/listen-UZA68Upt.cjs +45 -0
- package/dist/listen-UZA68Upt.cjs.map +1 -0
- package/dist/{promptSafety-CpiME8pj.js → promptSafety-CSdyp7RF.js} +2 -2
- package/dist/{promptSafety-CpiME8pj.js.map → promptSafety-CSdyp7RF.js.map} +1 -1
- package/dist/{promptSafety-NNGViiCr.cjs → promptSafety-CWqYq-NS.cjs} +8 -8
- package/dist/{promptSafety-NNGViiCr.cjs.map → promptSafety-CWqYq-NS.cjs.map} +1 -1
- package/dist/remote-host/server/hostRunner.d.ts +2 -4
- package/dist/remote-host/server/index.cjs +4 -19
- package/dist/remote-host/server/index.cjs.map +1 -1
- package/dist/remote-host/server/index.js +1 -16
- package/dist/remote-host/server/index.js.map +1 -1
- package/dist/{server-CWZyg8fn.cjs → server-X_magqUa.cjs} +620 -734
- package/dist/server-X_magqUa.cjs.map +1 -0
- package/dist/{server-B48Jyxcj.js → server-zcM1ViCE.js} +418 -568
- package/dist/server-zcM1ViCE.js.map +1 -0
- package/dist/whisper/index.cjs +1 -1
- package/dist/whisper/index.js +1 -1
- package/package.json +1 -1
- package/dist/calendarGrid-CQ8MVSRb.js.map +0 -1
- package/dist/calendarGrid-DGILaVxI.cjs.map +0 -1
- package/dist/collection/core/shortHexId.d.ts +0 -8
- package/dist/collection/server/publish.d.ts +0 -56
- package/dist/server-B48Jyxcj.js.map +0 -1
- package/dist/server-CWZyg8fn.cjs.map +0 -1
- package/dist/store-5_P_NsGa.cjs.map +0 -1
- 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
|
|
4
|
-
const require_promptSafety = require("../promptSafety-
|
|
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
|
|
7
|
-
const require_server = require("../server-
|
|
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 (!
|
|
90
|
-
if (aid !== void 0 && !
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ??
|
|
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
|
|
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 (
|
|
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
|
|
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.
|
|
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
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
741
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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)(
|
|
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
|
-
|
|
938
|
-
|
|
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
|
-
|
|
984
|
+
require_discovery.publishCollectionChange(enriched);
|
|
941
985
|
} catch (err) {
|
|
942
986
|
log().warn("collection change publish failed", {
|
|
943
987
|
slug: enriched.slug,
|