@mulmoclaude/core 3.4.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-Csy2rpjp.js → calendarGrid-CQ8MVSRb.js} +141 -2
- package/dist/calendarGrid-CQ8MVSRb.js.map +1 -0
- package/dist/{calendarGrid-p8K-7cuB.cjs → calendarGrid-DGILaVxI.cjs} +194 -1
- package/dist/calendarGrid-DGILaVxI.cjs.map +1 -0
- package/dist/collection/core/collectionKey.d.ts +67 -0
- 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/index.cjs +11 -2
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +3 -3
- 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 +90 -8
- package/dist/collection/server/index.cjs +13 -3
- package/dist/collection/server/index.d.ts +4 -1
- package/dist/collection/server/index.js +4 -4
- package/dist/collection-watchers/index.cjs +161 -49
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.js +161 -49
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/collection-watchers/reconciler.d.ts +36 -1
- package/dist/{discovery-D7LCN8gJ.js → discovery-CdkaURVY.js} +411 -57
- package/dist/discovery-CdkaURVY.js.map +1 -0
- package/dist/{discovery-CrA_EqUJ.cjs → discovery-Cw5xWzZD.cjs} +469 -55
- package/dist/discovery-Cw5xWzZD.cjs.map +1 -0
- package/dist/feeds/index.cjs +2 -2
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/server/index.cjs +4 -4
- package/dist/feeds/server/index.js +4 -4
- package/dist/google/index.cjs +2 -2
- package/dist/google/index.js +2 -2
- package/dist/{ingestTypes-F2IJhOLJ.js → ingestTypes-CEi-Ot7F.js} +2 -2
- package/dist/{ingestTypes-F2IJhOLJ.js.map → ingestTypes-CEi-Ot7F.js.map} +1 -1
- package/dist/{ingestTypes-CPR-h-OZ.cjs → ingestTypes-iaiq33Ph.cjs} +2 -2
- package/dist/{ingestTypes-CPR-h-OZ.cjs.map → ingestTypes-iaiq33Ph.cjs.map} +1 -1
- package/dist/{promptSafety-ACXw_2Yu.js → promptSafety-CpiME8pj.js} +2 -2
- package/dist/{promptSafety-ACXw_2Yu.js.map → promptSafety-CpiME8pj.js.map} +1 -1
- package/dist/{promptSafety-CNEW3_qS.cjs → promptSafety-NNGViiCr.cjs} +2 -2
- package/dist/{promptSafety-CNEW3_qS.cjs.map → promptSafety-NNGViiCr.cjs.map} +1 -1
- package/dist/{server-CorfPQVQ.cjs → server-9mFaGLdq.cjs} +25 -6
- package/dist/server-9mFaGLdq.cjs.map +1 -0
- package/dist/{server-D6QY853r.js → server-DdHaX_Uu.js} +25 -6
- package/dist/server-DdHaX_Uu.js.map +1 -0
- package/package.json +7 -1
- package/dist/calendarGrid-Csy2rpjp.js.map +0 -1
- package/dist/calendarGrid-p8K-7cuB.cjs.map +0 -1
- package/dist/discovery-CrA_EqUJ.cjs.map +0 -1
- package/dist/discovery-D7LCN8gJ.js.map +0 -1
- package/dist/server-CorfPQVQ.cjs.map +0 -1
- package/dist/server-D6QY853r.js.map +0 -1
|
@@ -59,9 +59,9 @@ export declare const SubFieldSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
59
59
|
string: "string";
|
|
60
60
|
number: "number";
|
|
61
61
|
boolean: "boolean";
|
|
62
|
+
date: "date";
|
|
62
63
|
text: "text";
|
|
63
64
|
email: "email";
|
|
64
|
-
date: "date";
|
|
65
65
|
datetime: "datetime";
|
|
66
66
|
markdown: "markdown";
|
|
67
67
|
}>;
|
|
@@ -94,13 +94,13 @@ export declare const FieldSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
94
94
|
string: "string";
|
|
95
95
|
number: "number";
|
|
96
96
|
boolean: "boolean";
|
|
97
|
+
date: "date";
|
|
98
|
+
file: "file";
|
|
97
99
|
text: "text";
|
|
98
100
|
email: "email";
|
|
99
|
-
date: "date";
|
|
100
101
|
datetime: "datetime";
|
|
101
102
|
markdown: "markdown";
|
|
102
103
|
image: "image";
|
|
103
|
-
file: "file";
|
|
104
104
|
}>;
|
|
105
105
|
}, z.core.$strip>, z.ZodObject<{
|
|
106
106
|
to: z.ZodString;
|
|
@@ -141,9 +141,9 @@ export declare const FieldSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
141
141
|
string: "string";
|
|
142
142
|
number: "number";
|
|
143
143
|
boolean: "boolean";
|
|
144
|
+
date: "date";
|
|
144
145
|
text: "text";
|
|
145
146
|
email: "email";
|
|
146
|
-
date: "date";
|
|
147
147
|
datetime: "datetime";
|
|
148
148
|
markdown: "markdown";
|
|
149
149
|
}>;
|
|
@@ -308,9 +308,9 @@ export declare const ActionSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
308
308
|
string: "string";
|
|
309
309
|
number: "number";
|
|
310
310
|
boolean: "boolean";
|
|
311
|
+
date: "date";
|
|
311
312
|
text: "text";
|
|
312
313
|
email: "email";
|
|
313
|
-
date: "date";
|
|
314
314
|
datetime: "datetime";
|
|
315
315
|
markdown: "markdown";
|
|
316
316
|
}>;
|
|
@@ -672,15 +672,21 @@ export declare const DataSourceZ: z.ZodObject<{
|
|
|
672
672
|
/** Alternative WRITABLE storage backend for a collection's records —
|
|
673
673
|
* unlike `dataSource` (external read-only file), a `storage` collection
|
|
674
674
|
* behaves like a normal writable collection; only where the rows live
|
|
675
|
-
* changes.
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
|
|
675
|
+
* changes. The store factory registry (`server/store.ts`) picks the
|
|
676
|
+
* implementation by `type` (plans/done/refactor-storage-virtualization.md).
|
|
677
|
+
*
|
|
678
|
+
* A discriminated union rather than one shape with optional keys, because
|
|
679
|
+
* only the sqlite variant is a workspace FILE: its `path` is
|
|
680
|
+
* workspace-relative and containment-checked exactly like `dataPath`, while
|
|
681
|
+
* the firestore variant has no path to check — its records are not on this
|
|
682
|
+
* machine at all. Optional keys would let each arm accept the other's, and
|
|
683
|
+
* the compiler would stop being the thing that tells you which. */
|
|
684
|
+
export declare const StorageZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
681
685
|
type: z.ZodLiteral<"sqlite">;
|
|
682
686
|
path: z.ZodString;
|
|
683
|
-
}, z.core.$strip
|
|
687
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
688
|
+
type: z.ZodLiteral<"firestore">;
|
|
689
|
+
}, z.core.$strict>], "type">;
|
|
684
690
|
/** The schema's SHAPE, before any cross-field rule runs. Named so the rules in
|
|
685
691
|
* `./schemaRules` can type their argument against it — they receive whatever
|
|
686
692
|
* this parses to, and nothing narrower. */
|
|
@@ -692,10 +698,12 @@ declare const CollectionObjectZ: z.ZodObject<{
|
|
|
692
698
|
type: z.ZodLiteral<"csv">;
|
|
693
699
|
path: z.ZodString;
|
|
694
700
|
}, z.core.$strip>>;
|
|
695
|
-
storage: z.ZodOptional<z.ZodObject<{
|
|
701
|
+
storage: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
696
702
|
type: z.ZodLiteral<"sqlite">;
|
|
697
703
|
path: z.ZodString;
|
|
698
|
-
}, z.core.$strip
|
|
704
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
705
|
+
type: z.ZodLiteral<"firestore">;
|
|
706
|
+
}, z.core.$strict>], "type">>;
|
|
699
707
|
primaryKey: z.ZodString;
|
|
700
708
|
singleton: z.ZodOptional<z.ZodString>;
|
|
701
709
|
fields: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -710,13 +718,13 @@ declare const CollectionObjectZ: z.ZodObject<{
|
|
|
710
718
|
string: "string";
|
|
711
719
|
number: "number";
|
|
712
720
|
boolean: "boolean";
|
|
721
|
+
date: "date";
|
|
722
|
+
file: "file";
|
|
713
723
|
text: "text";
|
|
714
724
|
email: "email";
|
|
715
|
-
date: "date";
|
|
716
725
|
datetime: "datetime";
|
|
717
726
|
markdown: "markdown";
|
|
718
727
|
image: "image";
|
|
719
|
-
file: "file";
|
|
720
728
|
}>;
|
|
721
729
|
}, z.core.$strip>, z.ZodObject<{
|
|
722
730
|
to: z.ZodString;
|
|
@@ -757,9 +765,9 @@ declare const CollectionObjectZ: z.ZodObject<{
|
|
|
757
765
|
string: "string";
|
|
758
766
|
number: "number";
|
|
759
767
|
boolean: "boolean";
|
|
768
|
+
date: "date";
|
|
760
769
|
text: "text";
|
|
761
770
|
email: "email";
|
|
762
|
-
date: "date";
|
|
763
771
|
datetime: "datetime";
|
|
764
772
|
markdown: "markdown";
|
|
765
773
|
}>;
|
|
@@ -920,9 +928,9 @@ declare const CollectionObjectZ: z.ZodObject<{
|
|
|
920
928
|
string: "string";
|
|
921
929
|
number: "number";
|
|
922
930
|
boolean: "boolean";
|
|
931
|
+
date: "date";
|
|
923
932
|
text: "text";
|
|
924
933
|
email: "email";
|
|
925
|
-
date: "date";
|
|
926
934
|
datetime: "datetime";
|
|
927
935
|
markdown: "markdown";
|
|
928
936
|
}>;
|
|
@@ -975,9 +983,9 @@ declare const CollectionObjectZ: z.ZodObject<{
|
|
|
975
983
|
string: "string";
|
|
976
984
|
number: "number";
|
|
977
985
|
boolean: "boolean";
|
|
986
|
+
date: "date";
|
|
978
987
|
text: "text";
|
|
979
988
|
email: "email";
|
|
980
|
-
date: "date";
|
|
981
989
|
datetime: "datetime";
|
|
982
990
|
markdown: "markdown";
|
|
983
991
|
}>;
|
|
@@ -1171,10 +1179,12 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
|
|
|
1171
1179
|
type: z.ZodLiteral<"csv">;
|
|
1172
1180
|
path: z.ZodString;
|
|
1173
1181
|
}, z.core.$strip>>;
|
|
1174
|
-
storage: z.ZodOptional<z.ZodObject<{
|
|
1182
|
+
storage: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1175
1183
|
type: z.ZodLiteral<"sqlite">;
|
|
1176
1184
|
path: z.ZodString;
|
|
1177
|
-
}, z.core.$strip
|
|
1185
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1186
|
+
type: z.ZodLiteral<"firestore">;
|
|
1187
|
+
}, z.core.$strict>], "type">>;
|
|
1178
1188
|
primaryKey: z.ZodString;
|
|
1179
1189
|
singleton: z.ZodOptional<z.ZodString>;
|
|
1180
1190
|
fields: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -1189,13 +1199,13 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
|
|
|
1189
1199
|
string: "string";
|
|
1190
1200
|
number: "number";
|
|
1191
1201
|
boolean: "boolean";
|
|
1202
|
+
date: "date";
|
|
1203
|
+
file: "file";
|
|
1192
1204
|
text: "text";
|
|
1193
1205
|
email: "email";
|
|
1194
|
-
date: "date";
|
|
1195
1206
|
datetime: "datetime";
|
|
1196
1207
|
markdown: "markdown";
|
|
1197
1208
|
image: "image";
|
|
1198
|
-
file: "file";
|
|
1199
1209
|
}>;
|
|
1200
1210
|
}, z.core.$strip>, z.ZodObject<{
|
|
1201
1211
|
to: z.ZodString;
|
|
@@ -1236,9 +1246,9 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
|
|
|
1236
1246
|
string: "string";
|
|
1237
1247
|
number: "number";
|
|
1238
1248
|
boolean: "boolean";
|
|
1249
|
+
date: "date";
|
|
1239
1250
|
text: "text";
|
|
1240
1251
|
email: "email";
|
|
1241
|
-
date: "date";
|
|
1242
1252
|
datetime: "datetime";
|
|
1243
1253
|
markdown: "markdown";
|
|
1244
1254
|
}>;
|
|
@@ -1399,9 +1409,9 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
|
|
|
1399
1409
|
string: "string";
|
|
1400
1410
|
number: "number";
|
|
1401
1411
|
boolean: "boolean";
|
|
1412
|
+
date: "date";
|
|
1402
1413
|
text: "text";
|
|
1403
1414
|
email: "email";
|
|
1404
|
-
date: "date";
|
|
1405
1415
|
datetime: "datetime";
|
|
1406
1416
|
markdown: "markdown";
|
|
1407
1417
|
}>;
|
|
@@ -1454,9 +1464,9 @@ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
|
|
|
1454
1464
|
string: "string";
|
|
1455
1465
|
number: "number";
|
|
1456
1466
|
boolean: "boolean";
|
|
1467
|
+
date: "date";
|
|
1457
1468
|
text: "text";
|
|
1458
1469
|
email: "email";
|
|
1459
|
-
date: "date";
|
|
1460
1470
|
datetime: "datetime";
|
|
1461
1471
|
markdown: "markdown";
|
|
1462
1472
|
}>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let firebase_firestore = require("firebase/firestore");
|
|
3
|
+
//#region src/collection/server/firestoreDocs.ts
|
|
4
|
+
/** The real implementation over the modular SDK.
|
|
5
|
+
*
|
|
6
|
+
* THE RECORD IS THE DOCUMENT. Its fields are written at the top level, not
|
|
7
|
+
* nested under a `data` key. This is not a matter of taste: the deployed
|
|
8
|
+
* security rules read `request.resource.data.<field>` and
|
|
9
|
+
* `resource.data[submit.emailField]` — a wrapper would put every field one
|
|
10
|
+
* level down, so the required-field checks, the status state machine and the
|
|
11
|
+
* "your own row" predicate would all read absent values and fail closed. A
|
|
12
|
+
* shared record's shape is part of the authorization contract.
|
|
13
|
+
*
|
|
14
|
+
* `orderBy("__name__")` orders by DOCUMENT ID. Ordering by a record field
|
|
15
|
+
* would silently EXCLUDE documents missing that field (the trap
|
|
16
|
+
* `remote-host/server/hostRunner.ts:158-169` documents), turning a read into a
|
|
17
|
+
* partial one; the document id is always present and gives the stable order
|
|
18
|
+
* the store contract requires. */
|
|
19
|
+
function createFirestoreDocs(database) {
|
|
20
|
+
return {
|
|
21
|
+
list: async (collectionPath) => {
|
|
22
|
+
return (await (0, firebase_firestore.getDocs)((0, firebase_firestore.query)((0, firebase_firestore.collection)(database, collectionPath), (0, firebase_firestore.orderBy)("__name__")))).docs.map((entry) => ({
|
|
23
|
+
id: entry.id,
|
|
24
|
+
data: entry.data()
|
|
25
|
+
}));
|
|
26
|
+
},
|
|
27
|
+
get: async (collectionPath, docId) => {
|
|
28
|
+
const snapshot = await (0, firebase_firestore.getDoc)((0, firebase_firestore.doc)(database, collectionPath, docId));
|
|
29
|
+
return snapshot.exists() ? snapshot.data() : null;
|
|
30
|
+
},
|
|
31
|
+
set: async (collectionPath, docId, data) => {
|
|
32
|
+
await (0, firebase_firestore.setDoc)((0, firebase_firestore.doc)(database, collectionPath, docId), data);
|
|
33
|
+
},
|
|
34
|
+
create: (collectionPath, docId, data) => (0, firebase_firestore.runTransaction)(database, async (transaction) => {
|
|
35
|
+
const ref = (0, firebase_firestore.doc)(database, collectionPath, docId);
|
|
36
|
+
if ((await transaction.get(ref)).exists()) return false;
|
|
37
|
+
transaction.set(ref, data);
|
|
38
|
+
return true;
|
|
39
|
+
}),
|
|
40
|
+
delete: (collectionPath, docId) => (0, firebase_firestore.runTransaction)(database, async (transaction) => {
|
|
41
|
+
const ref = (0, firebase_firestore.doc)(database, collectionPath, docId);
|
|
42
|
+
if (!(await transaction.get(ref)).exists()) return false;
|
|
43
|
+
transaction.delete(ref);
|
|
44
|
+
return true;
|
|
45
|
+
})
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
exports.createFirestoreDocs = createFirestoreDocs;
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=firestore.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firestore.cjs","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,OAAA,GADgB,mBAAA,QAAA,EAAA,GAAQ,mBAAA,MAAA,EAAA,GAAe,mBAAA,WAAA,CAAoB,UAAU,cAAc,IAAA,GAAG,mBAAA,QAAA,CAAQ,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,OAAA,GAAM,mBAAA,OAAA,EAAA,GAAO,mBAAA,IAAA,CAAI,UAAU,gBAAgB,KAAK,CAAC;GAClE,OAAO,SAAS,OAAO,IAAI,SAAS,KAAK,IAAI;EAC/C;EACA,KAAK,OAAO,gBAAgB,OAAO,SAAS;GAC1C,OAAA,GAAM,mBAAA,OAAA,EAAA,GAAO,mBAAA,IAAA,CAAI,UAAU,gBAAgB,KAAK,GAAG,IAAI;EACzD;EACA,SAAS,gBAAgB,OAAO,UAAA,GAC9B,mBAAA,eAAA,CAAe,UAAU,OAAO,gBAAgB;GAC9C,MAAM,OAAA,GAAM,mBAAA,IAAA,CAAI,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,WAAA,GACvB,mBAAA,eAAA,CAAe,UAAU,OAAO,gBAAgB;GAC9C,MAAM,OAAA,GAAM,mBAAA,IAAA,CAAI,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createFirestoreDocs, type FirestoreDoc, type FirestoreDocs } from './server/firestoreDocs';
|
|
@@ -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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_dist = require("../dist-CsgSfWwR.cjs");
|
|
3
|
-
const require_calendarGrid = require("../calendarGrid-
|
|
3
|
+
const require_calendarGrid = require("../calendarGrid-DGILaVxI.cjs");
|
|
4
4
|
const require_project = require("../project-C1ep9pvo.cjs");
|
|
5
|
-
const require_promptSafety = require("../promptSafety-
|
|
5
|
+
const require_promptSafety = require("../promptSafety-NNGViiCr.cjs");
|
|
6
6
|
//#region src/collection/core/presentCollection.ts
|
|
7
7
|
var TOOL_NAME = "presentCollection";
|
|
8
8
|
/** Stamp a host's opaque project scope onto a card payload. The ONLY way a
|
|
@@ -665,6 +665,8 @@ exports.chipMatches = require_promptSafety.chipMatches;
|
|
|
665
665
|
exports.coerceInlineValue = coerceInlineValue;
|
|
666
666
|
exports.coerceNumeric = require_promptSafety.coerceNumeric;
|
|
667
667
|
exports.collectionCardKey = collectionCardKey;
|
|
668
|
+
exports.collectionKeyId = require_calendarGrid.collectionKeyId;
|
|
669
|
+
exports.collectionKeyName = require_calendarGrid.collectionKeyName;
|
|
668
670
|
exports.compareSortValues = compareSortValues;
|
|
669
671
|
exports.compareYmd = require_calendarGrid.compareYmd;
|
|
670
672
|
exports.completionCoveredByFieldChip = require_promptSafety.completionCoveredByFieldChip;
|
|
@@ -691,21 +693,26 @@ exports.firstMissingRequiredField = firstMissingRequiredField;
|
|
|
691
693
|
exports.flagFieldValue = require_promptSafety.flagFieldValue;
|
|
692
694
|
exports.generateUniqueId = require_calendarGrid.generateUniqueId;
|
|
693
695
|
exports.isFieldDrivenEvery = require_calendarGrid.isFieldDrivenEvery;
|
|
696
|
+
exports.isLocalCollectionKey = require_calendarGrid.isLocalCollectionKey;
|
|
694
697
|
exports.isReadOnlySchema = require_calendarGrid.isReadOnlySchema;
|
|
695
698
|
exports.isSafeRecordId = require_calendarGrid.isSafeRecordId;
|
|
696
699
|
exports.isSafeSlug = require_calendarGrid.isSafeSlug;
|
|
700
|
+
exports.isSharedCollectionKey = require_calendarGrid.isSharedCollectionKey;
|
|
697
701
|
exports.isSortableField = isSortableField;
|
|
702
|
+
exports.isValidCollectionName = require_calendarGrid.isValidCollectionName;
|
|
698
703
|
exports.itemIdOf = itemIdOf;
|
|
699
704
|
exports.itemIsDone = require_promptSafety.itemIsDone;
|
|
700
705
|
exports.itemLabelOf = itemLabelOf;
|
|
701
706
|
exports.itemMatchesQuery = itemMatchesQuery;
|
|
702
707
|
exports.labelFieldFor = labelFieldFor;
|
|
708
|
+
exports.localCollectionKeyOf = require_calendarGrid.localCollectionKeyOf;
|
|
703
709
|
exports.matchesWhere = require_promptSafety.matchesWhere;
|
|
704
710
|
exports.mergeIntoExisting = require_project.mergeIntoExisting;
|
|
705
711
|
exports.monthAnchorDate = require_calendarGrid.monthAnchorDate;
|
|
706
712
|
exports.nextSortDirection = nextSortDirection;
|
|
707
713
|
exports.nextUniqueItemId = require_calendarGrid.nextUniqueItemId;
|
|
708
714
|
exports.numericSortValue = numericSortValue;
|
|
715
|
+
exports.parseCollectionKeyId = require_calendarGrid.parseCollectionKeyId;
|
|
709
716
|
exports.parseIsoDate = require_calendarGrid.parseIsoDate;
|
|
710
717
|
exports.parseIsoDateTime = require_calendarGrid.parseIsoDateTime;
|
|
711
718
|
exports.parseTimeRange = require_calendarGrid.parseTimeRange;
|
|
@@ -719,8 +726,10 @@ exports.rollupValue = require_promptSafety.rollupValue;
|
|
|
719
726
|
exports.rowFromItem = rowFromItem;
|
|
720
727
|
exports.rowIdOf = rowIdOf;
|
|
721
728
|
exports.sameCollectionCard = sameCollectionCard;
|
|
729
|
+
exports.sameCollectionKey = require_calendarGrid.sameCollectionKey;
|
|
722
730
|
exports.scalarSortValue = scalarSortValue;
|
|
723
731
|
exports.selectDynamicRecord = require_promptSafety.selectDynamicRecord;
|
|
732
|
+
exports.sharedCollectionKey = require_calendarGrid.sharedCollectionKey;
|
|
724
733
|
exports.shortHexId = shortHexId;
|
|
725
734
|
exports.skillCommandSeed = skillCommandSeed;
|
|
726
735
|
exports.snapshotEmptyEnums = snapshotEmptyEnums;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './core/schema';
|
|
2
2
|
export type { CollectionQuery, CollectionQueryAggregate, CollectionQueryOrder, CollectionQueryWhere } from './core/queryZ';
|
|
3
3
|
export * from './core/ids';
|
|
4
|
+
export * from './core/collectionKey';
|
|
4
5
|
export * from './core/fieldText';
|
|
5
6
|
export * from './core/project';
|
|
6
7
|
export * from './core/uiTypes';
|
package/dist/collection/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as errorMessage } from "../dist-D8zokgGo.js";
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as AGENT_INGEST_KIND, C as sharedCollectionKey, D as isSafeRecordId, E as generateUniqueId, F as isFieldDrivenEvery, I as isReadOnlySchema, L as storageKindFor, M as FEED_SCHEDULES, N as INGEST_KINDS, O as isSafeSlug, P as embedTargetId, R as fieldText, S as sameCollectionKey, T as SAFE_SLUG_PATTERN, _ as isLocalCollectionKey, a as compareYmd, b as localCollectionKeyOf, c as monthAnchorDate, d as parseTimeRange, f as recordSpan, g as collectionKeyName, h as collectionKeyId, i as buildMonthGrid, j as COMPUTED_TYPES, k as nextUniqueItemId, l as parseIsoDate, m as ymdKey, n as assignLanes, o as dateOf, p as spanCoversDay, r as bucketRecords, s as daySlice, t as MINUTES_PER_DAY, u as parseIsoDateTime, v as isSharedCollectionKey, w as SAFE_RECORD_ID_PATTERN, x as parseCollectionKeyId, y as isValidCollectionName, z as fieldTextOrNull } from "../calendarGrid-CQ8MVSRb.js";
|
|
3
3
|
import { n as projectRecordFields, t as mergeIntoExisting } from "../project-B0j_Vf_Q.js";
|
|
4
|
-
import { C as actionVisible, E as whenMatches, S as viaMatches, T as fieldVisible, _ as uniqueRefTargets, a as evaluateDerived, b as projectBacklinkRow, c as selectDynamicRecord, d as flagFieldValue, f as itemIsDone, g as uniqueEmbedTargets, h as uniqueBacklinkSources, l as chipMatches, m as matchesWhere, n as deriveAll, o as firstDateField, p as toggleChecked, r as resolveRowRefs, s as resolveIcon, t as defangForPrompt, u as completionCoveredByFieldChip, v as backlinkRows, w as agentActionRunKey, x as rollupValue, y as coerceNumeric } from "../promptSafety-
|
|
4
|
+
import { C as actionVisible, E as whenMatches, S as viaMatches, T as fieldVisible, _ as uniqueRefTargets, a as evaluateDerived, b as projectBacklinkRow, c as selectDynamicRecord, d as flagFieldValue, f as itemIsDone, g as uniqueEmbedTargets, h as uniqueBacklinkSources, l as chipMatches, m as matchesWhere, n as deriveAll, o as firstDateField, p as toggleChecked, r as resolveRowRefs, s as resolveIcon, t as defangForPrompt, u as completionCoveredByFieldChip, v as backlinkRows, w as agentActionRunKey, x as rollupValue, y as coerceNumeric } from "../promptSafety-CpiME8pj.js";
|
|
5
5
|
//#region src/collection/core/presentCollection.ts
|
|
6
6
|
var TOOL_NAME = "presentCollection";
|
|
7
7
|
/** Stamp a host's opaque project scope onto a card payload. The ONLY way a
|
|
@@ -638,6 +638,6 @@ var buildOntologyGraph = (entries) => {
|
|
|
638
638
|
};
|
|
639
639
|
};
|
|
640
640
|
//#endregion
|
|
641
|
-
export { AGENT_INGEST_KIND, COMPUTED_TYPES, ENUM_ALERT, ENUM_NEUTRAL, ENUM_NUDGE, FEED_SCHEDULES, INGEST_KINDS, MINUTES_PER_DAY, SAFE_RECORD_ID_PATTERN, SAFE_SLUG_PATTERN, TOOL_DEFINITION, TOOL_NAME, actionVisible, agentActionRunKey, assignLanes, backlinkRows, boolSortValue, bucketRecords, buildMonthGrid, buildOntologyGraph, buildUpdatedRecord, cellKey, chipMatches, coerceInlineValue, coerceNumeric, collectionCardKey, compareSortValues, compareYmd, completionCoveredByFieldChip, dateOf, dateSortValue, daySlice, defangForPrompt, deriveAll, derivedSortValue, draftToRecord, embedTargetId, emptyRow, enumColorClasses, enumSortValue, enumValueIndex, errorMessage, evaluateDerived, executePresentCollection, fieldText, fieldTextOrNull, fieldVisible, firstDateField, firstMissingRequiredField, flagFieldValue, generateUniqueId, isFieldDrivenEvery, isReadOnlySchema, isSafeRecordId, isSafeSlug, isSortableField, itemIdOf, itemIsDone, itemLabelOf, itemMatchesQuery, labelFieldFor, matchesWhere, mergeIntoExisting, monthAnchorDate, nextSortDirection, nextUniqueItemId, numericSortValue, parseIsoDate, parseIsoDateTime, parseTimeRange, projectBacklinkRow, projectRecordFields, recordSpan, resolveEnumColor, resolveIcon, resolveRowRefs, rollupValue, rowFromItem, rowIdOf, sameCollectionCard, scalarSortValue, selectDynamicRecord, shortHexId, skillCommandSeed, snapshotEmptyEnums, sortItems, sortValueOf, spanCoversDay, storageKindFor, stringSortValue, toPresentCollectionData, toggleChecked, uniqueBacklinkSources, uniqueEmbedTargets, uniqueRefTargets, viaMatches, whenMatches, withCardScope, ymdKey };
|
|
641
|
+
export { AGENT_INGEST_KIND, COMPUTED_TYPES, ENUM_ALERT, ENUM_NEUTRAL, ENUM_NUDGE, FEED_SCHEDULES, INGEST_KINDS, MINUTES_PER_DAY, SAFE_RECORD_ID_PATTERN, SAFE_SLUG_PATTERN, TOOL_DEFINITION, TOOL_NAME, actionVisible, agentActionRunKey, assignLanes, backlinkRows, boolSortValue, bucketRecords, buildMonthGrid, buildOntologyGraph, buildUpdatedRecord, cellKey, chipMatches, coerceInlineValue, coerceNumeric, collectionCardKey, collectionKeyId, collectionKeyName, compareSortValues, compareYmd, completionCoveredByFieldChip, dateOf, dateSortValue, daySlice, defangForPrompt, deriveAll, derivedSortValue, draftToRecord, embedTargetId, emptyRow, enumColorClasses, enumSortValue, enumValueIndex, errorMessage, evaluateDerived, executePresentCollection, fieldText, fieldTextOrNull, fieldVisible, firstDateField, firstMissingRequiredField, flagFieldValue, generateUniqueId, isFieldDrivenEvery, isLocalCollectionKey, isReadOnlySchema, isSafeRecordId, isSafeSlug, isSharedCollectionKey, isSortableField, isValidCollectionName, itemIdOf, itemIsDone, itemLabelOf, itemMatchesQuery, labelFieldFor, localCollectionKeyOf, matchesWhere, mergeIntoExisting, monthAnchorDate, nextSortDirection, nextUniqueItemId, numericSortValue, parseCollectionKeyId, parseIsoDate, parseIsoDateTime, parseTimeRange, projectBacklinkRow, projectRecordFields, recordSpan, resolveEnumColor, resolveIcon, resolveRowRefs, rollupValue, rowFromItem, rowIdOf, sameCollectionCard, sameCollectionKey, scalarSortValue, selectDynamicRecord, sharedCollectionKey, shortHexId, skillCommandSeed, snapshotEmptyEnums, sortItems, sortValueOf, spanCoversDay, storageKindFor, stringSortValue, toPresentCollectionData, toggleChecked, uniqueBacklinkSources, uniqueEmbedTargets, uniqueRefTargets, viaMatches, whenMatches, withCardScope, ymdKey };
|
|
642
642
|
|
|
643
643
|
//# sourceMappingURL=index.js.map
|
|
@@ -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;
|