@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.
Files changed (71) 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 +2 -2
  12. package/dist/collection/registry/server/index.js +2 -2
  13. package/dist/collection/server/firestoreDocs.d.ts +18 -0
  14. package/dist/collection/server/host.d.ts +40 -0
  15. package/dist/collection/server/index.cjs +25 -4
  16. package/dist/collection/server/index.d.ts +4 -1
  17. package/dist/collection/server/index.js +4 -4
  18. package/dist/collection/server/io.d.ts +5 -3
  19. package/dist/collection/server/publishChecks.d.ts +29 -0
  20. package/dist/collection/server/publishManifest.d.ts +181 -0
  21. package/dist/collection/server/publishProject.d.ts +255 -0
  22. package/dist/collection/server/validate.d.ts +12 -0
  23. package/dist/collection-watchers/index.cjs +70 -26
  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/{discovery-CdkaURVY.js → discovery-B9zdNkgW.js} +189 -18
  28. package/dist/discovery-B9zdNkgW.js.map +1 -0
  29. package/dist/{discovery-Cw5xWzZD.cjs → discovery-BsXiDZBR.cjs} +226 -43
  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 +6 -6
  34. package/dist/feeds/server/index.js +4 -4
  35. package/dist/firestore/listen.d.ts +4 -0
  36. package/dist/google/index.cjs +5 -5
  37. package/dist/google/index.cjs.map +1 -1
  38. package/dist/google/index.js +2 -2
  39. package/dist/{ingestTypes-CEi-Ot7F.js → ingestTypes-B312BwXg.js} +2 -2
  40. package/dist/{ingestTypes-CEi-Ot7F.js.map → ingestTypes-B312BwXg.js.map} +1 -1
  41. package/dist/{ingestTypes-iaiq33Ph.cjs → ingestTypes-B4XfrUoq.cjs} +3 -3
  42. package/dist/{ingestTypes-iaiq33Ph.cjs.map → ingestTypes-B4XfrUoq.cjs.map} +1 -1
  43. package/dist/{calendarGrid-CQ8MVSRb.js → itemId-DfTm08jm.js} +34 -2
  44. package/dist/itemId-DfTm08jm.js.map +1 -0
  45. package/dist/{calendarGrid-DGILaVxI.cjs → itemId-Dzrr3vs5.cjs} +45 -1
  46. package/dist/itemId-Dzrr3vs5.cjs.map +1 -0
  47. package/dist/listen-D5av7GzB.js +22 -0
  48. package/dist/listen-D5av7GzB.js.map +1 -0
  49. package/dist/listen-UZA68Upt.cjs +45 -0
  50. package/dist/listen-UZA68Upt.cjs.map +1 -0
  51. package/dist/{promptSafety-CpiME8pj.js → promptSafety-CSdyp7RF.js} +2 -2
  52. package/dist/{promptSafety-CpiME8pj.js.map → promptSafety-CSdyp7RF.js.map} +1 -1
  53. package/dist/{promptSafety-NNGViiCr.cjs → promptSafety-CWqYq-NS.cjs} +8 -8
  54. package/dist/{promptSafety-NNGViiCr.cjs.map → promptSafety-CWqYq-NS.cjs.map} +1 -1
  55. package/dist/remote-host/server/hostRunner.d.ts +2 -4
  56. package/dist/remote-host/server/index.cjs +4 -19
  57. package/dist/remote-host/server/index.cjs.map +1 -1
  58. package/dist/remote-host/server/index.js +1 -16
  59. package/dist/remote-host/server/index.js.map +1 -1
  60. package/dist/{server-DdHaX_Uu.js → server-BNe8XMtu.js} +677 -6
  61. package/dist/server-BNe8XMtu.js.map +1 -0
  62. package/dist/{server-9mFaGLdq.cjs → server-CgkkWWBd.cjs} +807 -22
  63. package/dist/server-CgkkWWBd.cjs.map +1 -0
  64. package/package.json +1 -1
  65. package/dist/calendarGrid-CQ8MVSRb.js.map +0 -1
  66. package/dist/calendarGrid-DGILaVxI.cjs.map +0 -1
  67. package/dist/collection/core/shortHexId.d.ts +0 -8
  68. package/dist/discovery-CdkaURVY.js.map +0 -1
  69. package/dist/discovery-Cw5xWzZD.cjs.map +0 -1
  70. package/dist/server-9mFaGLdq.cjs.map +0 -1
  71. 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 AGENT_INGEST_KIND, C as sharedCollectionKey, D as isSafeRecordId, I as isReadOnlySchema, L as storageKindFor, M as FEED_SCHEDULES, N as INGEST_KINDS, O as isSafeSlug, R as fieldText, T as SAFE_SLUG_PATTERN, b as localCollectionKeyOf, j as COMPUTED_TYPES, w as SAFE_RECORD_ID_PATTERN, y as isValidCollectionName, z as fieldTextOrNull } from "./calendarGrid-CQ8MVSRb.js";
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
- function toItem(data) {
421
- return isRecord(data) ? data : null;
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 short random hex id. Used by POST when the form doesn't
814
- * carry a primary-key value (UI shortcut — Claude normally derives a
815
- * semantic id from the record's name). */
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 randomBytes(4).toString("hex");
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, log as at, queryCsv as b, resolveMutateSet as c, publishCollectionChange as ct, storeFor as d, sharedCollectionChangePayload as dt, configureCollectionHost as et, checkpointSqliteDatabase as f, skillsStagingDir as ft, decodeCsvRecordId as g, csvRowToItem as h, createHostSlot as ht, resolvePrimaryField as i, localCollectionKey as it, resolveCreateItemId as j, listItems as k, collectionWritable as l, setCollectionChangePublisher as lt, cacheDir as m, createForwardingLogger as mt, discoverCollections as n, getWorkspaceRoot as nt, toSummary as o, peekWorkspaceRoot as ot, MAX_CSV_ROWS as p, stagingSkillDir as pt, BackendUnavailableError as q, loadCollection as r, isPresetSlug as rt, CollectionSchemaZ as s, projectSkillsDir as st, acceptParsedSchema as t, firestoreHandle as tt, readOnlyRefusal as u, setFirestoreAccessor as ut, encodeCsvRecordId as v, DEFAULT_QUERY_ROWS as w, compileCsvQuery as x, normalizeCsvValue as y, projectItemFields as z };
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-CdkaURVY.js.map
3306
+ //# sourceMappingURL=discovery-B9zdNkgW.js.map