appwrite-utils-cli 1.5.2 → 1.6.1

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 (233) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +251 -29
  3. package/dist/adapters/AdapterFactory.d.ts +10 -3
  4. package/dist/adapters/AdapterFactory.js +213 -17
  5. package/dist/adapters/TablesDBAdapter.js +60 -17
  6. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  7. package/dist/backups/operations/bucketBackup.js +197 -0
  8. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  9. package/dist/backups/operations/collectionBackup.js +201 -0
  10. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  11. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  12. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  13. package/dist/backups/schemas/bucketManifest.js +33 -0
  14. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  15. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  16. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  17. package/dist/backups/tracking/centralizedTracking.js +274 -0
  18. package/dist/cli/commands/configCommands.d.ts +8 -0
  19. package/dist/cli/commands/configCommands.js +160 -0
  20. package/dist/cli/commands/databaseCommands.d.ts +13 -0
  21. package/dist/cli/commands/databaseCommands.js +479 -0
  22. package/dist/cli/commands/functionCommands.d.ts +7 -0
  23. package/dist/cli/commands/functionCommands.js +289 -0
  24. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  25. package/dist/cli/commands/schemaCommands.js +134 -0
  26. package/dist/cli/commands/transferCommands.d.ts +5 -0
  27. package/dist/cli/commands/transferCommands.js +384 -0
  28. package/dist/collections/attributes.d.ts +5 -4
  29. package/dist/collections/attributes.js +539 -246
  30. package/dist/collections/indexes.js +39 -37
  31. package/dist/collections/methods.d.ts +2 -16
  32. package/dist/collections/methods.js +90 -538
  33. package/dist/collections/transferOperations.d.ts +7 -0
  34. package/dist/collections/transferOperations.js +331 -0
  35. package/dist/collections/wipeOperations.d.ts +16 -0
  36. package/dist/collections/wipeOperations.js +328 -0
  37. package/dist/config/configMigration.d.ts +87 -0
  38. package/dist/config/configMigration.js +390 -0
  39. package/dist/config/configValidation.d.ts +66 -0
  40. package/dist/config/configValidation.js +358 -0
  41. package/dist/config/yamlConfig.d.ts +455 -1
  42. package/dist/config/yamlConfig.js +145 -52
  43. package/dist/databases/methods.js +3 -2
  44. package/dist/databases/setup.d.ts +1 -2
  45. package/dist/databases/setup.js +9 -87
  46. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  47. package/dist/examples/yamlTerminologyExample.js +269 -0
  48. package/dist/functions/deployments.js +11 -10
  49. package/dist/functions/methods.d.ts +1 -1
  50. package/dist/functions/methods.js +5 -4
  51. package/dist/init.js +9 -9
  52. package/dist/interactiveCLI.d.ts +8 -17
  53. package/dist/interactiveCLI.js +209 -1172
  54. package/dist/main.js +364 -21
  55. package/dist/migrations/afterImportActions.js +22 -30
  56. package/dist/migrations/appwriteToX.js +71 -25
  57. package/dist/migrations/dataLoader.js +35 -26
  58. package/dist/migrations/importController.js +29 -30
  59. package/dist/migrations/relationships.js +13 -12
  60. package/dist/migrations/services/ImportOrchestrator.js +16 -19
  61. package/dist/migrations/transfer.js +46 -46
  62. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
  63. package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
  64. package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
  65. package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
  66. package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
  67. package/dist/migrations/yaml/generateImportSchemas.js +736 -7
  68. package/dist/schemas/authUser.d.ts +1 -1
  69. package/dist/setupController.js +3 -2
  70. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  71. package/dist/shared/backupMetadataSchema.js +38 -0
  72. package/dist/shared/backupTracking.d.ts +18 -0
  73. package/dist/shared/backupTracking.js +176 -0
  74. package/dist/shared/confirmationDialogs.js +15 -15
  75. package/dist/shared/errorUtils.d.ts +54 -0
  76. package/dist/shared/errorUtils.js +95 -0
  77. package/dist/shared/functionManager.js +20 -19
  78. package/dist/shared/indexManager.js +12 -11
  79. package/dist/shared/jsonSchemaGenerator.js +10 -26
  80. package/dist/shared/logging.d.ts +51 -0
  81. package/dist/shared/logging.js +70 -0
  82. package/dist/shared/messageFormatter.d.ts +2 -0
  83. package/dist/shared/messageFormatter.js +10 -0
  84. package/dist/shared/migrationHelpers.d.ts +6 -16
  85. package/dist/shared/migrationHelpers.js +24 -21
  86. package/dist/shared/operationLogger.d.ts +8 -1
  87. package/dist/shared/operationLogger.js +11 -24
  88. package/dist/shared/operationQueue.d.ts +28 -1
  89. package/dist/shared/operationQueue.js +268 -66
  90. package/dist/shared/operationsTable.d.ts +26 -0
  91. package/dist/shared/operationsTable.js +286 -0
  92. package/dist/shared/operationsTableSchema.d.ts +48 -0
  93. package/dist/shared/operationsTableSchema.js +35 -0
  94. package/dist/shared/relationshipExtractor.d.ts +56 -0
  95. package/dist/shared/relationshipExtractor.js +138 -0
  96. package/dist/shared/schemaGenerator.d.ts +19 -1
  97. package/dist/shared/schemaGenerator.js +56 -75
  98. package/dist/storage/backupCompression.d.ts +20 -0
  99. package/dist/storage/backupCompression.js +67 -0
  100. package/dist/storage/methods.d.ts +16 -2
  101. package/dist/storage/methods.js +98 -14
  102. package/dist/users/methods.js +9 -8
  103. package/dist/utils/configDiscovery.d.ts +78 -0
  104. package/dist/utils/configDiscovery.js +430 -0
  105. package/dist/utils/directoryUtils.d.ts +22 -0
  106. package/dist/utils/directoryUtils.js +59 -0
  107. package/dist/utils/getClientFromConfig.d.ts +17 -8
  108. package/dist/utils/getClientFromConfig.js +162 -17
  109. package/dist/utils/helperFunctions.d.ts +16 -2
  110. package/dist/utils/helperFunctions.js +19 -5
  111. package/dist/utils/loadConfigs.d.ts +34 -9
  112. package/dist/utils/loadConfigs.js +236 -316
  113. package/dist/utils/pathResolvers.d.ts +53 -0
  114. package/dist/utils/pathResolvers.js +72 -0
  115. package/dist/utils/projectConfig.d.ts +119 -0
  116. package/dist/utils/projectConfig.js +171 -0
  117. package/dist/utils/retryFailedPromises.js +4 -2
  118. package/dist/utils/sessionAuth.d.ts +48 -0
  119. package/dist/utils/sessionAuth.js +164 -0
  120. package/dist/utils/sessionPreservationExample.d.ts +1666 -0
  121. package/dist/utils/sessionPreservationExample.js +101 -0
  122. package/dist/utils/setupFiles.js +301 -41
  123. package/dist/utils/typeGuards.d.ts +35 -0
  124. package/dist/utils/typeGuards.js +57 -0
  125. package/dist/utils/versionDetection.js +145 -9
  126. package/dist/utils/yamlConverter.d.ts +53 -3
  127. package/dist/utils/yamlConverter.js +232 -13
  128. package/dist/utils/yamlLoader.d.ts +70 -0
  129. package/dist/utils/yamlLoader.js +263 -0
  130. package/dist/utilsController.d.ts +36 -3
  131. package/dist/utilsController.js +186 -56
  132. package/package.json +12 -2
  133. package/src/adapters/AdapterFactory.ts +263 -35
  134. package/src/adapters/TablesDBAdapter.ts +225 -36
  135. package/src/backups/operations/bucketBackup.ts +277 -0
  136. package/src/backups/operations/collectionBackup.ts +310 -0
  137. package/src/backups/operations/comprehensiveBackup.ts +342 -0
  138. package/src/backups/schemas/bucketManifest.ts +78 -0
  139. package/src/backups/schemas/comprehensiveManifest.ts +76 -0
  140. package/src/backups/tracking/centralizedTracking.ts +352 -0
  141. package/src/cli/commands/configCommands.ts +194 -0
  142. package/src/cli/commands/databaseCommands.ts +635 -0
  143. package/src/cli/commands/functionCommands.ts +379 -0
  144. package/src/cli/commands/schemaCommands.ts +163 -0
  145. package/src/cli/commands/transferCommands.ts +457 -0
  146. package/src/collections/attributes.ts +900 -621
  147. package/src/collections/attributes.ts.backup +1555 -0
  148. package/src/collections/indexes.ts +116 -114
  149. package/src/collections/methods.ts +295 -968
  150. package/src/collections/transferOperations.ts +516 -0
  151. package/src/collections/wipeOperations.ts +501 -0
  152. package/src/config/README.md +274 -0
  153. package/src/config/configMigration.ts +575 -0
  154. package/src/config/configValidation.ts +445 -0
  155. package/src/config/yamlConfig.ts +168 -55
  156. package/src/databases/methods.ts +3 -2
  157. package/src/databases/setup.ts +11 -138
  158. package/src/examples/yamlTerminologyExample.ts +341 -0
  159. package/src/functions/deployments.ts +14 -12
  160. package/src/functions/methods.ts +11 -11
  161. package/src/functions/templates/hono-typescript/README.md +286 -0
  162. package/src/functions/templates/hono-typescript/package.json +26 -0
  163. package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  164. package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  165. package/src/functions/templates/hono-typescript/src/app.ts +180 -0
  166. package/src/functions/templates/hono-typescript/src/context.ts +103 -0
  167. package/src/functions/templates/hono-typescript/src/index.ts +54 -0
  168. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  169. package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
  170. package/src/functions/templates/typescript-node/package.json +2 -1
  171. package/src/functions/templates/typescript-node/src/context.ts +103 -0
  172. package/src/functions/templates/typescript-node/src/index.ts +18 -12
  173. package/src/functions/templates/uv/pyproject.toml +1 -0
  174. package/src/functions/templates/uv/src/context.py +125 -0
  175. package/src/functions/templates/uv/src/index.py +35 -5
  176. package/src/init.ts +9 -11
  177. package/src/interactiveCLI.ts +274 -1563
  178. package/src/main.ts +418 -24
  179. package/src/migrations/afterImportActions.ts +71 -44
  180. package/src/migrations/appwriteToX.ts +100 -34
  181. package/src/migrations/dataLoader.ts +48 -34
  182. package/src/migrations/importController.ts +44 -39
  183. package/src/migrations/relationships.ts +28 -18
  184. package/src/migrations/services/ImportOrchestrator.ts +24 -27
  185. package/src/migrations/transfer.ts +159 -121
  186. package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
  187. package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
  188. package/src/migrations/yaml/generateImportSchemas.ts +751 -12
  189. package/src/setupController.ts +3 -2
  190. package/src/shared/backupMetadataSchema.ts +93 -0
  191. package/src/shared/backupTracking.ts +211 -0
  192. package/src/shared/confirmationDialogs.ts +19 -19
  193. package/src/shared/errorUtils.ts +110 -0
  194. package/src/shared/functionManager.ts +21 -20
  195. package/src/shared/indexManager.ts +12 -11
  196. package/src/shared/jsonSchemaGenerator.ts +38 -52
  197. package/src/shared/logging.ts +75 -0
  198. package/src/shared/messageFormatter.ts +14 -1
  199. package/src/shared/migrationHelpers.ts +45 -38
  200. package/src/shared/operationLogger.ts +11 -36
  201. package/src/shared/operationQueue.ts +322 -93
  202. package/src/shared/operationsTable.ts +338 -0
  203. package/src/shared/operationsTableSchema.ts +60 -0
  204. package/src/shared/relationshipExtractor.ts +214 -0
  205. package/src/shared/schemaGenerator.ts +179 -219
  206. package/src/storage/backupCompression.ts +88 -0
  207. package/src/storage/methods.ts +131 -34
  208. package/src/users/methods.ts +11 -9
  209. package/src/utils/configDiscovery.ts +502 -0
  210. package/src/utils/directoryUtils.ts +61 -0
  211. package/src/utils/getClientFromConfig.ts +205 -22
  212. package/src/utils/helperFunctions.ts +23 -5
  213. package/src/utils/loadConfigs.ts +313 -345
  214. package/src/utils/pathResolvers.ts +81 -0
  215. package/src/utils/projectConfig.ts +299 -0
  216. package/src/utils/retryFailedPromises.ts +4 -2
  217. package/src/utils/sessionAuth.ts +230 -0
  218. package/src/utils/setupFiles.ts +322 -54
  219. package/src/utils/typeGuards.ts +65 -0
  220. package/src/utils/versionDetection.ts +218 -64
  221. package/src/utils/yamlConverter.ts +296 -13
  222. package/src/utils/yamlLoader.ts +364 -0
  223. package/src/utilsController.ts +314 -110
  224. package/tests/README.md +497 -0
  225. package/tests/adapters/AdapterFactory.test.ts +277 -0
  226. package/tests/integration/syncOperations.test.ts +463 -0
  227. package/tests/jest.config.js +25 -0
  228. package/tests/migration/configMigration.test.ts +546 -0
  229. package/tests/setup.ts +62 -0
  230. package/tests/testUtils.ts +340 -0
  231. package/tests/utils/loadConfigs.test.ts +350 -0
  232. package/tests/validation/configValidation.test.ts +412 -0
  233. package/src/utils/schemaStrings.ts +0 -517
@@ -64,7 +64,14 @@ export class TablesDBAdapter extends BaseAdapter {
64
64
 
65
65
  async createRow(params: CreateRowParams): Promise<ApiResponse> {
66
66
  try {
67
- const result = await this.tablesDB.createRow(params);
67
+ // Remap 'id' to 'rowId' for TablesDB SDK compatibility
68
+ const result = await this.tablesDB.createRow({
69
+ databaseId: params.databaseId,
70
+ tableId: params.tableId,
71
+ rowId: params.id,
72
+ data: params.data,
73
+ permissions: params.permissions
74
+ });
68
75
  return {
69
76
  data: result,
70
77
  rows: [result]
@@ -80,7 +87,13 @@ export class TablesDBAdapter extends BaseAdapter {
80
87
 
81
88
  async updateRow(params: UpdateRowParams): Promise<ApiResponse> {
82
89
  try {
83
- const result = await this.tablesDB.updateRow(params);
90
+ const result = await this.tablesDB.updateRow(
91
+ params.databaseId,
92
+ params.tableId,
93
+ params.id,
94
+ params.data,
95
+ params.permissions || []
96
+ );
84
97
  return {
85
98
  data: result,
86
99
  rows: [result]
@@ -93,10 +106,14 @@ export class TablesDBAdapter extends BaseAdapter {
93
106
  );
94
107
  }
95
108
  }
96
-
109
+
97
110
  async deleteRow(params: DeleteRowParams): Promise<ApiResponse> {
98
111
  try {
99
- const result = await this.tablesDB.deleteRow(params);
112
+ const result = await this.tablesDB.deleteRow(
113
+ params.databaseId,
114
+ params.tableId,
115
+ params.id
116
+ );
100
117
  return { data: result };
101
118
  } catch (error) {
102
119
  throw new AdapterError(
@@ -106,10 +123,14 @@ export class TablesDBAdapter extends BaseAdapter {
106
123
  );
107
124
  }
108
125
  }
109
-
126
+
110
127
  async getRow(params: { databaseId: string; tableId: string; id: string }): Promise<ApiResponse> {
111
128
  try {
112
- const result = await this.tablesDB.getRow(params);
129
+ const result = await this.tablesDB.getRow(
130
+ params.databaseId,
131
+ params.tableId,
132
+ params.id
133
+ );
113
134
  return {
114
135
  data: result,
115
136
  rows: [result]
@@ -143,7 +164,14 @@ export class TablesDBAdapter extends BaseAdapter {
143
164
 
144
165
  async createTable(params: CreateTableParams): Promise<ApiResponse> {
145
166
  try {
146
- const result = await this.tablesDB.createTable(params);
167
+ const result = await this.tablesDB.createTable(
168
+ params.databaseId,
169
+ params.id, // tableId
170
+ params.name,
171
+ params.permissions || [],
172
+ params.documentSecurity ?? false,
173
+ params.enabled ?? true
174
+ );
147
175
  return {
148
176
  data: result,
149
177
  tables: [result]
@@ -159,7 +187,14 @@ export class TablesDBAdapter extends BaseAdapter {
159
187
 
160
188
  async updateTable(params: UpdateTableParams): Promise<ApiResponse> {
161
189
  try {
162
- const result = await this.tablesDB.updateTable(params);
190
+ const result = await this.tablesDB.updateTable(
191
+ params.databaseId,
192
+ params.id, // tableId
193
+ params.name,
194
+ params.permissions,
195
+ params.documentSecurity,
196
+ params.enabled
197
+ );
163
198
  return {
164
199
  data: result,
165
200
  tables: [result]
@@ -172,10 +207,13 @@ export class TablesDBAdapter extends BaseAdapter {
172
207
  );
173
208
  }
174
209
  }
175
-
210
+
176
211
  async deleteTable(params: DeleteTableParams): Promise<ApiResponse> {
177
212
  try {
178
- const result = await this.tablesDB.deleteTable(params);
213
+ const result = await this.tablesDB.deleteTable(
214
+ params.databaseId,
215
+ params.tableId
216
+ );
179
217
  return { data: result };
180
218
  } catch (error) {
181
219
  throw new AdapterError(
@@ -185,10 +223,13 @@ export class TablesDBAdapter extends BaseAdapter {
185
223
  );
186
224
  }
187
225
  }
188
-
226
+
189
227
  async getTable(params: GetTableParams): Promise<ApiResponse> {
190
228
  try {
191
- const result = await this.tablesDB.getTable(params);
229
+ const result = await this.tablesDB.getTable(
230
+ params.databaseId,
231
+ params.tableId
232
+ );
192
233
  return {
193
234
  data: result,
194
235
  tables: [result]
@@ -221,7 +262,14 @@ export class TablesDBAdapter extends BaseAdapter {
221
262
 
222
263
  async createIndex(params: CreateIndexParams): Promise<ApiResponse> {
223
264
  try {
224
- const result = await this.tablesDB.createIndex(params);
265
+ const result = await this.tablesDB.createIndex(
266
+ params.databaseId,
267
+ params.tableId,
268
+ params.key,
269
+ params.type,
270
+ params.attributes,
271
+ params.orders || []
272
+ );
225
273
  return { data: result };
226
274
  } catch (error) {
227
275
  throw new AdapterError(
@@ -231,10 +279,14 @@ export class TablesDBAdapter extends BaseAdapter {
231
279
  );
232
280
  }
233
281
  }
234
-
282
+
235
283
  async deleteIndex(params: DeleteIndexParams): Promise<ApiResponse> {
236
284
  try {
237
- const result = await this.tablesDB.deleteIndex(params);
285
+ const result = await this.tablesDB.deleteIndex(
286
+ params.databaseId,
287
+ params.tableId,
288
+ params.key
289
+ );
238
290
  return { data: result };
239
291
  } catch (error) {
240
292
  throw new AdapterError(
@@ -246,13 +298,140 @@ export class TablesDBAdapter extends BaseAdapter {
246
298
  }
247
299
 
248
300
  // Attribute Operations
249
- async createAttribute(params: CreateAttributeParams): Promise<ApiResponse> {
250
- try {
251
- // Prefer createColumn if available, fallback to createAttribute
252
- const fn = this.tablesDB.createColumn || this.tablesDB.createAttribute;
253
- const result = await fn.call(this.tablesDB, params);
254
- return { data: result };
255
- } catch (error) {
301
+ async createAttribute(params: CreateAttributeParams): Promise<ApiResponse> {
302
+ try {
303
+ // TablesDB uses type-specific attribute methods like the legacy SDK
304
+ let result;
305
+
306
+ switch (params.type.toLowerCase()) {
307
+ case 'string':
308
+ result = await this.tablesDB.createStringAttribute(
309
+ params.databaseId,
310
+ params.tableId,
311
+ params.key,
312
+ params.size || 255,
313
+ params.required ?? false,
314
+ params.default,
315
+ params.array ?? false,
316
+ params.encrypt ?? false
317
+ );
318
+ break;
319
+
320
+ case 'integer':
321
+ result = await this.tablesDB.createIntegerAttribute(
322
+ params.databaseId,
323
+ params.tableId,
324
+ params.key,
325
+ params.required ?? false,
326
+ params.min,
327
+ params.max,
328
+ params.default,
329
+ params.array ?? false
330
+ );
331
+ break;
332
+
333
+ case 'float':
334
+ case 'double':
335
+ result = await this.tablesDB.createFloatAttribute(
336
+ params.databaseId,
337
+ params.tableId,
338
+ params.key,
339
+ params.required ?? false,
340
+ params.min,
341
+ params.max,
342
+ params.default,
343
+ params.array ?? false
344
+ );
345
+ break;
346
+
347
+ case 'boolean':
348
+ result = await this.tablesDB.createBooleanAttribute(
349
+ params.databaseId,
350
+ params.tableId,
351
+ params.key,
352
+ params.required ?? false,
353
+ params.default,
354
+ params.array ?? false
355
+ );
356
+ break;
357
+
358
+ case 'datetime':
359
+ result = await this.tablesDB.createDatetimeAttribute(
360
+ params.databaseId,
361
+ params.tableId,
362
+ params.key,
363
+ params.required ?? false,
364
+ params.default,
365
+ params.array ?? false
366
+ );
367
+ break;
368
+
369
+ case 'email':
370
+ result = await this.tablesDB.createEmailAttribute(
371
+ params.databaseId,
372
+ params.tableId,
373
+ params.key,
374
+ params.required ?? false,
375
+ params.default,
376
+ params.array ?? false
377
+ );
378
+ break;
379
+
380
+ case 'enum':
381
+ result = await this.tablesDB.createEnumAttribute(
382
+ params.databaseId,
383
+ params.tableId,
384
+ params.key,
385
+ params.elements || [],
386
+ params.required ?? false,
387
+ params.default,
388
+ params.array ?? false
389
+ );
390
+ break;
391
+
392
+ case 'ip':
393
+ result = await this.tablesDB.createIpAttribute(
394
+ params.databaseId,
395
+ params.tableId,
396
+ params.key,
397
+ params.required ?? false,
398
+ params.default,
399
+ params.array ?? false
400
+ );
401
+ break;
402
+
403
+ case 'url':
404
+ result = await this.tablesDB.createUrlAttribute(
405
+ params.databaseId,
406
+ params.tableId,
407
+ params.key,
408
+ params.required ?? false,
409
+ params.default,
410
+ params.array ?? false
411
+ );
412
+ break;
413
+
414
+ case 'relationship':
415
+ result = await this.tablesDB.createRelationshipAttribute(
416
+ params.databaseId,
417
+ params.tableId,
418
+ params.key,
419
+ params.relatedCollection || '',
420
+ params.type || 'oneToOne',
421
+ params.twoWay ?? false,
422
+ params.onDelete || 'restrict'
423
+ );
424
+ break;
425
+
426
+ default:
427
+ throw new AdapterError(
428
+ `Unsupported attribute type: ${params.type}`,
429
+ 'UNSUPPORTED_ATTRIBUTE_TYPE'
430
+ );
431
+ }
432
+
433
+ return { data: result };
434
+ } catch (error) {
256
435
  throw new AdapterError(
257
436
  `Failed to create attribute: ${error instanceof Error ? error.message : 'Unknown error'}`,
258
437
  'CREATE_ATTRIBUTE_FAILED',
@@ -261,12 +440,19 @@ export class TablesDBAdapter extends BaseAdapter {
261
440
  }
262
441
  }
263
442
 
264
- async updateAttribute(params: UpdateAttributeParams): Promise<ApiResponse> {
265
- try {
266
- const fn = this.tablesDB.updateColumn || this.tablesDB.updateAttribute;
267
- const result = await fn.call(this.tablesDB, params);
268
- return { data: result };
269
- } catch (error) {
443
+ async updateAttribute(params: UpdateAttributeParams): Promise<ApiResponse> {
444
+ try {
445
+ // TablesDB uses type-specific update methods or generic updateAttribute with positional params
446
+ // Try type-specific first, fallback to generic
447
+ const result = await this.tablesDB.updateStringAttribute(
448
+ params.databaseId,
449
+ params.tableId,
450
+ params.key,
451
+ params.required ?? false,
452
+ params.default
453
+ );
454
+ return { data: result };
455
+ } catch (error) {
270
456
  throw new AdapterError(
271
457
  `Failed to update attribute: ${error instanceof Error ? error.message : 'Unknown error'}`,
272
458
  'UPDATE_ATTRIBUTE_FAILED',
@@ -274,13 +460,16 @@ export class TablesDBAdapter extends BaseAdapter {
274
460
  );
275
461
  }
276
462
  }
277
-
278
- async deleteAttribute(params: DeleteAttributeParams): Promise<ApiResponse> {
279
- try {
280
- const fn = this.tablesDB.deleteColumn || this.tablesDB.deleteAttribute;
281
- const result = await fn.call(this.tablesDB, params);
282
- return { data: result };
283
- } catch (error) {
463
+
464
+ async deleteAttribute(params: DeleteAttributeParams): Promise<ApiResponse> {
465
+ try {
466
+ const result = await this.tablesDB.deleteAttribute(
467
+ params.databaseId,
468
+ params.tableId,
469
+ params.key
470
+ );
471
+ return { data: result };
472
+ } catch (error) {
284
473
  throw new AdapterError(
285
474
  `Failed to delete attribute: ${error instanceof Error ? error.message : 'Unknown error'}`,
286
475
  'DELETE_ATTRIBUTE_FAILED',
@@ -429,4 +618,4 @@ export class TablesDBAdapter extends BaseAdapter {
429
618
  );
430
619
  }
431
620
  }
432
- }
621
+ }
@@ -0,0 +1,277 @@
1
+ import type { Storage, Models } from "node-appwrite";
2
+ import JSZip from "jszip";
3
+ import { ID, Query } from "node-appwrite";
4
+ import { InputFile } from "node-appwrite/file";
5
+ import pLimit from "p-limit";
6
+ import { MessageFormatter } from "../../shared/messageFormatter.js";
7
+ import { logger } from "../../shared/logging.js";
8
+ import type { BucketManifest, BucketFileMetadata } from "../schemas/bucketManifest.js";
9
+ import { ulid } from "ulidx";
10
+
11
+ export interface BucketBackupOptions {
12
+ compressionLevel?: number; // 0-9, default 6
13
+ parallelDownloads?: number; // Number of concurrent downloads, default 10
14
+ onProgress?: (current: number, total: number, fileName: string) => void;
15
+ }
16
+
17
+ export interface BucketBackupResult {
18
+ backupFileId: string;
19
+ manifestFileId: string;
20
+ fileCount: number;
21
+ totalSizeBytes: number;
22
+ zipSizeBytes: number;
23
+ status: 'completed' | 'partial' | 'failed';
24
+ errors?: string[];
25
+ }
26
+
27
+ /**
28
+ * Downloads all files from a bucket in parallel and creates a ZIP backup
29
+ */
30
+ export async function backupBucket(
31
+ storage: Storage,
32
+ bucketId: string,
33
+ backupBucketId: string,
34
+ options: BucketBackupOptions = {}
35
+ ): Promise<BucketBackupResult> {
36
+ const {
37
+ compressionLevel = 6,
38
+ parallelDownloads = 10,
39
+ onProgress
40
+ } = options;
41
+
42
+ const errors: string[] = [];
43
+ let totalSizeBytes = 0;
44
+
45
+ try {
46
+ // Step 1: Get bucket metadata
47
+ MessageFormatter.info(`Fetching bucket metadata for ${bucketId}`, { prefix: "Backup" });
48
+ const bucket = await storage.getBucket(bucketId);
49
+
50
+ // Step 2: List all files in bucket with pagination
51
+ MessageFormatter.info(`Listing all files in bucket ${bucketId}`, { prefix: "Backup" });
52
+ const allFiles: Models.File[] = [];
53
+ let lastFileId: string | undefined;
54
+
55
+ while (true) {
56
+ const queries = [Query.limit(100)];
57
+ if (lastFileId) {
58
+ queries.push(Query.cursorAfter(lastFileId));
59
+ }
60
+
61
+ const filesPage = await storage.listFiles(bucketId, queries);
62
+ allFiles.push(...filesPage.files);
63
+
64
+ if (filesPage.files.length < 100) break;
65
+ lastFileId = filesPage.files[filesPage.files.length - 1].$id;
66
+ }
67
+
68
+ MessageFormatter.info(`Found ${allFiles.length} files to backup`, { prefix: "Backup" });
69
+
70
+ if (allFiles.length === 0) {
71
+ // Empty bucket - create minimal backup
72
+ const manifest: BucketManifest = {
73
+ version: "1.0",
74
+ bucketId: bucket.$id,
75
+ bucketName: bucket.name,
76
+ createdAt: new Date().toISOString(),
77
+ fileCount: 0,
78
+ totalSizeBytes: 0,
79
+ compression: bucket.compression === 'gzip' ? 'gzip' : 'none',
80
+ files: [],
81
+ bucketConfiguration: {
82
+ $permissions: bucket.$permissions,
83
+ fileSecurity: bucket.fileSecurity,
84
+ enabled: bucket.enabled,
85
+ maximumFileSize: bucket.maximumFileSize,
86
+ allowedFileExtensions: bucket.allowedFileExtensions,
87
+ compression: bucket.compression,
88
+ encryption: bucket.encryption,
89
+ antivirus: bucket.antivirus
90
+ }
91
+ };
92
+
93
+ const manifestFileId = await uploadManifest(storage, backupBucketId, bucketId, manifest);
94
+
95
+ return {
96
+ backupFileId: '',
97
+ manifestFileId,
98
+ fileCount: 0,
99
+ totalSizeBytes: 0,
100
+ zipSizeBytes: 0,
101
+ status: 'completed'
102
+ };
103
+ }
104
+
105
+ // Step 3: Download all files in parallel with concurrency limit
106
+ MessageFormatter.info(`Downloading ${allFiles.length} files in parallel (max ${parallelDownloads} concurrent)`, { prefix: "Backup" });
107
+
108
+ const limit = pLimit(parallelDownloads);
109
+ const downloadedFiles: Map<string, { buffer: Buffer; file: Models.File }> = new Map();
110
+ let successCount = 0;
111
+ let errorCount = 0;
112
+
113
+ const downloadTasks = allFiles.map((file, index) =>
114
+ limit(async () => {
115
+ try {
116
+ const fileBuffer = await storage.getFileDownload(bucketId, file.$id);
117
+ const buffer = Buffer.from(fileBuffer as ArrayBuffer);
118
+
119
+ downloadedFiles.set(file.$id, { buffer, file });
120
+ successCount++;
121
+ totalSizeBytes += file.sizeOriginal || buffer.length;
122
+
123
+ if (onProgress) {
124
+ onProgress(successCount + errorCount, allFiles.length, file.name);
125
+ }
126
+
127
+ logger.debug(`Downloaded file ${file.name}`, {
128
+ fileId: file.$id,
129
+ size: buffer.length
130
+ });
131
+ } catch (error) {
132
+ errorCount++;
133
+ const errorMsg = `Failed to download file ${file.name} (${file.$id}): ${error instanceof Error ? error.message : String(error)}`;
134
+ errors.push(errorMsg);
135
+ logger.error(errorMsg);
136
+
137
+ if (onProgress) {
138
+ onProgress(successCount + errorCount, allFiles.length, file.name);
139
+ }
140
+ }
141
+ })
142
+ );
143
+
144
+ await Promise.all(downloadTasks);
145
+
146
+ if (successCount === 0) {
147
+ throw new Error(`Failed to download any files from bucket ${bucketId}`);
148
+ }
149
+
150
+ MessageFormatter.info(`Successfully downloaded ${successCount}/${allFiles.length} files`, { prefix: "Backup" });
151
+
152
+ // Step 4: Create ZIP archive with all files
153
+ MessageFormatter.info(`Creating ZIP archive for bucket ${bucketId}`, { prefix: "Backup" });
154
+ const zip = new JSZip();
155
+
156
+ for (const [fileId, { buffer, file }] of downloadedFiles.entries()) {
157
+ // Preserve file structure by using file name
158
+ const fileName = file.name || `file_${fileId}`;
159
+ zip.file(fileName, new Uint8Array(buffer));
160
+ }
161
+
162
+ // Generate ZIP buffer
163
+ const zipBuffer = await zip.generateAsync({
164
+ type: "nodebuffer",
165
+ compression: "DEFLATE",
166
+ compressionOptions: { level: compressionLevel }
167
+ });
168
+
169
+ const zipSizeBytes = zipBuffer.length;
170
+ MessageFormatter.info(`ZIP archive created: ${MessageFormatter.formatBytes(zipSizeBytes)}`, { prefix: "Backup" });
171
+
172
+ // Step 5: Create manifest
173
+ const fileMetadata: BucketFileMetadata[] = Array.from(downloadedFiles.values()).map(({ file }) => ({
174
+ $id: file.$id,
175
+ name: file.name,
176
+ size: file.sizeOriginal,
177
+ mimeType: file.mimeType,
178
+ $permissions: file.$permissions,
179
+ chunksCount: file.chunksTotal,
180
+ signature: file.signature,
181
+ $createdAt: file.$createdAt,
182
+ $updatedAt: file.$updatedAt
183
+ }));
184
+
185
+ const manifest: BucketManifest = {
186
+ version: "1.0",
187
+ bucketId: bucket.$id,
188
+ bucketName: bucket.name,
189
+ createdAt: new Date().toISOString(),
190
+ fileCount: successCount,
191
+ totalSizeBytes,
192
+ compression: bucket.compression === 'gzip' ? 'gzip' : 'none',
193
+ files: fileMetadata,
194
+ bucketConfiguration: {
195
+ $permissions: bucket.$permissions,
196
+ fileSecurity: bucket.fileSecurity,
197
+ enabled: bucket.enabled,
198
+ maximumFileSize: bucket.maximumFileSize,
199
+ allowedFileExtensions: bucket.allowedFileExtensions,
200
+ compression: bucket.compression,
201
+ encryption: bucket.encryption,
202
+ antivirus: bucket.antivirus
203
+ }
204
+ };
205
+
206
+ // Step 6: Upload backup ZIP to backup bucket
207
+ MessageFormatter.info(`Uploading backup ZIP to bucket ${backupBucketId}`, { prefix: "Backup" });
208
+ const backupFileName = `${bucketId}.zip`;
209
+ const backupFile = await storage.createFile(
210
+ backupBucketId,
211
+ ID.unique(),
212
+ InputFile.fromBuffer(new Uint8Array(zipBuffer), backupFileName)
213
+ );
214
+
215
+ // Step 7: Upload manifest JSON
216
+ const manifestFileId = await uploadManifest(storage, backupBucketId, bucketId, manifest);
217
+
218
+ const status: 'completed' | 'partial' | 'failed' =
219
+ errorCount === 0 ? 'completed' :
220
+ successCount > 0 ? 'partial' :
221
+ 'failed';
222
+
223
+ MessageFormatter.success(
224
+ `Bucket backup ${status}: ${successCount}/${allFiles.length} files backed up`,
225
+ { prefix: "Backup" }
226
+ );
227
+
228
+ return {
229
+ backupFileId: backupFile.$id,
230
+ manifestFileId,
231
+ fileCount: successCount,
232
+ totalSizeBytes,
233
+ zipSizeBytes,
234
+ status,
235
+ errors: errors.length > 0 ? errors : undefined
236
+ };
237
+ } catch (error) {
238
+ const errorMsg = `Bucket backup failed: ${error instanceof Error ? error.message : String(error)}`;
239
+ MessageFormatter.error(errorMsg, error instanceof Error ? error : new Error(errorMsg), { prefix: "Backup" });
240
+
241
+ return {
242
+ backupFileId: '',
243
+ manifestFileId: '',
244
+ fileCount: 0,
245
+ totalSizeBytes: 0,
246
+ zipSizeBytes: 0,
247
+ status: 'failed',
248
+ errors: [errorMsg, ...errors]
249
+ };
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Uploads bucket manifest JSON to backup bucket
255
+ */
256
+ async function uploadManifest(
257
+ storage: Storage,
258
+ backupBucketId: string,
259
+ bucketId: string,
260
+ manifest: BucketManifest
261
+ ): Promise<string> {
262
+ const manifestFileName = `${bucketId}.json`;
263
+ const manifestBuffer = Buffer.from(JSON.stringify(manifest, null, 2), 'utf-8');
264
+
265
+ const manifestFile = await storage.createFile(
266
+ backupBucketId,
267
+ ID.unique(),
268
+ InputFile.fromBuffer(new Uint8Array(manifestBuffer), manifestFileName)
269
+ );
270
+
271
+ logger.info("Uploaded bucket manifest", {
272
+ manifestFileId: manifestFile.$id,
273
+ bucketId
274
+ });
275
+
276
+ return manifestFile.$id;
277
+ }