@x12i/catalox 2.2.2 → 2.4.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 (75) hide show
  1. package/README.md +42 -7
  2. package/dist/src/catalox/backup-data.d.ts +40 -0
  3. package/dist/src/catalox/backup-data.d.ts.map +1 -0
  4. package/dist/src/catalox/backup-data.js +522 -0
  5. package/dist/src/catalox/backup-data.js.map +1 -0
  6. package/dist/src/catalox/catalox.d.ts +25 -0
  7. package/dist/src/catalox/catalox.d.ts.map +1 -1
  8. package/dist/src/catalox/catalox.js +63 -1
  9. package/dist/src/catalox/catalox.js.map +1 -1
  10. package/dist/src/catalox/index.d.ts +3 -0
  11. package/dist/src/catalox/index.d.ts.map +1 -1
  12. package/dist/src/catalox/index.js +3 -0
  13. package/dist/src/catalox/index.js.map +1 -1
  14. package/dist/src/catalox/native-catalog-layout-diagnostics.d.ts +29 -0
  15. package/dist/src/catalox/native-catalog-layout-diagnostics.d.ts.map +1 -0
  16. package/dist/src/catalox/native-catalog-layout-diagnostics.js +55 -0
  17. package/dist/src/catalox/native-catalog-layout-diagnostics.js.map +1 -0
  18. package/dist/src/catalox/restore-firestore-backup.d.ts +13 -0
  19. package/dist/src/catalox/restore-firestore-backup.d.ts.map +1 -0
  20. package/dist/src/catalox/restore-firestore-backup.js +326 -0
  21. package/dist/src/catalox/restore-firestore-backup.js.map +1 -0
  22. package/dist/src/cli/index.js +156 -5
  23. package/dist/src/cli/index.js.map +1 -1
  24. package/dist/src/contracts/backup.d.ts +36 -0
  25. package/dist/src/contracts/backup.d.ts.map +1 -0
  26. package/dist/src/contracts/backup.js +2 -0
  27. package/dist/src/contracts/backup.js.map +1 -0
  28. package/dist/src/contracts/catalog-data-index.d.ts +13 -0
  29. package/dist/src/contracts/catalog-data-index.d.ts.map +1 -0
  30. package/dist/src/contracts/catalog-data-index.js +2 -0
  31. package/dist/src/contracts/catalog-data-index.js.map +1 -0
  32. package/dist/src/contracts/catalogs.d.ts +2 -1
  33. package/dist/src/contracts/catalogs.d.ts.map +1 -1
  34. package/dist/src/contracts/index.d.ts +3 -0
  35. package/dist/src/contracts/index.d.ts.map +1 -1
  36. package/dist/src/contracts/index.js.map +1 -1
  37. package/dist/src/contracts/restore.d.ts +44 -0
  38. package/dist/src/contracts/restore.d.ts.map +1 -0
  39. package/dist/src/contracts/restore.js +2 -0
  40. package/dist/src/contracts/restore.js.map +1 -0
  41. package/dist/src/firebase/catalog-data-index-store.d.ts +14 -0
  42. package/dist/src/firebase/catalog-data-index-store.d.ts.map +1 -0
  43. package/dist/src/firebase/catalog-data-index-store.js +33 -0
  44. package/dist/src/firebase/catalog-data-index-store.js.map +1 -0
  45. package/dist/src/firebase/catalog-data-paths.d.ts +14 -0
  46. package/dist/src/firebase/catalog-data-paths.d.ts.map +1 -0
  47. package/dist/src/firebase/catalog-data-paths.js +31 -0
  48. package/dist/src/firebase/catalog-data-paths.js.map +1 -0
  49. package/dist/src/firebase/index.d.ts +2 -0
  50. package/dist/src/firebase/index.d.ts.map +1 -1
  51. package/dist/src/firebase/index.js +2 -0
  52. package/dist/src/firebase/index.js.map +1 -1
  53. package/dist/src/firebase/native-item-store.d.ts +5 -1
  54. package/dist/src/firebase/native-item-store.d.ts.map +1 -1
  55. package/dist/src/firebase/native-item-store.js +33 -15
  56. package/dist/src/firebase/native-item-store.js.map +1 -1
  57. package/dist/src/migrations/index.d.ts +1 -0
  58. package/dist/src/migrations/index.d.ts.map +1 -1
  59. package/dist/src/migrations/index.js +1 -0
  60. package/dist/src/migrations/index.js.map +1 -1
  61. package/dist/src/migrations/migrate-native-catalog-layout.d.ts +51 -0
  62. package/dist/src/migrations/migrate-native-catalog-layout.d.ts.map +1 -0
  63. package/dist/src/migrations/migrate-native-catalog-layout.js +224 -0
  64. package/dist/src/migrations/migrate-native-catalog-layout.js.map +1 -0
  65. package/dist/test/integration/firestore.emulator.test.js +10 -2
  66. package/dist/test/integration/firestore.emulator.test.js.map +1 -1
  67. package/dist/test/unit/backup-timestamp.test.d.ts +2 -0
  68. package/dist/test/unit/backup-timestamp.test.d.ts.map +1 -0
  69. package/dist/test/unit/backup-timestamp.test.js +11 -0
  70. package/dist/test/unit/backup-timestamp.test.js.map +1 -0
  71. package/dist/test/unit/resolve-native-items-layout.test.d.ts +2 -0
  72. package/dist/test/unit/resolve-native-items-layout.test.d.ts.map +1 -0
  73. package/dist/test/unit/resolve-native-items-layout.test.js +50 -0
  74. package/dist/test/unit/resolve-native-items-layout.test.js.map +1 -0
  75. package/package.json +2 -1
@@ -0,0 +1,326 @@
1
+ import { BACKUP_METADATA_SOURCE_NAMES, deleteAllDocsInFirestoreCollection, discoverNativeCatalogIds, firebaseNativeLogical, firebaseSnapshotLogical, firebaseSnapshotRunLogical, firebaseVersionedName, formatBackupTimestamp, paginatedCopyFirestoreToFirestore, firebaseLatestName, } from "./backup-data.js";
2
+ import { NativeItemStore } from "../firebase/native-item-store.js";
3
+ import { DefinitionStore } from "../firebase/definition-store.js";
4
+ import { CatalogDataIndexStore } from "../firebase/catalog-data-index-store.js";
5
+ import { legacyNativeItemsCollectionRef, resolveNativeItemsLayout, nativeItemsCollectionId, } from "../firebase/catalog-data-paths.js";
6
+ import { fixNativeCatalogDataMetadata } from "../migrations/migrate-native-catalog-layout.js";
7
+ const RESTORE_MANIFEST_COLLECTION = "backup-restoreSessions";
8
+ function sourceBackupCollectionId(source, logical) {
9
+ if (source.kind === "latest")
10
+ return firebaseLatestName(logical);
11
+ return firebaseVersionedName(source.versionTimestamp, logical);
12
+ }
13
+ function preRestoreCollectionId(restoreSessionId, logical) {
14
+ return `${restoreSessionId}__preRestore-${logical}`;
15
+ }
16
+ /**
17
+ * Discover snapshot backup mirrors by scanning top-level collection ids.
18
+ * Parses `catalogSnapshotsRun--{catalogId}--{runId}` with the last `--` as delimiter (catalogId / runId must not contain `--`).
19
+ */
20
+ async function listSnapshotRestoreWork(fs, source, includeSnapshots) {
21
+ if (!includeSnapshots)
22
+ return { snapshotCatalogIds: [], snapshotRuns: [] };
23
+ const cols = await fs.listCollections();
24
+ const itemPrefix = source.kind === "latest" ? "backup-catalogSnapshots--" : `${source.versionTimestamp}__backup-catalogSnapshots--`;
25
+ const runPrefix = source.kind === "latest"
26
+ ? "backup-catalogSnapshotsRun--"
27
+ : `${source.versionTimestamp}__backup-catalogSnapshotsRun--`;
28
+ const snapshotCatalogIds = new Set();
29
+ const snapshotRuns = [];
30
+ for (const c of cols) {
31
+ const id = c.id;
32
+ if (!id.startsWith(itemPrefix) || id.includes("catalogSnapshotsRun"))
33
+ continue;
34
+ const catalogId = id.slice(itemPrefix.length);
35
+ if (!catalogId)
36
+ continue;
37
+ const probe = await fs.collection(id).limit(1).get();
38
+ if (!probe.empty)
39
+ snapshotCatalogIds.add(catalogId);
40
+ }
41
+ const runSeen = new Set();
42
+ for (const c of cols) {
43
+ const id = c.id;
44
+ if (!id.startsWith(runPrefix))
45
+ continue;
46
+ const rest = id.slice(runPrefix.length);
47
+ const delim = rest.lastIndexOf("--");
48
+ if (delim === -1)
49
+ continue;
50
+ const catalogId = rest.slice(0, delim);
51
+ const runId = rest.slice(delim + 2);
52
+ const probe = await fs.collection(id).limit(1).get();
53
+ if (probe.empty)
54
+ continue;
55
+ const rk = `${catalogId}\0${runId}`;
56
+ if (runSeen.has(rk))
57
+ continue;
58
+ runSeen.add(rk);
59
+ snapshotRuns.push({ catalogId, runId });
60
+ }
61
+ return { snapshotCatalogIds: [...snapshotCatalogIds], snapshotRuns };
62
+ }
63
+ async function writeManifest(fs, manifest) {
64
+ await fs.collection(RESTORE_MANIFEST_COLLECTION).doc(manifest.restoreSessionId).set(manifest, { merge: true });
65
+ }
66
+ async function readManifest(fs, restoreSessionId) {
67
+ const snap = await fs.collection(RESTORE_MANIFEST_COLLECTION).doc(restoreSessionId).get();
68
+ if (!snap.exists)
69
+ return null;
70
+ return snap.data();
71
+ }
72
+ export async function runRestoreFirestoreBackup(deps, input) {
73
+ const issues = [];
74
+ const counts = {};
75
+ const nativeItemSourceLayoutByCatalogId = {};
76
+ if (input.source.kind === "versioned" && !input.source.versionTimestamp?.trim()) {
77
+ return {
78
+ ok: false,
79
+ restoreSessionId: "",
80
+ counts: {},
81
+ issues: [{ code: "missing_version_timestamp", message: "versioned restore requires versionTimestamp" }],
82
+ nativeItemSourceLayoutByCatalogId: {},
83
+ };
84
+ }
85
+ const fs = deps.firestoreStore.firestore;
86
+ const restoreSessionId = (input.restoreSessionId?.trim() && input.restoreSessionId) || formatBackupTimestamp();
87
+ const includeSnapshots = Boolean(input.includeSnapshots);
88
+ const allCatalogs = await deps.catalogs.listAll();
89
+ const nativeCatalogIds = await discoverNativeCatalogIds(fs, allCatalogs, {
90
+ bindings: deps.bindings,
91
+ ...(input.catalogIds != null && input.catalogIds.length ? { catalogIds: input.catalogIds } : {}),
92
+ ...(input.appId != null && input.appId !== "" ? { appId: input.appId } : {}),
93
+ });
94
+ const probeLogical = BACKUP_METADATA_SOURCE_NAMES[0];
95
+ const probeId = sourceBackupCollectionId(input.source, probeLogical);
96
+ const probeSnap = await fs.collection(probeId).limit(1).get();
97
+ if (probeSnap.empty) {
98
+ return {
99
+ ok: false,
100
+ restoreSessionId,
101
+ counts,
102
+ issues: [
103
+ {
104
+ code: "backup_source_empty",
105
+ message: `No documents in backup collection ${probeId}; nothing to restore from this source`,
106
+ detail: { probeId },
107
+ },
108
+ ],
109
+ nativeItemSourceLayoutByCatalogId: {},
110
+ };
111
+ }
112
+ const { snapshotCatalogIds, snapshotRuns } = await listSnapshotRestoreWork(fs, input.source, includeSnapshots);
113
+ try {
114
+ for (const name of BACKUP_METADATA_SOURCE_NAMES) {
115
+ const destPre = preRestoreCollectionId(restoreSessionId, name);
116
+ await deleteAllDocsInFirestoreCollection(fs, destPre);
117
+ await paginatedCopyFirestoreToFirestore(fs, name, destPre, counts, `pre:${destPre}`);
118
+ }
119
+ for (const catalogId of nativeCatalogIds) {
120
+ const layout = await resolveNativeItemsLayout(fs, catalogId);
121
+ nativeItemSourceLayoutByCatalogId[String(catalogId)] = layout;
122
+ const logical = firebaseNativeLogical(String(catalogId));
123
+ const destPre = preRestoreCollectionId(restoreSessionId, logical);
124
+ await deleteAllDocsInFirestoreCollection(fs, destPre);
125
+ const livePath = layout === "legacy"
126
+ ? legacyNativeItemsCollectionRef(fs, catalogId).path
127
+ : fs.collection(nativeItemsCollectionId(String(catalogId))).path;
128
+ await paginatedCopyFirestoreToFirestore(fs, livePath, destPre, counts, `pre:${destPre}`);
129
+ }
130
+ if (includeSnapshots) {
131
+ for (const catalogId of snapshotCatalogIds) {
132
+ const logical = firebaseSnapshotLogical(catalogId);
133
+ const destPre = preRestoreCollectionId(restoreSessionId, logical);
134
+ await deleteAllDocsInFirestoreCollection(fs, destPre);
135
+ const liveItems = fs.collection("catalogSnapshots").doc(catalogId).collection("items").path;
136
+ await paginatedCopyFirestoreToFirestore(fs, liveItems, destPre, counts, `pre:${destPre}`);
137
+ }
138
+ for (const { catalogId, runId } of snapshotRuns) {
139
+ const logical = firebaseSnapshotRunLogical(catalogId, runId);
140
+ const destPre = preRestoreCollectionId(restoreSessionId, logical);
141
+ await deleteAllDocsInFirestoreCollection(fs, destPre);
142
+ const livePath = fs.collection("catalogSnapshots").doc(catalogId).collection("runs").doc(runId).collection("items")
143
+ .path;
144
+ await paginatedCopyFirestoreToFirestore(fs, livePath, destPre, counts, `pre:${destPre}`);
145
+ }
146
+ }
147
+ const manifest = {
148
+ restoreSessionId,
149
+ createdAt: new Date().toISOString(),
150
+ source: input.source,
151
+ includeSnapshots,
152
+ phase: "preRestoreCaptured",
153
+ nativeCatalogIds: nativeCatalogIds.map(String),
154
+ nativeItemSourceLayoutByCatalogId: { ...nativeItemSourceLayoutByCatalogId },
155
+ metadataSourceNames: BACKUP_METADATA_SOURCE_NAMES,
156
+ snapshotCatalogIds,
157
+ snapshotRuns,
158
+ };
159
+ await writeManifest(fs, manifest);
160
+ for (const name of BACKUP_METADATA_SOURCE_NAMES) {
161
+ const src = sourceBackupCollectionId(input.source, name);
162
+ await deleteAllDocsInFirestoreCollection(fs, name);
163
+ await paginatedCopyFirestoreToFirestore(fs, src, name, counts, `restore:${name}`);
164
+ }
165
+ for (const catalogId of nativeCatalogIds) {
166
+ const cid = String(catalogId);
167
+ const flatPath = nativeItemsCollectionId(cid);
168
+ await deleteAllDocsInFirestoreCollection(fs, flatPath);
169
+ const src = sourceBackupCollectionId(input.source, firebaseNativeLogical(cid));
170
+ await paginatedCopyFirestoreToFirestore(fs, src, flatPath, counts, `restore:nativeFlat:${cid}`);
171
+ const legacyPath = legacyNativeItemsCollectionRef(fs, catalogId).path;
172
+ await deleteAllDocsInFirestoreCollection(fs, legacyPath);
173
+ }
174
+ const nowIso = new Date().toISOString();
175
+ if (nativeCatalogIds.length > 0) {
176
+ await fixNativeCatalogDataMetadata({
177
+ definitions: deps.definitions,
178
+ catalogDataIndex: deps.catalogDataIndex,
179
+ nativeItems: deps.nativeItems,
180
+ }, nativeCatalogIds, nowIso);
181
+ }
182
+ if (includeSnapshots) {
183
+ for (const catalogId of snapshotCatalogIds) {
184
+ const dest = fs.collection("catalogSnapshots").doc(catalogId).collection("items").path;
185
+ const src = sourceBackupCollectionId(input.source, firebaseSnapshotLogical(catalogId));
186
+ await deleteAllDocsInFirestoreCollection(fs, dest);
187
+ await paginatedCopyFirestoreToFirestore(fs, src, dest, counts, `restore:snap:${catalogId}`);
188
+ }
189
+ for (const { catalogId, runId } of snapshotRuns) {
190
+ const dest = fs
191
+ .collection("catalogSnapshots")
192
+ .doc(catalogId)
193
+ .collection("runs")
194
+ .doc(runId)
195
+ .collection("items").path;
196
+ const src = sourceBackupCollectionId(input.source, firebaseSnapshotRunLogical(catalogId, runId));
197
+ await deleteAllDocsInFirestoreCollection(fs, dest);
198
+ await paginatedCopyFirestoreToFirestore(fs, src, dest, counts, `restore:snaprun:${catalogId}:${runId}`);
199
+ }
200
+ }
201
+ await writeManifest(fs, { ...manifest, phase: "restored" });
202
+ deps.nativeItems.invalidateLayoutCache();
203
+ return {
204
+ ok: true,
205
+ restoreSessionId,
206
+ counts,
207
+ issues,
208
+ nativeItemSourceLayoutByCatalogId,
209
+ };
210
+ }
211
+ catch (e) {
212
+ const msg = e instanceof Error ? e.message : String(e);
213
+ issues.push({ code: "restore_failed", message: msg });
214
+ try {
215
+ await writeManifest(fs, {
216
+ restoreSessionId,
217
+ createdAt: new Date().toISOString(),
218
+ source: input.source,
219
+ includeSnapshots,
220
+ phase: "failed",
221
+ nativeCatalogIds: nativeCatalogIds.map(String),
222
+ nativeItemSourceLayoutByCatalogId: { ...nativeItemSourceLayoutByCatalogId },
223
+ metadataSourceNames: BACKUP_METADATA_SOURCE_NAMES,
224
+ snapshotCatalogIds,
225
+ snapshotRuns,
226
+ });
227
+ }
228
+ catch {
229
+ // ignore manifest write errors during failure handling
230
+ }
231
+ deps.nativeItems.invalidateLayoutCache();
232
+ return {
233
+ ok: false,
234
+ restoreSessionId,
235
+ counts,
236
+ issues,
237
+ nativeItemSourceLayoutByCatalogId,
238
+ };
239
+ }
240
+ }
241
+ export async function runUndoFirestoreRestore(deps, input) {
242
+ const issues = [];
243
+ const counts = {};
244
+ const fs = deps.firestoreStore.firestore;
245
+ const id = input.restoreSessionId.trim();
246
+ if (!id) {
247
+ return { ok: false, counts, issues: [{ code: "missing_session", message: "restoreSessionId is required" }] };
248
+ }
249
+ const manifest = await readManifest(fs, id);
250
+ if (!manifest) {
251
+ return {
252
+ ok: false,
253
+ counts,
254
+ issues: [{ code: "manifest_not_found", message: `No restore manifest at ${RESTORE_MANIFEST_COLLECTION}/${id}` }],
255
+ };
256
+ }
257
+ if (manifest.phase === "undone") {
258
+ return { ok: false, counts, issues: [{ code: "already_undone", message: "This restore session was already undone" }] };
259
+ }
260
+ if (manifest.phase !== "restored") {
261
+ return {
262
+ ok: false,
263
+ counts,
264
+ issues: [
265
+ {
266
+ code: "invalid_phase",
267
+ message: `Undo requires phase "restored" (current: ${manifest.phase})`,
268
+ },
269
+ ],
270
+ };
271
+ }
272
+ try {
273
+ if (manifest.includeSnapshots) {
274
+ for (const { catalogId, runId } of manifest.snapshotRuns) {
275
+ const logical = firebaseSnapshotRunLogical(catalogId, runId);
276
+ const src = preRestoreCollectionId(id, logical);
277
+ const dest = fs
278
+ .collection("catalogSnapshots")
279
+ .doc(catalogId)
280
+ .collection("runs")
281
+ .doc(runId)
282
+ .collection("items").path;
283
+ await deleteAllDocsInFirestoreCollection(fs, dest);
284
+ await paginatedCopyFirestoreToFirestore(fs, src, dest, counts, `undo:snaprun:${catalogId}:${runId}`);
285
+ }
286
+ for (const catalogId of manifest.snapshotCatalogIds) {
287
+ const logical = firebaseSnapshotLogical(catalogId);
288
+ const src = preRestoreCollectionId(id, logical);
289
+ const dest = fs.collection("catalogSnapshots").doc(catalogId).collection("items").path;
290
+ await deleteAllDocsInFirestoreCollection(fs, dest);
291
+ await paginatedCopyFirestoreToFirestore(fs, src, dest, counts, `undo:snap:${catalogId}`);
292
+ }
293
+ }
294
+ for (const catalogId of manifest.nativeCatalogIds) {
295
+ const layout = manifest.nativeItemSourceLayoutByCatalogId[catalogId] ?? "flat";
296
+ const cid = catalogId;
297
+ const flatPath = nativeItemsCollectionId(catalogId);
298
+ const legacyPath = legacyNativeItemsCollectionRef(fs, cid).path;
299
+ const src = preRestoreCollectionId(id, firebaseNativeLogical(catalogId));
300
+ if (layout === "legacy") {
301
+ await deleteAllDocsInFirestoreCollection(fs, flatPath);
302
+ await deleteAllDocsInFirestoreCollection(fs, legacyPath);
303
+ await paginatedCopyFirestoreToFirestore(fs, src, legacyPath, counts, `undo:native:${catalogId}`);
304
+ }
305
+ else {
306
+ await deleteAllDocsInFirestoreCollection(fs, flatPath);
307
+ await deleteAllDocsInFirestoreCollection(fs, legacyPath);
308
+ await paginatedCopyFirestoreToFirestore(fs, src, flatPath, counts, `undo:native:${catalogId}`);
309
+ }
310
+ }
311
+ for (const name of manifest.metadataSourceNames) {
312
+ const src = preRestoreCollectionId(id, name);
313
+ await deleteAllDocsInFirestoreCollection(fs, name);
314
+ await paginatedCopyFirestoreToFirestore(fs, src, name, counts, `undo:${name}`);
315
+ }
316
+ await writeManifest(fs, { ...manifest, phase: "undone" });
317
+ deps.nativeItems.invalidateLayoutCache();
318
+ return { ok: true, counts, issues };
319
+ }
320
+ catch (e) {
321
+ const msg = e instanceof Error ? e.message : String(e);
322
+ issues.push({ code: "undo_failed", message: msg });
323
+ return { ok: false, counts, issues };
324
+ }
325
+ }
326
+ //# sourceMappingURL=restore-firestore-backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore-firestore-backup.js","sourceRoot":"","sources":["../../../src/catalox/restore-firestore-backup.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ import { resolve as resolvePath } from "node:path";
10
10
  import { ApiCatalogAdapter, AuthorizationService, BindingStore, CatalogStore, Catalox, DefinitionStore, DescriptorStore, FirestoreStore, MappingStore, NativeItemStore, ReferenceStore, SnapshotStore, StoreAppBindingStore, } from "../index.js";
11
11
  import { AdapterStore } from "../firebase/adapter-store.js";
12
12
  import { AppStore } from "../firebase/app-store.js";
13
+ import { CatalogDataIndexStore } from "../firebase/catalog-data-index-store.js";
13
14
  import { MongoCatalogAdapter } from "../adapters/mongo/mongo-adapter.js";
14
15
  import { buildCatalogListMarkdownMap, renderCatalogListMarkdown } from "../markdown/render-list-markdown.js";
15
16
  import { buildCatalogItemMarkdownMap, renderCatalogItemMarkdown } from "../markdown/render-item-markdown.js";
@@ -38,6 +39,7 @@ function createCatalox() {
38
39
  const descriptors = new DescriptorStore(store);
39
40
  const references = new ReferenceStore(store);
40
41
  const nativeItems = new NativeItemStore(store);
42
+ const catalogDataIndex = new CatalogDataIndexStore(store);
41
43
  const snapshots = new SnapshotStore(store);
42
44
  const adapters = new AdapterStore(store);
43
45
  const authz = new AuthorizationService(bindings);
@@ -56,19 +58,27 @@ function createCatalox() {
56
58
  nativeItems,
57
59
  snapshots,
58
60
  adapters,
61
+ firestoreStore: store,
62
+ catalogDataIndex,
59
63
  authz,
60
64
  ...(mongoAdapter ? { mongoAdapter } : {}),
61
65
  apiAdapter,
62
66
  });
63
67
  }
68
+ /** CLI `--store` wins over `CATALOX_STORE_ID` when both are set. */
69
+ function resolvedStoreId(cliStore) {
70
+ const id = (cliStore ?? process.env.CATALOX_STORE_ID ?? "").trim();
71
+ return id || undefined;
72
+ }
64
73
  function baseContext(opts) {
65
74
  const appId = (opts.app ?? process.env.CATALOX_APP_ID ?? "").trim();
66
75
  if (!appId)
67
76
  throw new Error("Missing --app or CATALOX_APP_ID");
68
77
  const userId = (process.env.CATALOX_USER_ID ?? "").trim() || undefined;
78
+ const storeId = resolvedStoreId(opts.store);
69
79
  return {
70
80
  appId,
71
- ...(opts.store ? { storeId: opts.store } : {}),
81
+ ...(storeId ? { storeId } : {}),
72
82
  ...(opts.god ? { isGodMode: true } : {}),
73
83
  ...(userId ? { userId, actor: { type: "user", id: userId } } : {}),
74
84
  };
@@ -102,7 +112,7 @@ program
102
112
  .command("report")
103
113
  .description("Generate a smart inventory report (Markdown)")
104
114
  .requiredOption("--app <appId>", "AppId context")
105
- .option("--store <storeId>", "StoreId context (export/report across bound apps)")
115
+ .option("--store <storeId>", "StoreId context (overrides CATALOX_STORE_ID)")
106
116
  .option("--catalog <catalogId...>", "Limit to specific catalogIds")
107
117
  .option("--top <n>", "Top examples per catalog", (v) => Number(v), 10)
108
118
  .option("--diff", "Include snapshot run diff section", false)
@@ -111,8 +121,9 @@ program
111
121
  .action(async (opts) => {
112
122
  const catalox = createCatalox();
113
123
  const ctx = baseContext(opts);
124
+ const storeId = resolvedStoreId(opts.store);
114
125
  const { markdown } = await catalox.generateInventoryReport(ctx, {
115
- ...(opts.store ? { storeId: opts.store } : {}),
126
+ ...(storeId ? { storeId } : {}),
116
127
  ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
117
128
  top: opts.top,
118
129
  includeDiff: Boolean(opts.diff),
@@ -123,7 +134,7 @@ program
123
134
  .command("export")
124
135
  .description("Export inventory to JSON")
125
136
  .requiredOption("--app <appId>", "AppId context")
126
- .option("--store <storeId>", "StoreId context (export across bound apps)")
137
+ .option("--store <storeId>", "StoreId context (overrides CATALOX_STORE_ID)")
127
138
  .option("--catalog <catalogId...>", "Limit to specific catalogIds")
128
139
  .option("--include-items", "Include items in export", false)
129
140
  .option("--no-item-data", "Exclude `items[].data` (metadata-only items)")
@@ -134,8 +145,9 @@ program
134
145
  .action(async (opts) => {
135
146
  const catalox = createCatalox();
136
147
  const ctx = baseContext(opts);
148
+ const storeId = resolvedStoreId(opts.store);
137
149
  const json = await catalox.exportInventoryToJson(ctx, {
138
- ...(opts.store ? { storeId: opts.store } : {}),
150
+ ...(storeId ? { storeId } : {}),
139
151
  ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
140
152
  includeItems: Boolean(opts.includeItems),
141
153
  includeItemData: Boolean(opts.itemData),
@@ -279,6 +291,145 @@ storeCmd
279
291
  const rows = await catalox.listAppsForStore(ctx, opts.store);
280
292
  await writeOrStdout(JSON.stringify(rows, null, 2));
281
293
  });
294
+ const firestoreCmd = program
295
+ .command("firestore")
296
+ .description("Firestore backup, restore (Firebase mirrors), and native layout migration");
297
+ firestoreCmd
298
+ .command("backup")
299
+ .description("Generic Catalox backup to Mongo (catalox-backups) or Firebase (backup-* collections)")
300
+ .requiredOption("--app <appId>", "AppId for Catalox context")
301
+ .requiredOption("--mode <mongo|firebase>", "Backup target")
302
+ .option("--mongo-uri <uri>", "Mongo connection string (required when mode=mongo)")
303
+ .option("--label <text>", "Backup label stored on manifest")
304
+ .option("--snapshots", "Include catalogSnapshots data", false)
305
+ .option("--catalog <catalogId...>", "Restrict which native catalogs are copied")
306
+ .option("--god", "God mode", false)
307
+ .action(async (opts) => {
308
+ const catalox = createCatalox();
309
+ const ctx = baseContext({ app: opts.app, god: opts.god });
310
+ const mode = String(opts.mode) === "mongo" ? "mongo" : "firebase";
311
+ const res = await catalox.backupData(ctx, {
312
+ mode,
313
+ mongoBackupUri: opts.mongoUri,
314
+ includeSnapshots: Boolean(opts.snapshots),
315
+ ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
316
+ ...(opts.label != null && String(opts.label) !== "" ? { backupLabel: String(opts.label) } : {}),
317
+ });
318
+ await writeOrStdout(JSON.stringify(res, null, 2));
319
+ if (!res.ok)
320
+ process.exitCode = 1;
321
+ });
322
+ firestoreCmd
323
+ .command("restore-backup")
324
+ .description("Restore live Firestore from backup-* (same DB). Captures pre-restore copies to {session}__preRestore-* for undo.")
325
+ .requiredOption("--app <appId>", "AppId for Catalox context")
326
+ .option("--from <latest|versioned>", "Backup source", "latest")
327
+ .option("--version-ts <token>", "When --from versioned: timestamp token matching backup collection prefix")
328
+ .option("--snapshots", "Also restore catalogSnapshots from backup mirrors", false)
329
+ .option("--catalog <catalogId...>", "Restrict which native catalogs are restored")
330
+ .option("--god", "God mode", false)
331
+ .action(async (opts) => {
332
+ const catalox = createCatalox();
333
+ const ctx = baseContext({ app: opts.app, god: opts.god });
334
+ const from = String(opts.from).toLowerCase();
335
+ const source = from === "versioned"
336
+ ? { kind: "versioned", versionTimestamp: String(opts.versionTs ?? "") }
337
+ : { kind: "latest" };
338
+ if (from === "versioned" && !String(opts.versionTs ?? "").trim()) {
339
+ // eslint-disable-next-line no-console
340
+ console.error("--version-ts is required when --from versioned");
341
+ process.exitCode = 1;
342
+ return;
343
+ }
344
+ const res = await catalox.restoreFirestoreBackup(ctx, {
345
+ source,
346
+ includeSnapshots: Boolean(opts.snapshots),
347
+ ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
348
+ });
349
+ await writeOrStdout(JSON.stringify(res, null, 2));
350
+ if (!res.ok)
351
+ process.exitCode = 1;
352
+ });
353
+ firestoreCmd
354
+ .command("undo-restore-backup")
355
+ .description("Revert a prior restore-backup using backup-restoreSessions/{sessionId} and preRestore collections")
356
+ .requiredOption("--app <appId>", "AppId for Catalox context")
357
+ .requiredOption("--session <restoreSessionId>", "restoreSessionId returned by restore-backup")
358
+ .option("--god", "God mode", false)
359
+ .action(async (opts) => {
360
+ const catalox = createCatalox();
361
+ const ctx = baseContext({ app: opts.app, god: opts.god });
362
+ const res = await catalox.undoFirestoreRestore(ctx, {
363
+ restoreSessionId: String(opts.session),
364
+ });
365
+ await writeOrStdout(JSON.stringify(res, null, 2));
366
+ if (!res.ok)
367
+ process.exitCode = 1;
368
+ });
369
+ firestoreCmd
370
+ .command("report-native-layout")
371
+ .description("JSON report: legacy vs flat doc counts per native catalog (when you still see catalogData/{id}/items in console)")
372
+ .requiredOption("--app <appId>", "AppId for Catalox context")
373
+ .option("--catalog <catalogId...>", "Restrict to these catalog ids")
374
+ .option("--god", "God mode", false)
375
+ .action(async (opts) => {
376
+ const catalox = createCatalox();
377
+ const ctx = baseContext({ app: opts.app, god: opts.god });
378
+ const rows = await catalox.reportNativeCatalogLayoutDiagnostics(ctx, {
379
+ ...(opts.catalog?.length ? { catalogIds: opts.catalog } : {}),
380
+ });
381
+ const summary = {
382
+ hint: "If needsLegacyToFlatMigration is true, run: catalox firestore migrate-native-catalog-data --app <sameApp>",
383
+ catalogs: rows,
384
+ };
385
+ await writeOrStdout(JSON.stringify(summary, null, 2));
386
+ });
387
+ firestoreCmd
388
+ .command("migrate-native-catalog-data")
389
+ .description("Backup (Firebase) then copy legacy catalogData/{catalogId}/items → flat catalogData-{catalogId}-items (stops OLD layout)")
390
+ .requiredOption("--app <appId>", "AppId for Catalox context")
391
+ .option("--skip-backup", "Skip Firebase backupData before migrate", false)
392
+ .option("--backup-label <text>", "Label for pre-migrate backup", "pre-native-layout-migration")
393
+ .option("--snapshots", "Include snapshots in pre-migrate backup", false)
394
+ .option("--dry-run", "Count/copy simulation only for migrate", false)
395
+ .option("--verify-only", "Compare legacy vs flat doc counts", false)
396
+ .option("--overwrite", "Overwrite existing flat docs", false)
397
+ .option("--delete-legacy", "Delete legacy catalogData/{id}/items after migrate", false)
398
+ .option("--i-have-backups", "Required with --delete-legacy", false)
399
+ .option("--continue-on-error", "Continue other catalogs if one fails", false)
400
+ .option("--also-export-files", "Write NDJSON+manifest under .catalox-migration-backups/", false)
401
+ .option("--god", "God mode", false)
402
+ .action(async (opts) => {
403
+ const catalox = createCatalox();
404
+ const ctx = baseContext({ app: opts.app, god: opts.god });
405
+ if (!opts.skipBackup) {
406
+ const b = await catalox.backupData(ctx, {
407
+ mode: "firebase",
408
+ backupLabel: String(opts.backupLabel != null ? opts.backupLabel : "pre-native-layout-migration"),
409
+ includeSnapshots: Boolean(opts.snapshots),
410
+ });
411
+ await writeOrStdout(JSON.stringify({ phase: "backup", result: b }, null, 2));
412
+ if (!b.ok) {
413
+ process.exitCode = 1;
414
+ return;
415
+ }
416
+ }
417
+ const exportDir = opts.alsoExportFiles
418
+ ? resolvePath(process.cwd(), ".catalox-migration-backups")
419
+ : undefined;
420
+ const m = await catalox.migrateNativeCatalogLayout(ctx, {
421
+ dryRun: Boolean(opts.dryRun),
422
+ verifyOnly: Boolean(opts.verifyOnly),
423
+ overwrite: Boolean(opts.overwrite),
424
+ deleteLegacy: Boolean(opts.deleteLegacy),
425
+ iHaveBackups: Boolean(opts.iHaveBackups),
426
+ continueOnError: Boolean(opts.continueOnError),
427
+ ...(exportDir ? { exportFilesDir: exportDir } : {}),
428
+ });
429
+ await writeOrStdout(JSON.stringify({ phase: "migrate", result: m }, null, 2));
430
+ if (!m.ok)
431
+ process.exitCode = 1;
432
+ });
282
433
  program.parseAsync(process.argv).catch((err) => {
283
434
  // eslint-disable-next-line no-console
284
435
  console.error(err instanceof Error ? err.message : err);