@mulmoclaude/core 3.6.0 → 3.7.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 (37) hide show
  1. package/dist/collection/registry/server/index.cjs +19 -19
  2. package/dist/collection/registry/server/index.cjs.map +1 -1
  3. package/dist/collection/registry/server/index.js +2 -2
  4. package/dist/collection/server/host.d.ts +11 -0
  5. package/dist/collection/server/index.cjs +72 -59
  6. package/dist/collection/server/index.d.ts +4 -0
  7. package/dist/collection/server/index.js +3 -3
  8. package/dist/collection/server/manageTool.d.ts +4 -0
  9. package/dist/collection/server/publish.d.ts +56 -0
  10. package/dist/collection/server/publishChecks.d.ts +29 -0
  11. package/dist/collection/server/publishManifest.d.ts +181 -0
  12. package/dist/collection/server/publishProject.d.ts +86 -0
  13. package/dist/collection/server/validate.d.ts +12 -0
  14. package/dist/collection-watchers/index.cjs +15 -15
  15. package/dist/collection-watchers/index.cjs.map +1 -1
  16. package/dist/collection-watchers/index.js +2 -2
  17. package/dist/feeds/server/index.cjs +10 -10
  18. package/dist/feeds/server/index.cjs.map +1 -1
  19. package/dist/feeds/server/index.js +2 -2
  20. package/dist/google/index.cjs +12 -12
  21. package/dist/google/index.cjs.map +1 -1
  22. package/dist/google/index.js +1 -1
  23. package/dist/{server-DdHaX_Uu.js → server-B48Jyxcj.js} +1079 -228
  24. package/dist/server-B48Jyxcj.js.map +1 -0
  25. package/dist/{server-9mFaGLdq.cjs → server-CWZyg8fn.cjs} +1318 -389
  26. package/dist/server-CWZyg8fn.cjs.map +1 -0
  27. package/dist/{discovery-Cw5xWzZD.cjs → store-5_P_NsGa.cjs} +1947 -1947
  28. package/dist/store-5_P_NsGa.cjs.map +1 -0
  29. package/dist/{discovery-CdkaURVY.js → store-_61sO8K8.js} +1948 -1948
  30. package/dist/store-_61sO8K8.js.map +1 -0
  31. package/dist/whisper/index.cjs +1 -1
  32. package/dist/whisper/index.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/discovery-CdkaURVY.js.map +0 -1
  35. package/dist/discovery-Cw5xWzZD.cjs.map +0 -1
  36. package/dist/server-9mFaGLdq.cjs.map +0 -1
  37. package/dist/server-DdHaX_Uu.js.map +0 -1
@@ -121,6 +121,17 @@ export interface CollectionHost {
121
121
  export interface FirestoreHandle {
122
122
  docs: FirestoreDocs;
123
123
  email: string;
124
+ /** The signed-in Firebase uid.
125
+ *
126
+ * The EMAIL is the principal the roster is keyed by, and it is what every
127
+ * record operation needs. The uid is needed by exactly one caller —
128
+ * `publish` — because the app document's `owner` is a uid: the rules
129
+ * require `owner == request.auth.uid` when the app is created and require
130
+ * it unchanged afterwards. Required rather than optional so a host cannot
131
+ * wire a session that can read and write records but silently cannot
132
+ * create an app; the failure would surface as a permission denial with
133
+ * nothing in it about identity. */
134
+ uid: string;
124
135
  }
125
136
  /** A collection's records changed on disk. Carries the `slug` so the host can
126
137
  * publish on a per-collection channel; `ids` lists the affected record ids
@@ -1,102 +1,115 @@
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-Cw5xWzZD.cjs");
4
+ const require_store = require("../../store-5_P_NsGa.cjs");
5
+ const require_server = require("../../server-CWZyg8fn.cjs");
5
6
  const require_templatePath = require("../../templatePath-27vUZowm.cjs");
6
- const require_server = require("../../server-9mFaGLdq.cjs");
7
- exports.APP_MANIFEST_FILE = require_discovery.APP_MANIFEST_FILE;
8
- exports.BackendUnavailableError = require_discovery.BackendUnavailableError;
9
- exports.COLLECTION_ROOT_REQUIRED = require_discovery.COLLECTION_ROOT_REQUIRED;
7
+ exports.APPS_COLLECTION = require_server.APPS_COLLECTION;
8
+ exports.APP_MANIFEST_FILE = require_store.APP_MANIFEST_FILE;
9
+ exports.APP_ROLES = require_server.APP_ROLES;
10
+ exports.AuthoredAppZ = require_server.AuthoredAppZ;
11
+ exports.BackendUnavailableError = require_store.BackendUnavailableError;
12
+ exports.COLLECTION_ROOT_REQUIRED = require_store.COLLECTION_ROOT_REQUIRED;
10
13
  exports.COMPUTED_TYPES = require_calendarGrid.COMPUTED_TYPES;
11
- exports.CollectionQueryZ = require_discovery.CollectionQueryZ;
12
- exports.CollectionSchemaZ = require_discovery.CollectionSchemaZ;
13
- exports.DEFAULT_QUERY_ROWS = require_discovery.DEFAULT_QUERY_ROWS;
14
- exports.MAX_CSV_ROWS = require_discovery.MAX_CSV_ROWS;
15
- exports.MAX_QUERY_ROWS = require_discovery.MAX_QUERY_ROWS;
14
+ exports.CollectionQueryZ = require_store.CollectionQueryZ;
15
+ exports.CollectionSchemaZ = require_store.CollectionSchemaZ;
16
+ exports.DEFAULT_QUERY_ROWS = require_store.DEFAULT_QUERY_ROWS;
17
+ exports.MAX_CSV_ROWS = require_store.MAX_CSV_ROWS;
18
+ exports.MAX_QUERY_ROWS = require_store.MAX_QUERY_ROWS;
19
+ exports.MAX_RECORD_ISSUES = require_server.MAX_RECORD_ISSUES;
16
20
  exports.MAX_SCHEMA_ISSUES = require_server.MAX_SCHEMA_ISSUES;
17
21
  exports.MAX_UNSELECTIVE_ITEMS = require_server.MAX_UNSELECTIVE_ITEMS;
18
- exports.SCHEMA_FILE = require_discovery.SCHEMA_FILE;
19
- exports.acceptParsedSchema = require_discovery.acceptParsedSchema;
22
+ exports.PUBLIC_CONFIG_DOC = require_server.PUBLIC_CONFIG_DOC;
23
+ exports.SCHEMA_FILE = require_store.SCHEMA_FILE;
24
+ exports.STORE_UNREADABLE = require_server.STORE_UNREADABLE;
25
+ exports.acceptParsedSchema = require_store.acceptParsedSchema;
20
26
  exports.advanceTriggerDate = require_server.advanceTriggerDate;
21
- exports.appManifestReason = require_discovery.appManifestReason;
27
+ exports.appConfigPath = require_server.appConfigPath;
28
+ exports.appManifestReason = require_store.appManifestReason;
29
+ exports.appSchemasPath = require_server.appSchemasPath;
22
30
  exports.applyMutateAction = require_server.applyMutateAction;
31
+ exports.bindsSubmitterIdentity = require_server.bindsSubmitterIdentity;
23
32
  exports.buildActionSeedPrompt = require_server.buildActionSeedPrompt;
24
33
  exports.buildCollectionActionSeedPrompt = require_server.buildCollectionActionSeedPrompt;
25
34
  exports.buildWorkspaceOntology = require_server.buildWorkspaceOntology;
26
35
  exports.canonicalRoot = require_root.canonicalRoot;
27
- exports.collectionChangeKey = require_discovery.collectionChangeKey;
28
- exports.collectionChangePayload = require_discovery.collectionChangePayload;
29
- exports.collectionWritable = require_discovery.collectionWritable;
30
- exports.compileCsvQuery = require_discovery.compileCsvQuery;
31
- exports.compileJsonlQuery = require_discovery.compileJsonlQuery;
36
+ exports.collectionChangeKey = require_store.collectionChangeKey;
37
+ exports.collectionChangePayload = require_store.collectionChangePayload;
38
+ exports.collectionWritable = require_store.collectionWritable;
39
+ exports.compileCsvQuery = require_store.compileCsvQuery;
40
+ exports.compileJsonlQuery = require_store.compileJsonlQuery;
32
41
  exports.compileRecordZ = require_server.compileRecordZ;
33
42
  exports.computeCollectionIcon = require_server.computeCollectionIcon;
34
43
  exports.computeSuccessor = require_server.computeSuccessor;
35
- exports.configureCollectionHost = require_discovery.configureCollectionHost;
36
- exports.csvRowToItem = require_discovery.csvRowToItem;
44
+ exports.configureCollectionHost = require_store.configureCollectionHost;
45
+ exports.csvRowToItem = require_store.csvRowToItem;
37
46
  exports.daysInMonth = require_server.daysInMonth;
38
- exports.decodeCsvRecordId = require_discovery.decodeCsvRecordId;
39
- exports.dedupeByRecordId = require_discovery.dedupeByRecordId;
47
+ exports.decodeCsvRecordId = require_store.decodeCsvRecordId;
48
+ exports.dedupeByRecordId = require_store.dedupeByRecordId;
40
49
  exports.deleteCollection = require_server.deleteCollection;
41
50
  exports.deleteCollectionRefusalMessage = require_server.deleteCollectionRefusalMessage;
42
51
  exports.deleteCustomView = require_server.deleteCustomView;
43
- exports.deleteItem = require_discovery.deleteItem;
44
- exports.discoverCollections = require_discovery.discoverCollections;
45
- exports.encodeCsvRecordId = require_discovery.encodeCsvRecordId;
52
+ exports.deleteItem = require_store.deleteItem;
53
+ exports.discoverCollections = require_store.discoverCollections;
54
+ exports.encodeCsvRecordId = require_store.encodeCsvRecordId;
46
55
  exports.enrichItems = require_server.enrichItems;
47
- exports.firestoreHandle = require_discovery.firestoreHandle;
56
+ exports.firestoreHandle = require_store.firestoreHandle;
48
57
  exports.firstMutateParamProblem = require_server.firstMutateParamProblem;
49
58
  exports.formatCivil = require_server.formatCivil;
50
- exports.generateItemId = require_discovery.generateItemId;
51
- exports.getWorkspaceRoot = require_discovery.getWorkspaceRoot;
52
- exports.isBackendUnavailable = require_discovery.isBackendUnavailable;
53
- exports.isContainedInRoot = require_discovery.isContainedInRoot;
54
- exports.isRegularFile = require_discovery.isRegularFile;
59
+ exports.generateItemId = require_store.generateItemId;
60
+ exports.getWorkspaceRoot = require_store.getWorkspaceRoot;
61
+ exports.isBackendUnavailable = require_store.isBackendUnavailable;
62
+ exports.isContainedInRoot = require_store.isContainedInRoot;
63
+ exports.isRegularFile = require_store.isRegularFile;
55
64
  exports.isSafeActionTemplatePath = require_templatePath.isSafeActionTemplatePath;
56
65
  exports.isSafeCustomViewI18nPath = require_templatePath.isSafeCustomViewI18nPath;
57
66
  exports.isSafeCustomViewPath = require_templatePath.isSafeCustomViewPath;
58
67
  exports.isSafeTemplatePath = require_templatePath.isSafeTemplatePath;
59
68
  exports.isTriggerDue = require_server.isTriggerDue;
60
- exports.itemFilePath = require_discovery.itemFilePath;
61
- exports.listItems = require_discovery.listItems;
62
- exports.loadAppManifest = require_discovery.loadAppManifest;
63
- exports.loadCollection = require_discovery.loadCollection;
64
- exports.localCollectionKey = require_discovery.localCollectionKey;
65
- exports.log = require_discovery.log;
69
+ exports.itemFilePath = require_store.itemFilePath;
70
+ exports.listItems = require_store.listItems;
71
+ exports.loadAppManifest = require_store.loadAppManifest;
72
+ exports.loadCollection = require_store.loadCollection;
73
+ exports.localCollectionKey = require_store.localCollectionKey;
74
+ exports.log = require_store.log;
66
75
  exports.makeManageCollectionTool = require_server.makeManageCollectionTool;
67
76
  exports.maybeSpawnSuccessor = require_server.maybeSpawnSuccessor;
68
- exports.normalizeCsvValue = require_discovery.normalizeCsvValue;
69
- exports.pageFromFullRead = require_discovery.pageFromFullRead;
70
- exports.parseAppManifest = require_discovery.parseAppManifest;
77
+ exports.normalizeCsvValue = require_store.normalizeCsvValue;
78
+ exports.pageFromFullRead = require_store.pageFromFullRead;
79
+ exports.parseAppManifest = require_store.parseAppManifest;
80
+ exports.parseAuthoredApp = require_server.parseAuthoredApp;
71
81
  exports.parseCivil = require_server.parseCivil;
72
- exports.peekWorkspaceRoot = require_discovery.peekWorkspaceRoot;
73
- exports.projectItemFields = require_discovery.projectItemFields;
82
+ exports.peekWorkspaceRoot = require_store.peekWorkspaceRoot;
83
+ exports.projectApp = require_server.projectApp;
84
+ exports.projectItemFields = require_store.projectItemFields;
74
85
  exports.promptPathsFor = require_server.promptPathsFor;
75
- exports.publishCollectionChange = require_discovery.publishCollectionChange;
86
+ exports.publishApp = require_server.publishApp;
87
+ exports.publishCollectionChange = require_store.publishCollectionChange;
88
+ exports.publishProblems = require_server.publishProblems;
76
89
  exports.readCustomViewHtml = require_server.readCustomViewHtml;
77
90
  exports.readCustomViewI18n = require_server.readCustomViewI18n;
78
- exports.readItem = require_discovery.readItem;
79
- exports.readOnlyRefusal = require_discovery.readOnlyRefusal;
91
+ exports.readItem = require_store.readItem;
92
+ exports.readOnlyRefusal = require_store.readOnlyRefusal;
80
93
  exports.readSkillTemplate = require_server.readSkillTemplate;
81
94
  exports.recordFieldProblem = require_server.recordFieldProblem;
82
- exports.resolveCreateItemId = require_discovery.resolveCreateItemId;
83
- exports.resolveDataDir = require_discovery.resolveDataDir;
95
+ exports.resolveCreateItemId = require_store.resolveCreateItemId;
96
+ exports.resolveDataDir = require_store.resolveDataDir;
84
97
  exports.resolveEvery = require_server.resolveEvery;
85
- exports.resolvePrimaryField = require_discovery.resolvePrimaryField;
86
- exports.resolveTemplatePath = require_discovery.resolveTemplatePath;
98
+ exports.resolvePrimaryField = require_store.resolvePrimaryField;
99
+ exports.resolveTemplatePath = require_store.resolveTemplatePath;
87
100
  exports.runCollectionQuery = require_server.runCollectionQuery;
88
101
  exports.runQueryOverRows = require_server.runQueryOverRows;
89
- exports.safeRecordId = require_discovery.safeRecordId;
90
- exports.safeSlugName = require_discovery.safeSlugName;
102
+ exports.safeRecordId = require_store.safeRecordId;
103
+ exports.safeSlugName = require_store.safeSlugName;
91
104
  exports.schemaRelations = require_server.schemaRelations;
92
- exports.setCollectionChangePublisher = require_discovery.setCollectionChangePublisher;
93
- exports.setFirestoreAccessor = require_discovery.setFirestoreAccessor;
94
- exports.sharedCollectionChangePayload = require_discovery.sharedCollectionChangePayload;
95
- exports.sharedItemsPath = require_discovery.sharedItemsPath;
96
- exports.storeFor = require_discovery.storeFor;
105
+ exports.setCollectionChangePublisher = require_store.setCollectionChangePublisher;
106
+ exports.setFirestoreAccessor = require_store.setFirestoreAccessor;
107
+ exports.sharedCollectionChangePayload = require_store.sharedCollectionChangePayload;
108
+ exports.sharedItemsPath = require_store.sharedItemsPath;
109
+ exports.storeFor = require_store.storeFor;
97
110
  exports.successorId = require_server.successorId;
98
- exports.toDetail = require_discovery.toDetail;
99
- exports.toSummary = require_discovery.toSummary;
111
+ exports.toDetail = require_store.toDetail;
112
+ exports.toSummary = require_store.toSummary;
100
113
  exports.validateCollectionRecords = require_server.validateCollectionRecords;
101
114
  exports.validateRecordObject = require_server.validateRecordObject;
102
- exports.writeItem = require_discovery.writeItem;
115
+ exports.writeItem = require_store.writeItem;
@@ -2,6 +2,10 @@ export { configureCollectionHost, canonicalRoot, getWorkspaceRoot, peekWorkspace
2
2
  export type { FirestoreDoc, FirestoreDocs } from './firestoreDocs';
3
3
  export { sharedItemsPath } from './firestoreStore';
4
4
  export { loadAppManifest, parseAppManifest, appManifestReason, APP_MANIFEST_FILE, type AppManifest, type AppManifestResult } from './appManifest';
5
+ export { AuthoredAppZ, parseAuthoredApp, APP_ROLES, type AuthoredApp, type AuthoredCollectionConfig, type AuthoredMail, type AuthoredSubmit, } from './publishManifest';
6
+ export { projectApp, APPS_COLLECTION, PUBLIC_CONFIG_DOC, appConfigPath, appSchemasPath, type PublishStamp, type PublishedApp, type PublishedConfigDoc, type PublishedSchemaDoc, } from './publishProject';
7
+ export { publishProblems, bindsSubmitterIdentity, type PublishableCollection } from './publishChecks';
8
+ export { publishApp, type PublishOptions, type PublishResult } from './publish';
5
9
  export type { LoadedCollection } from './discoveredCollection';
6
10
  export * from './paths';
7
11
  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 { 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";
3
+ import { A as toSummary, B as SCHEMA_FILE, C as resolveCreateItemId, D as loadCollection, E as discoverCollections, F as loadAppManifest, G as safeRecordId, H as itemFilePath, I as parseAppManifest, J as isBackendUnavailable, K as safeSlugName, L as sharedItemsPath, N as APP_MANIFEST_FILE, O as resolvePrimaryField, P as appManifestReason, Q as collectionChangePayload, R as pageFromFullRead, S as readItem, T as acceptParsedSchema, U as resolveDataDir, V as isContainedInRoot, W as resolveTemplatePath, Y as COLLECTION_ROOT_REQUIRED, Z as collectionChangeKey, _ as MAX_QUERY_ROWS, a as MAX_CSV_ROWS, at as log, b as isRegularFile, c as decodeCsvRecordId, ct as publishCollectionChange, d as normalizeCsvValue, dt as sharedCollectionChangePayload, et as configureCollectionHost, g as DEFAULT_QUERY_ROWS, h as CollectionQueryZ, it as localCollectionKey, j as CollectionSchemaZ, k as toDetail, l as dedupeByRecordId, lt as setCollectionChangePublisher, m as compileJsonlQuery, n as readOnlyRefusal, nt as getWorkspaceRoot, ot as peekWorkspaceRoot, p as compileCsvQuery, q as BackendUnavailableError, r as storeFor, s as csvRowToItem, t as collectionWritable, tt as firestoreHandle, u as encodeCsvRecordId, ut as setFirestoreAccessor, v as deleteItem, w as writeItem, x as listItems, y as generateItemId, z as projectItemFields } from "../../store-_61sO8K8.js";
4
+ import { A as readSkillTemplate, B as APPS_COLLECTION, C as enrichItems, D as promptPathsFor, E as buildCollectionActionSeedPrompt, F as validateRecordObject, G as APP_ROLES, H as appConfigPath, I as compileRecordZ, K as AuthoredAppZ, L as recordFieldProblem, M as MAX_RECORD_ISSUES, N as STORE_UNREADABLE, O as readCustomViewHtml, P as validateCollectionRecords, R as bindsSubmitterIdentity, S as runCollectionQuery, T as buildActionSeedPrompt, U as appSchemasPath, V as PUBLIC_CONFIG_DOC, W as projectApp, _ 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 publishApp, k as readCustomViewI18n, l as daysInMonth, m as resolveEvery, n as MAX_UNSELECTIVE_ITEMS, o as deleteCollectionRefusalMessage, p as parseCivil, q as parseAuthoredApp, r as makeManageCollectionTool, s as advanceTriggerDate, t as MAX_SCHEMA_ISSUES, u as formatCivil, v as buildWorkspaceOntology, w as runQueryOverRows, x as firstMutateParamProblem, y as schemaRelations, z as publishProblems } from "../../server-B48Jyxcj.js";
4
5
  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-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 };
6
+ export { APPS_COLLECTION, APP_MANIFEST_FILE, APP_ROLES, AuthoredAppZ, BackendUnavailableError, COLLECTION_ROOT_REQUIRED, COMPUTED_TYPES, CollectionQueryZ, CollectionSchemaZ, DEFAULT_QUERY_ROWS, MAX_CSV_ROWS, MAX_QUERY_ROWS, MAX_RECORD_ISSUES, MAX_SCHEMA_ISSUES, MAX_UNSELECTIVE_ITEMS, PUBLIC_CONFIG_DOC, SCHEMA_FILE, STORE_UNREADABLE, acceptParsedSchema, advanceTriggerDate, appConfigPath, appManifestReason, appSchemasPath, applyMutateAction, bindsSubmitterIdentity, 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, parseAuthoredApp, parseCivil, peekWorkspaceRoot, projectApp, projectItemFields, promptPathsFor, publishApp, publishCollectionChange, publishProblems, 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 };
@@ -91,6 +91,10 @@ export declare function makeManageCollectionTool(deps?: ManageCollectionDeps): {
91
91
  type: string;
92
92
  description: string;
93
93
  };
94
+ confirm: {
95
+ type: string;
96
+ description: string;
97
+ };
94
98
  topic: {
95
99
  type: string;
96
100
  description: string;
@@ -0,0 +1,56 @@
1
+ import { DiscoveryOptions } from './discovery';
2
+ import { PublishedApp } from './publishProject';
3
+ export interface PublishOptions extends DiscoveryOptions {
4
+ /** Proceed even though existing records fail the schemas about to be
5
+ * published. Never a default: the pre-check is the migration gate, and a
6
+ * gate that opens itself is a log line. */
7
+ confirm?: boolean | undefined;
8
+ /** Wall clock, injectable so a test can assert an exact document. */
9
+ now?: (() => number) | undefined;
10
+ /** Resolve the commit the declaration is being published from. Injectable
11
+ * for the same reason, and because a repository without git is a normal
12
+ * state rather than a failure. */
13
+ resolveCommit?: ((root: string) => Promise<{
14
+ commit?: string | undefined;
15
+ dirty?: boolean | undefined;
16
+ }>) | undefined;
17
+ }
18
+ export type PublishResult = {
19
+ ok: false;
20
+ problems: string[];
21
+ /** Did any document reach Firestore before this failed?
22
+ *
23
+ * Almost every refusal happens before the first write, and saying so is
24
+ * most of the value of refusing. But a write that fails part-way is a
25
+ * DIFFERENT state — the roster and configuration are live while the
26
+ * schemas are one publish behind — and it is the state a caller most
27
+ * needs to hear about. Carried as a field rather than left for the
28
+ * caller to infer from prose, because a caller that guesses will guess
29
+ * "nothing was written": that is what every other failure means. */
30
+ partial: boolean;
31
+ } | {
32
+ ok: true;
33
+ aid: string;
34
+ cids: string[];
35
+ created: boolean;
36
+ commit?: string | undefined;
37
+ dirty: boolean;
38
+ /** How many live records the pre-check found that the published schemas
39
+ * reject — and whether that number is a FLOOR.
40
+ *
41
+ * `validateCollectionRecords` stops at its own cap per collection, so a
42
+ * full batch means "at least this many". The count and the cap travel
43
+ * together because they are read together: a caller that reports the
44
+ * number without the flag understates how much repair is owed, on the
45
+ * one path (a confirmed publish) where the damage is already done. */
46
+ recordIssues: number;
47
+ recordIssuesCapped: boolean;
48
+ published: PublishedApp;
49
+ };
50
+ /** Publish this repository's declaration to its app.
51
+ *
52
+ * Everything variable is a parameter or comes from the host binding, so the
53
+ * whole path is exercisable against an in-memory `FirestoreDocs` with no
54
+ * network and no API key — which is the only way the conversion table gets
55
+ * tested as a table. */
56
+ export declare function publishApp(opts?: PublishOptions): Promise<PublishResult>;
@@ -0,0 +1,29 @@
1
+ import { AuthoredApp, AuthoredSubmit } from './publishManifest';
2
+ /** What publish knows about a shared collection in this repository, as far as
3
+ * these checks are concerned: its cid and the schema key its records are
4
+ * identified by. The whole schema is deliberately not threaded in — these
5
+ * checks are about the DECLARATION, and the primary key is the one part of
6
+ * the schema the declaration can contradict. */
7
+ export interface PublishableCollection {
8
+ cid: string;
9
+ primaryKey: string;
10
+ }
11
+ /** Does this submit declaration bind a record to the submitter's identity?
12
+ *
13
+ * The condition for requiring `submitOnly`, and deliberately NOT "declares an
14
+ * `audience`": `audience` appears only in the rules' public-create branch, so
15
+ * an owner or editor never meets it and can add records freely. `immutable`
16
+ * is the wrong condition too — a survey's responses are not immutable and
17
+ * can be padded exactly the same way.
18
+ *
19
+ * What these four have in common is that each one makes the record MEAN "the
20
+ * person who submitted it said this": a per-uid id, a per-uid+field id, a
21
+ * row stamped with the submitter's verified address, or a submission
22
+ * restricted to a named participant. A record created through the writer
23
+ * branch carries the same shape and none of that meaning. */
24
+ export declare function bindsSubmitterIdentity(submit: AuthoredSubmit): boolean;
25
+ /** Everything publish refuses, as lines the author can act on.
26
+ *
27
+ * All of them, every time. Publish is a manual step with a human waiting on
28
+ * it; stopping at the first problem turns one review into five. */
29
+ export declare function publishProblems(app: AuthoredApp, collections: readonly PublishableCollection[], publisherEmail: string): string[];
@@ -0,0 +1,181 @@
1
+ import { z } from 'zod';
2
+ /** The four roles the deployed rules understand. `participant` is the layer
3
+ * that is NAMED but reads only its own rows — see `readerOf` vs `listedIn`. */
4
+ export declare const APP_ROLES: readonly ["owner", "editor", "viewer", "participant"];
5
+ /** The declarative mail queue, as the rules re-derive it: a transition of the
6
+ * status field, a recipient read off the RECORD, and a fixed template. */
7
+ declare const MailZ: z.ZodObject<{
8
+ toField: z.ZodString;
9
+ on: z.ZodRecord<z.ZodString, z.ZodObject<{
10
+ from: z.ZodArray<z.ZodString>;
11
+ to: z.ZodString;
12
+ }, z.core.$strict>>;
13
+ dataFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
+ }, z.core.$strict>;
15
+ /** What the rules read out of `collections[cid]`. NOT the schema — the schema
16
+ * is published beside it, untouched, for clients to render from. */
17
+ declare const CollectionConfigZ: z.ZodObject<{
18
+ statusField: z.ZodOptional<z.ZodString>;
19
+ transitions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
20
+ immutable: z.ZodOptional<z.ZodBoolean>;
21
+ submitOnly: z.ZodOptional<z.ZodBoolean>;
22
+ peerVisibility: z.ZodOptional<z.ZodEnum<{
23
+ public: "public";
24
+ hidden: "hidden";
25
+ }>>;
26
+ revealGated: z.ZodOptional<z.ZodBoolean>;
27
+ gatedFrom: z.ZodOptional<z.ZodString>;
28
+ revealBy: z.ZodOptional<z.ZodString>;
29
+ mail: z.ZodOptional<z.ZodObject<{
30
+ toField: z.ZodString;
31
+ on: z.ZodRecord<z.ZodString, z.ZodObject<{
32
+ from: z.ZodArray<z.ZodString>;
33
+ to: z.ZodString;
34
+ }, z.core.$strict>>;
35
+ dataFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
36
+ }, z.core.$strict>>;
37
+ aggregate: z.ZodOptional<z.ZodObject<{
38
+ by: z.ZodArray<z.ZodString>;
39
+ }, z.core.$strict>>;
40
+ }, z.core.$strict>;
41
+ declare const SubmitZ: z.ZodObject<{
42
+ auth: z.ZodEnum<{
43
+ none: "none";
44
+ anonymous: "anonymous";
45
+ verifiedEmail: "verifiedEmail";
46
+ }>;
47
+ emailField: z.ZodOptional<z.ZodString>;
48
+ createFields: z.ZodArray<z.ZodString>;
49
+ initialStatus: z.ZodOptional<z.ZodString>;
50
+ idFrom: z.ZodOptional<z.ZodEnum<{
51
+ auto: "auto";
52
+ "auth.uid": "auth.uid";
53
+ "auth.uid+field": "auth.uid+field";
54
+ }>>;
55
+ idField: z.ZodOptional<z.ZodString>;
56
+ validate: z.ZodOptional<z.ZodObject<{
57
+ required: z.ZodOptional<z.ZodArray<z.ZodString>>;
58
+ keyFields: z.ZodOptional<z.ZodArray<z.ZodObject<{
59
+ field: z.ZodString;
60
+ values: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
61
+ }, z.core.$strict>>>;
62
+ }, z.core.$strict>>;
63
+ window: z.ZodOptional<z.ZodObject<{
64
+ from: z.ZodOptional<z.ZodISODateTime>;
65
+ until: z.ZodOptional<z.ZodISODateTime>;
66
+ }, z.core.$strict>>;
67
+ selfUpdate: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
68
+ selfTransitions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
69
+ finalize: z.ZodOptional<z.ZodBoolean>;
70
+ audience: z.ZodOptional<z.ZodLiteral<"participant">>;
71
+ gateOn: z.ZodOptional<z.ZodObject<{
72
+ phase: z.ZodString;
73
+ match: z.ZodString;
74
+ }, z.core.$strict>>;
75
+ }, z.core.$strict>;
76
+ /** The whole authored declaration.
77
+ *
78
+ * `owner` is accepted but is NOT the published value — publish stamps the
79
+ * publisher's uid (or carries the existing one forward, which is what the
80
+ * rules require on update) and refuses a declaration that disagrees. It is
81
+ * accepted rather than banned because the sample app.json in the design note
82
+ * shows it, and a hard refusal on a key the samples contain would be a worse
83
+ * first experience than a message naming the mismatch. */
84
+ export declare const AuthoredAppZ: z.ZodObject<{
85
+ aid: z.ZodString;
86
+ name: z.ZodOptional<z.ZodString>;
87
+ aidEnv: z.ZodOptional<z.ZodString>;
88
+ owner: z.ZodOptional<z.ZodString>;
89
+ members: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodUnion<readonly [z.ZodLiteral<"*">, z.ZodString]>, z.ZodEnum<{
90
+ owner: "owner";
91
+ editor: "editor";
92
+ viewer: "viewer";
93
+ participant: "participant";
94
+ }>>>;
95
+ collections: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
96
+ statusField: z.ZodOptional<z.ZodString>;
97
+ transitions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
98
+ immutable: z.ZodOptional<z.ZodBoolean>;
99
+ submitOnly: z.ZodOptional<z.ZodBoolean>;
100
+ peerVisibility: z.ZodOptional<z.ZodEnum<{
101
+ public: "public";
102
+ hidden: "hidden";
103
+ }>>;
104
+ revealGated: z.ZodOptional<z.ZodBoolean>;
105
+ gatedFrom: z.ZodOptional<z.ZodString>;
106
+ revealBy: z.ZodOptional<z.ZodString>;
107
+ mail: z.ZodOptional<z.ZodObject<{
108
+ toField: z.ZodString;
109
+ on: z.ZodRecord<z.ZodString, z.ZodObject<{
110
+ from: z.ZodArray<z.ZodString>;
111
+ to: z.ZodString;
112
+ }, z.core.$strict>>;
113
+ dataFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
114
+ }, z.core.$strict>>;
115
+ aggregate: z.ZodOptional<z.ZodObject<{
116
+ by: z.ZodArray<z.ZodString>;
117
+ }, z.core.$strict>>;
118
+ }, z.core.$strict>>>;
119
+ participantRead: z.ZodOptional<z.ZodArray<z.ZodString>>;
120
+ public: z.ZodOptional<z.ZodObject<{
121
+ enabled: z.ZodOptional<z.ZodBoolean>;
122
+ read: z.ZodOptional<z.ZodArray<z.ZodString>>;
123
+ submit: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
124
+ auth: z.ZodEnum<{
125
+ none: "none";
126
+ anonymous: "anonymous";
127
+ verifiedEmail: "verifiedEmail";
128
+ }>;
129
+ emailField: z.ZodOptional<z.ZodString>;
130
+ createFields: z.ZodArray<z.ZodString>;
131
+ initialStatus: z.ZodOptional<z.ZodString>;
132
+ idFrom: z.ZodOptional<z.ZodEnum<{
133
+ auto: "auto";
134
+ "auth.uid": "auth.uid";
135
+ "auth.uid+field": "auth.uid+field";
136
+ }>>;
137
+ idField: z.ZodOptional<z.ZodString>;
138
+ validate: z.ZodOptional<z.ZodObject<{
139
+ required: z.ZodOptional<z.ZodArray<z.ZodString>>;
140
+ keyFields: z.ZodOptional<z.ZodArray<z.ZodObject<{
141
+ field: z.ZodString;
142
+ values: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
143
+ }, z.core.$strict>>>;
144
+ }, z.core.$strict>>;
145
+ window: z.ZodOptional<z.ZodObject<{
146
+ from: z.ZodOptional<z.ZodISODateTime>;
147
+ until: z.ZodOptional<z.ZodISODateTime>;
148
+ }, z.core.$strict>>;
149
+ selfUpdate: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
150
+ selfTransitions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
151
+ finalize: z.ZodOptional<z.ZodBoolean>;
152
+ audience: z.ZodOptional<z.ZodLiteral<"participant">>;
153
+ gateOn: z.ZodOptional<z.ZodObject<{
154
+ phase: z.ZodString;
155
+ match: z.ZodString;
156
+ }, z.core.$strict>>;
157
+ }, z.core.$strict>>>;
158
+ }, z.core.$strict>>;
159
+ }, z.core.$strict>;
160
+ export type AuthoredApp = z.infer<typeof AuthoredAppZ>;
161
+ export type AuthoredCollectionConfig = z.infer<typeof CollectionConfigZ>;
162
+ export type AuthoredSubmit = z.infer<typeof SubmitZ>;
163
+ export type AuthoredMail = z.infer<typeof MailZ>;
164
+ export type AuthoredAppResult = {
165
+ ok: true;
166
+ app: AuthoredApp;
167
+ } | {
168
+ ok: false;
169
+ problems: string[];
170
+ };
171
+ /** Parse the authored declaration out of `app.json`'s text.
172
+ *
173
+ * Returns a LIST of problems rather than throwing, for the same reason
174
+ * `loadAppManifest` returns a failure: the caller is a gate whose entire job
175
+ * is to hand the author something to act on. Every problem is reported at
176
+ * once — publish is a manual step, and a parser that stops at the first key
177
+ * makes it N round trips. */
178
+ export declare function parseAuthoredApp(raw: string): AuthoredAppResult;
179
+ /** zod issues as one actionable line each: `public.submit.responses.auth: …`. */
180
+ export declare function authoredProblems(error: z.ZodError): string[];
181
+ export {};
@@ -0,0 +1,86 @@
1
+ import { CollectionSchema } from '../core/schema';
2
+ import { AuthoredApp } from './publishManifest';
3
+ /** Who and when. Threaded in rather than read from a clock inside the
4
+ * projection so the projection stays pure and the tests can assert on an
5
+ * exact document. */
6
+ export interface PublishStamp {
7
+ /** The publisher's Firebase uid. The rules require `owner ==
8
+ * request.auth.uid` on CREATE and `owner` unchanged on UPDATE, so this is
9
+ * used only when the app document does not exist yet. */
10
+ uid: string;
11
+ /** The publisher's verified email — the principal the roster is keyed by. */
12
+ email: string;
13
+ /** Wall-clock millis of this publish. */
14
+ publishedAt: number;
15
+ /** The git commit the declaration was read from, when the host could
16
+ * resolve one. Absent is a normal state (a dirty tree, a repository
17
+ * without git), and absent is more honest than a fabricated value. */
18
+ commit?: string | undefined;
19
+ }
20
+ /** One collection's published schema document (`apps/{aid}/collections/{cid}`).
21
+ *
22
+ * The rules never read `publishedSchema` — `schemaRead` gates the whole
23
+ * document and stops there. It is here for the CLIENTS: a member's host
24
+ * renders from it, and a public webview has no other way to know the fields.
25
+ * The whole schema is published rather than a projection of it, because
26
+ * every attempt to guess "the part a view needs" is a guess about a view
27
+ * that has not been written yet. */
28
+ export interface PublishedSchemaDoc extends Record<string, unknown> {
29
+ publishedSchema: CollectionSchema;
30
+ publishedAt: number;
31
+ publishedBy: string;
32
+ publishedCommit?: string;
33
+ }
34
+ /** The public settings document (`apps/{aid}/config/public`).
35
+ *
36
+ * `allow read: if true` — this is the one document an anonymous visitor can
37
+ * read, and it is the reason `apps/{aid}` itself is reader-only: a
38
+ * participant reading the app document would see their classmates'
39
+ * addresses. So the roster is NOT here, and neither is `owner`. What is here
40
+ * is what a public form needs in order to render itself and to tell a visitor
41
+ * "this closed yesterday" instead of failing a write with no explanation. */
42
+ export interface PublishedConfigDoc extends Record<string, unknown> {
43
+ name?: string;
44
+ enabled: boolean;
45
+ read: string[];
46
+ submit: Record<string, Record<string, unknown>>;
47
+ publishedAt: number;
48
+ }
49
+ /** Everything one publish writes. Separate documents because they live at
50
+ * separate paths with separate rules — and in the order given: the app
51
+ * document authorizes the other two, so it goes first. */
52
+ export interface PublishedApp {
53
+ app: Record<string, unknown>;
54
+ schemas: {
55
+ cid: string;
56
+ doc: PublishedSchemaDoc;
57
+ }[];
58
+ config: PublishedConfigDoc;
59
+ }
60
+ /** The document id under `apps/{aid}/config`. One document, named, rather than
61
+ * a spread of them: a second public document is a second thing to keep in
62
+ * step, and nothing yet needs one. */
63
+ export declare const PUBLIC_CONFIG_DOC = "public";
64
+ /** Project the authored declaration into the documents publish writes.
65
+ *
66
+ * `existing` is the app document as it is in Firestore right now, or null on
67
+ * a first publish. Two things need it, both required by the rules:
68
+ * - `owner` must be UNCHANGED on update. Re-stamping the publisher's uid
69
+ * would be refused for any app whose owner ever signed in as a different
70
+ * account, and would silently transfer ownership if it were not.
71
+ * - `previousPublished` is that document, so a rollback has something to
72
+ * put back.
73
+ *
74
+ * Pure: no clock, no filesystem, no Firestore. Everything variable arrives as
75
+ * a parameter, which is what makes the conversion table testable as a table. */
76
+ export declare function projectApp(authored: AuthoredApp, schemas: {
77
+ cid: string;
78
+ schema: CollectionSchema;
79
+ }[], stamp: PublishStamp, existing: Record<string, unknown> | null): PublishedApp;
80
+ /** The app documents' parent path — the `FirestoreDocs` seam takes a
81
+ * collection path plus a document id, and the app document's id is the aid. */
82
+ export declare const APPS_COLLECTION = "apps";
83
+ /** The collection (schema) documents' parent path. */
84
+ export declare const appSchemasPath: (aid: string) => string;
85
+ /** The public-config documents' parent path. */
86
+ export declare const appConfigPath: (aid: string) => string;
@@ -8,6 +8,18 @@ export interface RecordIssue {
8
8
  /** Human-readable problem, written to be actionable by the LLM. */
9
9
  problem: string;
10
10
  }
11
+ /** Don't flood the result; the first batch is enough to act on. Exported
12
+ * because a caller that REPORTS a count has to know the count is a floor —
13
+ * `publish` presents a full batch as "at least N" rather than as a total. */
14
+ export declare const MAX_RECORD_ISSUES = 25;
15
+ /** The `file` of the pseudo-issue reported when the backend could not be read
16
+ * at all.
17
+ *
18
+ * Exported because it is a DIFFERENT KIND of answer from "this record is
19
+ * invalid", and a caller that treats the two alike gets it wrong in the
20
+ * direction that matters: `publish` lets the user override invalid records,
21
+ * and overriding this one would mean publishing without ever having looked. */
22
+ export declare const STORE_UNREADABLE = "(store)";
11
23
  export declare function validateCollectionRecords(collection: LoadedCollection, opts?: {
12
24
  workspaceRoot?: string | undefined;
13
25
  }): Promise<RecordIssue[]>;