@mulmoclaude/core 3.6.0 → 3.10.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 +2 -2
- 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 +40 -0
- package/dist/collection/server/index.cjs +25 -4
- package/dist/collection/server/index.d.ts +4 -1
- package/dist/collection/server/index.js +4 -4
- package/dist/collection/server/io.d.ts +5 -3
- package/dist/collection/server/publishChecks.d.ts +29 -0
- package/dist/collection/server/publishManifest.d.ts +181 -0
- package/dist/collection/server/publishProject.d.ts +255 -0
- package/dist/collection/server/validate.d.ts +12 -0
- package/dist/collection-watchers/index.cjs +70 -26
- 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/{discovery-CdkaURVY.js → discovery-B9zdNkgW.js} +189 -18
- package/dist/discovery-B9zdNkgW.js.map +1 -0
- package/dist/{discovery-Cw5xWzZD.cjs → discovery-BsXiDZBR.cjs} +226 -43
- 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 +6 -6
- package/dist/feeds/server/index.js +4 -4
- package/dist/firestore/listen.d.ts +4 -0
- package/dist/google/index.cjs +5 -5
- 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-DdHaX_Uu.js → server-BNe8XMtu.js} +677 -6
- package/dist/server-BNe8XMtu.js.map +1 -0
- package/dist/{server-9mFaGLdq.cjs → server-CgkkWWBd.cjs} +807 -22
- package/dist/server-CgkkWWBd.cjs.map +1 -0
- 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/discovery-CdkaURVY.js.map +0 -1
- package/dist/discovery-Cw5xWzZD.cjs.map +0 -1
- package/dist/server-9mFaGLdq.cjs.map +0 -1
- package/dist/server-DdHaX_Uu.js.map +0 -1
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { a as isErrorWithCode, l as isUnknownArray, r as hasNumberProp, s as isRecord } from "./dist-D8zokgGo.js";
|
|
2
2
|
import { n as writeFileAtomic, t as canonicalRoot } from "./root-BMroU_mB.js";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as isSafeSlug, B as fieldText, D as SAFE_SLUG_PATTERN, E as SAFE_RECORD_ID_PATTERN, F as INGEST_KINDS, M as AGENT_INGEST_KIND, N as COMPUTED_TYPES, P as FEED_SCHEDULES, R as isReadOnlySchema, S as localCollectionKeyOf, T as sharedCollectionKey, V as fieldTextOrNull, k as isSafeRecordId, t as newItemId, x as isValidCollectionName, z as storageKindFor } from "./itemId-DfTm08jm.js";
|
|
4
4
|
import { n as projectRecordFields } from "./project-B0j_Vf_Q.js";
|
|
5
|
+
import { n as backoffDelayMs, r as classifyListenerError } from "./listen-D5av7GzB.js";
|
|
5
6
|
import { n as isSafeCustomViewI18nPath, r as isSafeCustomViewPath, t as isSafeActionTemplatePath } from "./templatePath-k_WNbL_Q.js";
|
|
6
7
|
import { readFileSync, realpathSync, watch } from "node:fs";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { createHash, randomBytes } from "node:crypto";
|
|
9
10
|
import { lstat, mkdir, open, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
11
|
+
import { z } from "zod";
|
|
10
12
|
import { tmpdir } from "node:os";
|
|
11
13
|
import iconv from "iconv-lite";
|
|
12
|
-
import { z } from "zod";
|
|
13
14
|
//#region src/host/hostSlot.ts
|
|
14
15
|
function createHostSlot(name) {
|
|
15
16
|
let current = null;
|
|
@@ -104,6 +105,7 @@ function collectionChangeKey(payload, fallbackRoot) {
|
|
|
104
105
|
var hostSlot = createHostSlot("@mulmoclaude/core/collection/server: configureCollectionHost()");
|
|
105
106
|
var changePublisher = null;
|
|
106
107
|
var firestoreAccessor = null;
|
|
108
|
+
var sharedCollectionsSupported = false;
|
|
107
109
|
/** Wire the engine to a host. Call once at server startup, before any
|
|
108
110
|
* collection storage operation. Re-binding to a *different* host throws —
|
|
109
111
|
* silently redirecting later filesystem operations to another workspace
|
|
@@ -127,6 +129,39 @@ function setCollectionChangePublisher(publish) {
|
|
|
127
129
|
function publishCollectionChange(payload) {
|
|
128
130
|
changePublisher?.(payload);
|
|
129
131
|
}
|
|
132
|
+
/** Does this host serve SHARED (firestore-backed) collections at all?
|
|
133
|
+
*
|
|
134
|
+
* Opt-in, and `false` until a host says otherwise, because the default has to
|
|
135
|
+
* be the safe one: a host that cannot reach Firestore must not accept a
|
|
136
|
+
* shared schema and then report the collection as empty.
|
|
137
|
+
*
|
|
138
|
+
* WHY A CAPABILITY AND NOT A HOST CHECK. The engine must not know which host
|
|
139
|
+
* it is running in. The rule being expressed — shared collections live in a
|
|
140
|
+
* PROJECT REPOSITORY, not in a single managed workspace where one roster
|
|
141
|
+
* would govern every unrelated collection beside it — is a property the host
|
|
142
|
+
* knows about ITSELF. Asking the engine to test for a particular host's
|
|
143
|
+
* workspace would put a host's name in shared code and make every change to
|
|
144
|
+
* a one-host feature a change to this package.
|
|
145
|
+
*
|
|
146
|
+
* WHY NOT A FIELD ON `CollectionHost`. Same reason the accessor below is not
|
|
147
|
+
* one: `configureCollectionHost` is a ONE-SHOT binding a host sets at startup
|
|
148
|
+
* and cannot re-bind, so a suite exercising this engine could not turn the
|
|
149
|
+
* capability on for itself without owning the whole host. This is the same
|
|
150
|
+
* concern as the accessor and belongs beside it.
|
|
151
|
+
*
|
|
152
|
+
* NOT DERIVED FROM THE ACCESSOR. "Has a session right now" and "serves shared
|
|
153
|
+
* collections at all" are different questions: the accessor answers null
|
|
154
|
+
* between connections, and a collection must not stop being ACCEPTABLE
|
|
155
|
+
* because nobody is signed in — that would turn "connect first" into "this
|
|
156
|
+
* schema is invalid". */
|
|
157
|
+
function setSharedCollectionsSupport(supported) {
|
|
158
|
+
sharedCollectionsSupported = supported;
|
|
159
|
+
}
|
|
160
|
+
/** Whether the host declared support. Consulted from the schema ACCEPTANCE
|
|
161
|
+
* gate, whose contract is to return a reason rather than raise. */
|
|
162
|
+
function hostSupportsSharedCollections() {
|
|
163
|
+
return sharedCollectionsSupported;
|
|
164
|
+
}
|
|
130
165
|
/** Wire the accessor for the host's authenticated Firestore session.
|
|
131
166
|
*
|
|
132
167
|
* Separate from `configureCollectionHost` for the same reason
|
|
@@ -417,8 +452,32 @@ async function guarded(key, email, run) {
|
|
|
417
452
|
* older version) can hold anything, so a non-object is dropped rather than
|
|
418
453
|
* surfaced as a broken record — the same fail-soft the file store applies to
|
|
419
454
|
* an unparseable `.json`. */
|
|
420
|
-
|
|
421
|
-
|
|
455
|
+
/** A stored document as a record — with its identity taken from the DOCUMENT
|
|
456
|
+
* ID, never from the document's own fields.
|
|
457
|
+
*
|
|
458
|
+
* This is the one place a shared record's identity is decided, and it is
|
|
459
|
+
* decided against the field the writer supplied on purpose.
|
|
460
|
+
*
|
|
461
|
+
* The rules can constrain the document id (`idFrom` pins it to the
|
|
462
|
+
* submitter's uid, or to uid+field) and they CANNOT constrain the value of a
|
|
463
|
+
* field: `validateOk` checks which keys are present, `keyFieldsOk` checks a
|
|
464
|
+
* declared enum, and nothing compares `request.resource.data[primaryKey]`
|
|
465
|
+
* with the path being written. So a public submitter writing at their one
|
|
466
|
+
* permitted document id could put ANY value in the primary-key field —
|
|
467
|
+
* another member's record id, or a duplicate — and every reader would take it
|
|
468
|
+
* as the record's identity. Overwriting it here makes that unreachable rather
|
|
469
|
+
* than merely discouraged, and it costs nothing: for a record written through
|
|
470
|
+
* this store the two already agree, because `firestoreWrite` writes at the id
|
|
471
|
+
* it was given.
|
|
472
|
+
*
|
|
473
|
+
* It also removes the reason a submit path would have had to accept the
|
|
474
|
+
* primary key as a `createField` at all — a submission that cannot name its
|
|
475
|
+
* own id is exactly right when the id is the thing being assigned. */
|
|
476
|
+
function toItem(data, docId, primaryKey) {
|
|
477
|
+
return isRecord(data) ? {
|
|
478
|
+
...data,
|
|
479
|
+
[primaryKey]: docId
|
|
480
|
+
} : null;
|
|
422
481
|
}
|
|
423
482
|
/** Record ids are validated with the SAME helper every other backend uses.
|
|
424
483
|
* Firestore would accept ids the file store refuses, but a record should stay
|
|
@@ -429,9 +488,9 @@ function withSafeId(itemId, onInvalid, run) {
|
|
|
429
488
|
if (safeId === null) return onInvalid();
|
|
430
489
|
return run(safeId, requireHandle());
|
|
431
490
|
}
|
|
432
|
-
async function firestoreList(key) {
|
|
491
|
+
async function firestoreList(key, primaryKey) {
|
|
433
492
|
const { docs, email } = requireHandle();
|
|
434
|
-
return (await guarded(key, email, () => docs.list(sharedItemsPath(key)))).map((entry) => toItem(entry.data)).filter((item) => item !== null);
|
|
493
|
+
return (await guarded(key, email, () => docs.list(sharedItemsPath(key)))).map((entry) => toItem(entry.data, entry.id, primaryKey)).filter((item) => item !== null);
|
|
435
494
|
}
|
|
436
495
|
/** Paging is emulated over a full ordered read rather than pushed into
|
|
437
496
|
* Firestore: `offset` has no server-side form there (the cursor API needs the
|
|
@@ -439,7 +498,7 @@ async function firestoreList(key) {
|
|
|
439
498
|
* `total` needs the full count anyway. Hence `nativePaging: false` — the
|
|
440
499
|
* capability is honest about the cost. */
|
|
441
500
|
async function firestorePage(key, primaryKey, opts) {
|
|
442
|
-
const items = await firestoreList(key);
|
|
501
|
+
const items = await firestoreList(key, primaryKey);
|
|
443
502
|
const offset = Math.max(0, opts.offset ?? 0);
|
|
444
503
|
return {
|
|
445
504
|
items: projectItemFields(opts.limit === void 0 ? items.slice(offset) : items.slice(offset, offset + Math.max(0, opts.limit)), opts.fields, primaryKey),
|
|
@@ -447,8 +506,8 @@ async function firestorePage(key, primaryKey, opts) {
|
|
|
447
506
|
truncated: false
|
|
448
507
|
};
|
|
449
508
|
}
|
|
450
|
-
async function firestoreRead(key, itemId) {
|
|
451
|
-
return withSafeId(itemId, () => Promise.resolve(null), async (safeId, { docs, email }) => toItem(await guarded(key, email, () => docs.get(sharedItemsPath(key), safeId))));
|
|
509
|
+
async function firestoreRead(key, itemId, primaryKey) {
|
|
510
|
+
return withSafeId(itemId, () => Promise.resolve(null), async (safeId, { docs, email }) => toItem(await guarded(key, email, () => docs.get(sharedItemsPath(key), safeId)), safeId, primaryKey));
|
|
452
511
|
}
|
|
453
512
|
/** Publish the "records changed" ping for a shared collection.
|
|
454
513
|
*
|
|
@@ -499,6 +558,111 @@ async function firestoreDelete(key, itemId, opts) {
|
|
|
499
558
|
};
|
|
500
559
|
});
|
|
501
560
|
}
|
|
561
|
+
/** Deliver one snapshot's changes.
|
|
562
|
+
*
|
|
563
|
+
* THE FIRST SNAPSHOT IS ONE COLLECTION-LEVEL REPORT, not N per-record ones.
|
|
564
|
+
* `onSnapshot` hands over the collection's current contents immediately, as a
|
|
565
|
+
* snapshot in which every existing document reads as `added`.
|
|
566
|
+
*
|
|
567
|
+
* Dropping it is wrong. There is a GAP on either side of a subscription — the
|
|
568
|
+
* boot reconcile finishes before the listener arms, and a listener that died
|
|
569
|
+
* is re-armed after a backoff — and a record that moved inside one of those
|
|
570
|
+
* gaps appears only in that first snapshot. Since this backend now HAS a
|
|
571
|
+
* `watch`, the watcher's periodic re-derivation no longer covers it, so a
|
|
572
|
+
* dropped first snapshot means stale bells and stale views until that record
|
|
573
|
+
* happens to change again. That is exactly the failure this step was supposed
|
|
574
|
+
* to remove.
|
|
575
|
+
*
|
|
576
|
+
* Announcing every record individually is also wrong: it is a refresh storm
|
|
577
|
+
* to every open view, on every mount and every reconnect. `{ kind: "collection" }`
|
|
578
|
+
* says the same thing in one event — the watcher answers it with a full
|
|
579
|
+
* re-derivation plus a sweep, which is precisely "work out what changed while
|
|
580
|
+
* I was not listening".
|
|
581
|
+
*
|
|
582
|
+
* A change this process made itself also arrives here — the write path has
|
|
583
|
+
* already published for it, so the record is reconciled twice. Harmless
|
|
584
|
+
* (reconciling is idempotent) and left alone: suppressing it would mean
|
|
585
|
+
* tracking our own in-flight writes, and getting that wrong loses a real
|
|
586
|
+
* change rather than a duplicate one. */
|
|
587
|
+
function deliverSnapshot(run, ids, initial) {
|
|
588
|
+
if (run.stopped) return;
|
|
589
|
+
if (initial) {
|
|
590
|
+
run.onChange({ kind: "collection" });
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
for (const itemId of ids) run.onChange({
|
|
594
|
+
kind: "item",
|
|
595
|
+
itemId
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
/** A listener died. Firestore never revives one on its own, so the choice is
|
|
599
|
+
* re-subscribe or go dark.
|
|
600
|
+
*
|
|
601
|
+
* NO OVERALL RETRY WINDOW, unlike `hostRunner`'s listener. There, giving up
|
|
602
|
+
* escalates to a lifecycle owner that can re-authenticate; here there is
|
|
603
|
+
* nobody above to escalate to — the watcher registers a mounted collection
|
|
604
|
+
* once and never re-arms it, so "give up" means this collection serves stale
|
|
605
|
+
* data until the server restarts. A capped 30s re-subscribe is cheap and
|
|
606
|
+
* makes recovery automatic when the session comes back.
|
|
607
|
+
*
|
|
608
|
+
* A FATAL error still stops: `permission-denied` is what a revoked membership
|
|
609
|
+
* looks like, and re-listening cannot restore a grant. It is logged as such
|
|
610
|
+
* rather than retried in silence. */
|
|
611
|
+
function handleWatchError(run, error) {
|
|
612
|
+
if (run.stopped) return;
|
|
613
|
+
if (classifyListenerError(error) === "fatal") {
|
|
614
|
+
log.warn("collections", "shared collection listener stopped", {
|
|
615
|
+
aid: run.key.aid,
|
|
616
|
+
cid: run.key.cid,
|
|
617
|
+
error: isRecord(error) && typeof error.message === "string" ? error.message : String(error),
|
|
618
|
+
detail: "live updates for this collection are off until the server re-syncs; a revoked membership looks exactly like this"
|
|
619
|
+
});
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
run.retryTimer = setTimeout(() => subscribeShared(run), backoffDelayMs(run.attempt));
|
|
623
|
+
run.attempt += 1;
|
|
624
|
+
}
|
|
625
|
+
/** (Re-)arm the listener. The handle is fetched HERE rather than captured: a
|
|
626
|
+
* retry may land after the session was replaced, and listening through a
|
|
627
|
+
* closed session's handle would fail forever. */
|
|
628
|
+
function subscribeShared(run) {
|
|
629
|
+
run.retryTimer = null;
|
|
630
|
+
if (run.stopped) return;
|
|
631
|
+
const handle = firestoreHandle();
|
|
632
|
+
if (handle === null) {
|
|
633
|
+
run.retryTimer = setTimeout(() => subscribeShared(run), backoffDelayMs(run.attempt));
|
|
634
|
+
run.attempt += 1;
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
run.detach = handle.docs.watch(sharedItemsPath(run.key), (ids, meta) => {
|
|
638
|
+
run.attempt = 0;
|
|
639
|
+
deliverSnapshot(run, ids, meta.initial);
|
|
640
|
+
}, (error) => handleWatchError(run, error));
|
|
641
|
+
}
|
|
642
|
+
/** Subscribe to a shared collection's records.
|
|
643
|
+
*
|
|
644
|
+
* Returns `null` when there is no session: the watcher reads that as "has a
|
|
645
|
+
* watch but could not arm it", leaves the collection unmounted and retries on
|
|
646
|
+
* the next sync tick — which is the right channel for a connection that has
|
|
647
|
+
* not happened yet. Arming a dead listener instead would mark the slug mounted
|
|
648
|
+
* forever. */
|
|
649
|
+
function armSharedWatch(key, onChange) {
|
|
650
|
+
if (firestoreHandle() === null) return null;
|
|
651
|
+
const run = {
|
|
652
|
+
key,
|
|
653
|
+
onChange,
|
|
654
|
+
stopped: false,
|
|
655
|
+
detach: () => {},
|
|
656
|
+
retryTimer: null,
|
|
657
|
+
attempt: 0
|
|
658
|
+
};
|
|
659
|
+
subscribeShared(run);
|
|
660
|
+
return () => {
|
|
661
|
+
run.stopped = true;
|
|
662
|
+
if (run.retryTimer !== null) clearTimeout(run.retryTimer);
|
|
663
|
+
run.detach();
|
|
664
|
+
};
|
|
665
|
+
}
|
|
502
666
|
/** The store factory registered for `storage.type === "firestore"`.
|
|
503
667
|
* Synchronous and connection-agnostic by contract — see the header. */
|
|
504
668
|
function firestoreStoreFor(collection, opts) {
|
|
@@ -513,14 +677,15 @@ function firestoreStoreFor(collection, opts) {
|
|
|
513
677
|
nativeQuery: false,
|
|
514
678
|
nativePaging: false
|
|
515
679
|
},
|
|
516
|
-
list: async () => firestoreList(keyOf(collection)),
|
|
680
|
+
list: async () => firestoreList(keyOf(collection), primaryKey),
|
|
517
681
|
page: async (pageOpts = {}) => firestorePage(keyOf(collection), primaryKey, pageOpts),
|
|
518
|
-
read: async (itemId) => firestoreRead(keyOf(collection), itemId),
|
|
682
|
+
read: async (itemId) => firestoreRead(keyOf(collection), itemId, primaryKey),
|
|
519
683
|
write: async (itemId, item, writeOpts = {}) => firestoreWrite(keyOf(collection), itemId, item, {
|
|
520
684
|
...ioOpts,
|
|
521
685
|
refuseOverwrite: writeOpts.refuseOverwrite
|
|
522
686
|
}),
|
|
523
|
-
delete: async (itemId) => firestoreDelete(keyOf(collection), itemId, ioOpts)
|
|
687
|
+
delete: async (itemId) => firestoreDelete(keyOf(collection), itemId, ioOpts),
|
|
688
|
+
watch: async (onChange) => armSharedWatch(keyOf(collection), onChange)
|
|
524
689
|
};
|
|
525
690
|
}
|
|
526
691
|
//#endregion
|
|
@@ -810,11 +975,13 @@ async function deleteItem(dataDir, itemId, opts = {}) {
|
|
|
810
975
|
throw err;
|
|
811
976
|
}
|
|
812
977
|
}
|
|
813
|
-
/** Generate a
|
|
814
|
-
*
|
|
815
|
-
*
|
|
978
|
+
/** Generate a record id. Used by POST when the form doesn't carry a
|
|
979
|
+
* primary-key value (UI shortcut — Claude normally derives a semantic
|
|
980
|
+
* id from the record's name). Delegates to the isomorphic generator, so
|
|
981
|
+
* the id the UI pre-fills and the id a blank-id POST mints stay the
|
|
982
|
+
* same thing rather than two implementations that have to agree. */
|
|
816
983
|
function generateItemId() {
|
|
817
|
-
return
|
|
984
|
+
return newItemId();
|
|
818
985
|
}
|
|
819
986
|
/** The item id a CREATE should use for `schema`, or null when the
|
|
820
987
|
* caller should generate one. A singleton collection pins every
|
|
@@ -2972,6 +3139,10 @@ function acceptStorageSchema(storage, opts) {
|
|
|
2972
3139
|
storageFile
|
|
2973
3140
|
};
|
|
2974
3141
|
}
|
|
3142
|
+
if (!hostSupportsSharedCollections()) return {
|
|
3143
|
+
ok: false,
|
|
3144
|
+
reason: "this host does not support shared collections — they live in a project repository, not a managed workspace"
|
|
3145
|
+
};
|
|
2975
3146
|
const manifest = loadAppManifest(opts.workspaceRoot);
|
|
2976
3147
|
if (!manifest.ok) return {
|
|
2977
3148
|
ok: false,
|
|
@@ -3130,6 +3301,6 @@ function toDetail(collection) {
|
|
|
3130
3301
|
};
|
|
3131
3302
|
}
|
|
3132
3303
|
//#endregion
|
|
3133
|
-
export { collectionsRegistriesConfigPath as $, readItem as A, SCHEMA_FILE as B, CollectionQueryZ as C, generateItemId as D, deleteItem as E, loadAppManifest as F, safeRecordId as G, itemFilePath as H, parseAppManifest as I, isBackendUnavailable as J, safeSlugName as K, sharedItemsPath as L, writeItem as M, APP_MANIFEST_FILE as N, isRegularFile as O, appManifestReason as P, collectionChangePayload as Q, pageFromFullRead as R, compileJsonlQuery as S, MAX_QUERY_ROWS as T, resolveDataDir as U, isContainedInRoot as V, resolveTemplatePath as W, archiveDir as X, COLLECTION_ROOT_REQUIRED as Y, collectionChangeKey as Z, dedupeByRecordId as _, toDetail as a,
|
|
3304
|
+
export { collectionsRegistriesConfigPath as $, readItem as A, SCHEMA_FILE as B, CollectionQueryZ as C, generateItemId as D, deleteItem as E, loadAppManifest as F, safeRecordId as G, itemFilePath as H, parseAppManifest as I, isBackendUnavailable as J, safeSlugName as K, sharedItemsPath as L, writeItem as M, APP_MANIFEST_FILE as N, isRegularFile as O, appManifestReason as P, collectionChangePayload as Q, pageFromFullRead as R, compileJsonlQuery as S, MAX_QUERY_ROWS as T, resolveDataDir as U, isContainedInRoot as V, resolveTemplatePath as W, archiveDir as X, COLLECTION_ROOT_REQUIRED as Y, collectionChangeKey as Z, dedupeByRecordId as _, createHostSlot as _t, toDetail as a, localCollectionKey as at, queryCsv as b, resolveMutateSet as c, projectSkillsDir as ct, storeFor as d, setFirestoreAccessor as dt, configureCollectionHost as et, checkpointSqliteDatabase as f, setSharedCollectionsSupport as ft, decodeCsvRecordId as g, createForwardingLogger as gt, csvRowToItem as h, stagingSkillDir as ht, resolvePrimaryField as i, isPresetSlug as it, resolveCreateItemId as j, listItems as k, collectionWritable as l, publishCollectionChange as lt, cacheDir as m, skillsStagingDir as mt, discoverCollections as n, getWorkspaceRoot as nt, toSummary as o, log as ot, MAX_CSV_ROWS as p, sharedCollectionChangePayload as pt, BackendUnavailableError as q, loadCollection as r, hostSupportsSharedCollections as rt, CollectionSchemaZ as s, peekWorkspaceRoot as st, acceptParsedSchema as t, firestoreHandle as tt, readOnlyRefusal as u, setCollectionChangePublisher as ut, encodeCsvRecordId as v, DEFAULT_QUERY_ROWS as w, compileCsvQuery as x, normalizeCsvValue as y, projectItemFields as z };
|
|
3134
3305
|
|
|
3135
|
-
//# sourceMappingURL=discovery-
|
|
3306
|
+
//# sourceMappingURL=discovery-B9zdNkgW.js.map
|