@njdamstra/appwrite-utils-cli 1.8.9

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 (392) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1133 -0
  3. package/dist/adapters/AdapterFactory.d.ts +94 -0
  4. package/dist/adapters/AdapterFactory.js +405 -0
  5. package/dist/adapters/DatabaseAdapter.d.ts +233 -0
  6. package/dist/adapters/DatabaseAdapter.js +50 -0
  7. package/dist/adapters/LegacyAdapter.d.ts +50 -0
  8. package/dist/adapters/LegacyAdapter.js +612 -0
  9. package/dist/adapters/TablesDBAdapter.d.ts +45 -0
  10. package/dist/adapters/TablesDBAdapter.js +571 -0
  11. package/dist/adapters/index.d.ts +11 -0
  12. package/dist/adapters/index.js +12 -0
  13. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  14. package/dist/backups/operations/bucketBackup.js +197 -0
  15. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  16. package/dist/backups/operations/collectionBackup.js +201 -0
  17. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  18. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  19. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  20. package/dist/backups/schemas/bucketManifest.js +33 -0
  21. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  22. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  23. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  24. package/dist/backups/tracking/centralizedTracking.js +274 -0
  25. package/dist/cli/commands/configCommands.d.ts +8 -0
  26. package/dist/cli/commands/configCommands.js +166 -0
  27. package/dist/cli/commands/databaseCommands.d.ts +13 -0
  28. package/dist/cli/commands/databaseCommands.js +554 -0
  29. package/dist/cli/commands/functionCommands.d.ts +7 -0
  30. package/dist/cli/commands/functionCommands.js +330 -0
  31. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  32. package/dist/cli/commands/schemaCommands.js +169 -0
  33. package/dist/cli/commands/storageCommands.d.ts +5 -0
  34. package/dist/cli/commands/storageCommands.js +143 -0
  35. package/dist/cli/commands/transferCommands.d.ts +5 -0
  36. package/dist/cli/commands/transferCommands.js +384 -0
  37. package/dist/collections/attributes.d.ts +13 -0
  38. package/dist/collections/attributes.js +1364 -0
  39. package/dist/collections/indexes.d.ts +12 -0
  40. package/dist/collections/indexes.js +217 -0
  41. package/dist/collections/methods.d.ts +19 -0
  42. package/dist/collections/methods.js +682 -0
  43. package/dist/collections/tableOperations.d.ts +86 -0
  44. package/dist/collections/tableOperations.js +434 -0
  45. package/dist/collections/transferOperations.d.ts +8 -0
  46. package/dist/collections/transferOperations.js +412 -0
  47. package/dist/collections/wipeOperations.d.ts +16 -0
  48. package/dist/collections/wipeOperations.js +233 -0
  49. package/dist/config/ConfigManager.d.ts +445 -0
  50. package/dist/config/ConfigManager.js +625 -0
  51. package/dist/config/configMigration.d.ts +87 -0
  52. package/dist/config/configMigration.js +390 -0
  53. package/dist/config/configValidation.d.ts +66 -0
  54. package/dist/config/configValidation.js +358 -0
  55. package/dist/config/index.d.ts +8 -0
  56. package/dist/config/index.js +7 -0
  57. package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
  58. package/dist/config/services/ConfigDiscoveryService.js +374 -0
  59. package/dist/config/services/ConfigLoaderService.d.ts +129 -0
  60. package/dist/config/services/ConfigLoaderService.js +540 -0
  61. package/dist/config/services/ConfigMergeService.d.ts +208 -0
  62. package/dist/config/services/ConfigMergeService.js +308 -0
  63. package/dist/config/services/ConfigValidationService.d.ts +214 -0
  64. package/dist/config/services/ConfigValidationService.js +310 -0
  65. package/dist/config/services/SessionAuthService.d.ts +225 -0
  66. package/dist/config/services/SessionAuthService.js +456 -0
  67. package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
  68. package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
  69. package/dist/config/services/index.d.ts +13 -0
  70. package/dist/config/services/index.js +10 -0
  71. package/dist/config/yamlConfig.d.ts +722 -0
  72. package/dist/config/yamlConfig.js +702 -0
  73. package/dist/databases/methods.d.ts +6 -0
  74. package/dist/databases/methods.js +35 -0
  75. package/dist/databases/setup.d.ts +5 -0
  76. package/dist/databases/setup.js +45 -0
  77. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  78. package/dist/examples/yamlTerminologyExample.js +272 -0
  79. package/dist/functions/deployments.d.ts +4 -0
  80. package/dist/functions/deployments.js +146 -0
  81. package/dist/functions/fnConfigDiscovery.d.ts +3 -0
  82. package/dist/functions/fnConfigDiscovery.js +108 -0
  83. package/dist/functions/methods.d.ts +16 -0
  84. package/dist/functions/methods.js +162 -0
  85. package/dist/functions/pathResolution.d.ts +37 -0
  86. package/dist/functions/pathResolution.js +185 -0
  87. package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
  88. package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
  89. package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
  90. package/dist/functions/templates/hono-typescript/README.md +286 -0
  91. package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  92. package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  93. package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
  94. package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
  95. package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
  96. package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  97. package/dist/functions/templates/typescript-node/README.md +32 -0
  98. package/dist/functions/templates/typescript-node/src/context.ts +103 -0
  99. package/dist/functions/templates/typescript-node/src/index.ts +29 -0
  100. package/dist/functions/templates/uv/README.md +31 -0
  101. package/dist/functions/templates/uv/pyproject.toml +30 -0
  102. package/dist/functions/templates/uv/src/__init__.py +0 -0
  103. package/dist/functions/templates/uv/src/context.py +125 -0
  104. package/dist/functions/templates/uv/src/index.py +46 -0
  105. package/dist/init.d.ts +2 -0
  106. package/dist/init.js +57 -0
  107. package/dist/interactiveCLI.d.ts +31 -0
  108. package/dist/interactiveCLI.js +898 -0
  109. package/dist/main.d.ts +2 -0
  110. package/dist/main.js +1172 -0
  111. package/dist/migrations/afterImportActions.d.ts +17 -0
  112. package/dist/migrations/afterImportActions.js +306 -0
  113. package/dist/migrations/appwriteToX.d.ts +211 -0
  114. package/dist/migrations/appwriteToX.js +491 -0
  115. package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
  116. package/dist/migrations/comprehensiveTransfer.js +1317 -0
  117. package/dist/migrations/dataLoader.d.ts +753 -0
  118. package/dist/migrations/dataLoader.js +1250 -0
  119. package/dist/migrations/importController.d.ts +23 -0
  120. package/dist/migrations/importController.js +268 -0
  121. package/dist/migrations/importDataActions.d.ts +50 -0
  122. package/dist/migrations/importDataActions.js +230 -0
  123. package/dist/migrations/relationships.d.ts +29 -0
  124. package/dist/migrations/relationships.js +204 -0
  125. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  126. package/dist/migrations/services/DataTransformationService.js +158 -0
  127. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  128. package/dist/migrations/services/FileHandlerService.js +236 -0
  129. package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
  130. package/dist/migrations/services/ImportOrchestrator.js +485 -0
  131. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  132. package/dist/migrations/services/RateLimitManager.js +279 -0
  133. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  134. package/dist/migrations/services/RelationshipResolver.js +332 -0
  135. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  136. package/dist/migrations/services/UserMappingService.js +277 -0
  137. package/dist/migrations/services/ValidationService.d.ts +74 -0
  138. package/dist/migrations/services/ValidationService.js +260 -0
  139. package/dist/migrations/transfer.d.ts +26 -0
  140. package/dist/migrations/transfer.js +608 -0
  141. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
  142. package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
  143. package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
  144. package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
  145. package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
  146. package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
  147. package/dist/schemas/authUser.d.ts +24 -0
  148. package/dist/schemas/authUser.js +17 -0
  149. package/dist/setup.d.ts +2 -0
  150. package/dist/setup.js +5 -0
  151. package/dist/setupCommands.d.ts +58 -0
  152. package/dist/setupCommands.js +490 -0
  153. package/dist/setupController.d.ts +9 -0
  154. package/dist/setupController.js +34 -0
  155. package/dist/shared/attributeMapper.d.ts +20 -0
  156. package/dist/shared/attributeMapper.js +203 -0
  157. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  158. package/dist/shared/backupMetadataSchema.js +38 -0
  159. package/dist/shared/backupTracking.d.ts +18 -0
  160. package/dist/shared/backupTracking.js +176 -0
  161. package/dist/shared/confirmationDialogs.d.ts +75 -0
  162. package/dist/shared/confirmationDialogs.js +236 -0
  163. package/dist/shared/errorUtils.d.ts +54 -0
  164. package/dist/shared/errorUtils.js +95 -0
  165. package/dist/shared/functionManager.d.ts +48 -0
  166. package/dist/shared/functionManager.js +336 -0
  167. package/dist/shared/indexManager.d.ts +24 -0
  168. package/dist/shared/indexManager.js +151 -0
  169. package/dist/shared/jsonSchemaGenerator.d.ts +50 -0
  170. package/dist/shared/jsonSchemaGenerator.js +290 -0
  171. package/dist/shared/logging.d.ts +61 -0
  172. package/dist/shared/logging.js +116 -0
  173. package/dist/shared/messageFormatter.d.ts +39 -0
  174. package/dist/shared/messageFormatter.js +162 -0
  175. package/dist/shared/migrationHelpers.d.ts +61 -0
  176. package/dist/shared/migrationHelpers.js +145 -0
  177. package/dist/shared/operationLogger.d.ts +10 -0
  178. package/dist/shared/operationLogger.js +12 -0
  179. package/dist/shared/operationQueue.d.ts +40 -0
  180. package/dist/shared/operationQueue.js +311 -0
  181. package/dist/shared/operationsTable.d.ts +26 -0
  182. package/dist/shared/operationsTable.js +286 -0
  183. package/dist/shared/operationsTableSchema.d.ts +48 -0
  184. package/dist/shared/operationsTableSchema.js +35 -0
  185. package/dist/shared/progressManager.d.ts +62 -0
  186. package/dist/shared/progressManager.js +215 -0
  187. package/dist/shared/pydanticModelGenerator.d.ts +17 -0
  188. package/dist/shared/pydanticModelGenerator.js +615 -0
  189. package/dist/shared/relationshipExtractor.d.ts +56 -0
  190. package/dist/shared/relationshipExtractor.js +138 -0
  191. package/dist/shared/schemaGenerator.d.ts +40 -0
  192. package/dist/shared/schemaGenerator.js +556 -0
  193. package/dist/shared/selectionDialogs.d.ts +214 -0
  194. package/dist/shared/selectionDialogs.js +544 -0
  195. package/dist/storage/backupCompression.d.ts +20 -0
  196. package/dist/storage/backupCompression.js +67 -0
  197. package/dist/storage/methods.d.ts +32 -0
  198. package/dist/storage/methods.js +472 -0
  199. package/dist/storage/schemas.d.ts +842 -0
  200. package/dist/storage/schemas.js +175 -0
  201. package/dist/types.d.ts +4 -0
  202. package/dist/types.js +3 -0
  203. package/dist/users/methods.d.ts +16 -0
  204. package/dist/users/methods.js +277 -0
  205. package/dist/utils/ClientFactory.d.ts +87 -0
  206. package/dist/utils/ClientFactory.js +212 -0
  207. package/dist/utils/configDiscovery.d.ts +78 -0
  208. package/dist/utils/configDiscovery.js +472 -0
  209. package/dist/utils/configMigration.d.ts +1 -0
  210. package/dist/utils/configMigration.js +261 -0
  211. package/dist/utils/constantsGenerator.d.ts +31 -0
  212. package/dist/utils/constantsGenerator.js +321 -0
  213. package/dist/utils/dataConverters.d.ts +46 -0
  214. package/dist/utils/dataConverters.js +139 -0
  215. package/dist/utils/directoryUtils.d.ts +22 -0
  216. package/dist/utils/directoryUtils.js +59 -0
  217. package/dist/utils/getClientFromConfig.d.ts +39 -0
  218. package/dist/utils/getClientFromConfig.js +199 -0
  219. package/dist/utils/helperFunctions.d.ts +63 -0
  220. package/dist/utils/helperFunctions.js +156 -0
  221. package/dist/utils/index.d.ts +2 -0
  222. package/dist/utils/index.js +2 -0
  223. package/dist/utils/loadConfigs.d.ts +50 -0
  224. package/dist/utils/loadConfigs.js +358 -0
  225. package/dist/utils/pathResolvers.d.ts +53 -0
  226. package/dist/utils/pathResolvers.js +72 -0
  227. package/dist/utils/projectConfig.d.ts +119 -0
  228. package/dist/utils/projectConfig.js +171 -0
  229. package/dist/utils/retryFailedPromises.d.ts +2 -0
  230. package/dist/utils/retryFailedPromises.js +23 -0
  231. package/dist/utils/sessionAuth.d.ts +48 -0
  232. package/dist/utils/sessionAuth.js +164 -0
  233. package/dist/utils/setupFiles.d.ts +4 -0
  234. package/dist/utils/setupFiles.js +1192 -0
  235. package/dist/utils/typeGuards.d.ts +35 -0
  236. package/dist/utils/typeGuards.js +57 -0
  237. package/dist/utils/validationRules.d.ts +43 -0
  238. package/dist/utils/validationRules.js +42 -0
  239. package/dist/utils/versionDetection.d.ts +58 -0
  240. package/dist/utils/versionDetection.js +251 -0
  241. package/dist/utils/yamlConverter.d.ts +100 -0
  242. package/dist/utils/yamlConverter.js +428 -0
  243. package/dist/utils/yamlLoader.d.ts +70 -0
  244. package/dist/utils/yamlLoader.js +267 -0
  245. package/dist/utilsController.d.ts +106 -0
  246. package/dist/utilsController.js +863 -0
  247. package/package.json +75 -0
  248. package/scripts/copy-templates.ts +23 -0
  249. package/src/adapters/AdapterFactory.ts +510 -0
  250. package/src/adapters/DatabaseAdapter.ts +306 -0
  251. package/src/adapters/LegacyAdapter.ts +841 -0
  252. package/src/adapters/TablesDBAdapter.ts +773 -0
  253. package/src/adapters/index.ts +37 -0
  254. package/src/backups/operations/bucketBackup.ts +277 -0
  255. package/src/backups/operations/collectionBackup.ts +310 -0
  256. package/src/backups/operations/comprehensiveBackup.ts +342 -0
  257. package/src/backups/schemas/bucketManifest.ts +78 -0
  258. package/src/backups/schemas/comprehensiveManifest.ts +76 -0
  259. package/src/backups/tracking/centralizedTracking.ts +352 -0
  260. package/src/cli/commands/configCommands.ts +201 -0
  261. package/src/cli/commands/databaseCommands.ts +749 -0
  262. package/src/cli/commands/functionCommands.ts +418 -0
  263. package/src/cli/commands/schemaCommands.ts +200 -0
  264. package/src/cli/commands/storageCommands.ts +152 -0
  265. package/src/cli/commands/transferCommands.ts +457 -0
  266. package/src/collections/attributes.ts +2054 -0
  267. package/src/collections/attributes.ts.backup +1555 -0
  268. package/src/collections/indexes.ts +352 -0
  269. package/src/collections/methods.ts +745 -0
  270. package/src/collections/tableOperations.ts +506 -0
  271. package/src/collections/transferOperations.ts +590 -0
  272. package/src/collections/wipeOperations.ts +346 -0
  273. package/src/config/ConfigManager.ts +808 -0
  274. package/src/config/README.md +274 -0
  275. package/src/config/configMigration.ts +575 -0
  276. package/src/config/configValidation.ts +445 -0
  277. package/src/config/index.ts +10 -0
  278. package/src/config/services/ConfigDiscoveryService.ts +463 -0
  279. package/src/config/services/ConfigLoaderService.ts +740 -0
  280. package/src/config/services/ConfigMergeService.ts +388 -0
  281. package/src/config/services/ConfigValidationService.ts +394 -0
  282. package/src/config/services/SessionAuthService.ts +565 -0
  283. package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
  284. package/src/config/services/index.ts +29 -0
  285. package/src/config/yamlConfig.ts +761 -0
  286. package/src/databases/methods.ts +49 -0
  287. package/src/databases/setup.ts +77 -0
  288. package/src/examples/yamlTerminologyExample.ts +346 -0
  289. package/src/functions/deployments.ts +220 -0
  290. package/src/functions/fnConfigDiscovery.ts +103 -0
  291. package/src/functions/methods.ts +271 -0
  292. package/src/functions/pathResolution.ts +227 -0
  293. package/src/functions/templates/count-docs-in-collection/README.md +54 -0
  294. package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
  295. package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
  296. package/src/functions/templates/hono-typescript/README.md +286 -0
  297. package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  298. package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  299. package/src/functions/templates/hono-typescript/src/app.ts +180 -0
  300. package/src/functions/templates/hono-typescript/src/context.ts +103 -0
  301. package/src/functions/templates/hono-typescript/src/index.ts +54 -0
  302. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  303. package/src/functions/templates/typescript-node/README.md +32 -0
  304. package/src/functions/templates/typescript-node/src/context.ts +103 -0
  305. package/src/functions/templates/typescript-node/src/index.ts +29 -0
  306. package/src/functions/templates/uv/README.md +31 -0
  307. package/src/functions/templates/uv/pyproject.toml +30 -0
  308. package/src/functions/templates/uv/src/__init__.py +0 -0
  309. package/src/functions/templates/uv/src/context.py +125 -0
  310. package/src/functions/templates/uv/src/index.py +46 -0
  311. package/src/init.ts +62 -0
  312. package/src/interactiveCLI.ts +1136 -0
  313. package/src/main.ts +1661 -0
  314. package/src/migrations/afterImportActions.ts +580 -0
  315. package/src/migrations/appwriteToX.ts +664 -0
  316. package/src/migrations/comprehensiveTransfer.ts +2285 -0
  317. package/src/migrations/dataLoader.ts +1702 -0
  318. package/src/migrations/importController.ts +428 -0
  319. package/src/migrations/importDataActions.ts +315 -0
  320. package/src/migrations/relationships.ts +334 -0
  321. package/src/migrations/services/DataTransformationService.ts +196 -0
  322. package/src/migrations/services/FileHandlerService.ts +311 -0
  323. package/src/migrations/services/ImportOrchestrator.ts +666 -0
  324. package/src/migrations/services/RateLimitManager.ts +363 -0
  325. package/src/migrations/services/RelationshipResolver.ts +461 -0
  326. package/src/migrations/services/UserMappingService.ts +345 -0
  327. package/src/migrations/services/ValidationService.ts +349 -0
  328. package/src/migrations/transfer.ts +1068 -0
  329. package/src/migrations/yaml/YamlImportConfigLoader.ts +439 -0
  330. package/src/migrations/yaml/YamlImportIntegration.ts +446 -0
  331. package/src/migrations/yaml/generateImportSchemas.ts +1354 -0
  332. package/src/schemas/authUser.ts +23 -0
  333. package/src/setup.ts +8 -0
  334. package/src/setupCommands.ts +603 -0
  335. package/src/setupController.ts +43 -0
  336. package/src/shared/attributeMapper.ts +229 -0
  337. package/src/shared/backupMetadataSchema.ts +93 -0
  338. package/src/shared/backupTracking.ts +211 -0
  339. package/src/shared/confirmationDialogs.ts +327 -0
  340. package/src/shared/errorUtils.ts +110 -0
  341. package/src/shared/functionManager.ts +525 -0
  342. package/src/shared/indexManager.ts +254 -0
  343. package/src/shared/jsonSchemaGenerator.ts +383 -0
  344. package/src/shared/logging.ts +149 -0
  345. package/src/shared/messageFormatter.ts +208 -0
  346. package/src/shared/migrationHelpers.ts +232 -0
  347. package/src/shared/operationLogger.ts +20 -0
  348. package/src/shared/operationQueue.ts +377 -0
  349. package/src/shared/operationsTable.ts +338 -0
  350. package/src/shared/operationsTableSchema.ts +60 -0
  351. package/src/shared/progressManager.ts +278 -0
  352. package/src/shared/pydanticModelGenerator.ts +618 -0
  353. package/src/shared/relationshipExtractor.ts +214 -0
  354. package/src/shared/schemaGenerator.ts +644 -0
  355. package/src/shared/selectionDialogs.ts +749 -0
  356. package/src/storage/backupCompression.ts +88 -0
  357. package/src/storage/methods.ts +698 -0
  358. package/src/storage/schemas.ts +205 -0
  359. package/src/types/node-appwrite-tablesdb.d.ts +44 -0
  360. package/src/types.ts +9 -0
  361. package/src/users/methods.ts +359 -0
  362. package/src/utils/ClientFactory.ts +240 -0
  363. package/src/utils/configDiscovery.ts +557 -0
  364. package/src/utils/configMigration.ts +348 -0
  365. package/src/utils/constantsGenerator.ts +369 -0
  366. package/src/utils/dataConverters.ts +159 -0
  367. package/src/utils/directoryUtils.ts +61 -0
  368. package/src/utils/getClientFromConfig.ts +257 -0
  369. package/src/utils/helperFunctions.ts +228 -0
  370. package/src/utils/index.ts +2 -0
  371. package/src/utils/loadConfigs.ts +449 -0
  372. package/src/utils/pathResolvers.ts +81 -0
  373. package/src/utils/projectConfig.ts +299 -0
  374. package/src/utils/retryFailedPromises.ts +29 -0
  375. package/src/utils/sessionAuth.ts +230 -0
  376. package/src/utils/setupFiles.ts +1238 -0
  377. package/src/utils/typeGuards.ts +65 -0
  378. package/src/utils/validationRules.ts +88 -0
  379. package/src/utils/versionDetection.ts +292 -0
  380. package/src/utils/yamlConverter.ts +542 -0
  381. package/src/utils/yamlLoader.ts +371 -0
  382. package/src/utilsController.ts +1203 -0
  383. package/tests/README.md +497 -0
  384. package/tests/adapters/AdapterFactory.test.ts +277 -0
  385. package/tests/integration/syncOperations.test.ts +463 -0
  386. package/tests/jest.config.js +25 -0
  387. package/tests/migration/configMigration.test.ts +546 -0
  388. package/tests/setup.ts +62 -0
  389. package/tests/testUtils.ts +340 -0
  390. package/tests/utils/loadConfigs.test.ts +350 -0
  391. package/tests/validation/configValidation.test.ts +412 -0
  392. package/tsconfig.json +44 -0
@@ -0,0 +1,682 @@
1
+ import { Databases, ID, Permission, Query, } from "node-appwrite";
2
+ import { getAdapterFromConfig } from "../utils/getClientFromConfig.js";
3
+ import { nameToIdMapping, processQueue, queuedOperations, clearProcessingState, isCollectionProcessed, markCollectionProcessed } from "../shared/operationQueue.js";
4
+ import { logger } from "../shared/logging.js";
5
+ // Legacy attribute/index helpers removed in favor of unified adapter path
6
+ import { SchemaGenerator } from "../shared/schemaGenerator.js";
7
+ import { isNull, isUndefined, isNil, isPlainObject, isString, } from "es-toolkit";
8
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
9
+ import { MessageFormatter } from "../shared/messageFormatter.js";
10
+ import { isLegacyDatabases } from "../utils/typeGuards.js";
11
+ import { mapToCreateAttributeParams, mapToUpdateAttributeParams } from "../shared/attributeMapper.js";
12
+ import { diffTableColumns, isIndexEqualToIndex, diffColumnsDetailed, executeColumnOperations } from "./tableOperations.js";
13
+ // Re-export wipe operations
14
+ export { wipeDatabase, wipeCollection, wipeAllTables, wipeTableRows, } from "./wipeOperations.js";
15
+ // Re-export transfer operations
16
+ export { transferDocumentsBetweenDbsLocalToLocal, transferDocumentsBetweenDbsLocalToRemote, } from "./transferOperations.js";
17
+ export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
18
+ const collection = await (isLegacyDatabases(db) ?
19
+ db.getCollection(dbId, targetCollectionId) :
20
+ db.getTable({ databaseId: dbId, tableId: targetCollectionId }));
21
+ const attributes = collection.attributes;
22
+ let arrayTypeAttributes = attributes
23
+ .filter((attribute) => attribute.array === true)
24
+ .map((attribute) => attribute.key);
25
+ const isJsonString = (str) => {
26
+ try {
27
+ const json = JSON.parse(str);
28
+ return typeof json === "object" && json !== null;
29
+ }
30
+ catch (e) {
31
+ return false;
32
+ }
33
+ };
34
+ // Convert object to entries and filter
35
+ const validEntries = Object.entries(toCreateObject).filter(([key, value]) => !arrayTypeAttributes.includes(key) &&
36
+ !key.startsWith("$") &&
37
+ !isNull(value) &&
38
+ !isUndefined(value) &&
39
+ !isNil(value) &&
40
+ !isPlainObject(value) &&
41
+ !Array.isArray(value) &&
42
+ !(isString(value) && isJsonString(value)) &&
43
+ (isString(value) ? value.length < 4096 && value.length > 0 : true));
44
+ // Map and filter valid entries
45
+ const validMappedEntries = validEntries
46
+ .map(([key, value]) => [
47
+ key,
48
+ isString(value) || typeof value === "number" || typeof value === "boolean"
49
+ ? value
50
+ : null,
51
+ ])
52
+ .filter(([key, value]) => !isNull(value) && isString(key))
53
+ .slice(0, 25);
54
+ // Convert to Query parameters
55
+ const validQueryParams = validMappedEntries.map(([key, value]) => Query.equal(key, value));
56
+ // Execute the query with the validated and prepared parameters
57
+ const result = await (isLegacyDatabases(db) ?
58
+ db.listDocuments(dbId, targetCollectionId, validQueryParams) :
59
+ db.listRows({ databaseId: dbId, tableId: targetCollectionId, queries: validQueryParams }));
60
+ const items = isLegacyDatabases(db) ? result.documents : (result.rows || result.documents);
61
+ return items?.[0] || null;
62
+ };
63
+ export const checkForCollection = async (db, dbId, collection) => {
64
+ try {
65
+ MessageFormatter.progress(`Checking for collection with name: ${collection.name}`, { prefix: "Collections" });
66
+ const response = await tryAwaitWithRetry(async () => isLegacyDatabases(db) ?
67
+ await db.listCollections(dbId, [Query.equal("name", collection.name)]) :
68
+ await db.listTables({ databaseId: dbId, queries: [Query.equal("name", collection.name)] }));
69
+ const items = isLegacyDatabases(db) ? response.collections : (response.tables || response.collections);
70
+ if (items && items.length > 0) {
71
+ MessageFormatter.info(`Collection found: ${items[0].$id}`, { prefix: "Collections" });
72
+ // Return remote collection for update operations (don't merge local config over it)
73
+ return items[0];
74
+ }
75
+ else {
76
+ MessageFormatter.info(`No collection found with name: ${collection.name}`, { prefix: "Collections" });
77
+ return null;
78
+ }
79
+ }
80
+ catch (error) {
81
+ const errorMessage = error instanceof Error ? error.message : String(error);
82
+ MessageFormatter.error(`Error checking for collection: ${collection.name}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Collections" });
83
+ logger.error('Collection check failed', {
84
+ collectionName: collection.name,
85
+ dbId,
86
+ error: errorMessage,
87
+ operation: 'checkForCollection'
88
+ });
89
+ return null;
90
+ }
91
+ };
92
+ // Helper function to fetch and cache collection by name
93
+ export const fetchAndCacheCollectionByName = async (db, dbId, collectionName) => {
94
+ if (nameToIdMapping.has(collectionName)) {
95
+ const collectionId = nameToIdMapping.get(collectionName);
96
+ MessageFormatter.debug(`Collection found in cache: ${collectionId}`, undefined, { prefix: "Collections" });
97
+ return await tryAwaitWithRetry(async () => isLegacyDatabases(db) ?
98
+ await db.getCollection(dbId, collectionId) :
99
+ await db.getTable({ databaseId: dbId, tableId: collectionId }));
100
+ }
101
+ else {
102
+ MessageFormatter.progress(`Fetching collection by name: ${collectionName}`, { prefix: "Collections" });
103
+ const collectionsPulled = await tryAwaitWithRetry(async () => isLegacyDatabases(db) ?
104
+ await db.listCollections(dbId, [Query.equal("name", collectionName)]) :
105
+ await db.listTables({ databaseId: dbId, queries: [Query.equal("name", collectionName)] }));
106
+ const items = isLegacyDatabases(db) ? collectionsPulled.collections : (collectionsPulled.tables || collectionsPulled.collections);
107
+ if ((collectionsPulled.total || items?.length) > 0) {
108
+ const collection = items[0];
109
+ MessageFormatter.info(`Collection found: ${collection.$id}`, { prefix: "Collections" });
110
+ nameToIdMapping.set(collectionName, collection.$id);
111
+ return collection;
112
+ }
113
+ else {
114
+ MessageFormatter.warning(`Collection not found by name: ${collectionName}`, { prefix: "Collections" });
115
+ return undefined;
116
+ }
117
+ }
118
+ };
119
+ export const generateSchemas = async (config, appwriteFolderPath) => {
120
+ const schemaGenerator = new SchemaGenerator(config, appwriteFolderPath);
121
+ await schemaGenerator.generateSchemas();
122
+ };
123
+ export const createOrUpdateCollections = async (database, databaseId, config, deletedCollections, selectedCollections = []) => {
124
+ // Clear processing state at the start of a new operation
125
+ clearProcessingState();
126
+ // Always use adapter path (LegacyAdapter translates when pre-1.8)
127
+ const { adapter } = await getAdapterFromConfig(config);
128
+ await createOrUpdateCollectionsViaAdapter(adapter, databaseId, config, deletedCollections, selectedCollections);
129
+ };
130
+ // New: Adapter-based implementation for TablesDB with state management
131
+ export const createOrUpdateCollectionsViaAdapter = async (adapter, databaseId, config, deletedCollections, selectedCollections = []) => {
132
+ const collectionsToProcess = selectedCollections.length > 0 ? selectedCollections : (config.collections || []);
133
+ if (!collectionsToProcess || collectionsToProcess.length === 0)
134
+ return;
135
+ const usedIds = new Set();
136
+ MessageFormatter.info(`Processing ${collectionsToProcess.length} tables via adapter with intelligent state management`, { prefix: "Tables" });
137
+ // Helpers for attribute operations through adapter
138
+ const createAttr = async (tableId, attr) => {
139
+ const params = mapToCreateAttributeParams(attr, { databaseId, tableId });
140
+ await adapter.createAttribute(params);
141
+ await delay(150);
142
+ };
143
+ const updateAttr = async (tableId, attr) => {
144
+ const params = mapToUpdateAttributeParams(attr, { databaseId, tableId });
145
+ await adapter.updateAttribute(params);
146
+ await delay(150);
147
+ };
148
+ // Local queue for unresolved relationships
149
+ const relQueue = [];
150
+ for (const collection of collectionsToProcess) {
151
+ const { attributes, indexes, ...collectionData } = collection;
152
+ // Check if this table has already been processed in this session (per database)
153
+ if (collectionData.$id && isCollectionProcessed(collectionData.$id, databaseId)) {
154
+ MessageFormatter.info(`Table '${collectionData.name}' already processed, skipping`, { prefix: "Tables" });
155
+ continue;
156
+ }
157
+ // Prepare permissions as strings (reuse Permission helper)
158
+ const permissions = [];
159
+ if (collection.$permissions && collection.$permissions.length > 0) {
160
+ for (const p of collection.$permissions) {
161
+ if (typeof p === 'string')
162
+ permissions.push(p);
163
+ else {
164
+ switch (p.permission) {
165
+ case 'read':
166
+ permissions.push(Permission.read(p.target));
167
+ break;
168
+ case 'create':
169
+ permissions.push(Permission.create(p.target));
170
+ break;
171
+ case 'update':
172
+ permissions.push(Permission.update(p.target));
173
+ break;
174
+ case 'delete':
175
+ permissions.push(Permission.delete(p.target));
176
+ break;
177
+ case 'write':
178
+ permissions.push(Permission.write(p.target));
179
+ break;
180
+ default: break;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ // Find existing table — prefer lookup by ID (if provided), then by name
186
+ let table;
187
+ let tableId;
188
+ // 1) Try by explicit $id first (handles rename scenarios)
189
+ if (collectionData.$id) {
190
+ try {
191
+ const byId = await adapter.getTable({ databaseId, tableId: collectionData.$id });
192
+ table = byId.data || byId.tables?.[0];
193
+ if (table?.$id) {
194
+ MessageFormatter.info(`Found existing table by ID: ${table.$id}`, { prefix: 'Tables' });
195
+ }
196
+ }
197
+ catch {
198
+ // Not found by ID; fall back to name lookup
199
+ }
200
+ }
201
+ // 2) If not found by ID, try by name
202
+ if (!table) {
203
+ const list = await adapter.listTables({ databaseId, queries: [Query.equal('name', collectionData.name)] });
204
+ const items = list.tables || [];
205
+ table = items[0];
206
+ if (table?.$id) {
207
+ // If local has $id that differs from remote, prefer remote (IDs are immutable)
208
+ if (collectionData.$id && collectionData.$id !== table.$id) {
209
+ MessageFormatter.warning(`Config $id '${collectionData.$id}' differs from existing table ID '${table.$id}'. Using existing table.`, { prefix: 'Tables' });
210
+ }
211
+ }
212
+ }
213
+ if (!table) {
214
+ // Determine ID (prefer provided $id or re-use deleted one)
215
+ let foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") === collectionData.name.toLowerCase().trim().replace(" ", ""));
216
+ if (collectionData.$id)
217
+ tableId = collectionData.$id;
218
+ else if (foundColl && !usedIds.has(foundColl.collectionId))
219
+ tableId = foundColl.collectionId;
220
+ else
221
+ tableId = ID.unique();
222
+ usedIds.add(tableId);
223
+ const res = await adapter.createTable({
224
+ databaseId,
225
+ id: tableId,
226
+ name: collectionData.name,
227
+ permissions,
228
+ documentSecurity: !!collectionData.documentSecurity,
229
+ enabled: collectionData.enabled !== false
230
+ });
231
+ table = res.data || res;
232
+ nameToIdMapping.set(collectionData.name, tableId);
233
+ }
234
+ else {
235
+ tableId = table.$id;
236
+ await adapter.updateTable({
237
+ databaseId,
238
+ id: tableId,
239
+ name: collectionData.name,
240
+ permissions,
241
+ documentSecurity: !!collectionData.documentSecurity,
242
+ enabled: collectionData.enabled !== false
243
+ });
244
+ // Cache the existing table ID
245
+ nameToIdMapping.set(collectionData.name, tableId);
246
+ }
247
+ // Add small delay after table create/update
248
+ await delay(250);
249
+ // Create/Update attributes: non-relationship first using enhanced planning
250
+ const nonRel = (attributes || []).filter((a) => a.type !== 'relationship');
251
+ if (nonRel.length > 0) {
252
+ // Fetch existing columns once
253
+ const tableInfo = await adapter.getTable({ databaseId, tableId });
254
+ const existingCols = tableInfo.data?.columns || tableInfo.data?.attributes || [];
255
+ // Plan with icons
256
+ const plan = diffColumnsDetailed(nonRel, existingCols);
257
+ const plus = plan.toCreate.map((a) => a.key);
258
+ const plusminus = plan.toUpdate.map((u) => u.attribute.key);
259
+ const minus = plan.toRecreate.map((r) => r.newAttribute.key);
260
+ const skip = plan.unchanged;
261
+ // Compute deletions (remote extras not present locally)
262
+ const desiredKeysForDelete = new Set((attributes || []).map((a) => a.key));
263
+ const extraRemoteKeys = (existingCols || [])
264
+ .map((c) => c?.key)
265
+ .filter((k) => !!k && !desiredKeysForDelete.has(k));
266
+ const parts = [];
267
+ if (plus.length)
268
+ parts.push(`➕ ${plus.length} (${plus.join(', ')})`);
269
+ if (plusminus.length)
270
+ parts.push(`🔧 ${plusminus.length} (${plusminus.join(', ')})`);
271
+ if (minus.length)
272
+ parts.push(`♻️ ${minus.length} (${minus.join(', ')})`);
273
+ if (skip.length)
274
+ parts.push(`⏭️ ${skip.length}`);
275
+ parts.push(`🗑️ ${extraRemoteKeys.length}${extraRemoteKeys.length ? ` (${extraRemoteKeys.join(', ')})` : ''}`);
276
+ MessageFormatter.info(`Plan → ${parts.join(' | ') || 'no changes'}`, { prefix: 'Attributes' });
277
+ // Execute
278
+ const colResults = await executeColumnOperations(adapter, databaseId, tableId, plan);
279
+ if (colResults.success.length > 0) {
280
+ MessageFormatter.success(`Processed ${colResults.success.length} ops`, { prefix: 'Attributes' });
281
+ }
282
+ if (colResults.errors.length > 0) {
283
+ MessageFormatter.error(`${colResults.errors.length} attribute operations failed:`, undefined, { prefix: 'Attributes' });
284
+ for (const err of colResults.errors) {
285
+ MessageFormatter.error(` ${err.column}: ${err.error}`, undefined, { prefix: 'Attributes' });
286
+ }
287
+ }
288
+ MessageFormatter.info(`Summary → ➕ ${plan.toCreate.length} | 🔧 ${plan.toUpdate.length} | ♻️ ${plan.toRecreate.length} | ⏭️ ${plan.unchanged.length}`, { prefix: 'Attributes' });
289
+ }
290
+ // Relationship attributes — resolve relatedCollection to ID, then diff and create/update
291
+ const rels = (attributes || []).filter((a) => a.type === 'relationship');
292
+ if (rels.length > 0) {
293
+ for (const attr of rels) {
294
+ const relNameOrId = attr.relatedCollection;
295
+ if (!relNameOrId)
296
+ continue;
297
+ let relId = nameToIdMapping.get(relNameOrId) || relNameOrId;
298
+ if (!nameToIdMapping.has(relNameOrId)) {
299
+ try {
300
+ const relList = await adapter.listTables({ databaseId, queries: [Query.equal('name', relNameOrId)] });
301
+ const relItems = relList.tables || [];
302
+ if (relItems[0]?.$id) {
303
+ relId = relItems[0].$id;
304
+ nameToIdMapping.set(relNameOrId, relId);
305
+ }
306
+ }
307
+ catch { }
308
+ }
309
+ if (relId && typeof relId === 'string')
310
+ attr.relatedCollection = relId;
311
+ }
312
+ const tableInfo2 = await adapter.getTable({ databaseId, tableId });
313
+ const existingCols2 = tableInfo2.data?.columns || tableInfo2.data?.attributes || [];
314
+ const { toCreate: relCreate, toUpdate: relUpdate, unchanged: relUnchanged } = diffTableColumns(existingCols2, rels);
315
+ // Relationship plan with icons
316
+ {
317
+ const parts = [];
318
+ if (relCreate.length)
319
+ parts.push(`➕ ${relCreate.length} (${relCreate.map((a) => a.key).join(', ')})`);
320
+ if (relUpdate.length)
321
+ parts.push(`🔧 ${relUpdate.length} (${relUpdate.map((a) => a.key).join(', ')})`);
322
+ if (relUnchanged.length)
323
+ parts.push(`⏭️ ${relUnchanged.length}`);
324
+ MessageFormatter.info(`Plan → ${parts.join(' | ') || 'no changes'}`, { prefix: 'Relationships' });
325
+ }
326
+ for (const attr of relUpdate) {
327
+ try {
328
+ await updateAttr(tableId, attr);
329
+ }
330
+ catch (e) {
331
+ MessageFormatter.error(`Failed to update relationship ${attr.key}`, e instanceof Error ? e : new Error(String(e)), { prefix: 'Attributes' });
332
+ }
333
+ }
334
+ for (const attr of relCreate) {
335
+ try {
336
+ await createAttr(tableId, attr);
337
+ }
338
+ catch (e) {
339
+ MessageFormatter.error(`Failed to create relationship ${attr.key}`, e instanceof Error ? e : new Error(String(e)), { prefix: 'Attributes' });
340
+ }
341
+ }
342
+ }
343
+ // Wait for all attributes to become available before creating indexes
344
+ const allAttrKeys = [
345
+ ...nonRel.map((a) => a.key),
346
+ ...rels.filter((a) => a.relatedCollection).map((a) => a.key)
347
+ ];
348
+ if (allAttrKeys.length > 0) {
349
+ for (const attrKey of allAttrKeys) {
350
+ const maxWait = 60000; // 60 seconds
351
+ const startTime = Date.now();
352
+ let lastStatus = '';
353
+ while (Date.now() - startTime < maxWait) {
354
+ try {
355
+ const tableData = await adapter.getTable({ databaseId, tableId });
356
+ const attrs = tableData.data?.columns || tableData.data?.attributes || [];
357
+ const attr = attrs.find((a) => a.key === attrKey);
358
+ if (attr) {
359
+ if (attr.status === 'available') {
360
+ break; // Attribute is ready
361
+ }
362
+ if (attr.status === 'failed' || attr.status === 'stuck') {
363
+ throw new Error(`Attribute ${attrKey} failed to create: ${attr.error || 'unknown error'}`);
364
+ }
365
+ // Still processing, continue waiting
366
+ lastStatus = attr.status;
367
+ }
368
+ await delay(2000); // Check every 2 seconds
369
+ }
370
+ catch (e) {
371
+ // If we can't check status, assume it's processing and continue
372
+ await delay(2000);
373
+ }
374
+ }
375
+ // Timeout check
376
+ if (Date.now() - startTime >= maxWait) {
377
+ MessageFormatter.warning(`Attribute ${attrKey} did not become available within ${maxWait / 1000}s (last status: ${lastStatus}). Proceeding anyway.`, { prefix: 'Attributes' });
378
+ }
379
+ }
380
+ }
381
+ // Prefer local config indexes, but fall back to collection's own indexes if no local config exists (TablesDB path)
382
+ const localTableConfig = config.collections?.find(c => c.name === collectionData.name || c.$id === collectionData.$id);
383
+ const idxs = (localTableConfig?.indexes ?? indexes ?? []);
384
+ // Compare with existing indexes and create/update accordingly with status checks
385
+ try {
386
+ const existingIdxRes = await adapter.listIndexes({ databaseId, tableId });
387
+ const existingIdx = existingIdxRes.data || existingIdxRes.indexes || [];
388
+ MessageFormatter.debug(`Existing index keys: ${existingIdx.map((i) => i.key).join(', ')}`, undefined, { prefix: 'Indexes' });
389
+ // Show a concise plan with icons before executing
390
+ const idxPlanPlus = [];
391
+ const idxPlanPlusMinus = [];
392
+ const idxPlanSkip = [];
393
+ for (const idx of idxs) {
394
+ const found = existingIdx.find((i) => i.key === idx.key);
395
+ if (found) {
396
+ if (isIndexEqualToIndex(found, idx))
397
+ idxPlanSkip.push(idx.key);
398
+ else
399
+ idxPlanPlusMinus.push(idx.key);
400
+ }
401
+ else
402
+ idxPlanPlus.push(idx.key);
403
+ }
404
+ const planParts = [];
405
+ if (idxPlanPlus.length)
406
+ planParts.push(`➕ ${idxPlanPlus.length} (${idxPlanPlus.join(', ')})`);
407
+ if (idxPlanPlusMinus.length)
408
+ planParts.push(`🔧 ${idxPlanPlusMinus.length} (${idxPlanPlusMinus.join(', ')})`);
409
+ if (idxPlanSkip.length)
410
+ planParts.push(`⏭️ ${idxPlanSkip.length}`);
411
+ MessageFormatter.info(`Plan → ${planParts.join(' | ') || 'no changes'}`, { prefix: 'Indexes' });
412
+ const created = [];
413
+ const updated = [];
414
+ const skipped = [];
415
+ for (const idx of idxs) {
416
+ const found = existingIdx.find((i) => i.key === idx.key);
417
+ if (found) {
418
+ if (isIndexEqualToIndex(found, idx)) {
419
+ MessageFormatter.info(`Index ${idx.key} unchanged`, { prefix: 'Indexes' });
420
+ skipped.push(idx.key);
421
+ }
422
+ else {
423
+ try {
424
+ await adapter.deleteIndex({ databaseId, tableId, key: idx.key });
425
+ await delay(100);
426
+ }
427
+ catch { }
428
+ try {
429
+ await adapter.createIndex({ databaseId, tableId, key: idx.key, type: idx.type, attributes: idx.attributes, orders: idx.orders || [] });
430
+ updated.push(idx.key);
431
+ }
432
+ catch (e) {
433
+ const msg = (e?.message || '').toString().toLowerCase();
434
+ if (msg.includes('already exists')) {
435
+ MessageFormatter.info(`Index ${idx.key} already exists after delete attempt, skipping`, { prefix: 'Indexes' });
436
+ skipped.push(idx.key);
437
+ }
438
+ else {
439
+ throw e;
440
+ }
441
+ }
442
+ }
443
+ }
444
+ else {
445
+ try {
446
+ await adapter.createIndex({ databaseId, tableId, key: idx.key, type: idx.type, attributes: idx.attributes, orders: idx.orders || [] });
447
+ created.push(idx.key);
448
+ }
449
+ catch (e) {
450
+ const msg = (e?.message || '').toString().toLowerCase();
451
+ if (msg.includes('already exists')) {
452
+ MessageFormatter.info(`Index ${idx.key} already exists (create), skipping`, { prefix: 'Indexes' });
453
+ skipped.push(idx.key);
454
+ }
455
+ else {
456
+ throw e;
457
+ }
458
+ }
459
+ }
460
+ // Wait for index availability
461
+ const maxWait = 60000;
462
+ const start = Date.now();
463
+ let lastStatus = '';
464
+ while (Date.now() - start < maxWait) {
465
+ try {
466
+ const li = await adapter.listIndexes({ databaseId, tableId });
467
+ const list = li.data || li.indexes || [];
468
+ const cur = list.find((i) => i.key === idx.key);
469
+ if (cur) {
470
+ if (cur.status === 'available')
471
+ break;
472
+ if (cur.status === 'failed' || cur.status === 'stuck') {
473
+ throw new Error(cur.error || `Index ${idx.key} failed`);
474
+ }
475
+ lastStatus = cur.status;
476
+ }
477
+ await delay(2000);
478
+ }
479
+ catch {
480
+ await delay(2000);
481
+ }
482
+ }
483
+ await delay(150);
484
+ }
485
+ MessageFormatter.info(`Summary → ➕ ${created.length} | 🔧 ${updated.length} | ⏭️ ${skipped.length}`, { prefix: 'Indexes' });
486
+ }
487
+ catch (e) {
488
+ MessageFormatter.error(`Failed to list/create indexes`, e instanceof Error ? e : new Error(String(e)), { prefix: 'Indexes' });
489
+ }
490
+ // Deletions for indexes: remove remote indexes not declared in YAML/config
491
+ try {
492
+ const desiredIndexKeys = new Set((indexes || []).map((i) => i.key));
493
+ const idxRes = await adapter.listIndexes({ databaseId, tableId });
494
+ const existingIdx = idxRes.data || idxRes.indexes || [];
495
+ const extraIdx = existingIdx
496
+ .filter((i) => i?.key && !desiredIndexKeys.has(i.key))
497
+ .map((i) => i.key);
498
+ if (extraIdx.length > 0) {
499
+ MessageFormatter.info(`Plan → 🗑️ ${extraIdx.length} indexes (${extraIdx.join(', ')})`, { prefix: 'Indexes' });
500
+ const deleted = [];
501
+ const errors = [];
502
+ for (const key of extraIdx) {
503
+ try {
504
+ await adapter.deleteIndex({ databaseId, tableId, key });
505
+ // Optionally wait for index to disappear
506
+ const start = Date.now();
507
+ const maxWait = 30000;
508
+ while (Date.now() - start < maxWait) {
509
+ try {
510
+ const li = await adapter.listIndexes({ databaseId, tableId });
511
+ const list = li.data || li.indexes || [];
512
+ if (!list.find((ix) => ix.key === key))
513
+ break;
514
+ }
515
+ catch { }
516
+ await delay(1000);
517
+ }
518
+ deleted.push(key);
519
+ }
520
+ catch (e) {
521
+ errors.push({ key, error: e?.message || String(e) });
522
+ }
523
+ }
524
+ if (deleted.length) {
525
+ MessageFormatter.success(`Deleted ${deleted.length} indexes: ${deleted.join(', ')}`, { prefix: 'Indexes' });
526
+ }
527
+ if (errors.length) {
528
+ MessageFormatter.error(`${errors.length} index deletions failed`, undefined, { prefix: 'Indexes' });
529
+ errors.forEach(er => MessageFormatter.error(` ${er.key}: ${er.error}`, undefined, { prefix: 'Indexes' }));
530
+ }
531
+ }
532
+ else {
533
+ MessageFormatter.info(`Plan → 🗑️ 0 indexes`, { prefix: 'Indexes' });
534
+ }
535
+ }
536
+ catch (e) {
537
+ MessageFormatter.warning(`Could not evaluate index deletions: ${e?.message || e}`, { prefix: 'Indexes' });
538
+ }
539
+ // Deletions: remove columns/attributes that are present remotely but not in desired config
540
+ try {
541
+ const desiredKeys = new Set((attributes || []).map((a) => a.key));
542
+ const tableInfo3 = await adapter.getTable({ databaseId, tableId });
543
+ const existingCols3 = tableInfo3.data?.columns || tableInfo3.data?.attributes || [];
544
+ const toDelete = existingCols3
545
+ .filter((col) => col?.key && !desiredKeys.has(col.key))
546
+ .map((col) => col.key);
547
+ if (toDelete.length > 0) {
548
+ MessageFormatter.info(`Plan → 🗑️ ${toDelete.length} (${toDelete.join(', ')})`, { prefix: 'Attributes' });
549
+ const deleted = [];
550
+ const errors = [];
551
+ for (const key of toDelete) {
552
+ try {
553
+ // Drop any indexes that reference this attribute to avoid server errors
554
+ try {
555
+ const idxRes = await adapter.listIndexes({ databaseId, tableId });
556
+ const ilist = idxRes.data || idxRes.indexes || [];
557
+ for (const idx of ilist) {
558
+ const attrs = Array.isArray(idx.attributes) ? idx.attributes : [];
559
+ if (attrs.includes(key)) {
560
+ MessageFormatter.info(`🗑️ Deleting index '${idx.key}' referencing '${key}'`, { prefix: 'Indexes' });
561
+ await adapter.deleteIndex({ databaseId, tableId, key: idx.key });
562
+ await delay(500);
563
+ }
564
+ }
565
+ }
566
+ catch { }
567
+ await adapter.deleteAttribute({ databaseId, tableId, key });
568
+ // Wait briefly for deletion to settle
569
+ const start = Date.now();
570
+ const maxWaitMs = 60000;
571
+ while (Date.now() - start < maxWaitMs) {
572
+ try {
573
+ const tinfo = await adapter.getTable({ databaseId, tableId });
574
+ const cols = tinfo.data?.columns || tinfo.data?.attributes || [];
575
+ const found = cols.find((c) => c.key === key);
576
+ if (!found)
577
+ break;
578
+ if (found.status && found.status !== 'deleting')
579
+ break;
580
+ }
581
+ catch { }
582
+ await delay(1000);
583
+ }
584
+ deleted.push(key);
585
+ }
586
+ catch (e) {
587
+ errors.push({ key, error: e?.message || String(e) });
588
+ }
589
+ }
590
+ if (deleted.length) {
591
+ MessageFormatter.success(`Deleted ${deleted.length} attributes: ${deleted.join(', ')}`, { prefix: 'Attributes' });
592
+ }
593
+ if (errors.length) {
594
+ MessageFormatter.error(`${errors.length} deletions failed`, undefined, { prefix: 'Attributes' });
595
+ errors.forEach(er => MessageFormatter.error(` ${er.key}: ${er.error}`, undefined, { prefix: 'Attributes' }));
596
+ }
597
+ }
598
+ else {
599
+ MessageFormatter.info(`Plan → 🗑️ 0`, { prefix: 'Attributes' });
600
+ }
601
+ }
602
+ catch (e) {
603
+ MessageFormatter.warning(`Could not evaluate deletions: ${e?.message || e}`, { prefix: 'Attributes' });
604
+ }
605
+ // Mark this table as fully processed for this database to prevent re-processing in the same DB only
606
+ markCollectionProcessed(tableId, collectionData.name, databaseId);
607
+ }
608
+ // Process queued relationships once mapping likely populated
609
+ if (relQueue.length > 0) {
610
+ MessageFormatter.info(`🔧 Processing ${relQueue.length} queued relationship attributes for tables`, { prefix: "Tables" });
611
+ for (const { tableId, attr } of relQueue) {
612
+ const relNameOrId = attr.relatedCollection;
613
+ if (!relNameOrId)
614
+ continue;
615
+ const relId = nameToIdMapping.get(relNameOrId) || relNameOrId;
616
+ if (relId) {
617
+ attr.relatedCollection = relId;
618
+ try {
619
+ await adapter.createAttribute({
620
+ databaseId,
621
+ tableId,
622
+ key: attr.key,
623
+ type: attr.type,
624
+ size: attr.size,
625
+ required: !!attr.required,
626
+ default: attr.xdefault,
627
+ array: !!attr.array,
628
+ min: attr.min,
629
+ max: attr.max,
630
+ elements: attr.elements,
631
+ relatedCollection: relId,
632
+ relationType: attr.relationType,
633
+ twoWay: attr.twoWay,
634
+ twoWayKey: attr.twoWayKey,
635
+ onDelete: attr.onDelete,
636
+ side: attr.side
637
+ });
638
+ await delay(150);
639
+ MessageFormatter.info(`✅ Successfully processed queued relationship: ${attr.key}`, { prefix: "Tables" });
640
+ }
641
+ catch (e) {
642
+ MessageFormatter.error(`Failed queued relationship ${attr.key}`, e instanceof Error ? e : new Error(String(e)), { prefix: 'Attributes' });
643
+ }
644
+ }
645
+ else {
646
+ MessageFormatter.warning(`Could not resolve relationship ${attr.key} -> ${relNameOrId}`, { prefix: "Tables" });
647
+ }
648
+ }
649
+ }
650
+ };
651
+ export const generateMockData = async (database, databaseId, configCollections) => {
652
+ for (const { collection, mockFunction } of configCollections) {
653
+ if (mockFunction) {
654
+ MessageFormatter.progress(`Generating mock data for collection: ${collection.name}`, { prefix: "Mock Data" });
655
+ const mockData = mockFunction();
656
+ for (const data of mockData) {
657
+ await database.createDocument(databaseId, collection.$id, ID.unique(), data);
658
+ }
659
+ }
660
+ }
661
+ };
662
+ export const fetchAllCollections = async (dbId, database) => {
663
+ MessageFormatter.progress(`Fetching all collections for database ID: ${dbId}`, { prefix: "Collections" });
664
+ let collections = [];
665
+ let moreCollections = true;
666
+ let lastCollectionId;
667
+ while (moreCollections) {
668
+ const queries = [Query.limit(500)];
669
+ if (lastCollectionId) {
670
+ queries.push(Query.cursorAfter(lastCollectionId));
671
+ }
672
+ const response = await tryAwaitWithRetry(async () => await database.listCollections(dbId, queries));
673
+ collections = collections.concat(response.collections);
674
+ moreCollections = response.collections.length === 500;
675
+ if (moreCollections) {
676
+ lastCollectionId =
677
+ response.collections[response.collections.length - 1].$id;
678
+ }
679
+ }
680
+ MessageFormatter.success(`Fetched a total of ${collections.length} collections`, { prefix: "Collections" });
681
+ return collections;
682
+ };