appwrite-utils-cli 1.5.1 → 1.6.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 (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 +478 -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 +186 -1171
  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 +276 -1591
  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
@@ -1,8 +1,335 @@
1
1
  import { Query } from "node-appwrite";
2
2
  import { attributeSchema, parseAttribute, } from "appwrite-utils";
3
- import { nameToIdMapping, enqueueOperation } from "../shared/operationQueue.js";
4
- import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
3
+ import { nameToIdMapping, enqueueOperation, markAttributeProcessed, isAttributeProcessed } from "../shared/operationQueue.js";
4
+ import { delay, tryAwaitWithRetry, calculateExponentialBackoff } from "../utils/helperFunctions.js";
5
5
  import chalk from "chalk";
6
+ import { logger } from "../shared/logging.js";
7
+ import { MessageFormatter } from "../shared/messageFormatter.js";
8
+ import { isDatabaseAdapter } from "../utils/typeGuards.js";
9
+ // Threshold for treating min/max values as undefined (10 billion)
10
+ const MIN_MAX_THRESHOLD = 10_000_000_000;
11
+ // Extreme values that Appwrite may return, which should be treated as undefined
12
+ const EXTREME_MIN_INTEGER = -9223372036854776000;
13
+ const EXTREME_MAX_INTEGER = 9223372036854776000;
14
+ const EXTREME_MIN_FLOAT = -1.7976931348623157e+308;
15
+ const EXTREME_MAX_FLOAT = 1.7976931348623157e+308;
16
+ /**
17
+ * Type guard to check if an attribute has min/max properties
18
+ */
19
+ const hasMinMaxProperties = (attribute) => {
20
+ return attribute.type === 'integer' || attribute.type === 'double' || attribute.type === 'float';
21
+ };
22
+ /**
23
+ * Normalizes min/max values for integer and float attributes
24
+ * Sets values to undefined if they exceed the threshold or are extreme values from database
25
+ */
26
+ const normalizeMinMaxValues = (attribute) => {
27
+ if (!hasMinMaxProperties(attribute)) {
28
+ logger.debug(`Attribute '${attribute.key}' does not have min/max properties`, {
29
+ type: attribute.type,
30
+ operation: 'normalizeMinMaxValues'
31
+ });
32
+ return {};
33
+ }
34
+ const { type, min, max } = attribute;
35
+ let normalizedMin = min;
36
+ let normalizedMax = max;
37
+ logger.debug(`Normalizing min/max values for attribute '${attribute.key}'`, {
38
+ type,
39
+ originalMin: min,
40
+ originalMax: max,
41
+ operation: 'normalizeMinMaxValues'
42
+ });
43
+ // Handle min value
44
+ if (normalizedMin !== undefined && normalizedMin !== null) {
45
+ const minValue = Number(normalizedMin);
46
+ const originalMin = normalizedMin;
47
+ // Check if it exceeds threshold or is an extreme database value
48
+ if (type === 'integer') {
49
+ if (Math.abs(minValue) >= MIN_MAX_THRESHOLD || minValue === EXTREME_MIN_INTEGER) {
50
+ logger.debug(`Min value normalized to undefined for attribute '${attribute.key}'`, {
51
+ type,
52
+ originalValue: originalMin,
53
+ numericValue: minValue,
54
+ reason: Math.abs(minValue) >= MIN_MAX_THRESHOLD ? 'exceeds_threshold' : 'extreme_database_value',
55
+ threshold: MIN_MAX_THRESHOLD,
56
+ extremeValue: EXTREME_MIN_INTEGER,
57
+ operation: 'normalizeMinMaxValues'
58
+ });
59
+ normalizedMin = undefined;
60
+ }
61
+ }
62
+ else { // float/double
63
+ if (Math.abs(minValue) >= MIN_MAX_THRESHOLD || minValue === EXTREME_MIN_FLOAT) {
64
+ logger.debug(`Min value normalized to undefined for attribute '${attribute.key}'`, {
65
+ type,
66
+ originalValue: originalMin,
67
+ numericValue: minValue,
68
+ reason: Math.abs(minValue) >= MIN_MAX_THRESHOLD ? 'exceeds_threshold' : 'extreme_database_value',
69
+ threshold: MIN_MAX_THRESHOLD,
70
+ extremeValue: EXTREME_MIN_FLOAT,
71
+ operation: 'normalizeMinMaxValues'
72
+ });
73
+ normalizedMin = undefined;
74
+ }
75
+ }
76
+ }
77
+ // Handle max value
78
+ if (normalizedMax !== undefined && normalizedMax !== null) {
79
+ const maxValue = Number(normalizedMax);
80
+ const originalMax = normalizedMax;
81
+ // Check if it exceeds threshold or is an extreme database value
82
+ if (type === 'integer') {
83
+ if (Math.abs(maxValue) >= MIN_MAX_THRESHOLD || maxValue === EXTREME_MAX_INTEGER) {
84
+ logger.debug(`Max value normalized to undefined for attribute '${attribute.key}'`, {
85
+ type,
86
+ originalValue: originalMax,
87
+ numericValue: maxValue,
88
+ reason: Math.abs(maxValue) >= MIN_MAX_THRESHOLD ? 'exceeds_threshold' : 'extreme_database_value',
89
+ threshold: MIN_MAX_THRESHOLD,
90
+ extremeValue: EXTREME_MAX_INTEGER,
91
+ operation: 'normalizeMinMaxValues'
92
+ });
93
+ normalizedMax = undefined;
94
+ }
95
+ }
96
+ else { // float/double
97
+ if (Math.abs(maxValue) >= MIN_MAX_THRESHOLD || maxValue === EXTREME_MAX_FLOAT) {
98
+ logger.debug(`Max value normalized to undefined for attribute '${attribute.key}'`, {
99
+ type,
100
+ originalValue: originalMax,
101
+ numericValue: maxValue,
102
+ reason: Math.abs(maxValue) >= MIN_MAX_THRESHOLD ? 'exceeds_threshold' : 'extreme_database_value',
103
+ threshold: MIN_MAX_THRESHOLD,
104
+ extremeValue: EXTREME_MAX_FLOAT,
105
+ operation: 'normalizeMinMaxValues'
106
+ });
107
+ normalizedMax = undefined;
108
+ }
109
+ }
110
+ }
111
+ const result = { min: normalizedMin, max: normalizedMax };
112
+ logger.debug(`Min/max normalization complete for attribute '${attribute.key}'`, {
113
+ type,
114
+ result,
115
+ operation: 'normalizeMinMaxValues'
116
+ });
117
+ return result;
118
+ };
119
+ /**
120
+ * Normalizes an attribute for comparison by handling extreme database values
121
+ * This is used when comparing database attributes with config attributes
122
+ */
123
+ const normalizeAttributeForComparison = (attribute) => {
124
+ if (!hasMinMaxProperties(attribute)) {
125
+ return attribute;
126
+ }
127
+ const { min, max } = normalizeMinMaxValues(attribute);
128
+ return { ...attribute, min, max };
129
+ };
130
+ /**
131
+ * Helper function to create an attribute using either the adapter or legacy API
132
+ */
133
+ const createAttributeViaAdapter = async (db, dbId, collectionId, attribute) => {
134
+ const startTime = Date.now();
135
+ const adapterType = isDatabaseAdapter(db) ? 'adapter' : 'legacy';
136
+ logger.info(`Creating attribute '${attribute.key}' via ${adapterType}`, {
137
+ type: attribute.type,
138
+ dbId,
139
+ collectionId,
140
+ adapterType,
141
+ operation: 'createAttributeViaAdapter'
142
+ });
143
+ if (isDatabaseAdapter(db)) {
144
+ // Use the adapter's unified createAttribute method
145
+ const params = {
146
+ databaseId: dbId,
147
+ tableId: collectionId,
148
+ key: attribute.key,
149
+ type: attribute.type,
150
+ required: attribute.required || false,
151
+ array: attribute.array || false,
152
+ ...(attribute.size && { size: attribute.size }),
153
+ ...(attribute.xdefault !== undefined && !attribute.required && { default: attribute.xdefault }),
154
+ ...(attribute.encrypted && { encrypt: attribute.encrypted }),
155
+ ...(attribute.min !== undefined && { min: attribute.min }),
156
+ ...(attribute.max !== undefined && { max: attribute.max }),
157
+ ...(attribute.elements && { elements: attribute.elements }),
158
+ ...(attribute.relatedCollection && { relatedCollection: attribute.relatedCollection }),
159
+ ...(attribute.relationType && { relationType: attribute.relationType }),
160
+ ...(attribute.twoWay !== undefined && { twoWay: attribute.twoWay }),
161
+ ...(attribute.onDelete && { onDelete: attribute.onDelete }),
162
+ ...(attribute.twoWayKey && { twoWayKey: attribute.twoWayKey })
163
+ };
164
+ logger.debug(`Adapter create parameters for '${attribute.key}'`, {
165
+ params,
166
+ operation: 'createAttributeViaAdapter'
167
+ });
168
+ await db.createAttribute(params);
169
+ const duration = Date.now() - startTime;
170
+ logger.info(`Successfully created attribute '${attribute.key}' via adapter`, {
171
+ duration,
172
+ operation: 'createAttributeViaAdapter'
173
+ });
174
+ }
175
+ else {
176
+ // Use legacy type-specific methods
177
+ logger.debug(`Using legacy creation for attribute '${attribute.key}'`, {
178
+ operation: 'createAttributeViaAdapter'
179
+ });
180
+ await createLegacyAttribute(db, dbId, collectionId, attribute);
181
+ const duration = Date.now() - startTime;
182
+ logger.info(`Successfully created attribute '${attribute.key}' via legacy`, {
183
+ duration,
184
+ operation: 'createAttributeViaAdapter'
185
+ });
186
+ }
187
+ };
188
+ /**
189
+ * Helper function to update an attribute using either the adapter or legacy API
190
+ */
191
+ const updateAttributeViaAdapter = async (db, dbId, collectionId, attribute) => {
192
+ if (isDatabaseAdapter(db)) {
193
+ // Use the adapter's unified updateAttribute method
194
+ const params = {
195
+ databaseId: dbId,
196
+ tableId: collectionId,
197
+ key: attribute.key,
198
+ required: attribute.required || false,
199
+ ...(attribute.xdefault !== undefined && !attribute.required && { default: attribute.xdefault })
200
+ };
201
+ await db.updateAttribute(params);
202
+ }
203
+ else {
204
+ // Use legacy type-specific methods
205
+ await updateLegacyAttribute(db, dbId, collectionId, attribute);
206
+ }
207
+ };
208
+ /**
209
+ * Legacy attribute creation using type-specific methods
210
+ */
211
+ const createLegacyAttribute = async (db, dbId, collectionId, attribute) => {
212
+ const startTime = Date.now();
213
+ const { min: normalizedMin, max: normalizedMax } = normalizeMinMaxValues(attribute);
214
+ logger.info(`Creating legacy attribute '${attribute.key}'`, {
215
+ type: attribute.type,
216
+ dbId,
217
+ collectionId,
218
+ normalizedMin,
219
+ normalizedMax,
220
+ operation: 'createLegacyAttribute'
221
+ });
222
+ switch (attribute.type) {
223
+ case "string":
224
+ const stringParams = {
225
+ size: attribute.size || 255,
226
+ required: attribute.required || false,
227
+ defaultValue: attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined,
228
+ array: attribute.array || false,
229
+ encrypted: attribute.encrypted
230
+ };
231
+ logger.debug(`Creating string attribute '${attribute.key}'`, {
232
+ ...stringParams,
233
+ operation: 'createLegacyAttribute'
234
+ });
235
+ await db.createStringAttribute(dbId, collectionId, attribute.key, stringParams.size, stringParams.required, stringParams.defaultValue, stringParams.array, stringParams.encrypted);
236
+ break;
237
+ case "integer":
238
+ const integerParams = {
239
+ required: attribute.required || false,
240
+ min: normalizedMin !== undefined ? parseInt(String(normalizedMin)) : undefined,
241
+ max: normalizedMax !== undefined ? parseInt(String(normalizedMax)) : undefined,
242
+ defaultValue: attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined,
243
+ array: attribute.array || false
244
+ };
245
+ logger.debug(`Creating integer attribute '${attribute.key}'`, {
246
+ ...integerParams,
247
+ operation: 'createLegacyAttribute'
248
+ });
249
+ await db.createIntegerAttribute(dbId, collectionId, attribute.key, integerParams.required, integerParams.min, integerParams.max, integerParams.defaultValue, integerParams.array);
250
+ break;
251
+ case "double":
252
+ case "float":
253
+ await db.createFloatAttribute(dbId, collectionId, attribute.key, attribute.required || false, normalizedMin !== undefined ? Number(normalizedMin) : undefined, normalizedMax !== undefined ? Number(normalizedMax) : undefined, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
254
+ break;
255
+ case "boolean":
256
+ await db.createBooleanAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
257
+ break;
258
+ case "datetime":
259
+ await db.createDatetimeAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
260
+ break;
261
+ case "email":
262
+ await db.createEmailAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
263
+ break;
264
+ case "ip":
265
+ await db.createIpAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
266
+ break;
267
+ case "url":
268
+ await db.createUrlAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
269
+ break;
270
+ case "enum":
271
+ await db.createEnumAttribute(dbId, collectionId, attribute.key, attribute.elements || [], attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.array || false);
272
+ break;
273
+ case "relationship":
274
+ await db.createRelationshipAttribute(dbId, collectionId, attribute.relatedCollection, attribute.relationType, attribute.twoWay, attribute.key, attribute.twoWayKey, attribute.onDelete);
275
+ break;
276
+ default:
277
+ const error = new Error(`Unsupported attribute type: ${attribute.type}`);
278
+ logger.error(`Unsupported attribute type for '${attribute.key}'`, {
279
+ type: attribute.type,
280
+ supportedTypes: ['string', 'integer', 'double', 'float', 'boolean', 'datetime', 'email', 'ip', 'url', 'enum', 'relationship'],
281
+ operation: 'createLegacyAttribute'
282
+ });
283
+ throw error;
284
+ }
285
+ const duration = Date.now() - startTime;
286
+ logger.info(`Successfully created legacy attribute '${attribute.key}'`, {
287
+ type: attribute.type,
288
+ duration,
289
+ operation: 'createLegacyAttribute'
290
+ });
291
+ };
292
+ /**
293
+ * Legacy attribute update using type-specific methods
294
+ */
295
+ const updateLegacyAttribute = async (db, dbId, collectionId, attribute) => {
296
+ const { min: normalizedMin, max: normalizedMax } = normalizeMinMaxValues(attribute);
297
+ switch (attribute.type) {
298
+ case "string":
299
+ await db.updateStringAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, attribute.size);
300
+ break;
301
+ case "integer":
302
+ await db.updateIntegerAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && !attribute.required ? attribute.xdefault : undefined, normalizedMin !== undefined ? parseInt(String(normalizedMin)) : undefined, normalizedMax !== undefined ? parseInt(String(normalizedMax)) : undefined);
303
+ break;
304
+ case "double":
305
+ case "float":
306
+ await db.updateFloatAttribute(dbId, collectionId, attribute.key, attribute.required || false, normalizedMin !== undefined ? Number(normalizedMin) : undefined, normalizedMax !== undefined ? Number(normalizedMax) : undefined, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
307
+ break;
308
+ case "boolean":
309
+ await db.updateBooleanAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
310
+ break;
311
+ case "datetime":
312
+ await db.updateDatetimeAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
313
+ break;
314
+ case "email":
315
+ await db.updateEmailAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
316
+ break;
317
+ case "ip":
318
+ await db.updateIpAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
319
+ break;
320
+ case "url":
321
+ await db.updateUrlAttribute(dbId, collectionId, attribute.key, attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
322
+ break;
323
+ case "enum":
324
+ await db.updateEnumAttribute(dbId, collectionId, attribute.key, attribute.elements || [], attribute.required || false, attribute.xdefault !== undefined && attribute.xdefault !== null && !attribute.required ? attribute.xdefault : undefined);
325
+ break;
326
+ case "relationship":
327
+ await db.updateRelationshipAttribute(dbId, collectionId, attribute.key, attribute.onDelete);
328
+ break;
329
+ default:
330
+ throw new Error(`Unsupported attribute type for update: ${attribute.type}`);
331
+ }
332
+ };
6
333
  /**
7
334
  * Wait for attribute to become available, with retry logic for stuck attributes and exponential backoff
8
335
  */
@@ -10,56 +337,90 @@ const waitForAttributeAvailable = async (db, dbId, collectionId, attributeKey, m
10
337
  retryCount = 0, maxRetries = 5) => {
11
338
  const startTime = Date.now();
12
339
  let checkInterval = 2000; // Start with 2 seconds
340
+ logger.info(`Waiting for attribute '${attributeKey}' to become available`, {
341
+ dbId,
342
+ collectionId,
343
+ maxWaitTime,
344
+ retryCount,
345
+ maxRetries,
346
+ operation: 'waitForAttributeAvailable'
347
+ });
13
348
  // Calculate exponential backoff: 2s, 4s, 8s, 16s, 30s (capped at 30s)
14
349
  if (retryCount > 0) {
15
- const exponentialDelay = Math.min(2000 * Math.pow(2, retryCount), 30000);
16
- console.log(chalk.blue(`Waiting for attribute '${attributeKey}' to become available (retry ${retryCount}, backoff: ${exponentialDelay}ms)...`));
350
+ const exponentialDelay = calculateExponentialBackoff(retryCount);
351
+ MessageFormatter.info(chalk.blue(`Waiting for attribute '${attributeKey}' to become available (retry ${retryCount}, backoff: ${exponentialDelay}ms)...`));
17
352
  await delay(exponentialDelay);
18
353
  }
19
354
  else {
20
- console.log(chalk.blue(`Waiting for attribute '${attributeKey}' to become available...`));
355
+ MessageFormatter.info(chalk.blue(`Waiting for attribute '${attributeKey}' to become available...`));
21
356
  }
22
357
  while (Date.now() - startTime < maxWaitTime) {
23
358
  try {
24
- const collection = await db.getCollection(dbId, collectionId);
359
+ const collection = isDatabaseAdapter(db)
360
+ ? (await db.getTable({ databaseId: dbId, tableId: collectionId })).data
361
+ : await db.getCollection(dbId, collectionId);
25
362
  const attribute = collection.attributes.find((attr) => attr.key === attributeKey);
26
363
  if (!attribute) {
27
- console.log(chalk.red(`Attribute '${attributeKey}' not found`));
364
+ MessageFormatter.error(`Attribute '${attributeKey}' not found`);
28
365
  return false;
29
366
  }
30
- console.log(chalk.gray(`Attribute '${attributeKey}' status: ${attribute.status}`));
367
+ MessageFormatter.info(chalk.gray(`Attribute '${attributeKey}' status: ${attribute.status}`));
368
+ const statusInfo = {
369
+ attributeKey,
370
+ status: attribute.status,
371
+ error: attribute.error,
372
+ dbId,
373
+ collectionId,
374
+ waitTime: Date.now() - startTime,
375
+ operation: 'waitForAttributeAvailable'
376
+ };
31
377
  switch (attribute.status) {
32
378
  case "available":
33
- console.log(chalk.green(`✅ Attribute '${attributeKey}' is now available`));
379
+ MessageFormatter.info(chalk.green(`✅ Attribute '${attributeKey}' is now available`));
380
+ logger.info(`Attribute '${attributeKey}' became available`, statusInfo);
34
381
  return true;
35
382
  case "failed":
36
- console.log(chalk.red(`❌ Attribute '${attributeKey}' failed: ${attribute.error}`));
383
+ MessageFormatter.info(chalk.red(`❌ Attribute '${attributeKey}' failed: ${attribute.error}`));
384
+ logger.error(`Attribute '${attributeKey}' failed`, statusInfo);
37
385
  return false;
38
386
  case "stuck":
39
- console.log(chalk.yellow(`⚠️ Attribute '${attributeKey}' is stuck, will retry...`));
387
+ MessageFormatter.info(chalk.yellow(`⚠️ Attribute '${attributeKey}' is stuck, will retry...`));
388
+ logger.warn(`Attribute '${attributeKey}' is stuck`, statusInfo);
40
389
  return false;
41
390
  case "processing":
42
391
  // Continue waiting
392
+ logger.debug(`Attribute '${attributeKey}' still processing`, statusInfo);
43
393
  break;
44
394
  case "deleting":
45
- console.log(chalk.yellow(`Attribute '${attributeKey}' is being deleted`));
395
+ MessageFormatter.info(chalk.yellow(`Attribute '${attributeKey}' is being deleted`));
396
+ logger.warn(`Attribute '${attributeKey}' is being deleted`, statusInfo);
46
397
  break;
47
398
  default:
48
- console.log(chalk.yellow(`Unknown status '${attribute.status}' for attribute '${attributeKey}'`));
399
+ MessageFormatter.info(chalk.yellow(`Unknown status '${attribute.status}' for attribute '${attributeKey}'`));
400
+ logger.warn(`Unknown status for attribute '${attributeKey}'`, statusInfo);
49
401
  break;
50
402
  }
51
403
  await delay(checkInterval);
52
404
  }
53
405
  catch (error) {
54
- console.log(chalk.red(`Error checking attribute status: ${error}`));
406
+ const errorMessage = error instanceof Error ? error.message : String(error);
407
+ MessageFormatter.error(`Error checking attribute status: ${errorMessage}`);
408
+ logger.error('Error checking attribute status', {
409
+ attributeKey,
410
+ dbId,
411
+ collectionId,
412
+ error: errorMessage,
413
+ waitTime: Date.now() - startTime,
414
+ operation: 'waitForAttributeAvailable'
415
+ });
55
416
  return false;
56
417
  }
57
418
  }
58
419
  // Timeout reached
59
- console.log(chalk.yellow(`⏰ Timeout waiting for attribute '${attributeKey}' (${maxWaitTime}ms)`));
420
+ MessageFormatter.info(chalk.yellow(`⏰ Timeout waiting for attribute '${attributeKey}' (${maxWaitTime}ms)`));
60
421
  // If we have retries left and this isn't the last retry, try recreating
61
422
  if (retryCount < maxRetries) {
62
- console.log(chalk.yellow(`🔄 Retrying attribute creation (attempt ${retryCount + 1}/${maxRetries})`));
423
+ MessageFormatter.info(chalk.yellow(`🔄 Retrying attribute creation (attempt ${retryCount + 1}/${maxRetries})`));
63
424
  return false; // Signal that we need to retry
64
425
  }
65
426
  return false;
@@ -68,7 +429,7 @@ retryCount = 0, maxRetries = 5) => {
68
429
  * Wait for all attributes in a collection to become available
69
430
  */
70
431
  const waitForAllAttributesAvailable = async (db, dbId, collectionId, attributeKeys, maxWaitTime = 60000) => {
71
- console.log(chalk.blue(`Waiting for ${attributeKeys.length} attributes to become available...`));
432
+ MessageFormatter.info(chalk.blue(`Waiting for ${attributeKeys.length} attributes to become available...`));
72
433
  const failedAttributes = [];
73
434
  for (const attributeKey of attributeKeys) {
74
435
  const success = await waitForAttributeAvailable(db, dbId, collectionId, attributeKey, maxWaitTime);
@@ -83,24 +444,41 @@ const waitForAllAttributesAvailable = async (db, dbId, collectionId, attributeKe
83
444
  */
84
445
  const deleteAndRecreateCollection = async (db, dbId, collection, retryCount) => {
85
446
  try {
86
- console.log(chalk.yellow(`🗑️ Deleting collection '${collection.name}' for retry ${retryCount}`));
447
+ MessageFormatter.info(chalk.yellow(`🗑️ Deleting collection '${collection.name}' for retry ${retryCount}`));
87
448
  // Delete the collection
88
- await db.deleteCollection(dbId, collection.$id);
89
- console.log(chalk.yellow(`Deleted collection '${collection.name}'`));
449
+ if (isDatabaseAdapter(db)) {
450
+ await db.deleteTable({ databaseId: dbId, tableId: collection.$id });
451
+ }
452
+ else {
453
+ await db.deleteCollection(dbId, collection.$id);
454
+ }
455
+ MessageFormatter.warning(`Deleted collection '${collection.name}'`);
90
456
  // Wait a bit before recreating
91
457
  await delay(2000);
92
458
  // Recreate the collection
93
- console.log(chalk.blue(`🔄 Recreating collection '${collection.name}'`));
94
- const newCollection = await db.createCollection(dbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled);
95
- console.log(chalk.green(`✅ Recreated collection '${collection.name}'`));
459
+ MessageFormatter.info(`🔄 Recreating collection '${collection.name}'`);
460
+ const newCollection = isDatabaseAdapter(db)
461
+ ? (await db.createTable({
462
+ databaseId: dbId,
463
+ id: collection.$id,
464
+ name: collection.name,
465
+ permissions: collection.$permissions,
466
+ documentSecurity: collection.documentSecurity,
467
+ enabled: collection.enabled
468
+ })).data
469
+ : await db.createCollection(dbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled);
470
+ MessageFormatter.success(`✅ Recreated collection '${collection.name}'`);
96
471
  return newCollection;
97
472
  }
98
473
  catch (error) {
99
- console.log(chalk.red(`Failed to delete/recreate collection '${collection.name}': ${error}`));
474
+ MessageFormatter.info(chalk.red(`Failed to delete/recreate collection '${collection.name}': ${error}`));
100
475
  return null;
101
476
  }
102
477
  };
103
478
  const attributesSame = (databaseAttribute, configAttribute) => {
479
+ // Normalize both attributes for comparison (handle extreme database values)
480
+ const normalizedDbAttr = normalizeAttributeForComparison(databaseAttribute);
481
+ const normalizedConfigAttr = normalizeAttributeForComparison(configAttribute);
104
482
  const attributesToCheck = [
105
483
  "key",
106
484
  "type",
@@ -120,12 +498,12 @@ const attributesSame = (databaseAttribute, configAttribute) => {
120
498
  ];
121
499
  return attributesToCheck.every((attr) => {
122
500
  // Check if both objects have the attribute
123
- const dbHasAttr = attr in databaseAttribute;
124
- const configHasAttr = attr in configAttribute;
501
+ const dbHasAttr = attr in normalizedDbAttr;
502
+ const configHasAttr = attr in normalizedConfigAttr;
125
503
  // If both have the attribute, compare values
126
504
  if (dbHasAttr && configHasAttr) {
127
- const dbValue = databaseAttribute[attr];
128
- const configValue = configAttribute[attr];
505
+ const dbValue = normalizedDbAttr[attr];
506
+ const configValue = normalizedConfigAttr[attr];
129
507
  // Consider undefined and null as equivalent
130
508
  if ((dbValue === undefined || dbValue === null) &&
131
509
  (configValue === undefined || configValue === null)) {
@@ -148,7 +526,7 @@ const attributesSame = (databaseAttribute, configAttribute) => {
148
526
  }
149
527
  // If one has the attribute and the other doesn't, check if it's undefined or null
150
528
  if (dbHasAttr && !configHasAttr) {
151
- const dbValue = databaseAttribute[attr];
529
+ const dbValue = normalizedDbAttr[attr];
152
530
  // Consider default-false booleans as equal to missing in config
153
531
  if (typeof dbValue === "boolean") {
154
532
  return dbValue === false; // missing in config equals false in db
@@ -156,7 +534,7 @@ const attributesSame = (databaseAttribute, configAttribute) => {
156
534
  return dbValue === undefined || dbValue === null;
157
535
  }
158
536
  if (!dbHasAttr && configHasAttr) {
159
- const configValue = configAttribute[attr];
537
+ const configValue = normalizedConfigAttr[attr];
160
538
  // Consider default-false booleans as equal to missing in db
161
539
  if (typeof configValue === "boolean") {
162
540
  return configValue === false; // missing in db equals false in config
@@ -171,14 +549,14 @@ const attributesSame = (databaseAttribute, configAttribute) => {
171
549
  * Enhanced attribute creation with proper status monitoring and retry logic
172
550
  */
173
551
  export const createOrUpdateAttributeWithStatusCheck = async (db, dbId, collection, attribute, retryCount = 0, maxRetries = 5) => {
174
- console.log(chalk.blue(`Creating/updating attribute '${attribute.key}' (attempt ${retryCount + 1}/${maxRetries + 1})`));
552
+ MessageFormatter.info(chalk.blue(`Creating/updating attribute '${attribute.key}' (attempt ${retryCount + 1}/${maxRetries + 1})`));
175
553
  try {
176
554
  // First, try to create/update the attribute using existing logic
177
555
  const result = await createOrUpdateAttribute(db, dbId, collection, attribute);
178
556
  // If the attribute was queued (relationship dependency unresolved),
179
557
  // skip status polling and retry logic — the queue will handle it later.
180
558
  if (result === "queued") {
181
- console.log(chalk.yellow(`⏭️ Deferred relationship attribute '${attribute.key}' — queued for later once dependencies are available`));
559
+ MessageFormatter.info(chalk.yellow(`⏭️ Deferred relationship attribute '${attribute.key}' — queued for later once dependencies are available`));
182
560
  return true;
183
561
  }
184
562
  // Now wait for the attribute to become available
@@ -189,25 +567,34 @@ export const createOrUpdateAttributeWithStatusCheck = async (db, dbId, collectio
189
567
  }
190
568
  // If not successful and we have retries left, delete specific attribute and try again
191
569
  if (retryCount < maxRetries) {
192
- console.log(chalk.yellow(`Attribute '${attribute.key}' failed/stuck, deleting and retrying...`));
570
+ MessageFormatter.info(chalk.yellow(`Attribute '${attribute.key}' failed/stuck, deleting and retrying...`));
193
571
  // Try to delete the specific stuck attribute instead of the entire collection
194
572
  try {
195
- await db.deleteAttribute(dbId, collection.$id, attribute.key);
196
- console.log(chalk.yellow(`Deleted stuck attribute '${attribute.key}', will retry creation`));
573
+ if (isDatabaseAdapter(db)) {
574
+ await db.deleteAttribute({ databaseId: dbId, tableId: collection.$id, key: attribute.key });
575
+ }
576
+ else {
577
+ await db.deleteAttribute(dbId, collection.$id, attribute.key);
578
+ }
579
+ MessageFormatter.info(chalk.yellow(`Deleted stuck attribute '${attribute.key}', will retry creation`));
197
580
  // Wait a bit before retry
198
581
  await delay(3000);
199
582
  // Get fresh collection data
200
- const freshCollection = await db.getCollection(dbId, collection.$id);
583
+ const freshCollection = isDatabaseAdapter(db)
584
+ ? (await db.getTable({ databaseId: dbId, tableId: collection.$id })).data
585
+ : await db.getCollection(dbId, collection.$id);
201
586
  // Retry with the same collection (attribute should be gone now)
202
587
  return await createOrUpdateAttributeWithStatusCheck(db, dbId, freshCollection, attribute, retryCount + 1, maxRetries);
203
588
  }
204
589
  catch (deleteError) {
205
- console.log(chalk.red(`Failed to delete stuck attribute '${attribute.key}': ${deleteError}`));
590
+ MessageFormatter.info(chalk.red(`Failed to delete stuck attribute '${attribute.key}': ${deleteError}`));
206
591
  // If attribute deletion fails, only then try collection recreation as last resort
207
592
  if (retryCount >= maxRetries - 1) {
208
- console.log(chalk.yellow(`Last resort: Recreating collection for attribute '${attribute.key}'`));
593
+ MessageFormatter.info(chalk.yellow(`Last resort: Recreating collection for attribute '${attribute.key}'`));
209
594
  // Get fresh collection data
210
- const freshCollection = await db.getCollection(dbId, collection.$id);
595
+ const freshCollection = isDatabaseAdapter(db)
596
+ ? (await db.getTable({ databaseId: dbId, tableId: collection.$id })).data
597
+ : await db.getCollection(dbId, collection.$id);
211
598
  // Delete and recreate collection
212
599
  const newCollection = await deleteAndRecreateCollection(db, dbId, freshCollection, retryCount + 1);
213
600
  if (newCollection) {
@@ -221,13 +608,13 @@ export const createOrUpdateAttributeWithStatusCheck = async (db, dbId, collectio
221
608
  }
222
609
  }
223
610
  }
224
- console.log(chalk.red(`❌ Failed to create attribute '${attribute.key}' after ${maxRetries + 1} attempts`));
611
+ MessageFormatter.info(chalk.red(`❌ Failed to create attribute '${attribute.key}' after ${maxRetries + 1} attempts`));
225
612
  return false;
226
613
  }
227
614
  catch (error) {
228
- console.log(chalk.red(`Error creating attribute '${attribute.key}': ${error}`));
615
+ MessageFormatter.info(chalk.red(`Error creating attribute '${attribute.key}': ${error}`));
229
616
  if (retryCount < maxRetries) {
230
- console.log(chalk.yellow(`Retrying attribute '${attribute.key}' due to error...`));
617
+ MessageFormatter.info(chalk.yellow(`Retrying attribute '${attribute.key}' due to error...`));
231
618
  // Wait a bit before retry
232
619
  await delay(2000);
233
620
  return await createOrUpdateAttributeWithStatusCheck(db, dbId, collection, attribute, retryCount + 1, maxRetries);
@@ -243,7 +630,6 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
243
630
  try {
244
631
  const collectionAttr = collection.attributes.find((attr) => attr.key === attribute.key);
245
632
  foundAttribute = parseAttribute(collectionAttr);
246
- // console.log(`Found attribute: ${JSON.stringify(foundAttribute)}`);
247
633
  }
248
634
  catch (error) {
249
635
  foundAttribute = undefined;
@@ -257,7 +643,7 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
257
643
  else if (foundAttribute &&
258
644
  !attributesSame(foundAttribute, attribute) &&
259
645
  updateEnabled) {
260
- // console.log(
646
+ // MessageFormatter.info(
261
647
  // `Updating attribute with same key ${attribute.key} but different values`
262
648
  // );
263
649
  finalAttribute = {
@@ -269,11 +655,15 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
269
655
  else if (!updateEnabled &&
270
656
  foundAttribute &&
271
657
  !attributesSame(foundAttribute, attribute)) {
272
- await db.deleteAttribute(dbId, collection.$id, attribute.key);
273
- console.log(`Deleted attribute: ${attribute.key} to recreate it because they diff (update disabled temporarily)`);
658
+ if (isDatabaseAdapter(db)) {
659
+ await db.deleteAttribute({ databaseId: dbId, tableId: collection.$id, key: attribute.key });
660
+ }
661
+ else {
662
+ await db.deleteAttribute(dbId, collection.$id, attribute.key);
663
+ }
664
+ MessageFormatter.info(`Deleted attribute: ${attribute.key} to recreate it because they diff (update disabled temporarily)`);
274
665
  return "processed";
275
666
  }
276
- // console.log(`${action}-ing attribute: ${finalAttribute.key}`);
277
667
  // Relationship attribute logic with adjustments
278
668
  let collectionFoundViaRelatedCollection;
279
669
  let relatedCollectionId;
@@ -281,7 +671,9 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
281
671
  finalAttribute.relatedCollection) {
282
672
  // First try treating relatedCollection as an ID directly
283
673
  try {
284
- const byIdCollection = await db.getCollection(dbId, finalAttribute.relatedCollection);
674
+ const byIdCollection = isDatabaseAdapter(db)
675
+ ? (await db.getTable({ databaseId: dbId, tableId: finalAttribute.relatedCollection })).data
676
+ : await db.getCollection(dbId, finalAttribute.relatedCollection);
285
677
  collectionFoundViaRelatedCollection = byIdCollection;
286
678
  relatedCollectionId = byIdCollection.$id;
287
679
  // Cache by name for subsequent lookups
@@ -293,28 +685,34 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
293
685
  if (!collectionFoundViaRelatedCollection && nameToIdMapping.has(finalAttribute.relatedCollection)) {
294
686
  relatedCollectionId = nameToIdMapping.get(finalAttribute.relatedCollection);
295
687
  try {
296
- collectionFoundViaRelatedCollection = await db.getCollection(dbId, relatedCollectionId);
688
+ collectionFoundViaRelatedCollection = isDatabaseAdapter(db)
689
+ ? (await db.getTable({ databaseId: dbId, tableId: relatedCollectionId })).data
690
+ : await db.getCollection(dbId, relatedCollectionId);
297
691
  }
298
692
  catch (e) {
299
- // console.log(
693
+ // MessageFormatter.info(
300
694
  // `Collection not found: ${finalAttribute.relatedCollection} when nameToIdMapping was set`
301
695
  // );
302
696
  collectionFoundViaRelatedCollection = undefined;
303
697
  }
304
698
  }
305
699
  else if (!collectionFoundViaRelatedCollection) {
306
- const collectionsPulled = await db.listCollections(dbId, [
307
- Query.equal("name", finalAttribute.relatedCollection),
308
- ]);
309
- if (collectionsPulled.total > 0) {
310
- collectionFoundViaRelatedCollection = collectionsPulled.collections[0];
311
- relatedCollectionId = collectionFoundViaRelatedCollection.$id;
312
- nameToIdMapping.set(finalAttribute.relatedCollection, relatedCollectionId);
700
+ const collectionsPulled = isDatabaseAdapter(db)
701
+ ? await db.listTables({ databaseId: dbId, queries: [Query.equal("name", finalAttribute.relatedCollection)] })
702
+ : await db.listCollections(dbId, [Query.equal("name", finalAttribute.relatedCollection)]);
703
+ if (collectionsPulled.total && collectionsPulled.total > 0) {
704
+ collectionFoundViaRelatedCollection = isDatabaseAdapter(db)
705
+ ? collectionsPulled.tables?.[0]
706
+ : collectionsPulled.collections?.[0];
707
+ relatedCollectionId = collectionFoundViaRelatedCollection?.$id;
708
+ if (relatedCollectionId) {
709
+ nameToIdMapping.set(finalAttribute.relatedCollection, relatedCollectionId);
710
+ }
313
711
  }
314
712
  }
315
713
  // ONLY queue relationship attributes that have actual unresolved dependencies
316
714
  if (!(relatedCollectionId && collectionFoundViaRelatedCollection)) {
317
- console.log(chalk.yellow(`⏳ Queueing relationship attribute '${finalAttribute.key}' - related collection '${finalAttribute.relatedCollection}' not found yet`));
715
+ MessageFormatter.info(chalk.yellow(`⏳ Queueing relationship attribute '${finalAttribute.key}' - related collection '${finalAttribute.relatedCollection}' not found yet`));
318
716
  enqueueOperation({
319
717
  type: "attribute",
320
718
  collectionId: collection.$id,
@@ -326,158 +724,12 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
326
724
  }
327
725
  }
328
726
  finalAttribute = parseAttribute(finalAttribute);
329
- // console.log(`Final Attribute: ${JSON.stringify(finalAttribute)}`);
330
- switch (finalAttribute.type) {
331
- case "string":
332
- if (action === "create") {
333
- await tryAwaitWithRetry(async () => await db.createStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.size, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
334
- ? finalAttribute.xdefault
335
- : null, finalAttribute.array || false, finalAttribute.encrypted));
336
- }
337
- else {
338
- await tryAwaitWithRetry(async () => await db.updateStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
339
- ? finalAttribute.xdefault
340
- : null, finalAttribute.size));
341
- }
342
- break;
343
- case "integer":
344
- if (action === "create") {
345
- if (finalAttribute.min &&
346
- BigInt(finalAttribute.min) === BigInt(-9223372036854776000)) {
347
- finalAttribute.min = undefined;
348
- }
349
- if (finalAttribute.max &&
350
- BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
351
- finalAttribute.max = undefined;
352
- }
353
- const minValue = finalAttribute.min !== undefined && finalAttribute.min !== null
354
- ? parseInt(finalAttribute.min)
355
- : -9007199254740991;
356
- const maxValue = finalAttribute.max !== undefined && finalAttribute.max !== null
357
- ? parseInt(finalAttribute.max)
358
- : 9007199254740991;
359
- console.log(`DEBUG: Creating integer attribute '${finalAttribute.key}' with min=${minValue}, max=${maxValue}, minType=${typeof minValue}, maxType=${typeof maxValue}`);
360
- await tryAwaitWithRetry(async () => await db.createIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, minValue, maxValue, finalAttribute.xdefault !== undefined && !finalAttribute.required
361
- ? finalAttribute.xdefault
362
- : null, finalAttribute.array || false));
363
- }
364
- else {
365
- if (finalAttribute.min &&
366
- BigInt(finalAttribute.min) === BigInt(-9223372036854776000)) {
367
- finalAttribute.min = undefined;
368
- }
369
- if (finalAttribute.max &&
370
- BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
371
- finalAttribute.max = undefined;
372
- }
373
- const minValue = finalAttribute.min !== undefined && finalAttribute.min !== null
374
- ? parseInt(finalAttribute.min)
375
- : -9007199254740991;
376
- const maxValue = finalAttribute.max !== undefined && finalAttribute.max !== null
377
- ? parseInt(finalAttribute.max)
378
- : 9007199254740991;
379
- console.log(`DEBUG: Updating integer attribute '${finalAttribute.key}' with min=${minValue}, max=${maxValue}, minType=${typeof minValue}, maxType=${typeof maxValue}`);
380
- await tryAwaitWithRetry(async () => await db.updateIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
381
- ? finalAttribute.xdefault
382
- : null, minValue, maxValue));
383
- }
384
- break;
385
- case "double":
386
- case "float": // Backward compatibility
387
- if (action === "create") {
388
- await tryAwaitWithRetry(async () => await db.createFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min, finalAttribute.max, finalAttribute.xdefault !== undefined && !finalAttribute.required
389
- ? finalAttribute.xdefault
390
- : null, finalAttribute.array || false));
391
- }
392
- else {
393
- await tryAwaitWithRetry(async () => await db.updateFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min, finalAttribute.max, finalAttribute.xdefault !== undefined && !finalAttribute.required
394
- ? finalAttribute.xdefault
395
- : null));
396
- }
397
- break;
398
- case "boolean":
399
- if (action === "create") {
400
- await tryAwaitWithRetry(async () => await db.createBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
401
- ? finalAttribute.xdefault
402
- : null, finalAttribute.array || false));
403
- }
404
- else {
405
- await tryAwaitWithRetry(async () => await db.updateBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
406
- ? finalAttribute.xdefault
407
- : null));
408
- }
409
- break;
410
- case "datetime":
411
- if (action === "create") {
412
- await tryAwaitWithRetry(async () => await db.createDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
413
- ? finalAttribute.xdefault
414
- : null, finalAttribute.array || false));
415
- }
416
- else {
417
- await tryAwaitWithRetry(async () => await db.updateDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
418
- ? finalAttribute.xdefault
419
- : null));
420
- }
421
- break;
422
- case "email":
423
- if (action === "create") {
424
- await tryAwaitWithRetry(async () => await db.createEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
425
- ? finalAttribute.xdefault
426
- : null, finalAttribute.array || false));
427
- }
428
- else {
429
- await tryAwaitWithRetry(async () => await db.updateEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
430
- ? finalAttribute.xdefault
431
- : null));
432
- }
433
- break;
434
- case "ip":
435
- if (action === "create") {
436
- await tryAwaitWithRetry(async () => await db.createIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
437
- ? finalAttribute.xdefault
438
- : null, finalAttribute.array || false));
439
- }
440
- else {
441
- await tryAwaitWithRetry(async () => await db.updateIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
442
- ? finalAttribute.xdefault
443
- : null));
444
- }
445
- break;
446
- case "url":
447
- if (action === "create") {
448
- await tryAwaitWithRetry(async () => await db.createUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
449
- ? finalAttribute.xdefault
450
- : null, finalAttribute.array || false));
451
- }
452
- else {
453
- await tryAwaitWithRetry(async () => await db.updateUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
454
- ? finalAttribute.xdefault
455
- : null));
456
- }
457
- break;
458
- case "enum":
459
- if (action === "create") {
460
- await tryAwaitWithRetry(async () => await db.createEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
461
- ? finalAttribute.xdefault
462
- : null, finalAttribute.array || false));
463
- }
464
- else {
465
- await tryAwaitWithRetry(async () => await db.updateEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
466
- ? finalAttribute.xdefault
467
- : null));
468
- }
469
- break;
470
- case "relationship":
471
- if (action === "create") {
472
- await tryAwaitWithRetry(async () => await db.createRelationshipAttribute(dbId, collection.$id, relatedCollectionId, finalAttribute.relationType, finalAttribute.twoWay, finalAttribute.key, finalAttribute.twoWayKey, finalAttribute.onDelete));
473
- }
474
- else {
475
- await tryAwaitWithRetry(async () => await db.updateRelationshipAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.onDelete));
476
- }
477
- break;
478
- default:
479
- console.error("Invalid attribute type");
480
- break;
727
+ // Use adapter-based attribute creation/update
728
+ if (action === "create") {
729
+ await tryAwaitWithRetry(async () => await createAttributeViaAdapter(db, dbId, collection.$id, finalAttribute));
730
+ }
731
+ else {
732
+ await tryAwaitWithRetry(async () => await updateAttributeViaAdapter(db, dbId, collection.$id, finalAttribute));
481
733
  }
482
734
  return "processed";
483
735
  };
@@ -485,7 +737,7 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
485
737
  * Enhanced collection attribute creation with proper status monitoring
486
738
  */
487
739
  export const createUpdateCollectionAttributesWithStatusCheck = async (db, dbId, collection, attributes) => {
488
- console.log(chalk.green(`Creating/Updating attributes for collection: ${collection.name} with status monitoring`));
740
+ MessageFormatter.info(chalk.green(`Creating/Updating attributes for collection: ${collection.name} with status monitoring`));
489
741
  const existingAttributes =
490
742
  // @ts-expect-error
491
743
  collection.attributes.map((attr) => parseAttribute(attr)) || [];
@@ -494,28 +746,44 @@ export const createUpdateCollectionAttributesWithStatusCheck = async (db, dbId,
494
746
  // Handle attribute removal first
495
747
  if (attributesToRemove.length > 0) {
496
748
  if (indexesToRemove.length > 0) {
497
- console.log(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove
749
+ MessageFormatter.info(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove
498
750
  .map((index) => index.key)
499
751
  .join(", ")}`));
500
752
  for (const index of indexesToRemove) {
501
- await tryAwaitWithRetry(async () => await db.deleteIndex(dbId, collection.$id, index.key));
753
+ await tryAwaitWithRetry(async () => {
754
+ if (isDatabaseAdapter(db)) {
755
+ await db.deleteIndex({ databaseId: dbId, tableId: collection.$id, key: index.key });
756
+ }
757
+ else {
758
+ await db.deleteIndex(dbId, collection.$id, index.key);
759
+ }
760
+ });
502
761
  await delay(500); // Longer delay for deletions
503
762
  }
504
763
  }
505
764
  for (const attr of attributesToRemove) {
506
- console.log(chalk.red(`Removing attribute: ${attr.key} as it is no longer in the collection`));
507
- await tryAwaitWithRetry(async () => await db.deleteAttribute(dbId, collection.$id, attr.key));
765
+ MessageFormatter.info(chalk.red(`Removing attribute: ${attr.key} as it is no longer in the collection`));
766
+ await tryAwaitWithRetry(async () => {
767
+ if (isDatabaseAdapter(db)) {
768
+ await db.deleteAttribute({ databaseId: dbId, tableId: collection.$id, key: attr.key });
769
+ }
770
+ else {
771
+ await db.deleteAttribute(dbId, collection.$id, attr.key);
772
+ }
773
+ });
508
774
  await delay(500); // Longer delay for deletions
509
775
  }
510
776
  }
511
777
  // First, get fresh collection data and determine which attributes actually need processing
512
- console.log(chalk.blue(`Analyzing ${attributes.length} attributes to determine which need processing...`));
778
+ MessageFormatter.info(chalk.blue(`Analyzing ${attributes.length} attributes to determine which need processing...`));
513
779
  let currentCollection = collection;
514
780
  try {
515
- currentCollection = await db.getCollection(dbId, collection.$id);
781
+ currentCollection = isDatabaseAdapter(db)
782
+ ? (await db.getTable({ databaseId: dbId, tableId: collection.$id })).data
783
+ : await db.getCollection(dbId, collection.$id);
516
784
  }
517
785
  catch (error) {
518
- console.log(chalk.yellow(`Warning: Could not refresh collection data: ${error}`));
786
+ MessageFormatter.info(chalk.yellow(`Warning: Could not refresh collection data: ${error}`));
519
787
  }
520
788
  const existingAttributesMap = new Map();
521
789
  try {
@@ -525,29 +793,34 @@ export const createUpdateCollectionAttributesWithStatusCheck = async (db, dbId,
525
793
  parsedAttributes.forEach((attr) => existingAttributesMap.set(attr.key, attr));
526
794
  }
527
795
  catch (error) {
528
- console.log(chalk.yellow(`Warning: Could not parse existing attributes: ${error}`));
796
+ MessageFormatter.info(chalk.yellow(`Warning: Could not parse existing attributes: ${error}`));
529
797
  }
530
- // Filter to only attributes that need processing (new or changed)
798
+ // Filter to only attributes that need processing (new, changed, or not yet processed)
531
799
  const attributesToProcess = attributes.filter((attribute) => {
800
+ // Skip if already processed in this session
801
+ if (isAttributeProcessed(currentCollection.$id, attribute.key)) {
802
+ MessageFormatter.info(chalk.gray(`⏭️ Attribute '${attribute.key}' already processed in this session (skipping)`));
803
+ return false;
804
+ }
532
805
  const existing = existingAttributesMap.get(attribute.key);
533
806
  if (!existing) {
534
- console.log(chalk.blue(`➕ New attribute: ${attribute.key}`));
807
+ MessageFormatter.info(`➕ New attribute: ${attribute.key}`);
535
808
  return true;
536
809
  }
537
810
  const needsUpdate = !attributesSame(existing, attribute);
538
811
  if (needsUpdate) {
539
- console.log(chalk.blue(`🔄 Changed attribute: ${attribute.key}`));
812
+ MessageFormatter.info(`🔄 Changed attribute: ${attribute.key}`);
540
813
  }
541
814
  else {
542
- console.log(chalk.gray(`✅ Unchanged attribute: ${attribute.key} (skipping)`));
815
+ MessageFormatter.info(chalk.gray(`✅ Unchanged attribute: ${attribute.key} (skipping)`));
543
816
  }
544
817
  return needsUpdate;
545
818
  });
546
819
  if (attributesToProcess.length === 0) {
547
- console.log(chalk.green(`✅ All ${attributes.length} attributes are already up to date for collection: ${collection.name}`));
820
+ MessageFormatter.info(chalk.green(`✅ All ${attributes.length} attributes are already up to date for collection: ${collection.name}`));
548
821
  return true;
549
822
  }
550
- console.log(chalk.blue(`Creating ${attributesToProcess.length} attributes sequentially with status monitoring...`));
823
+ MessageFormatter.info(chalk.blue(`Creating ${attributesToProcess.length} attributes sequentially with status monitoring...`));
551
824
  let remainingAttributes = [...attributesToProcess];
552
825
  let overallRetryCount = 0;
553
826
  const maxOverallRetries = 3;
@@ -555,58 +828,64 @@ export const createUpdateCollectionAttributesWithStatusCheck = async (db, dbId,
555
828
  overallRetryCount < maxOverallRetries) {
556
829
  const attributesToProcessThisRound = [...remainingAttributes];
557
830
  remainingAttributes = []; // Reset for next iteration
558
- console.log(chalk.blue(`\n=== Attempt ${overallRetryCount + 1}/${maxOverallRetries} - Processing ${attributesToProcessThisRound.length} attributes ===`));
831
+ MessageFormatter.info(chalk.blue(`\n=== Attempt ${overallRetryCount + 1}/${maxOverallRetries} - Processing ${attributesToProcessThisRound.length} attributes ===`));
559
832
  for (const attribute of attributesToProcessThisRound) {
560
- console.log(chalk.blue(`\n--- Processing attribute: ${attribute.key} ---`));
833
+ MessageFormatter.info(chalk.blue(`\n--- Processing attribute: ${attribute.key} ---`));
561
834
  const success = await createOrUpdateAttributeWithStatusCheck(db, dbId, currentCollection, attribute);
562
835
  if (success) {
563
- console.log(chalk.green(`✅ Successfully created attribute: ${attribute.key}`));
836
+ MessageFormatter.info(chalk.green(`✅ Successfully created attribute: ${attribute.key}`));
837
+ // Mark this specific attribute as processed
838
+ markAttributeProcessed(currentCollection.$id, attribute.key);
564
839
  // Get updated collection data for next iteration
565
840
  try {
566
- currentCollection = await db.getCollection(dbId, collection.$id);
841
+ currentCollection = isDatabaseAdapter(db)
842
+ ? (await db.getTable({ databaseId: dbId, tableId: collection.$id })).data
843
+ : await db.getCollection(dbId, collection.$id);
567
844
  }
568
845
  catch (error) {
569
- console.log(chalk.yellow(`Warning: Could not refresh collection data: ${error}`));
846
+ MessageFormatter.info(chalk.yellow(`Warning: Could not refresh collection data: ${error}`));
570
847
  }
571
848
  // Add delay between successful attributes
572
849
  await delay(1000);
573
850
  }
574
851
  else {
575
- console.log(chalk.red(`❌ Failed to create attribute: ${attribute.key}, will retry in next round`));
852
+ MessageFormatter.info(chalk.red(`❌ Failed to create attribute: ${attribute.key}, will retry in next round`));
576
853
  remainingAttributes.push(attribute); // Add back to retry list
577
854
  }
578
855
  }
579
856
  if (remainingAttributes.length === 0) {
580
- console.log(chalk.green(`\n✅ Successfully created all ${attributesToProcess.length} attributes for collection: ${collection.name}`));
857
+ MessageFormatter.info(chalk.green(`\n✅ Successfully created all ${attributesToProcess.length} attributes for collection: ${collection.name}`));
581
858
  return true;
582
859
  }
583
860
  overallRetryCount++;
584
861
  if (overallRetryCount < maxOverallRetries) {
585
- console.log(chalk.yellow(`\n⏳ Waiting 5 seconds before retrying ${attributesToProcess.length} failed attributes...`));
862
+ MessageFormatter.info(chalk.yellow(`\n⏳ Waiting 5 seconds before retrying ${attributesToProcess.length} failed attributes...`));
586
863
  await delay(5000);
587
864
  // Refresh collection data before retry
588
865
  try {
589
- currentCollection = await db.getCollection(dbId, collection.$id);
590
- console.log(chalk.blue(`Refreshed collection data for retry`));
866
+ currentCollection = isDatabaseAdapter(db)
867
+ ? (await db.getTable({ databaseId: dbId, tableId: collection.$id })).data
868
+ : await db.getCollection(dbId, collection.$id);
869
+ MessageFormatter.info(`Refreshed collection data for retry`);
591
870
  }
592
871
  catch (error) {
593
- console.log(chalk.yellow(`Warning: Could not refresh collection data for retry: ${error}`));
872
+ MessageFormatter.info(chalk.yellow(`Warning: Could not refresh collection data for retry: ${error}`));
594
873
  }
595
874
  }
596
875
  }
597
876
  // If we get here, some attributes still failed after all retries
598
877
  if (attributesToProcess.length > 0) {
599
- console.log(chalk.red(`\n❌ Failed to create ${attributesToProcess.length} attributes after ${maxOverallRetries} attempts: ${attributesToProcess
878
+ MessageFormatter.info(chalk.red(`\n❌ Failed to create ${attributesToProcess.length} attributes after ${maxOverallRetries} attempts: ${attributesToProcess
600
879
  .map((a) => a.key)
601
880
  .join(", ")}`));
602
- console.log(chalk.red(`This may indicate a fundamental issue with the attribute definitions or Appwrite instance`));
881
+ MessageFormatter.info(chalk.red(`This may indicate a fundamental issue with the attribute definitions or Appwrite instance`));
603
882
  return false;
604
883
  }
605
- console.log(chalk.green(`\n✅ Successfully created all ${attributes.length} attributes for collection: ${collection.name}`));
884
+ MessageFormatter.info(chalk.green(`\n✅ Successfully created all ${attributes.length} attributes for collection: ${collection.name}`));
606
885
  return true;
607
886
  };
608
887
  export const createUpdateCollectionAttributes = async (db, dbId, collection, attributes) => {
609
- console.log(chalk.green(`Creating/Updating attributes for collection: ${collection.name}`));
888
+ MessageFormatter.info(chalk.green(`Creating/Updating attributes for collection: ${collection.name}`));
610
889
  const existingAttributes =
611
890
  // @ts-expect-error
612
891
  collection.attributes.map((attr) => parseAttribute(attr)) || [];
@@ -614,17 +893,31 @@ export const createUpdateCollectionAttributes = async (db, dbId, collection, att
614
893
  const indexesToRemove = collection.indexes.filter((index) => attributesToRemove.some((attr) => index.attributes.includes(attr.key)));
615
894
  if (attributesToRemove.length > 0) {
616
895
  if (indexesToRemove.length > 0) {
617
- console.log(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove
896
+ MessageFormatter.info(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove
618
897
  .map((index) => index.key)
619
898
  .join(", ")}`));
620
899
  for (const index of indexesToRemove) {
621
- await tryAwaitWithRetry(async () => await db.deleteIndex(dbId, collection.$id, index.key));
900
+ await tryAwaitWithRetry(async () => {
901
+ if (isDatabaseAdapter(db)) {
902
+ await db.deleteIndex({ databaseId: dbId, tableId: collection.$id, key: index.key });
903
+ }
904
+ else {
905
+ await db.deleteIndex(dbId, collection.$id, index.key);
906
+ }
907
+ });
622
908
  await delay(100);
623
909
  }
624
910
  }
625
911
  for (const attr of attributesToRemove) {
626
- console.log(chalk.red(`Removing attribute: ${attr.key} as it is no longer in the collection`));
627
- await tryAwaitWithRetry(async () => await db.deleteAttribute(dbId, collection.$id, attr.key));
912
+ MessageFormatter.info(chalk.red(`Removing attribute: ${attr.key} as it is no longer in the collection`));
913
+ await tryAwaitWithRetry(async () => {
914
+ if (isDatabaseAdapter(db)) {
915
+ await db.deleteAttribute({ databaseId: dbId, tableId: collection.$id, key: attr.key });
916
+ }
917
+ else {
918
+ await db.deleteAttribute(dbId, collection.$id, attr.key);
919
+ }
920
+ });
628
921
  await delay(50);
629
922
  }
630
923
  }
@@ -635,11 +928,11 @@ export const createUpdateCollectionAttributes = async (db, dbId, collection, att
635
928
  const results = await Promise.allSettled(attributePromises);
636
929
  results.forEach((result) => {
637
930
  if (result.status === "rejected") {
638
- console.error("An attribute promise was rejected:", result.reason);
931
+ MessageFormatter.error("An attribute promise was rejected:", result.reason);
639
932
  }
640
933
  });
641
934
  // Add delay after each batch
642
935
  await delay(200);
643
936
  }
644
- console.log(`Finished creating/updating attributes for collection: ${collection.name}`);
937
+ MessageFormatter.info(`Finished creating/updating attributes for collection: ${collection.name}`);
645
938
  };