@x12i/catalox 3.1.1 → 3.3.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 (324) hide show
  1. package/README.md +6 -1
  2. package/dist/src/catalox/backup-data.d.ts +1 -6
  3. package/dist/src/catalox/backup-data.d.ts.map +1 -1
  4. package/dist/src/catalox/backup-data.js +71 -418
  5. package/dist/src/catalox/backup-data.js.map +1 -1
  6. package/dist/src/catalox/catalox-bound.d.ts +1 -2
  7. package/dist/src/catalox/catalox-bound.d.ts.map +1 -1
  8. package/dist/src/catalox/catalox-bound.js +0 -3
  9. package/dist/src/catalox/catalox-bound.js.map +1 -1
  10. package/dist/src/catalox/catalox.d.ts +2 -7
  11. package/dist/src/catalox/catalox.d.ts.map +1 -1
  12. package/dist/src/catalox/catalox.js +2 -16
  13. package/dist/src/catalox/catalox.js.map +1 -1
  14. package/dist/src/catalox/create-catalox.d.ts.map +1 -1
  15. package/dist/src/catalox/create-catalox.js +4 -1
  16. package/dist/src/catalox/create-catalox.js.map +1 -1
  17. package/dist/src/catalox/index.d.ts +2 -2
  18. package/dist/src/catalox/index.d.ts.map +1 -1
  19. package/dist/src/catalox/index.js +2 -2
  20. package/dist/src/catalox/index.js.map +1 -1
  21. package/dist/src/catalox/record-history.d.ts.map +1 -1
  22. package/dist/src/catalox/record-history.js +34 -0
  23. package/dist/src/catalox/record-history.js.map +1 -1
  24. package/dist/src/catalox/restore-firestore-backup.d.ts +6 -3
  25. package/dist/src/catalox/restore-firestore-backup.d.ts.map +1 -1
  26. package/dist/src/catalox/restore-firestore-backup.js +3 -224
  27. package/dist/src/catalox/restore-firestore-backup.js.map +1 -1
  28. package/dist/src/cli/index.js +27 -66
  29. package/dist/src/cli/index.js.map +1 -1
  30. package/dist/src/contracts/backup.d.ts +2 -2
  31. package/dist/src/contracts/backup.d.ts.map +1 -1
  32. package/dist/src/contracts/index.d.ts +1 -1
  33. package/dist/src/contracts/index.d.ts.map +1 -1
  34. package/dist/src/contracts/index.js.map +1 -1
  35. package/dist/src/contracts/restore.d.ts +0 -39
  36. package/dist/src/contracts/restore.d.ts.map +1 -1
  37. package/dist/src/firebase/catalog-item-history-store.d.ts.map +1 -1
  38. package/dist/src/firebase/catalog-item-history-store.js +35 -6
  39. package/dist/src/firebase/catalog-item-history-store.js.map +1 -1
  40. package/package.json +6 -3
  41. package/dist/adapters/api/api-adapter.d.ts +0 -22
  42. package/dist/adapters/api/api-adapter.d.ts.map +0 -1
  43. package/dist/adapters/api/api-adapter.js +0 -60
  44. package/dist/adapters/api/api-adapter.js.map +0 -1
  45. package/dist/adapters/api/index.d.ts +0 -2
  46. package/dist/adapters/api/index.d.ts.map +0 -1
  47. package/dist/adapters/api/index.js +0 -2
  48. package/dist/adapters/api/index.js.map +0 -1
  49. package/dist/adapters/mongo/index.d.ts +0 -3
  50. package/dist/adapters/mongo/index.d.ts.map +0 -1
  51. package/dist/adapters/mongo/index.js +0 -3
  52. package/dist/adapters/mongo/index.js.map +0 -1
  53. package/dist/adapters/mongo/mongo-adapter.d.ts +0 -21
  54. package/dist/adapters/mongo/mongo-adapter.d.ts.map +0 -1
  55. package/dist/adapters/mongo/mongo-adapter.js +0 -57
  56. package/dist/adapters/mongo/mongo-adapter.js.map +0 -1
  57. package/dist/adapters/mongo/resolve-db.d.ts +0 -4
  58. package/dist/adapters/mongo/resolve-db.d.ts.map +0 -1
  59. package/dist/adapters/mongo/resolve-db.js +0 -10
  60. package/dist/adapters/mongo/resolve-db.js.map +0 -1
  61. package/dist/bindings/index.d.ts +0 -2
  62. package/dist/bindings/index.d.ts.map +0 -1
  63. package/dist/bindings/index.js +0 -3
  64. package/dist/bindings/index.js.map +0 -1
  65. package/dist/catalox/authorization.d.ts +0 -11
  66. package/dist/catalox/authorization.d.ts.map +0 -1
  67. package/dist/catalox/authorization.js +0 -37
  68. package/dist/catalox/authorization.js.map +0 -1
  69. package/dist/catalox/catalox.d.ts +0 -80
  70. package/dist/catalox/catalox.d.ts.map +0 -1
  71. package/dist/catalox/catalox.js +0 -492
  72. package/dist/catalox/catalox.js.map +0 -1
  73. package/dist/catalox/context.d.ts +0 -17
  74. package/dist/catalox/context.d.ts.map +0 -1
  75. package/dist/catalox/context.js +0 -21
  76. package/dist/catalox/context.js.map +0 -1
  77. package/dist/catalox/identity.d.ts +0 -7
  78. package/dist/catalox/identity.d.ts.map +0 -1
  79. package/dist/catalox/identity.js +0 -26
  80. package/dist/catalox/identity.js.map +0 -1
  81. package/dist/catalox/index.d.ts +0 -6
  82. package/dist/catalox/index.d.ts.map +0 -1
  83. package/dist/catalox/index.js +0 -6
  84. package/dist/catalox/index.js.map +0 -1
  85. package/dist/catalox/json-io.d.ts +0 -3
  86. package/dist/catalox/json-io.d.ts.map +0 -1
  87. package/dist/catalox/json-io.js +0 -7
  88. package/dist/catalox/json-io.js.map +0 -1
  89. package/dist/contracts/adapters.d.ts +0 -40
  90. package/dist/contracts/adapters.d.ts.map +0 -1
  91. package/dist/contracts/adapters.js +0 -2
  92. package/dist/contracts/adapters.js.map +0 -1
  93. package/dist/contracts/apps.d.ts +0 -11
  94. package/dist/contracts/apps.d.ts.map +0 -1
  95. package/dist/contracts/apps.js +0 -2
  96. package/dist/contracts/apps.js.map +0 -1
  97. package/dist/contracts/bindings.d.ts +0 -26
  98. package/dist/contracts/bindings.d.ts.map +0 -1
  99. package/dist/contracts/bindings.js +0 -2
  100. package/dist/contracts/bindings.js.map +0 -1
  101. package/dist/contracts/bootstrap.d.ts +0 -7
  102. package/dist/contracts/bootstrap.d.ts.map +0 -1
  103. package/dist/contracts/bootstrap.js +0 -2
  104. package/dist/contracts/bootstrap.js.map +0 -1
  105. package/dist/contracts/catalogs.d.ts +0 -51
  106. package/dist/contracts/catalogs.d.ts.map +0 -1
  107. package/dist/contracts/catalogs.js +0 -2
  108. package/dist/contracts/catalogs.js.map +0 -1
  109. package/dist/contracts/config.d.ts +0 -14
  110. package/dist/contracts/config.d.ts.map +0 -1
  111. package/dist/contracts/config.js +0 -2
  112. package/dist/contracts/config.js.map +0 -1
  113. package/dist/contracts/context.d.ts +0 -11
  114. package/dist/contracts/context.d.ts.map +0 -1
  115. package/dist/contracts/context.js +0 -2
  116. package/dist/contracts/context.js.map +0 -1
  117. package/dist/contracts/descriptors.d.ts +0 -73
  118. package/dist/contracts/descriptors.d.ts.map +0 -1
  119. package/dist/contracts/descriptors.js +0 -2
  120. package/dist/contracts/descriptors.js.map +0 -1
  121. package/dist/contracts/discovery.d.ts +0 -25
  122. package/dist/contracts/discovery.d.ts.map +0 -1
  123. package/dist/contracts/discovery.js +0 -2
  124. package/dist/contracts/discovery.js.map +0 -1
  125. package/dist/contracts/errors.d.ts +0 -32
  126. package/dist/contracts/errors.d.ts.map +0 -1
  127. package/dist/contracts/errors.js +0 -53
  128. package/dist/contracts/errors.js.map +0 -1
  129. package/dist/contracts/ids.d.ts +0 -7
  130. package/dist/contracts/ids.d.ts.map +0 -1
  131. package/dist/contracts/ids.js +0 -2
  132. package/dist/contracts/ids.js.map +0 -1
  133. package/dist/contracts/index.d.ts +0 -21
  134. package/dist/contracts/index.d.ts.map +0 -1
  135. package/dist/contracts/index.js +0 -2
  136. package/dist/contracts/index.js.map +0 -1
  137. package/dist/contracts/inputs.d.ts +0 -19
  138. package/dist/contracts/inputs.d.ts.map +0 -1
  139. package/dist/contracts/inputs.js +0 -2
  140. package/dist/contracts/inputs.js.map +0 -1
  141. package/dist/contracts/item-validation.d.ts +0 -11
  142. package/dist/contracts/item-validation.d.ts.map +0 -1
  143. package/dist/contracts/item-validation.js +0 -2
  144. package/dist/contracts/item-validation.js.map +0 -1
  145. package/dist/contracts/items.d.ts +0 -49
  146. package/dist/contracts/items.d.ts.map +0 -1
  147. package/dist/contracts/items.js +0 -2
  148. package/dist/contracts/items.js.map +0 -1
  149. package/dist/contracts/mappings.d.ts +0 -35
  150. package/dist/contracts/mappings.d.ts.map +0 -1
  151. package/dist/contracts/mappings.js +0 -2
  152. package/dist/contracts/mappings.js.map +0 -1
  153. package/dist/contracts/query.d.ts +0 -10
  154. package/dist/contracts/query.d.ts.map +0 -1
  155. package/dist/contracts/query.js +0 -2
  156. package/dist/contracts/query.js.map +0 -1
  157. package/dist/contracts/references.d.ts +0 -11
  158. package/dist/contracts/references.d.ts.map +0 -1
  159. package/dist/contracts/references.js +0 -2
  160. package/dist/contracts/references.js.map +0 -1
  161. package/dist/contracts/sync.d.ts +0 -7
  162. package/dist/contracts/sync.d.ts.map +0 -1
  163. package/dist/contracts/sync.js +0 -2
  164. package/dist/contracts/sync.js.map +0 -1
  165. package/dist/contracts/validation.d.ts +0 -12
  166. package/dist/contracts/validation.d.ts.map +0 -1
  167. package/dist/contracts/validation.js +0 -2
  168. package/dist/contracts/validation.js.map +0 -1
  169. package/dist/errors/index.d.ts +0 -2
  170. package/dist/errors/index.d.ts.map +0 -1
  171. package/dist/errors/index.js +0 -3
  172. package/dist/errors/index.js.map +0 -1
  173. package/dist/firebase/adapter-store.d.ts +0 -15
  174. package/dist/firebase/adapter-store.d.ts.map +0 -1
  175. package/dist/firebase/adapter-store.js +0 -18
  176. package/dist/firebase/adapter-store.js.map +0 -1
  177. package/dist/firebase/app-store.d.ts +0 -11
  178. package/dist/firebase/app-store.d.ts.map +0 -1
  179. package/dist/firebase/app-store.js +0 -18
  180. package/dist/firebase/app-store.js.map +0 -1
  181. package/dist/firebase/binding-store.d.ts +0 -13
  182. package/dist/firebase/binding-store.d.ts.map +0 -1
  183. package/dist/firebase/binding-store.js +0 -35
  184. package/dist/firebase/binding-store.js.map +0 -1
  185. package/dist/firebase/catalog-store.d.ts +0 -12
  186. package/dist/firebase/catalog-store.d.ts.map +0 -1
  187. package/dist/firebase/catalog-store.js +0 -22
  188. package/dist/firebase/catalog-store.js.map +0 -1
  189. package/dist/firebase/definition-store.d.ts +0 -39
  190. package/dist/firebase/definition-store.d.ts.map +0 -1
  191. package/dist/firebase/definition-store.js +0 -18
  192. package/dist/firebase/definition-store.js.map +0 -1
  193. package/dist/firebase/descriptor-store.d.ts +0 -11
  194. package/dist/firebase/descriptor-store.d.ts.map +0 -1
  195. package/dist/firebase/descriptor-store.js +0 -18
  196. package/dist/firebase/descriptor-store.js.map +0 -1
  197. package/dist/firebase/firestore-store.d.ts +0 -14
  198. package/dist/firebase/firestore-store.d.ts.map +0 -1
  199. package/dist/firebase/firestore-store.js +0 -15
  200. package/dist/firebase/firestore-store.js.map +0 -1
  201. package/dist/firebase/index.d.ts +0 -12
  202. package/dist/firebase/index.d.ts.map +0 -1
  203. package/dist/firebase/index.js +0 -12
  204. package/dist/firebase/index.js.map +0 -1
  205. package/dist/firebase/mapping-store.d.ts +0 -11
  206. package/dist/firebase/mapping-store.d.ts.map +0 -1
  207. package/dist/firebase/mapping-store.js +0 -18
  208. package/dist/firebase/mapping-store.js.map +0 -1
  209. package/dist/firebase/native-item-store.d.ts +0 -20
  210. package/dist/firebase/native-item-store.d.ts.map +0 -1
  211. package/dist/firebase/native-item-store.js +0 -55
  212. package/dist/firebase/native-item-store.js.map +0 -1
  213. package/dist/firebase/reference-store.d.ts +0 -17
  214. package/dist/firebase/reference-store.d.ts.map +0 -1
  215. package/dist/firebase/reference-store.js +0 -27
  216. package/dist/firebase/reference-store.js.map +0 -1
  217. package/dist/firebase/snapshot-store.d.ts +0 -11
  218. package/dist/firebase/snapshot-store.d.ts.map +0 -1
  219. package/dist/firebase/snapshot-store.js +0 -22
  220. package/dist/firebase/snapshot-store.js.map +0 -1
  221. package/dist/index.d.ts +0 -10
  222. package/dist/index.d.ts.map +0 -1
  223. package/dist/index.js +0 -10
  224. package/dist/index.js.map +0 -1
  225. package/dist/mapping/execute-mapping.d.ts +0 -8
  226. package/dist/mapping/execute-mapping.d.ts.map +0 -1
  227. package/dist/mapping/execute-mapping.js +0 -78
  228. package/dist/mapping/execute-mapping.js.map +0 -1
  229. package/dist/mapping/helper-gap-report.d.ts +0 -7
  230. package/dist/mapping/helper-gap-report.d.ts.map +0 -1
  231. package/dist/mapping/helper-gap-report.js +0 -2
  232. package/dist/mapping/helper-gap-report.js.map +0 -1
  233. package/dist/mapping/index.d.ts +0 -4
  234. package/dist/mapping/index.d.ts.map +0 -1
  235. package/dist/mapping/index.js +0 -4
  236. package/dist/mapping/index.js.map +0 -1
  237. package/dist/mapping/validate-mapping.d.ts +0 -3
  238. package/dist/mapping/validate-mapping.d.ts.map +0 -1
  239. package/dist/mapping/validate-mapping.js +0 -32
  240. package/dist/mapping/validate-mapping.js.map +0 -1
  241. package/dist/test/integration/firestore.emulator.test.d.ts +0 -2
  242. package/dist/test/integration/firestore.emulator.test.d.ts.map +0 -1
  243. package/dist/test/integration/firestore.emulator.test.js +0 -117
  244. package/dist/test/integration/firestore.emulator.test.js.map +0 -1
  245. package/dist/test/integration/record-history.live.test.d.ts +0 -2
  246. package/dist/test/integration/record-history.live.test.d.ts.map +0 -1
  247. package/dist/test/integration/record-history.live.test.js +0 -126
  248. package/dist/test/integration/record-history.live.test.js.map +0 -1
  249. package/dist/test/unit/backup-timestamp.test.d.ts +0 -2
  250. package/dist/test/unit/backup-timestamp.test.d.ts.map +0 -1
  251. package/dist/test/unit/backup-timestamp.test.js +0 -11
  252. package/dist/test/unit/backup-timestamp.test.js.map +0 -1
  253. package/dist/test/unit/catalox-gcs-backup-export-manifest.test.d.ts +0 -2
  254. package/dist/test/unit/catalox-gcs-backup-export-manifest.test.d.ts.map +0 -1
  255. package/dist/test/unit/catalox-gcs-backup-export-manifest.test.js +0 -20
  256. package/dist/test/unit/catalox-gcs-backup-export-manifest.test.js.map +0 -1
  257. package/dist/test/unit/compact-catalog-filter.test.d.ts +0 -2
  258. package/dist/test/unit/compact-catalog-filter.test.d.ts.map +0 -1
  259. package/dist/test/unit/compact-catalog-filter.test.js +0 -18
  260. package/dist/test/unit/compact-catalog-filter.test.js.map +0 -1
  261. package/dist/test/unit/field-source-resolution.test.d.ts +0 -2
  262. package/dist/test/unit/field-source-resolution.test.d.ts.map +0 -1
  263. package/dist/test/unit/field-source-resolution.test.js +0 -45
  264. package/dist/test/unit/field-source-resolution.test.js.map +0 -1
  265. package/dist/test/unit/gcs-backup-run-folder.test.d.ts +0 -2
  266. package/dist/test/unit/gcs-backup-run-folder.test.d.ts.map +0 -1
  267. package/dist/test/unit/gcs-backup-run-folder.test.js +0 -10
  268. package/dist/test/unit/gcs-backup-run-folder.test.js.map +0 -1
  269. package/dist/test/unit/gcs-firestore-compare.test.d.ts +0 -2
  270. package/dist/test/unit/gcs-firestore-compare.test.d.ts.map +0 -1
  271. package/dist/test/unit/gcs-firestore-compare.test.js +0 -14
  272. package/dist/test/unit/gcs-firestore-compare.test.js.map +0 -1
  273. package/dist/test/unit/gcs-firestore-paths.test.d.ts +0 -2
  274. package/dist/test/unit/gcs-firestore-paths.test.d.ts.map +0 -1
  275. package/dist/test/unit/gcs-firestore-paths.test.js +0 -16
  276. package/dist/test/unit/gcs-firestore-paths.test.js.map +0 -1
  277. package/dist/test/unit/identity.test.d.ts +0 -2
  278. package/dist/test/unit/identity.test.d.ts.map +0 -1
  279. package/dist/test/unit/identity.test.js +0 -18
  280. package/dist/test/unit/identity.test.js.map +0 -1
  281. package/dist/test/unit/jsx-snippets.test.d.ts +0 -2
  282. package/dist/test/unit/jsx-snippets.test.d.ts.map +0 -1
  283. package/dist/test/unit/jsx-snippets.test.js +0 -35
  284. package/dist/test/unit/jsx-snippets.test.js.map +0 -1
  285. package/dist/test/unit/mapping.test.d.ts +0 -2
  286. package/dist/test/unit/mapping.test.d.ts.map +0 -1
  287. package/dist/test/unit/mapping.test.js +0 -19
  288. package/dist/test/unit/mapping.test.js.map +0 -1
  289. package/dist/test/unit/markdown-and-diagrams.test.d.ts +0 -2
  290. package/dist/test/unit/markdown-and-diagrams.test.d.ts.map +0 -1
  291. package/dist/test/unit/markdown-and-diagrams.test.js +0 -62
  292. package/dist/test/unit/markdown-and-diagrams.test.js.map +0 -1
  293. package/dist/test/unit/native-catalog-merge.test.d.ts +0 -2
  294. package/dist/test/unit/native-catalog-merge.test.d.ts.map +0 -1
  295. package/dist/test/unit/native-catalog-merge.test.js +0 -33
  296. package/dist/test/unit/native-catalog-merge.test.js.map +0 -1
  297. package/dist/test/unit/native-scope.test.d.ts +0 -2
  298. package/dist/test/unit/native-scope.test.d.ts.map +0 -1
  299. package/dist/test/unit/native-scope.test.js +0 -29
  300. package/dist/test/unit/native-scope.test.js.map +0 -1
  301. package/dist/test/unit/record-history-path.test.d.ts +0 -2
  302. package/dist/test/unit/record-history-path.test.d.ts.map +0 -1
  303. package/dist/test/unit/record-history-path.test.js +0 -24
  304. package/dist/test/unit/record-history-path.test.js.map +0 -1
  305. package/dist/test/unit/renderer-metadata-migration.test.d.ts +0 -2
  306. package/dist/test/unit/renderer-metadata-migration.test.d.ts.map +0 -1
  307. package/dist/test/unit/renderer-metadata-migration.test.js +0 -33
  308. package/dist/test/unit/renderer-metadata-migration.test.js.map +0 -1
  309. package/dist/test/unit/renderer-metadata.test.d.ts +0 -2
  310. package/dist/test/unit/renderer-metadata.test.d.ts.map +0 -1
  311. package/dist/test/unit/renderer-metadata.test.js +0 -10
  312. package/dist/test/unit/renderer-metadata.test.js.map +0 -1
  313. package/dist/test/unit/resolve-native-items-layout.test.d.ts +0 -2
  314. package/dist/test/unit/resolve-native-items-layout.test.d.ts.map +0 -1
  315. package/dist/test/unit/resolve-native-items-layout.test.js +0 -50
  316. package/dist/test/unit/resolve-native-items-layout.test.js.map +0 -1
  317. package/dist/test/unit/ui-spec-validation.test.d.ts +0 -2
  318. package/dist/test/unit/ui-spec-validation.test.d.ts.map +0 -1
  319. package/dist/test/unit/ui-spec-validation.test.js +0 -90
  320. package/dist/test/unit/ui-spec-validation.test.js.map +0 -1
  321. package/dist/validation/index.d.ts +0 -2
  322. package/dist/validation/index.d.ts.map +0 -1
  323. package/dist/validation/index.js +0 -3
  324. package/dist/validation/index.js.map +0 -1
package/README.md CHANGED
@@ -54,7 +54,9 @@ Scoping:
54
54
  - You can scope the service account to **Firestore-related IAM roles** and to a specific **project**.
55
55
  - You generally cannot scope an Admin SDK credential to only certain collections/documents via IAM the way client rules work.
56
56
 
57
- To connect to a real Firebase project, initialize `firebase-admin` using `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON) or your preferred Admin initialization strategy.
57
+ To connect to a real Firebase project, initialize `firebase-admin` using `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON), workload identity, or your preferred Admin initialization strategy.
58
+
59
+ This repo’s CLI + live tests typically load `.env` via **`@x12i/env`** and support **`GOOGLE_SERVICE_ACCOUNT_BASE64`** as a convenient local/dev secret carrier. That value is picked up **automatically** by the supporting x12i tooling (including **`@x12i/helpers/gcs`**) when present—**upstream code does not need to plumb it through**; just set it in the environment.
58
60
 
59
61
  Minimal example:
60
62
 
@@ -81,6 +83,8 @@ Example `.env` (do not commit secrets):
81
83
  ```bash
82
84
  MONGO_URI=mongodb://127.0.0.1:27017
83
85
  GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
86
+ # Alternative (supported by this repo’s CLI/tests and x12i helpers): base64-encoded service account JSON
87
+ # GOOGLE_SERVICE_ACCOUNT_BASE64=...
84
88
  # Or (used by the packaged CLI migration + live integration tests):
85
89
  FIREBASE_SERVICE_ACCOUNT_PATH=/path/to/service-account.json
86
90
  FIREBASE_PROJECT_ID=your-project-id
@@ -104,6 +108,7 @@ The `catalox` binary loads `.env` via `dotenv`. Use **`CATALOX_*`** for **Catalo
104
108
  - **`CATALOX_USER_ID`** — Optional user id / actor for authz-sensitive CLI paths.
105
109
  - **`CATALOX_MONGO_URI`** — If set, enables the Mongo catalog adapter in the CLI (wired via **`createCatalox`** in `src/catalox/create-catalox.ts`, called from `src/cli/index.ts`).
106
110
  - **`CATALOX_RECORD_HISTORY_BUCKET`** — If set, **`createCatalox`** wires **`recordHistory`** (GCS payloads + **`catalogItemHistory`** on native writes; also required for **`catalox history …`** to persist new events).
111
+ - **`CATALOX_BUCKET`** — Default GCS bucket for Catalox storage features (used by the CLI and live tests; also used as a fallback for `CATALOX_RECORD_HISTORY_BUCKET` when wiring `recordHistory` via env).
107
112
  - **`CATALOX_RECORD_HISTORY_PREFIX`** — Optional GCS prefix for record history (default `catalox-record-history/`).
108
113
  - **`CATALOX_RECORD_HISTORY_FAIL_CLOSED`** — When **`1`**, failed history writes fail the parent Firestore mutation.
109
114
 
@@ -1,6 +1,5 @@
1
1
  import { Readable } from "node:stream";
2
2
  import { type Firestore } from "firebase-admin/firestore";
3
- import { type Db } from "mongodb";
4
3
  import type { CatalogId } from "../contracts/ids.js";
5
4
  import type { BackupDataInput, BackupDataResult } from "../contracts/backup.js";
6
5
  import type { PruneGcsBackupRunsInput, PruneGcsBackupRunsResult } from "../contracts/restore.js";
@@ -29,8 +28,6 @@ export declare function discoverNativeCatalogIds(fs: Firestore, catalogs: Catalo
29
28
  catalogIds?: CatalogId[];
30
29
  appId?: string;
31
30
  }): Promise<CatalogId[]>;
32
- /** Delete Mongo `backupRuns` documents with createdAt older than cutoff ISO string. */
33
- export declare function pruneMongoBackupRuns(db: Db, createdBeforeIso: string): Promise<number>;
34
31
  /** Exported for tests: `{gcsPrefix|default}/{formatBackupTimestamp}/` without trailing slash (helpers client prefix). */
35
32
  export declare function gcsBackupRunFolder(ts: string, gcsPrefix?: string): string;
36
33
  /** Subset of `@x12i/helpers/gcs` client used by Catalox backup / restore / prune. */
@@ -57,6 +54,7 @@ export type CataloxHelpersGcsClient = {
57
54
  deleteObject: (objectName: string) => Promise<void>;
58
55
  };
59
56
  export declare const CATALOX_GCS_BACKUP_MANIFEST_FILENAME = "catalox-backup-manifest.json";
57
+ export declare function defaultCataloxBucketName(): string;
60
58
  /** Same as internal backup client: `prefix` is the logical root prepended to relative object names. */
61
59
  export declare function createCataloxHelpersGcsClient(bucket: string, prefixWithoutTrailingSlash: string): CataloxHelpersGcsClient;
62
60
  /** Delete every object under one backup run folder (`…/{timestamp}/…`). */
@@ -68,8 +66,5 @@ export declare function deleteCataloxGcsBackupRunObjects(bucket: string, runFold
68
66
  * Folder names are sorted lexicographically (compatible with `formatBackupTimestamp` ISO-style tokens).
69
67
  */
70
68
  export declare function pruneGcsBackupRuns(input: PruneGcsBackupRunsInput): Promise<PruneGcsBackupRunsResult>;
71
- export declare function pruneFirebaseVersionedBackups(firestore: Firestore, keepLast: number): Promise<{
72
- deleted: string[];
73
- }>;
74
69
  export declare function runBackupData(deps: BackupDataDeps, input: BackupDataInput): Promise<BackupDataResult>;
75
70
  //# sourceMappingURL=backup-data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"backup-data.d.ts","sourceRoot":"","sources":["../../../src/catalox/backup-data.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backup-data.d.ts","sourceRoot":"","sources":["../../../src/catalox/backup-data.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,12 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { Readable } from "node:stream";
3
+ import { readFileSync } from "node:fs";
3
4
  import { FieldPath, } from "firebase-admin/firestore";
4
- import { MongoClient } from "mongodb";
5
5
  import { CatalogStore } from "../firebase/catalog-store.js";
6
6
  import { BindingStore } from "../firebase/binding-store.js";
7
7
  import { FirestoreStore } from "../firebase/firestore-store.js";
8
8
  import { flatNativeItemsCollectionRef, legacyNativeItemsCollectionRef, nativeItemsCollectionId, resolveNativeItemsLayout, } from "../firebase/catalog-data-paths.js";
9
9
  import { collectionRefFromSlashPath } from "../firebase/firestore-collection-path.js";
10
- const MONGO_BACKUP_DB = "catalox-backups";
11
10
  export const BACKUP_METADATA_SOURCE_NAMES = [
12
11
  "apps",
13
12
  "catalogs",
@@ -39,24 +38,7 @@ export function firebaseLatestName(logical) {
39
38
  export function firebaseVersionedName(ts, logical) {
40
39
  return `${ts}__backup-${logical}`;
41
40
  }
42
- function mongoTmpName(logical) {
43
- return `__tmp_backup_${logical}`;
44
- }
45
- function mongoVersionedName(ts, logical) {
46
- return `${ts}__${logical}`;
47
- }
48
- function mongoNativeItemsCollectionName(catalogId) {
49
- return `catalogData__${sanitizeMongoCollectionPart(catalogId)}`;
50
- }
51
- function mongoSnapshotCollectionName(catalogId) {
52
- return `catalogSnapshots__${sanitizeMongoCollectionPart(catalogId)}`;
53
- }
54
- function mongoSnapshotRunCollectionName(catalogId, runId) {
55
- return `catalogSnapshotsRun__${sanitizeMongoCollectionPart(catalogId)}__${sanitizeMongoCollectionPart(runId)}`;
56
- }
57
- function sanitizeMongoCollectionPart(s) {
58
- return String(s).replace(/[^a-zA-Z0-9_-]/g, "_");
59
- }
41
+ // Mongo backup mode has been removed; use explicit Mongo export/import instead.
60
42
  export function firebaseNativeLogical(catalogId) {
61
43
  return `catalogData--${catalogId}`;
62
44
  }
@@ -132,208 +114,26 @@ async function copyNativeCatalogToFirestoreBackup(fs, catalogId, destCollectionP
132
114
  : flatNativeItemsCollectionRef(fs, catalogId);
133
115
  await paginatedCopyFirestoreToFirestore(fs, src.path, destCollectionPath, counts, destCollectionPath);
134
116
  }
135
- async function mongoReplacePage(col, docs) {
136
- const bulk = docs.map((d) => {
137
- const payload = { ...d.data() };
138
- delete payload._id;
139
- return {
140
- replaceOne: {
141
- filter: { _id: d.id },
142
- replacement: { _id: d.id, ...payload },
143
- upsert: true,
144
- },
145
- };
146
- });
147
- if (bulk.length)
148
- await col.bulkWrite(bulk);
149
- }
150
- async function paginatedCopyFirestoreToMongo(fs, sourceCollectionPath, db, destCollectionName, counts, countKey) {
151
- const src = fs.collection(sourceCollectionPath);
152
- const col = db.collection(destCollectionName);
153
- let last;
154
- let total = 0;
155
- while (true) {
156
- let q = src.orderBy(FieldPath.documentId()).limit(200);
157
- if (last)
158
- q = q.startAfter(last);
159
- const snap = await q.get();
160
- if (snap.empty)
161
- break;
162
- await mongoReplacePage(col, snap.docs);
163
- total += snap.docs.length;
164
- last = snap.docs[snap.docs.length - 1];
165
- }
166
- counts[countKey] = (counts[countKey] ?? 0) + total;
167
- }
168
- async function promoteMongoLatest(db, tmpName, latestName) {
169
- const n = await db.collection(tmpName).countDocuments();
170
- if (n === 0) {
171
- await db.collection(latestName).drop().catch(() => undefined);
172
- return;
173
- }
174
- await db.collection(latestName).drop().catch(() => undefined);
175
- await db.collection(tmpName).rename(latestName, { dropTarget: true });
176
- }
177
- async function backupMetadataMongo(fs, db, ts, counts, collectionsWritten, versionedWritten, issues) {
178
- for (const name of BACKUP_METADATA_SOURCE_NAMES) {
179
- const tmp = mongoTmpName(name);
180
- const versioned = mongoVersionedName(ts, name);
181
- try {
182
- await db.collection(tmp).drop().catch(() => undefined);
183
- await paginatedCopyFirestoreToMongo(fs, name, db, tmp, counts, tmp);
184
- await db.collection(versioned).drop().catch(() => undefined);
185
- await paginatedCopyFirestoreToMongo(fs, name, db, versioned, counts, versioned);
186
- await promoteMongoLatest(db, tmp, name);
187
- collectionsWritten.push(name);
188
- versionedWritten.push(versioned);
189
- }
190
- catch (e) {
191
- issues.push({
192
- code: "mongo_metadata_copy_failed",
193
- message: e instanceof Error ? e.message : String(e),
194
- detail: { name },
195
- });
196
- throw e;
197
- }
198
- }
199
- }
200
- async function backupMetadataFirebase(fs, ts, counts, collectionsWritten, versionedWritten, issues) {
201
- for (const name of BACKUP_METADATA_SOURCE_NAMES) {
202
- const tmp = firebaseTmpName(name);
203
- const latest = firebaseLatestName(name);
204
- const ver = firebaseVersionedName(ts, name);
205
- try {
206
- await deleteAllDocsInFirestoreCollection(fs, tmp);
207
- await paginatedCopyFirestoreToFirestore(fs, name, tmp, counts, tmp);
208
- await paginatedCopyFirestoreToFirestore(fs, name, ver, counts, ver);
209
- await deleteAllDocsInFirestoreCollection(fs, latest);
210
- await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
211
- await deleteAllDocsInFirestoreCollection(fs, tmp);
212
- collectionsWritten.push(latest);
213
- versionedWritten.push(ver);
214
- }
215
- catch (e) {
216
- issues.push({
217
- code: "firebase_metadata_copy_failed",
218
- message: e instanceof Error ? e.message : String(e),
219
- detail: { name },
220
- });
221
- throw e;
222
- }
223
- }
224
- }
225
- async function backupSnapshotsFirebase(fs, ts, counts, collectionsWritten, versionedWritten, issues) {
226
- const roots = await fs.collection("catalogSnapshots").listDocuments();
227
- for (const pref of roots) {
228
- const catalogId = pref.id;
229
- const logical = firebaseSnapshotLogical(catalogId);
230
- const itemsPath = pref.collection("items").path;
231
- const tmp = firebaseTmpName(logical);
232
- const latest = firebaseLatestName(logical);
233
- const ver = firebaseVersionedName(ts, logical);
234
- try {
235
- await deleteAllDocsInFirestoreCollection(fs, tmp);
236
- await paginatedCopyFirestoreToFirestore(fs, itemsPath, tmp, counts, tmp);
237
- await paginatedCopyFirestoreToFirestore(fs, itemsPath, ver, counts, ver);
238
- await deleteAllDocsInFirestoreCollection(fs, latest);
239
- await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
240
- await deleteAllDocsInFirestoreCollection(fs, tmp);
241
- collectionsWritten.push(latest);
242
- versionedWritten.push(ver);
243
- const runs = await pref.collection("runs").listDocuments();
244
- for (const runRef of runs) {
245
- const runId = runRef.id;
246
- const runLogical = firebaseSnapshotRunLogical(catalogId, runId);
247
- const runItemsPath = runRef.collection("items").path;
248
- const rtmp = firebaseTmpName(runLogical);
249
- const rlatest = firebaseLatestName(runLogical);
250
- const rver = firebaseVersionedName(ts, runLogical);
251
- await deleteAllDocsInFirestoreCollection(fs, rtmp);
252
- await paginatedCopyFirestoreToFirestore(fs, runItemsPath, rtmp, counts, rtmp);
253
- await paginatedCopyFirestoreToFirestore(fs, runItemsPath, rver, counts, rver);
254
- await deleteAllDocsInFirestoreCollection(fs, rlatest);
255
- await paginatedCopyFirestoreToFirestore(fs, rtmp, rlatest, counts, rlatest);
256
- await deleteAllDocsInFirestoreCollection(fs, rtmp);
257
- collectionsWritten.push(rlatest);
258
- versionedWritten.push(rver);
259
- }
260
- }
261
- catch (e) {
262
- issues.push({
263
- code: "firebase_snapshot_copy_failed",
264
- message: e instanceof Error ? e.message : String(e),
265
- detail: { catalogId },
266
- });
267
- throw e;
268
- }
269
- }
270
- }
271
- async function backupSnapshotsMongo(fs, db, ts, counts, collectionsWritten, versionedWritten, issues) {
272
- const roots = await fs.collection("catalogSnapshots").listDocuments();
273
- for (const pref of roots) {
274
- const catalogId = pref.id;
275
- const itemsPath = pref.collection("items").path;
276
- const latestName = mongoSnapshotCollectionName(catalogId);
277
- const versionedName = mongoVersionedName(ts, latestName);
278
- const tmpName = mongoTmpName(latestName);
279
- try {
280
- await db.collection(tmpName).drop().catch(() => undefined);
281
- await paginatedCopyFirestoreToMongo(fs, itemsPath, db, tmpName, counts, tmpName);
282
- await db.collection(versionedName).drop().catch(() => undefined);
283
- await paginatedCopyFirestoreToMongo(fs, itemsPath, db, versionedName, counts, versionedName);
284
- await promoteMongoLatest(db, tmpName, latestName);
285
- collectionsWritten.push(latestName);
286
- versionedWritten.push(versionedName);
287
- const runs = await pref.collection("runs").listDocuments();
288
- for (const runRef of runs) {
289
- const runId = runRef.id;
290
- const runItemsPath = runRef.collection("items").path;
291
- const runLatest = mongoSnapshotRunCollectionName(catalogId, runId);
292
- const runVer = mongoVersionedName(ts, runLatest);
293
- const runTmp = mongoTmpName(runLatest);
294
- await db.collection(runTmp).drop().catch(() => undefined);
295
- await paginatedCopyFirestoreToMongo(fs, runItemsPath, db, runTmp, counts, runTmp);
296
- await db.collection(runVer).drop().catch(() => undefined);
297
- await paginatedCopyFirestoreToMongo(fs, runItemsPath, db, runVer, counts, runVer);
298
- await promoteMongoLatest(db, runTmp, runLatest);
299
- collectionsWritten.push(runLatest);
300
- versionedWritten.push(runVer);
301
- }
302
- }
303
- catch (e) {
304
- issues.push({
305
- code: "mongo_snapshot_copy_failed",
306
- message: e instanceof Error ? e.message : String(e),
307
- detail: { catalogId },
308
- });
309
- throw e;
310
- }
311
- }
312
- }
313
- async function writeBackupManifestFirebase(fs, payload) {
314
- await fs.collection("backup-backupRuns").add({
315
- ...payload,
316
- createdAt: new Date().toISOString(),
317
- });
318
- }
319
- async function writeBackupManifestMongo(db, payload) {
320
- await db.collection("backupRuns").insertOne({
321
- ...payload,
322
- createdAt: new Date().toISOString(),
323
- });
324
- }
325
- /** Delete Mongo `backupRuns` documents with createdAt older than cutoff ISO string. */
326
- export async function pruneMongoBackupRuns(db, createdBeforeIso) {
327
- const res = await db.collection("backupRuns").deleteMany({
328
- createdAt: { $lt: createdBeforeIso },
329
- });
330
- return res.deletedCount ?? 0;
331
- }
332
117
  /**
333
118
  * Delete documents in oldest Firestore versioned backup collections matching `{ts}__backup-*`
334
119
  * until at most `keepLast` such collections remain.
335
120
  */
336
121
  const require = createRequire(import.meta.url);
122
+ let warnedDefaultBucket = false;
123
+ function warnMissingCataloxBucketEnv(feature) {
124
+ if (warnedDefaultBucket)
125
+ return;
126
+ warnedDefaultBucket = true;
127
+ try {
128
+ const { createLogxer } = require("@x12i/logxer");
129
+ const log = createLogxer({ packageName: "CATALOX", envPrefix: "CATALOX", debugNamespace: "catalox" });
130
+ log.warn(`${feature}: CATALOX_BUCKET is not set; defaulting to bucket "catalox". ` +
131
+ `Set CATALOX_BUCKET to an existing bucket to avoid unexpected writes or bucket-create failures.`);
132
+ }
133
+ catch {
134
+ // If logxer can't initialize for any reason, do not crash the caller for a warning.
135
+ }
136
+ }
337
137
  function normalizeBackupGcsBasePrefix(prefix) {
338
138
  const p = (prefix ?? "").trim();
339
139
  if (!p)
@@ -347,6 +147,41 @@ export function gcsBackupRunFolder(ts, gcsPrefix) {
347
147
  return `${base.replace(/\/+$/, "")}${base ? "/" : ""}${ts}`.replace(/\/+$/, "");
348
148
  }
349
149
  export const CATALOX_GCS_BACKUP_MANIFEST_FILENAME = "catalox-backup-manifest.json";
150
+ function resolveGoogleCredentialsForStorage() {
151
+ // Prefer base64 carrier used in this repo.
152
+ const b64 = String(process.env.GOOGLE_SERVICE_ACCOUNT_BASE64 ?? "").trim();
153
+ if (b64) {
154
+ const json = Buffer.from(b64, "base64").toString("utf8");
155
+ const creds = JSON.parse(json);
156
+ const pid = String(creds.project_id ?? "").trim();
157
+ return { credentials: creds, ...(pid ? { projectId: pid } : {}) };
158
+ }
159
+ const p = String(process.env.FIREBASE_SERVICE_ACCOUNT_PATH ?? "").trim();
160
+ if (p) {
161
+ const json = readFileSync(p, "utf8");
162
+ const creds = JSON.parse(json);
163
+ const pid = String(creds.project_id ?? "").trim();
164
+ return { credentials: creds, ...(pid ? { projectId: pid } : {}) };
165
+ }
166
+ return {};
167
+ }
168
+ async function ensureGcsBucketExists(bucketName) {
169
+ // This uses @google-cloud/storage directly because @x12i/helpers/gcs does not create buckets.
170
+ // Requires permissions: storage.buckets.get + storage.buckets.create (if missing).
171
+ const { Storage } = require("@google-cloud/storage");
172
+ const auth = resolveGoogleCredentialsForStorage();
173
+ const storage = auth.credentials != null
174
+ ? new Storage({ credentials: auth.credentials, ...(auth.projectId ? { projectId: auth.projectId } : {}) })
175
+ : new Storage();
176
+ const b = storage.bucket(bucketName);
177
+ const [exists] = await b.exists();
178
+ if (exists)
179
+ return;
180
+ await storage.createBucket(bucketName);
181
+ }
182
+ export function defaultCataloxBucketName() {
183
+ return "catalox";
184
+ }
350
185
  function createHelpersGcsClient(bucket, prefixWithoutTrailingSlash) {
351
186
  const { createGcsClient } = require("@x12i/helpers/gcs");
352
187
  return createGcsClient({
@@ -512,23 +347,36 @@ async function runBackupDataGcs(deps, input, nativeCatalogIds) {
512
347
  const fs = deps.firestoreStore.firestore;
513
348
  const ts = formatBackupTimestamp(input.versionTimestamp);
514
349
  const includeSnapshots = Boolean(input.includeSnapshots);
515
- const bucket = input.gcsBucket?.trim() ?? "";
516
- if (!bucket) {
350
+ const envBucket = typeof process !== "undefined" ? String(process.env.CATALOX_BUCKET ?? "").trim() : "";
351
+ const rawBucket = input.gcsBucket?.trim() || envBucket || defaultCataloxBucketName();
352
+ if (!input.gcsBucket?.trim() && !envBucket)
353
+ warnMissingCataloxBucketEnv("backupData(gcs)");
354
+ const bucket = rawBucket.trim();
355
+ const runFolder = gcsBackupRunFolder(ts, input.gcsPrefix);
356
+ try {
357
+ await ensureGcsBucketExists(bucket);
358
+ }
359
+ catch (e) {
517
360
  return {
518
361
  ok: false,
519
362
  mode: "gcs",
520
- databaseName: "",
363
+ databaseName: `gs://${bucket}/`,
521
364
  backupTimestamp: new Date().toISOString(),
522
365
  ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
523
366
  includeSnapshots,
524
367
  collectionsWritten: [],
525
368
  versionedCollectionsWritten: [],
526
369
  counts: {},
527
- issues: [{ code: "missing_gcs_bucket", message: "gcsBucket is required when mode === \"gcs\"" }],
370
+ issues: [
371
+ {
372
+ code: "gcs_bucket_ensure_failed",
373
+ message: e instanceof Error ? e.message : String(e),
374
+ detail: { bucket },
375
+ },
376
+ ],
528
377
  nativeItemSourceLayoutByCatalogId: {},
529
378
  };
530
379
  }
531
- const runFolder = gcsBackupRunFolder(ts, input.gcsPrefix);
532
380
  const gcs = createHelpersGcsClient(bucket, runFolder);
533
381
  try {
534
382
  versionedCollectionsWritten.push(runFolder);
@@ -605,22 +453,6 @@ async function runBackupDataGcs(deps, input, nativeCatalogIds) {
605
453
  };
606
454
  }
607
455
  }
608
- export async function pruneFirebaseVersionedBackups(firestore, keepLast) {
609
- const cols = await firestore.listCollections();
610
- const versioned = cols
611
- .map((c) => c.id)
612
- .filter((id) => /^\d{4}_\d{2}_\d{2}T.*__backup-/.test(id))
613
- .sort();
614
- if (versioned.length <= keepLast)
615
- return { deleted: [] };
616
- const toRemove = versioned.slice(0, versioned.length - keepLast);
617
- const deleted = [];
618
- for (const id of toRemove) {
619
- await deleteAllDocsInFirestoreCollection(firestore, id);
620
- deleted.push(id);
621
- }
622
- return { deleted };
623
- }
624
456
  export async function runBackupData(deps, input) {
625
457
  const issues = [];
626
458
  const collectionsWritten = [];
@@ -636,185 +468,6 @@ export async function runBackupData(deps, input) {
636
468
  ...(input.catalogIds != null && input.catalogIds.length ? { catalogIds: input.catalogIds } : {}),
637
469
  ...(input.appId != null && input.appId !== "" ? { appId: input.appId } : {}),
638
470
  });
639
- if (input.mode === "mongo") {
640
- if (!input.mongoBackupUri?.trim()) {
641
- return {
642
- ok: false,
643
- mode: "mongo",
644
- databaseName: MONGO_BACKUP_DB,
645
- backupTimestamp: new Date().toISOString(),
646
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
647
- includeSnapshots,
648
- collectionsWritten: [],
649
- versionedCollectionsWritten: [],
650
- counts: {},
651
- issues: [{ code: "missing_mongo_uri", message: "mongoBackupUri is required for mongo mode" }],
652
- nativeItemSourceLayoutByCatalogId: {},
653
- };
654
- }
655
- const client = new MongoClient(input.mongoBackupUri);
656
- try {
657
- await client.connect();
658
- const db = client.db(MONGO_BACKUP_DB);
659
- await backupMetadataMongo(fs, db, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
660
- for (const catalogId of nativeCatalogIds) {
661
- const layout = await resolveNativeItemsLayout(fs, catalogId);
662
- nativeItemSourceLayoutByCatalogId[String(catalogId)] = layout;
663
- const logical = mongoNativeItemsCollectionName(String(catalogId));
664
- const tmp = mongoTmpName(logical);
665
- const versioned = mongoVersionedName(ts, logical);
666
- await db.collection(tmp).drop().catch(() => undefined);
667
- await copyNativeCatalogToMongo(fs, db, catalogId, tmp, counts, layout);
668
- await db.collection(versioned).drop().catch(() => undefined);
669
- await copyNativeCatalogToMongo(fs, db, catalogId, versioned, counts, layout);
670
- await promoteMongoLatest(db, tmp, logical);
671
- collectionsWritten.push(logical);
672
- versionedCollectionsWritten.push(versioned);
673
- }
674
- if (includeSnapshots) {
675
- await backupSnapshotsMongo(fs, db, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
676
- }
677
- await writeBackupManifestMongo(db, {
678
- backupTimestamp: new Date().toISOString(),
679
- mode: "mongo",
680
- backupLabel: input.backupLabel,
681
- includeSnapshots,
682
- collectionsWritten,
683
- versionedCollectionsWritten,
684
- counts,
685
- issues,
686
- nativeItemSourceLayoutByCatalogId,
687
- });
688
- return {
689
- ok: true,
690
- mode: "mongo",
691
- databaseName: MONGO_BACKUP_DB,
692
- backupTimestamp: new Date().toISOString(),
693
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
694
- includeSnapshots,
695
- collectionsWritten,
696
- versionedCollectionsWritten,
697
- counts,
698
- issues,
699
- nativeItemSourceLayoutByCatalogId,
700
- };
701
- }
702
- catch (e) {
703
- issues.push({
704
- code: "mongo_backup_failed",
705
- message: e instanceof Error ? e.message : String(e),
706
- });
707
- return {
708
- ok: false,
709
- mode: "mongo",
710
- databaseName: MONGO_BACKUP_DB,
711
- backupTimestamp: new Date().toISOString(),
712
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
713
- includeSnapshots,
714
- collectionsWritten,
715
- versionedCollectionsWritten,
716
- counts,
717
- issues,
718
- nativeItemSourceLayoutByCatalogId,
719
- };
720
- }
721
- finally {
722
- await client.close().catch(() => undefined);
723
- }
724
- }
725
- if (input.mode === "gcs") {
726
- return await runBackupDataGcs(deps, input, nativeCatalogIds);
727
- }
728
- if (input.mode !== "firebase") {
729
- return {
730
- ok: false,
731
- mode: input.mode,
732
- databaseName: "",
733
- backupTimestamp: new Date().toISOString(),
734
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
735
- includeSnapshots,
736
- collectionsWritten: [],
737
- versionedCollectionsWritten: [],
738
- counts: {},
739
- issues: [
740
- {
741
- code: "unknown_backup_mode",
742
- message: `Unsupported backup mode: ${String(input.mode)}`,
743
- },
744
- ],
745
- nativeItemSourceLayoutByCatalogId: {},
746
- };
747
- }
748
- try {
749
- await backupMetadataFirebase(fs, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
750
- for (const catalogId of nativeCatalogIds) {
751
- const layout = await resolveNativeItemsLayout(fs, catalogId);
752
- nativeItemSourceLayoutByCatalogId[String(catalogId)] = layout;
753
- const logical = firebaseNativeLogical(String(catalogId));
754
- const tmp = firebaseTmpName(logical);
755
- const latest = firebaseLatestName(logical);
756
- const ver = firebaseVersionedName(ts, logical);
757
- await deleteAllDocsInFirestoreCollection(fs, tmp);
758
- await copyNativeCatalogToFirestoreBackup(fs, catalogId, tmp, counts, layout);
759
- await copyNativeCatalogToFirestoreBackup(fs, catalogId, ver, counts, layout);
760
- await deleteAllDocsInFirestoreCollection(fs, latest);
761
- await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
762
- await deleteAllDocsInFirestoreCollection(fs, tmp);
763
- collectionsWritten.push(latest);
764
- versionedCollectionsWritten.push(ver);
765
- }
766
- if (includeSnapshots) {
767
- await backupSnapshotsFirebase(fs, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
768
- }
769
- await writeBackupManifestFirebase(fs, {
770
- backupTimestamp: new Date().toISOString(),
771
- mode: "firebase",
772
- backupLabel: input.backupLabel,
773
- includeSnapshots,
774
- collectionsWritten,
775
- versionedCollectionsWritten,
776
- counts,
777
- issues,
778
- nativeItemSourceLayoutByCatalogId,
779
- });
780
- return {
781
- ok: true,
782
- mode: "firebase",
783
- databaseName: "(same-firestore-target)",
784
- backupTimestamp: new Date().toISOString(),
785
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
786
- includeSnapshots,
787
- collectionsWritten,
788
- versionedCollectionsWritten,
789
- counts,
790
- issues,
791
- nativeItemSourceLayoutByCatalogId,
792
- };
793
- }
794
- catch (e) {
795
- issues.push({
796
- code: "firebase_backup_failed",
797
- message: e instanceof Error ? e.message : String(e),
798
- });
799
- return {
800
- ok: false,
801
- mode: "firebase",
802
- databaseName: "(same-firestore-target)",
803
- backupTimestamp: new Date().toISOString(),
804
- ...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
805
- includeSnapshots,
806
- collectionsWritten,
807
- versionedCollectionsWritten,
808
- counts,
809
- issues,
810
- nativeItemSourceLayoutByCatalogId,
811
- };
812
- }
813
- }
814
- async function copyNativeCatalogToMongo(fs, db, catalogId, destCollection, counts, layout) {
815
- const src = layout === "legacy"
816
- ? legacyNativeItemsCollectionRef(fs, catalogId)
817
- : flatNativeItemsCollectionRef(fs, catalogId);
818
- await paginatedCopyFirestoreToMongo(fs, src.path, db, destCollection, counts, destCollection);
471
+ return await runBackupDataGcs(deps, input, nativeCatalogIds);
819
472
  }
820
473
  //# sourceMappingURL=backup-data.js.map