@mulmoclaude/core 3.5.0 → 3.6.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/assets/helps/collection-skills.md +49 -1
- package/assets/helps/error-recovery.md +53 -0
- package/dist/calendarGrid-CQ8MVSRb.js.map +1 -1
- package/dist/calendarGrid-DGILaVxI.cjs.map +1 -1
- package/dist/collection/core/schema.d.ts +8 -1
- package/dist/collection/core/schemaZ.d.ts +36 -26
- package/dist/collection/firestore.cjs +51 -0
- package/dist/collection/firestore.cjs.map +1 -0
- package/dist/collection/firestore.d.ts +1 -0
- package/dist/collection/firestore.js +50 -0
- package/dist/collection/firestore.js.map +1 -0
- package/dist/collection/registry/server/index.cjs +2 -2
- package/dist/collection/registry/server/index.js +2 -2
- package/dist/collection/server/appManifest.d.ts +53 -0
- package/dist/collection/server/delete.d.ts +10 -0
- package/dist/collection/server/discoveredCollection.d.ts +10 -0
- package/dist/collection/server/discovery.d.ts +1 -0
- package/dist/collection/server/firestoreDocs.d.ts +39 -0
- package/dist/collection/server/firestoreStore.d.ts +14 -0
- package/dist/collection/server/host.d.ts +41 -0
- package/dist/collection/server/index.cjs +9 -2
- package/dist/collection/server/index.d.ts +4 -1
- package/dist/collection/server/index.js +3 -3
- package/dist/collection-watchers/index.cjs +132 -41
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.js +132 -41
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/collection-watchers/reconciler.d.ts +1 -1
- package/dist/{discovery-DH9wweuj.js → discovery-CdkaURVY.js} +368 -57
- package/dist/discovery-CdkaURVY.js.map +1 -0
- package/dist/{discovery-Ck4AqikY.cjs → discovery-Cw5xWzZD.cjs} +407 -54
- package/dist/discovery-Cw5xWzZD.cjs.map +1 -0
- package/dist/feeds/server/index.cjs +2 -2
- package/dist/feeds/server/index.js +2 -2
- package/dist/google/index.cjs +1 -1
- package/dist/google/index.js +1 -1
- package/dist/{server-5EMj3naj.cjs → server-9mFaGLdq.cjs} +23 -4
- package/dist/server-9mFaGLdq.cjs.map +1 -0
- package/dist/{server-BiRLLMpW.js → server-DdHaX_Uu.js} +23 -4
- package/dist/server-DdHaX_Uu.js.map +1 -0
- package/package.json +7 -1
- package/dist/discovery-Ck4AqikY.cjs.map +0 -1
- package/dist/discovery-DH9wweuj.js.map +0 -1
- package/dist/server-5EMj3naj.cjs.map +0 -1
- package/dist/server-BiRLLMpW.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { collection, doc, getDoc, getDocs, orderBy, query, runTransaction, setDoc } from "firebase/firestore";
|
|
2
|
+
//#region src/collection/server/firestoreDocs.ts
|
|
3
|
+
/** The real implementation over the modular SDK.
|
|
4
|
+
*
|
|
5
|
+
* THE RECORD IS THE DOCUMENT. Its fields are written at the top level, not
|
|
6
|
+
* nested under a `data` key. This is not a matter of taste: the deployed
|
|
7
|
+
* security rules read `request.resource.data.<field>` and
|
|
8
|
+
* `resource.data[submit.emailField]` — a wrapper would put every field one
|
|
9
|
+
* level down, so the required-field checks, the status state machine and the
|
|
10
|
+
* "your own row" predicate would all read absent values and fail closed. A
|
|
11
|
+
* shared record's shape is part of the authorization contract.
|
|
12
|
+
*
|
|
13
|
+
* `orderBy("__name__")` orders by DOCUMENT ID. Ordering by a record field
|
|
14
|
+
* would silently EXCLUDE documents missing that field (the trap
|
|
15
|
+
* `remote-host/server/hostRunner.ts:158-169` documents), turning a read into a
|
|
16
|
+
* partial one; the document id is always present and gives the stable order
|
|
17
|
+
* the store contract requires. */
|
|
18
|
+
function createFirestoreDocs(database) {
|
|
19
|
+
return {
|
|
20
|
+
list: async (collectionPath) => {
|
|
21
|
+
return (await getDocs(query(collection(database, collectionPath), orderBy("__name__")))).docs.map((entry) => ({
|
|
22
|
+
id: entry.id,
|
|
23
|
+
data: entry.data()
|
|
24
|
+
}));
|
|
25
|
+
},
|
|
26
|
+
get: async (collectionPath, docId) => {
|
|
27
|
+
const snapshot = await getDoc(doc(database, collectionPath, docId));
|
|
28
|
+
return snapshot.exists() ? snapshot.data() : null;
|
|
29
|
+
},
|
|
30
|
+
set: async (collectionPath, docId, data) => {
|
|
31
|
+
await setDoc(doc(database, collectionPath, docId), data);
|
|
32
|
+
},
|
|
33
|
+
create: (collectionPath, docId, data) => runTransaction(database, async (transaction) => {
|
|
34
|
+
const ref = doc(database, collectionPath, docId);
|
|
35
|
+
if ((await transaction.get(ref)).exists()) return false;
|
|
36
|
+
transaction.set(ref, data);
|
|
37
|
+
return true;
|
|
38
|
+
}),
|
|
39
|
+
delete: (collectionPath, docId) => runTransaction(database, async (transaction) => {
|
|
40
|
+
const ref = doc(database, collectionPath, docId);
|
|
41
|
+
if (!(await transaction.get(ref)).exists()) return false;
|
|
42
|
+
transaction.delete(ref);
|
|
43
|
+
return true;
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { createFirestoreDocs };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=firestore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firestore.js","names":[],"sources":["../../src/collection/server/firestoreDocs.ts"],"sourcesContent":["// The seam between the firestore store and the Firestore SDK.\n//\n// The modular SDK is function-based (`getDocs(query(collection(db, …)))`), so a\n// store that imported those functions directly could not be tested without a\n// real backend — a fake `db` would still be handed to the real functions.\n// Narrowing to this interface makes the backend swappable: core ships\n// `createFirestoreDocs` over the real SDK, and the tests inject an in-memory\n// fake that satisfies the same shape. That is what lets this repository's tests\n// run with no API key and no network.\n//\n// Deliberately minimal and id-keyed: no query builder, no field ordering, no\n// cursors. Everything the store needs is \"the documents of one collection path,\n// ordered by document id\" — see the store header for why field ordering is\n// avoided entirely.\n//\n// The collection path is an ARGUMENT, not something this module composes. It\n// owns the SDK calls; `firestoreStore` owns where the documents live.\n\nimport {\n collection as firestoreCollection,\n doc,\n getDoc,\n getDocs,\n orderBy,\n query as firestoreQuery,\n runTransaction,\n setDoc,\n type Firestore,\n} from \"firebase/firestore\";\n\n/** One stored record document. `data` is the document's own fields — the\n * record itself, not a wrapper around it; see `set` below. The store\n * validates its shape, because a document written by hand could be\n * anything. */\nexport interface FirestoreDoc {\n id: string;\n data: unknown;\n}\n\nexport interface FirestoreDocs {\n /** Every document under `collectionPath`, ordered by document id. */\n list: (collectionPath: string) => Promise<FirestoreDoc[]>;\n /** One document's fields, or null when it doesn't exist. */\n get: (collectionPath: string, docId: string) => Promise<unknown | null>;\n /** Create or replace. */\n set: (collectionPath: string, docId: string, data: Record<string, unknown>) => Promise<void>;\n /** Create only. Returns false when the id already exists — atomic, so two\n * concurrent creates can't both observe \"missing\". */\n create: (collectionPath: string, docId: string, data: Record<string, unknown>) => Promise<boolean>;\n /** Delete. Returns false when the id didn't exist, so a caller can tell a\n * real delete from a typo'd id. */\n delete: (collectionPath: string, docId: string) => Promise<boolean>;\n}\n\n/** The real implementation over the modular SDK.\n *\n * THE RECORD IS THE DOCUMENT. Its fields are written at the top level, not\n * nested under a `data` key. This is not a matter of taste: the deployed\n * security rules read `request.resource.data.<field>` and\n * `resource.data[submit.emailField]` — a wrapper would put every field one\n * level down, so the required-field checks, the status state machine and the\n * \"your own row\" predicate would all read absent values and fail closed. A\n * shared record's shape is part of the authorization contract.\n *\n * `orderBy(\"__name__\")` orders by DOCUMENT ID. Ordering by a record field\n * would silently EXCLUDE documents missing that field (the trap\n * `remote-host/server/hostRunner.ts:158-169` documents), turning a read into a\n * partial one; the document id is always present and gives the stable order\n * the store contract requires. */\nexport function createFirestoreDocs(database: Firestore): FirestoreDocs {\n return {\n list: async (collectionPath) => {\n const snapshot = await getDocs(firestoreQuery(firestoreCollection(database, collectionPath), orderBy(\"__name__\")));\n return snapshot.docs.map((entry) => ({ id: entry.id, data: entry.data() }));\n },\n get: async (collectionPath, docId) => {\n const snapshot = await getDoc(doc(database, collectionPath, docId));\n return snapshot.exists() ? snapshot.data() : null;\n },\n set: async (collectionPath, docId, data) => {\n await setDoc(doc(database, collectionPath, docId), data);\n },\n create: (collectionPath, docId, data) =>\n runTransaction(database, async (transaction) => {\n const ref = doc(database, collectionPath, docId);\n const existing = await transaction.get(ref);\n if (existing.exists()) return false;\n transaction.set(ref, data);\n return true;\n }),\n // Firestore's deleteDoc succeeds on a missing document, so an existence\n // check is what makes \"deleted\" distinguishable from \"there was nothing\".\n // It runs INSIDE the transaction (like `create`): a plain get-then-delete\n // would report `true` for a document a concurrent client had already\n // removed, i.e. claim a delete this call never performed.\n delete: (collectionPath, docId) =>\n runTransaction(database, async (transaction) => {\n const ref = doc(database, collectionPath, docId);\n const existing = await transaction.get(ref);\n if (!existing.exists()) return false;\n transaction.delete(ref);\n return true;\n }),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqEA,SAAgB,oBAAoB,UAAoC;CACtE,OAAO;EACL,MAAM,OAAO,mBAAmB;GAE9B,QAAO,MADgB,QAAQ,MAAe,WAAoB,UAAU,cAAc,GAAG,QAAQ,UAAU,CAAC,CAAC,EAAA,CACjG,KAAK,KAAK,WAAW;IAAE,IAAI,MAAM;IAAI,MAAM,MAAM,KAAK;GAAE,EAAE;EAC5E;EACA,KAAK,OAAO,gBAAgB,UAAU;GACpC,MAAM,WAAW,MAAM,OAAO,IAAI,UAAU,gBAAgB,KAAK,CAAC;GAClE,OAAO,SAAS,OAAO,IAAI,SAAS,KAAK,IAAI;EAC/C;EACA,KAAK,OAAO,gBAAgB,OAAO,SAAS;GAC1C,MAAM,OAAO,IAAI,UAAU,gBAAgB,KAAK,GAAG,IAAI;EACzD;EACA,SAAS,gBAAgB,OAAO,SAC9B,eAAe,UAAU,OAAO,gBAAgB;GAC9C,MAAM,MAAM,IAAI,UAAU,gBAAgB,KAAK;GAE/C,KAAI,MADmB,YAAY,IAAI,GAAG,EAAA,CAC7B,OAAO,GAAG,OAAO;GAC9B,YAAY,IAAI,KAAK,IAAI;GACzB,OAAO;EACT,CAAC;EAMH,SAAS,gBAAgB,UACvB,eAAe,UAAU,OAAO,gBAAgB;GAC9C,MAAM,MAAM,IAAI,UAAU,gBAAgB,KAAK;GAE/C,IAAI,EAAC,MADkB,YAAY,IAAI,GAAG,EAAA,CAC5B,OAAO,GAAG,OAAO;GAC/B,YAAY,OAAO,GAAG;GACtB,OAAO;EACT,CAAC;CACL;AACF"}
|
|
@@ -4,9 +4,9 @@ const require_dist = require("../../../dist-CsgSfWwR.cjs");
|
|
|
4
4
|
const require_root = require("../../../root-rPH6FGDT.cjs");
|
|
5
5
|
const require_relPath = require("../../../relPath-CTAjGdCL.cjs");
|
|
6
6
|
const require_utils_fetch = require("../../../utils/fetch.cjs");
|
|
7
|
-
const require_discovery = require("../../../discovery-
|
|
7
|
+
const require_discovery = require("../../../discovery-Cw5xWzZD.cjs");
|
|
8
8
|
const require_templatePath = require("../../../templatePath-27vUZowm.cjs");
|
|
9
|
-
const require_server = require("../../../server-
|
|
9
|
+
const require_server = require("../../../server-9mFaGLdq.cjs");
|
|
10
10
|
const require_skill_bridge_index = require("../../../skill-bridge/index.cjs");
|
|
11
11
|
const require_types = require("../../../types-CNqkLT4M.cjs");
|
|
12
12
|
let node_fs = require("node:fs");
|
|
@@ -2,9 +2,9 @@ import { t as errorMessage } from "../../../dist-D8zokgGo.js";
|
|
|
2
2
|
import { n as writeFileAtomic } from "../../../root-BMroU_mB.js";
|
|
3
3
|
import { n as toPosixRelPath } from "../../../relPath-DW8MC8VO.js";
|
|
4
4
|
import { fetchWithTimeout } from "../../../utils/fetch.js";
|
|
5
|
-
import { $ as
|
|
5
|
+
import { $ as collectionsRegistriesConfigPath, G as safeRecordId, at as log, ft as skillsStagingDir, nt as getWorkspaceRoot, r as loadCollection, s as CollectionSchemaZ, st as projectSkillsDir, t as acceptParsedSchema } from "../../../discovery-CdkaURVY.js";
|
|
6
6
|
import { t as isSafeActionTemplatePath } from "../../../templatePath-k_WNbL_Q.js";
|
|
7
|
-
import { g as ONE_SECOND_MS } from "../../../server-
|
|
7
|
+
import { g as ONE_SECOND_MS } from "../../../server-DdHaX_Uu.js";
|
|
8
8
|
import { claudeSkillDir, dataSkillDir, mirrorSkillWrite } from "../../../skill-bridge/index.js";
|
|
9
9
|
import { n as parseRegistryIndex, r as isRecord, t as OFFICIAL_REGISTRY_NAME } from "../../../types-BKVZrtyc.js";
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The app declaration's filename, at the repository root. */
|
|
2
|
+
export declare const APP_MANIFEST_FILE = "app.json";
|
|
3
|
+
/** What this step reads out of `app.json`. Deliberately one field: every key
|
|
4
|
+
* added here is a key `publish` and this loader could disagree about. */
|
|
5
|
+
export interface AppManifest {
|
|
6
|
+
/** The app id — the `{aid}` in `apps/{aid}/collections/{cid}/items`. */
|
|
7
|
+
aid: string;
|
|
8
|
+
}
|
|
9
|
+
/** Why a root has no usable `aid`. Returned rather than thrown because the
|
|
10
|
+
* caller is an acceptance gate whose whole job is to turn this into a
|
|
11
|
+
* one-line reason a collection was skipped. */
|
|
12
|
+
export type AppManifestFailure = {
|
|
13
|
+
kind: "missing";
|
|
14
|
+
} | {
|
|
15
|
+
kind: "unreadable";
|
|
16
|
+
detail: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "malformed";
|
|
19
|
+
detail: string;
|
|
20
|
+
};
|
|
21
|
+
export type AppManifestResult = {
|
|
22
|
+
ok: true;
|
|
23
|
+
manifest: AppManifest;
|
|
24
|
+
} | ({
|
|
25
|
+
ok: false;
|
|
26
|
+
} & AppManifestFailure);
|
|
27
|
+
/** Read `<root>/app.json` and return its `aid`.
|
|
28
|
+
*
|
|
29
|
+
* SYNCHRONOUS on purpose. The caller is `acceptParsedSchema`, which is sync
|
|
30
|
+
* and is shared by discovery and `manageCollection`'s `putSchema` precisely so
|
|
31
|
+
* that a schema which would be skipped on the next discovery cannot be written
|
|
32
|
+
* as if it were fine. Making this async would split that gate in two, and the
|
|
33
|
+
* half that lost the check is the half the author sees. The file is a few
|
|
34
|
+
* hundred bytes and is read once per firestore collection per discovery pass.
|
|
35
|
+
*
|
|
36
|
+
* Never cached. `app.json` is edited by hand and by the agent, and a cache
|
|
37
|
+
* here would mean the app a collection points at is whatever it was when the
|
|
38
|
+
* server started. */
|
|
39
|
+
export declare function loadAppManifest(root: string): AppManifestResult;
|
|
40
|
+
/** The parse half, exported so it can be tested without a filesystem.
|
|
41
|
+
*
|
|
42
|
+
* `aid` is validated with `isValidCollectionName` — the SAME predicate the
|
|
43
|
+
* `CollectionKey` constructors apply — rather than a rule of its own. An `aid`
|
|
44
|
+
* is re-encoded downstream as a Firestore document id, a pubsub channel
|
|
45
|
+
* segment and a cache key, each with a different character that would break
|
|
46
|
+
* it; one rule, stated once, is what keeps those layers from disagreeing.
|
|
47
|
+
* Rejecting here rather than at `sharedCollectionKey` only changes WHERE the
|
|
48
|
+
* author is told: a reason on the collection they wrote, instead of a throw
|
|
49
|
+
* from inside a store call. */
|
|
50
|
+
export declare function parseAppManifest(raw: string): AppManifestResult;
|
|
51
|
+
/** The failure as the one line an author can act on. Kept next to the failure
|
|
52
|
+
* type so a new variant cannot be added without wording it. */
|
|
53
|
+
export declare function appManifestReason(failure: AppManifestFailure, root: string): string;
|
|
@@ -15,6 +15,16 @@ export type DeleteCollectionResult = {
|
|
|
15
15
|
} | {
|
|
16
16
|
kind: "path-escape";
|
|
17
17
|
slug: string;
|
|
18
|
+
}
|
|
19
|
+
/** A `storage: firestore` collection: its records are documents of a SHARED
|
|
20
|
+
* app, which this delete can neither archive nor remove. Deleting the skill
|
|
21
|
+
* would leave them in `apps/{aid}/collections/{cid}/items` with no
|
|
22
|
+
* collection left to reach them through — and they are not only this
|
|
23
|
+
* machine's: other members' live views are served from the same documents.
|
|
24
|
+
* Refused outright rather than done partially. */
|
|
25
|
+
| {
|
|
26
|
+
kind: "unsupported-backend";
|
|
27
|
+
slug: string;
|
|
18
28
|
};
|
|
19
29
|
type DeleteRefusal = Exclude<DeleteCollectionResult, {
|
|
20
30
|
kind: "ok";
|
|
@@ -24,6 +24,16 @@ export interface LoadedCollection {
|
|
|
24
24
|
* `storage.path`, e.g. a SQLite database — same containment as dataDir).
|
|
25
25
|
* Present iff the schema declares `storage`. */
|
|
26
26
|
storageFile?: string;
|
|
27
|
+
/** The app id this collection's records belong to, from the repository's
|
|
28
|
+
* `app.json`. Present iff the schema declares `storage.type: "firestore"` —
|
|
29
|
+
* a shared collection's identity is `(appId, slug)`, and the store builds
|
|
30
|
+
* `apps/{appId}/collections/{slug}/items` from it.
|
|
31
|
+
*
|
|
32
|
+
* Resolved by discovery, exactly like `storageFile`, so the identity is
|
|
33
|
+
* settled ONCE and the store never reads `app.json` on a read path. Absent
|
|
34
|
+
* is not a fallback: a firestore schema whose root declares no `aid` is
|
|
35
|
+
* REFUSED at discovery, so this is present whenever the backend needs it. */
|
|
36
|
+
appId?: string;
|
|
27
37
|
/** Absolute path to the skill directory this collection was loaded from
|
|
28
38
|
* (`<skillsRoot>/<slug>/`). Action templates are read from here, path-safely. */
|
|
29
39
|
skillDir: string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Firestore } from 'firebase/firestore';
|
|
2
|
+
/** One stored record document. `data` is the document's own fields — the
|
|
3
|
+
* record itself, not a wrapper around it; see `set` below. The store
|
|
4
|
+
* validates its shape, because a document written by hand could be
|
|
5
|
+
* anything. */
|
|
6
|
+
export interface FirestoreDoc {
|
|
7
|
+
id: string;
|
|
8
|
+
data: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface FirestoreDocs {
|
|
11
|
+
/** Every document under `collectionPath`, ordered by document id. */
|
|
12
|
+
list: (collectionPath: string) => Promise<FirestoreDoc[]>;
|
|
13
|
+
/** One document's fields, or null when it doesn't exist. */
|
|
14
|
+
get: (collectionPath: string, docId: string) => Promise<unknown | null>;
|
|
15
|
+
/** Create or replace. */
|
|
16
|
+
set: (collectionPath: string, docId: string, data: Record<string, unknown>) => Promise<void>;
|
|
17
|
+
/** Create only. Returns false when the id already exists — atomic, so two
|
|
18
|
+
* concurrent creates can't both observe "missing". */
|
|
19
|
+
create: (collectionPath: string, docId: string, data: Record<string, unknown>) => Promise<boolean>;
|
|
20
|
+
/** Delete. Returns false when the id didn't exist, so a caller can tell a
|
|
21
|
+
* real delete from a typo'd id. */
|
|
22
|
+
delete: (collectionPath: string, docId: string) => Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
/** The real implementation over the modular SDK.
|
|
25
|
+
*
|
|
26
|
+
* THE RECORD IS THE DOCUMENT. Its fields are written at the top level, not
|
|
27
|
+
* nested under a `data` key. This is not a matter of taste: the deployed
|
|
28
|
+
* security rules read `request.resource.data.<field>` and
|
|
29
|
+
* `resource.data[submit.emailField]` — a wrapper would put every field one
|
|
30
|
+
* level down, so the required-field checks, the status state machine and the
|
|
31
|
+
* "your own row" predicate would all read absent values and fail closed. A
|
|
32
|
+
* shared record's shape is part of the authorization contract.
|
|
33
|
+
*
|
|
34
|
+
* `orderBy("__name__")` orders by DOCUMENT ID. Ordering by a record field
|
|
35
|
+
* would silently EXCLUDE documents missing that field (the trap
|
|
36
|
+
* `remote-host/server/hostRunner.ts:158-169` documents), turning a read into a
|
|
37
|
+
* partial one; the document id is always present and gives the stable order
|
|
38
|
+
* the store contract requires. */
|
|
39
|
+
export declare function createFirestoreDocs(database: Firestore): FirestoreDocs;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SharedCollectionKey } from '../core/collectionKey';
|
|
2
|
+
import { LoadedCollection } from './discoveredCollection';
|
|
3
|
+
import { IoOptions } from './io';
|
|
4
|
+
import { CollectionStore } from './store';
|
|
5
|
+
/** The records subcollection of one shared collection.
|
|
6
|
+
*
|
|
7
|
+
* Takes a KEY, never loose strings, and the key is the only way to reach this
|
|
8
|
+
* function. `sharedCollectionKey` is where the name rule lives (the charset a
|
|
9
|
+
* Firestore document id, a pubsub channel segment and the completion-bell id
|
|
10
|
+
* must all survive), so building a path cannot be a way around it. */
|
|
11
|
+
export declare function sharedItemsPath(key: SharedCollectionKey): string;
|
|
12
|
+
/** The store factory registered for `storage.type === "firestore"`.
|
|
13
|
+
* Synchronous and connection-agnostic by contract — see the header. */
|
|
14
|
+
export declare function firestoreStoreFor(collection: LoadedCollection, opts: IoOptions): CollectionStore;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { canonicalRoot } from '../../files/root.js';
|
|
2
2
|
import { CollectionKey } from '../core/collectionKey.js';
|
|
3
3
|
import { StructuredLogger } from '../../host/hostSlot.js';
|
|
4
|
+
import { FirestoreDocs } from './firestoreDocs';
|
|
4
5
|
/** Public alias of the shared `StructuredLogger` — keeps the domain surface name-stable. */
|
|
5
6
|
export type CollectionLogger = StructuredLogger;
|
|
6
7
|
/** Re-exported so collection-engine callers (the watcher, the reconciler) keep
|
|
@@ -96,6 +97,31 @@ export interface CollectionHost {
|
|
|
96
97
|
/** True for a preset-skill slug (host-owned naming convention). */
|
|
97
98
|
isPresetSlug: (slug: string) => boolean;
|
|
98
99
|
}
|
|
100
|
+
/** The authenticated Firestore access a shared collection is served through.
|
|
101
|
+
*
|
|
102
|
+
* It carries NO uid, and that is a decision rather than an omission. Nothing
|
|
103
|
+
* about a shared collection is keyed by uid any more: the documents live at
|
|
104
|
+
* `apps/{aid}/collections/{cid}/items`, and the deployed rules authorize on
|
|
105
|
+
* `request.auth.token.email` against the app's member roster. A uid kept
|
|
106
|
+
* "just for identity" would be a value nothing checks, sitting next to a path
|
|
107
|
+
* it no longer determines — which is how a later reader ends up deriving a
|
|
108
|
+
* path from it again.
|
|
109
|
+
*
|
|
110
|
+
* `email` is what the rules actually evaluate, so it is what makes a refusal
|
|
111
|
+
* explainable: `permission-denied` is the most common failure a shared
|
|
112
|
+
* collection has and the least informative, and naming the principal turns it
|
|
113
|
+
* into "signed in as a@b — this app's roster may not list you". A session with
|
|
114
|
+
* no verified email is NOT a session here (the accessor answers null): public
|
|
115
|
+
* anonymous submission is a visitor's path through the published web app, not
|
|
116
|
+
* the host's.
|
|
117
|
+
*
|
|
118
|
+
* `docs` is the narrow document interface rather than a raw `Firestore` so the
|
|
119
|
+
* backend stays testable: core ships `createFirestoreDocs` over the real SDK
|
|
120
|
+
* (`firestoreDocs.ts`) and tests inject an in-memory fake. */
|
|
121
|
+
export interface FirestoreHandle {
|
|
122
|
+
docs: FirestoreDocs;
|
|
123
|
+
email: string;
|
|
124
|
+
}
|
|
99
125
|
/** A collection's records changed on disk. Carries the `slug` so the host can
|
|
100
126
|
* publish on a per-collection channel; `ids` lists the affected record ids
|
|
101
127
|
* when known (a consumer may ignore them and refetch the whole collection),
|
|
@@ -173,6 +199,21 @@ export declare function setCollectionChangePublisher(publish: CollectionChangePu
|
|
|
173
199
|
* expected to be fire-and-forget (it wraps its own pubsub call in try/catch),
|
|
174
200
|
* so this stays a thin pass-through and never throws into the write. */
|
|
175
201
|
export declare function publishCollectionChange(payload: CollectionChangePayload): void;
|
|
202
|
+
/** Wire the accessor for the host's authenticated Firestore session.
|
|
203
|
+
*
|
|
204
|
+
* Separate from `configureCollectionHost` for the same reason
|
|
205
|
+
* `setCollectionChangePublisher` is: the host binding is set at the top of
|
|
206
|
+
* server startup, but this session doesn't exist until the user connects
|
|
207
|
+
* remote-host (and closes again on disconnect), so it cannot be part of a
|
|
208
|
+
* one-shot binding. Optional — left unwired, only shared collections are
|
|
209
|
+
* affected, and they report "not connected". Pass `null` to detach. */
|
|
210
|
+
export declare function setFirestoreAccessor(accessor: (() => FirestoreHandle | null) | null): void;
|
|
211
|
+
/** The host's live Firestore access, or null when there is no session (or the
|
|
212
|
+
* host never wired one — every non-shared backend leaves it unset).
|
|
213
|
+
* Callers MUST surface null as an actionable "connect remote-host first",
|
|
214
|
+
* never as an empty result: silence would be indistinguishable from a
|
|
215
|
+
* collection that genuinely has no records. */
|
|
216
|
+
export declare function firestoreHandle(): FirestoreHandle | null;
|
|
176
217
|
/** The configured workspace root. Throws if the host never configured one —
|
|
177
218
|
* and, under an explicit-root binding (`workspaceRoot: null`), throws rather
|
|
178
219
|
* than guessing, which is the whole point of that mode: on a multi-root host
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_root = require("../../root-rPH6FGDT.cjs");
|
|
3
3
|
const require_calendarGrid = require("../../calendarGrid-DGILaVxI.cjs");
|
|
4
|
-
const require_discovery = require("../../discovery-
|
|
4
|
+
const require_discovery = require("../../discovery-Cw5xWzZD.cjs");
|
|
5
5
|
const require_templatePath = require("../../templatePath-27vUZowm.cjs");
|
|
6
|
-
const require_server = require("../../server-
|
|
6
|
+
const require_server = require("../../server-9mFaGLdq.cjs");
|
|
7
|
+
exports.APP_MANIFEST_FILE = require_discovery.APP_MANIFEST_FILE;
|
|
7
8
|
exports.BackendUnavailableError = require_discovery.BackendUnavailableError;
|
|
8
9
|
exports.COLLECTION_ROOT_REQUIRED = require_discovery.COLLECTION_ROOT_REQUIRED;
|
|
9
10
|
exports.COMPUTED_TYPES = require_calendarGrid.COMPUTED_TYPES;
|
|
@@ -17,6 +18,7 @@ exports.MAX_UNSELECTIVE_ITEMS = require_server.MAX_UNSELECTIVE_ITEMS;
|
|
|
17
18
|
exports.SCHEMA_FILE = require_discovery.SCHEMA_FILE;
|
|
18
19
|
exports.acceptParsedSchema = require_discovery.acceptParsedSchema;
|
|
19
20
|
exports.advanceTriggerDate = require_server.advanceTriggerDate;
|
|
21
|
+
exports.appManifestReason = require_discovery.appManifestReason;
|
|
20
22
|
exports.applyMutateAction = require_server.applyMutateAction;
|
|
21
23
|
exports.buildActionSeedPrompt = require_server.buildActionSeedPrompt;
|
|
22
24
|
exports.buildCollectionActionSeedPrompt = require_server.buildCollectionActionSeedPrompt;
|
|
@@ -42,6 +44,7 @@ exports.deleteItem = require_discovery.deleteItem;
|
|
|
42
44
|
exports.discoverCollections = require_discovery.discoverCollections;
|
|
43
45
|
exports.encodeCsvRecordId = require_discovery.encodeCsvRecordId;
|
|
44
46
|
exports.enrichItems = require_server.enrichItems;
|
|
47
|
+
exports.firestoreHandle = require_discovery.firestoreHandle;
|
|
45
48
|
exports.firstMutateParamProblem = require_server.firstMutateParamProblem;
|
|
46
49
|
exports.formatCivil = require_server.formatCivil;
|
|
47
50
|
exports.generateItemId = require_discovery.generateItemId;
|
|
@@ -56,6 +59,7 @@ exports.isSafeTemplatePath = require_templatePath.isSafeTemplatePath;
|
|
|
56
59
|
exports.isTriggerDue = require_server.isTriggerDue;
|
|
57
60
|
exports.itemFilePath = require_discovery.itemFilePath;
|
|
58
61
|
exports.listItems = require_discovery.listItems;
|
|
62
|
+
exports.loadAppManifest = require_discovery.loadAppManifest;
|
|
59
63
|
exports.loadCollection = require_discovery.loadCollection;
|
|
60
64
|
exports.localCollectionKey = require_discovery.localCollectionKey;
|
|
61
65
|
exports.log = require_discovery.log;
|
|
@@ -63,6 +67,7 @@ exports.makeManageCollectionTool = require_server.makeManageCollectionTool;
|
|
|
63
67
|
exports.maybeSpawnSuccessor = require_server.maybeSpawnSuccessor;
|
|
64
68
|
exports.normalizeCsvValue = require_discovery.normalizeCsvValue;
|
|
65
69
|
exports.pageFromFullRead = require_discovery.pageFromFullRead;
|
|
70
|
+
exports.parseAppManifest = require_discovery.parseAppManifest;
|
|
66
71
|
exports.parseCivil = require_server.parseCivil;
|
|
67
72
|
exports.peekWorkspaceRoot = require_discovery.peekWorkspaceRoot;
|
|
68
73
|
exports.projectItemFields = require_discovery.projectItemFields;
|
|
@@ -85,7 +90,9 @@ exports.safeRecordId = require_discovery.safeRecordId;
|
|
|
85
90
|
exports.safeSlugName = require_discovery.safeSlugName;
|
|
86
91
|
exports.schemaRelations = require_server.schemaRelations;
|
|
87
92
|
exports.setCollectionChangePublisher = require_discovery.setCollectionChangePublisher;
|
|
93
|
+
exports.setFirestoreAccessor = require_discovery.setFirestoreAccessor;
|
|
88
94
|
exports.sharedCollectionChangePayload = require_discovery.sharedCollectionChangePayload;
|
|
95
|
+
exports.sharedItemsPath = require_discovery.sharedItemsPath;
|
|
89
96
|
exports.storeFor = require_discovery.storeFor;
|
|
90
97
|
exports.successorId = require_server.successorId;
|
|
91
98
|
exports.toDetail = require_discovery.toDetail;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { configureCollectionHost, canonicalRoot, getWorkspaceRoot, peekWorkspaceRoot, COLLECTION_ROOT_REQUIRED, log, setCollectionChangePublisher, publishCollectionChange, collectionChangePayload, sharedCollectionChangePayload, collectionChangeKey, localCollectionKey, type CollectionHost, type CollectionLogger, type CollectionChangePayload, type LocalCollectionChange, type SharedCollectionChange, } from './host';
|
|
1
|
+
export { configureCollectionHost, canonicalRoot, getWorkspaceRoot, peekWorkspaceRoot, COLLECTION_ROOT_REQUIRED, log, setCollectionChangePublisher, publishCollectionChange, collectionChangePayload, sharedCollectionChangePayload, collectionChangeKey, localCollectionKey, type CollectionHost, type CollectionLogger, type CollectionChangePayload, firestoreHandle, setFirestoreAccessor, type FirestoreHandle, type LocalCollectionChange, type SharedCollectionChange, } from './host';
|
|
2
|
+
export type { FirestoreDoc, FirestoreDocs } from './firestoreDocs';
|
|
3
|
+
export { sharedItemsPath } from './firestoreStore';
|
|
4
|
+
export { loadAppManifest, parseAppManifest, appManifestReason, APP_MANIFEST_FILE, type AppManifest, type AppManifestResult } from './appManifest';
|
|
2
5
|
export type { LoadedCollection } from './discoveredCollection';
|
|
3
6
|
export * from './paths';
|
|
4
7
|
export * from './templatePath';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as canonicalRoot } from "../../root-BMroU_mB.js";
|
|
2
2
|
import { j as COMPUTED_TYPES } from "../../calendarGrid-CQ8MVSRb.js";
|
|
3
|
-
import {
|
|
3
|
+
import { A as readItem, B as SCHEMA_FILE, C as CollectionQueryZ, D as generateItemId, E as deleteItem, F as loadAppManifest, G as safeRecordId, H as itemFilePath, I as parseAppManifest, J as isBackendUnavailable, K as safeSlugName, L as sharedItemsPath, M as writeItem, N as APP_MANIFEST_FILE, O as isRegularFile, P as appManifestReason, Q as collectionChangePayload, R as pageFromFullRead, S as compileJsonlQuery, T as MAX_QUERY_ROWS, U as resolveDataDir, V as isContainedInRoot, W as resolveTemplatePath, Y as COLLECTION_ROOT_REQUIRED, Z as collectionChangeKey, _ as dedupeByRecordId, a as toDetail, at as log, ct as publishCollectionChange, d as storeFor, dt as sharedCollectionChangePayload, et as configureCollectionHost, g as decodeCsvRecordId, h as csvRowToItem, i as resolvePrimaryField, it as localCollectionKey, j as resolveCreateItemId, k as listItems, l as collectionWritable, lt as setCollectionChangePublisher, n as discoverCollections, nt as getWorkspaceRoot, o as toSummary, ot as peekWorkspaceRoot, p as MAX_CSV_ROWS, q as BackendUnavailableError, r as loadCollection, s as CollectionSchemaZ, t as acceptParsedSchema, tt as firestoreHandle, u as readOnlyRefusal, ut as setFirestoreAccessor, v as encodeCsvRecordId, w as DEFAULT_QUERY_ROWS, x as compileCsvQuery, y as normalizeCsvValue, z as projectItemFields } from "../../discovery-CdkaURVY.js";
|
|
4
4
|
import { i as isSafeTemplatePath, n as isSafeCustomViewI18nPath, r as isSafeCustomViewPath, t as isSafeActionTemplatePath } from "../../templatePath-k_WNbL_Q.js";
|
|
5
|
-
import { A as buildCollectionActionSeedPrompt, C as validateRecordObject, D as enrichItems, E as runCollectionQuery, M as readCustomViewHtml, N as readCustomViewI18n, O as runQueryOverRows, P as readSkillTemplate, S as validateCollectionRecords, T as recordFieldProblem, _ as computeCollectionIcon, a as deleteCollection, b as applyMutateAction, c as computeSuccessor, d as isTriggerDue, f as maybeSpawnSuccessor, h as successorId, i as deleteCustomView, j as promptPathsFor, k as buildActionSeedPrompt, l as daysInMonth, m as resolveEvery, n as MAX_UNSELECTIVE_ITEMS, o as deleteCollectionRefusalMessage, p as parseCivil, r as makeManageCollectionTool, s as advanceTriggerDate, t as MAX_SCHEMA_ISSUES, u as formatCivil, v as buildWorkspaceOntology, w as compileRecordZ, x as firstMutateParamProblem, y as schemaRelations } from "../../server-
|
|
6
|
-
export { BackendUnavailableError, COLLECTION_ROOT_REQUIRED, COMPUTED_TYPES, CollectionQueryZ, CollectionSchemaZ, DEFAULT_QUERY_ROWS, MAX_CSV_ROWS, MAX_QUERY_ROWS, MAX_SCHEMA_ISSUES, MAX_UNSELECTIVE_ITEMS, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, applyMutateAction, buildActionSeedPrompt, buildCollectionActionSeedPrompt, buildWorkspaceOntology, canonicalRoot, collectionChangeKey, collectionChangePayload, collectionWritable, compileCsvQuery, compileJsonlQuery, compileRecordZ, computeCollectionIcon, computeSuccessor, configureCollectionHost, csvRowToItem, daysInMonth, decodeCsvRecordId, dedupeByRecordId, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, encodeCsvRecordId, enrichItems, firstMutateParamProblem, formatCivil, generateItemId, getWorkspaceRoot, isBackendUnavailable, isContainedInRoot, isRegularFile, isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, localCollectionKey, log, makeManageCollectionTool, maybeSpawnSuccessor, normalizeCsvValue, pageFromFullRead, parseCivil, peekWorkspaceRoot, projectItemFields, promptPathsFor, publishCollectionChange, readCustomViewHtml, readCustomViewI18n, readItem, readOnlyRefusal, readSkillTemplate, recordFieldProblem, resolveCreateItemId, resolveDataDir, resolveEvery, resolvePrimaryField, resolveTemplatePath, runCollectionQuery, runQueryOverRows, safeRecordId, safeSlugName, schemaRelations, setCollectionChangePublisher, sharedCollectionChangePayload, storeFor, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
|
|
5
|
+
import { A as buildCollectionActionSeedPrompt, C as validateRecordObject, D as enrichItems, E as runCollectionQuery, M as readCustomViewHtml, N as readCustomViewI18n, O as runQueryOverRows, P as readSkillTemplate, S as validateCollectionRecords, T as recordFieldProblem, _ as computeCollectionIcon, a as deleteCollection, b as applyMutateAction, c as computeSuccessor, d as isTriggerDue, f as maybeSpawnSuccessor, h as successorId, i as deleteCustomView, j as promptPathsFor, k as buildActionSeedPrompt, l as daysInMonth, m as resolveEvery, n as MAX_UNSELECTIVE_ITEMS, o as deleteCollectionRefusalMessage, p as parseCivil, r as makeManageCollectionTool, s as advanceTriggerDate, t as MAX_SCHEMA_ISSUES, u as formatCivil, v as buildWorkspaceOntology, w as compileRecordZ, x as firstMutateParamProblem, y as schemaRelations } from "../../server-DdHaX_Uu.js";
|
|
6
|
+
export { APP_MANIFEST_FILE, BackendUnavailableError, COLLECTION_ROOT_REQUIRED, COMPUTED_TYPES, CollectionQueryZ, CollectionSchemaZ, DEFAULT_QUERY_ROWS, MAX_CSV_ROWS, MAX_QUERY_ROWS, MAX_SCHEMA_ISSUES, MAX_UNSELECTIVE_ITEMS, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, appManifestReason, applyMutateAction, buildActionSeedPrompt, buildCollectionActionSeedPrompt, buildWorkspaceOntology, canonicalRoot, collectionChangeKey, collectionChangePayload, collectionWritable, compileCsvQuery, compileJsonlQuery, compileRecordZ, computeCollectionIcon, computeSuccessor, configureCollectionHost, csvRowToItem, daysInMonth, decodeCsvRecordId, dedupeByRecordId, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, encodeCsvRecordId, enrichItems, firestoreHandle, firstMutateParamProblem, formatCivil, generateItemId, getWorkspaceRoot, isBackendUnavailable, isContainedInRoot, isRegularFile, isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadAppManifest, loadCollection, localCollectionKey, log, makeManageCollectionTool, maybeSpawnSuccessor, normalizeCsvValue, pageFromFullRead, parseAppManifest, parseCivil, peekWorkspaceRoot, projectItemFields, promptPathsFor, publishCollectionChange, readCustomViewHtml, readCustomViewI18n, readItem, readOnlyRefusal, readSkillTemplate, recordFieldProblem, resolveCreateItemId, resolveDataDir, resolveEvery, resolvePrimaryField, resolveTemplatePath, runCollectionQuery, runQueryOverRows, safeRecordId, safeSlugName, schemaRelations, setCollectionChangePublisher, setFirestoreAccessor, sharedCollectionChangePayload, sharedItemsPath, storeFor, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
|