appwrite-utils-cli 1.11.0 → 1.12.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 (250) hide show
  1. package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -1
  2. package/dist/adapters/index.js +10 -0
  3. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  4. package/dist/backups/operations/bucketBackup.js +197 -0
  5. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  6. package/dist/backups/operations/collectionBackup.js +201 -0
  7. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  8. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  9. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  10. package/dist/backups/schemas/bucketManifest.js +33 -0
  11. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  12. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  13. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  14. package/dist/backups/tracking/centralizedTracking.js +274 -0
  15. package/dist/cli/commands/configCommands.d.ts +8 -0
  16. package/dist/cli/commands/configCommands.js +210 -0
  17. package/dist/cli/commands/databaseCommands.d.ts +14 -0
  18. package/dist/cli/commands/databaseCommands.js +696 -0
  19. package/dist/cli/commands/functionCommands.d.ts +7 -0
  20. package/dist/cli/commands/functionCommands.js +330 -0
  21. package/dist/cli/commands/importFileCommands.d.ts +7 -0
  22. package/dist/cli/commands/importFileCommands.js +674 -0
  23. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  24. package/dist/cli/commands/schemaCommands.js +169 -0
  25. package/dist/cli/commands/storageCommands.d.ts +5 -0
  26. package/dist/cli/commands/storageCommands.js +142 -0
  27. package/dist/cli/commands/transferCommands.d.ts +5 -0
  28. package/dist/cli/commands/transferCommands.js +382 -0
  29. package/dist/collections/columns.d.ts +13 -0
  30. package/dist/collections/columns.js +1339 -0
  31. package/dist/collections/indexes.d.ts +12 -0
  32. package/dist/collections/indexes.js +215 -0
  33. package/dist/collections/methods.d.ts +19 -0
  34. package/dist/collections/methods.js +605 -0
  35. package/dist/collections/tableOperations.d.ts +87 -0
  36. package/dist/collections/tableOperations.js +466 -0
  37. package/dist/collections/transferOperations.d.ts +8 -0
  38. package/dist/collections/transferOperations.js +411 -0
  39. package/dist/collections/wipeOperations.d.ts +17 -0
  40. package/dist/collections/wipeOperations.js +306 -0
  41. package/dist/databases/methods.d.ts +6 -0
  42. package/dist/databases/methods.js +35 -0
  43. package/dist/databases/setup.d.ts +5 -0
  44. package/dist/databases/setup.js +45 -0
  45. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  46. package/dist/examples/yamlTerminologyExample.js +272 -0
  47. package/dist/functions/deployments.d.ts +4 -0
  48. package/dist/functions/deployments.js +146 -0
  49. package/dist/functions/fnConfigDiscovery.d.ts +3 -0
  50. package/dist/functions/fnConfigDiscovery.js +108 -0
  51. package/dist/functions/methods.d.ts +16 -0
  52. package/dist/functions/methods.js +174 -0
  53. package/dist/init.d.ts +2 -0
  54. package/dist/init.js +57 -0
  55. package/dist/interactiveCLI.d.ts +36 -0
  56. package/dist/interactiveCLI.js +952 -0
  57. package/dist/main.d.ts +2 -0
  58. package/dist/main.js +1125 -0
  59. package/dist/migrations/afterImportActions.d.ts +17 -0
  60. package/dist/migrations/afterImportActions.js +305 -0
  61. package/dist/migrations/appwriteToX.d.ts +211 -0
  62. package/dist/migrations/appwriteToX.js +493 -0
  63. package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
  64. package/dist/migrations/comprehensiveTransfer.js +1315 -0
  65. package/dist/migrations/dataLoader.d.ts +755 -0
  66. package/dist/migrations/dataLoader.js +1272 -0
  67. package/dist/migrations/importController.d.ts +25 -0
  68. package/dist/migrations/importController.js +283 -0
  69. package/dist/migrations/importDataActions.d.ts +50 -0
  70. package/dist/migrations/importDataActions.js +230 -0
  71. package/dist/migrations/relationships.d.ts +29 -0
  72. package/dist/migrations/relationships.js +203 -0
  73. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  74. package/dist/migrations/services/DataTransformationService.js +158 -0
  75. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  76. package/dist/migrations/services/FileHandlerService.js +236 -0
  77. package/dist/migrations/services/ImportOrchestrator.d.ts +99 -0
  78. package/dist/migrations/services/ImportOrchestrator.js +493 -0
  79. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  80. package/dist/migrations/services/RateLimitManager.js +279 -0
  81. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  82. package/dist/migrations/services/RelationshipResolver.js +332 -0
  83. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  84. package/dist/migrations/services/UserMappingService.js +277 -0
  85. package/dist/migrations/services/ValidationService.d.ts +74 -0
  86. package/dist/migrations/services/ValidationService.js +260 -0
  87. package/dist/migrations/transfer.d.ts +30 -0
  88. package/dist/migrations/transfer.js +661 -0
  89. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
  90. package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
  91. package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
  92. package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
  93. package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
  94. package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
  95. package/dist/schemas/authUser.d.ts +24 -0
  96. package/dist/schemas/authUser.js +17 -0
  97. package/dist/setup.d.ts +2 -0
  98. package/{src/setup.ts → dist/setup.js} +0 -3
  99. package/dist/setupCommands.d.ts +58 -0
  100. package/dist/setupCommands.js +489 -0
  101. package/dist/setupController.d.ts +9 -0
  102. package/dist/setupController.js +34 -0
  103. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  104. package/dist/shared/backupMetadataSchema.js +38 -0
  105. package/dist/shared/backupTracking.d.ts +18 -0
  106. package/dist/shared/backupTracking.js +176 -0
  107. package/dist/shared/confirmationDialogs.d.ts +75 -0
  108. package/dist/shared/confirmationDialogs.js +236 -0
  109. package/dist/shared/migrationHelpers.d.ts +61 -0
  110. package/dist/shared/migrationHelpers.js +145 -0
  111. package/{src/shared/operationLogger.ts → dist/shared/operationLogger.d.ts} +1 -11
  112. package/dist/shared/operationLogger.js +12 -0
  113. package/dist/shared/operationQueue.d.ts +40 -0
  114. package/dist/shared/operationQueue.js +310 -0
  115. package/dist/shared/operationsTable.d.ts +26 -0
  116. package/dist/shared/operationsTable.js +287 -0
  117. package/dist/shared/operationsTableSchema.d.ts +48 -0
  118. package/dist/shared/operationsTableSchema.js +35 -0
  119. package/dist/shared/progressManager.d.ts +62 -0
  120. package/dist/shared/progressManager.js +215 -0
  121. package/dist/shared/relationshipExtractor.d.ts +56 -0
  122. package/dist/shared/relationshipExtractor.js +138 -0
  123. package/dist/shared/selectionDialogs.d.ts +220 -0
  124. package/dist/shared/selectionDialogs.js +588 -0
  125. package/dist/storage/backupCompression.d.ts +20 -0
  126. package/dist/storage/backupCompression.js +67 -0
  127. package/dist/storage/methods.d.ts +44 -0
  128. package/dist/storage/methods.js +475 -0
  129. package/dist/storage/schemas.d.ts +842 -0
  130. package/dist/storage/schemas.js +175 -0
  131. package/dist/tables/indexManager.d.ts +65 -0
  132. package/dist/tables/indexManager.js +294 -0
  133. package/{src/types.ts → dist/types.d.ts} +1 -6
  134. package/dist/types.js +3 -0
  135. package/dist/users/methods.d.ts +16 -0
  136. package/dist/users/methods.js +276 -0
  137. package/dist/utils/configMigration.d.ts +1 -0
  138. package/dist/utils/configMigration.js +261 -0
  139. package/dist/utils/index.js +2 -0
  140. package/dist/utils/loadConfigs.d.ts +50 -0
  141. package/dist/utils/loadConfigs.js +357 -0
  142. package/dist/utils/setupFiles.d.ts +4 -0
  143. package/dist/utils/setupFiles.js +1190 -0
  144. package/dist/utilsController.d.ts +114 -0
  145. package/dist/utilsController.js +898 -0
  146. package/package.json +6 -3
  147. package/CHANGELOG.md +0 -35
  148. package/CONFIG_TODO.md +0 -1189
  149. package/SELECTION_DIALOGS.md +0 -146
  150. package/SERVICE_IMPLEMENTATION_REPORT.md +0 -462
  151. package/scripts/copy-templates.ts +0 -23
  152. package/src/backups/operations/bucketBackup.ts +0 -277
  153. package/src/backups/operations/collectionBackup.ts +0 -310
  154. package/src/backups/operations/comprehensiveBackup.ts +0 -342
  155. package/src/backups/schemas/bucketManifest.ts +0 -78
  156. package/src/backups/schemas/comprehensiveManifest.ts +0 -76
  157. package/src/backups/tracking/centralizedTracking.ts +0 -352
  158. package/src/cli/commands/configCommands.ts +0 -265
  159. package/src/cli/commands/databaseCommands.ts +0 -931
  160. package/src/cli/commands/functionCommands.ts +0 -419
  161. package/src/cli/commands/importFileCommands.ts +0 -815
  162. package/src/cli/commands/schemaCommands.ts +0 -200
  163. package/src/cli/commands/storageCommands.ts +0 -151
  164. package/src/cli/commands/transferCommands.ts +0 -454
  165. package/src/collections/attributes.ts.backup +0 -1555
  166. package/src/collections/columns.ts +0 -2025
  167. package/src/collections/indexes.ts +0 -350
  168. package/src/collections/methods.ts +0 -714
  169. package/src/collections/tableOperations.ts +0 -542
  170. package/src/collections/transferOperations.ts +0 -589
  171. package/src/collections/wipeOperations.ts +0 -449
  172. package/src/databases/methods.ts +0 -49
  173. package/src/databases/setup.ts +0 -77
  174. package/src/examples/yamlTerminologyExample.ts +0 -346
  175. package/src/functions/deployments.ts +0 -221
  176. package/src/functions/fnConfigDiscovery.ts +0 -103
  177. package/src/functions/methods.ts +0 -284
  178. package/src/init.ts +0 -62
  179. package/src/interactiveCLI.ts +0 -1201
  180. package/src/main.ts +0 -1517
  181. package/src/migrations/afterImportActions.ts +0 -579
  182. package/src/migrations/appwriteToX.ts +0 -668
  183. package/src/migrations/comprehensiveTransfer.ts +0 -2285
  184. package/src/migrations/dataLoader.ts +0 -1729
  185. package/src/migrations/importController.ts +0 -440
  186. package/src/migrations/importDataActions.ts +0 -315
  187. package/src/migrations/relationships.ts +0 -333
  188. package/src/migrations/services/DataTransformationService.ts +0 -196
  189. package/src/migrations/services/FileHandlerService.ts +0 -311
  190. package/src/migrations/services/ImportOrchestrator.ts +0 -675
  191. package/src/migrations/services/RateLimitManager.ts +0 -363
  192. package/src/migrations/services/RelationshipResolver.ts +0 -461
  193. package/src/migrations/services/UserMappingService.ts +0 -345
  194. package/src/migrations/services/ValidationService.ts +0 -349
  195. package/src/migrations/transfer.ts +0 -1113
  196. package/src/migrations/yaml/YamlImportConfigLoader.ts +0 -439
  197. package/src/migrations/yaml/YamlImportIntegration.ts +0 -446
  198. package/src/migrations/yaml/generateImportSchemas.ts +0 -1354
  199. package/src/schemas/authUser.ts +0 -23
  200. package/src/setupCommands.ts +0 -602
  201. package/src/setupController.ts +0 -43
  202. package/src/shared/backupMetadataSchema.ts +0 -93
  203. package/src/shared/backupTracking.ts +0 -211
  204. package/src/shared/confirmationDialogs.ts +0 -327
  205. package/src/shared/migrationHelpers.ts +0 -232
  206. package/src/shared/operationQueue.ts +0 -376
  207. package/src/shared/operationsTable.ts +0 -338
  208. package/src/shared/operationsTableSchema.ts +0 -60
  209. package/src/shared/progressManager.ts +0 -278
  210. package/src/shared/relationshipExtractor.ts +0 -214
  211. package/src/shared/selectionDialogs.ts +0 -802
  212. package/src/storage/backupCompression.ts +0 -88
  213. package/src/storage/methods.ts +0 -711
  214. package/src/storage/schemas.ts +0 -205
  215. package/src/tables/indexManager.ts +0 -409
  216. package/src/types/node-appwrite-tablesdb.d.ts +0 -44
  217. package/src/users/methods.ts +0 -358
  218. package/src/utils/configMigration.ts +0 -348
  219. package/src/utils/loadConfigs.ts +0 -457
  220. package/src/utils/setupFiles.ts +0 -1236
  221. package/src/utilsController.ts +0 -1263
  222. package/tests/README.md +0 -497
  223. package/tests/adapters/AdapterFactory.test.ts +0 -277
  224. package/tests/integration/syncOperations.test.ts +0 -463
  225. package/tests/jest.config.js +0 -25
  226. package/tests/migration/configMigration.test.ts +0 -546
  227. package/tests/setup.ts +0 -62
  228. package/tests/testUtils.ts +0 -340
  229. package/tests/utils/loadConfigs.test.ts +0 -350
  230. package/tests/validation/configValidation.test.ts +0 -412
  231. package/tsconfig.json +0 -44
  232. /package/{src → dist}/functions/templates/count-docs-in-collection/README.md +0 -0
  233. /package/{src → dist}/functions/templates/count-docs-in-collection/src/main.ts +0 -0
  234. /package/{src → dist}/functions/templates/count-docs-in-collection/src/request.ts +0 -0
  235. /package/{src → dist}/functions/templates/hono-typescript/README.md +0 -0
  236. /package/{src → dist}/functions/templates/hono-typescript/src/adapters/request.ts +0 -0
  237. /package/{src → dist}/functions/templates/hono-typescript/src/adapters/response.ts +0 -0
  238. /package/{src → dist}/functions/templates/hono-typescript/src/app.ts +0 -0
  239. /package/{src → dist}/functions/templates/hono-typescript/src/context.ts +0 -0
  240. /package/{src → dist}/functions/templates/hono-typescript/src/main.ts +0 -0
  241. /package/{src → dist}/functions/templates/hono-typescript/src/middleware/appwrite.ts +0 -0
  242. /package/{src → dist}/functions/templates/typescript-node/README.md +0 -0
  243. /package/{src → dist}/functions/templates/typescript-node/src/context.ts +0 -0
  244. /package/{src → dist}/functions/templates/typescript-node/src/main.ts +0 -0
  245. /package/{src → dist}/functions/templates/uv/README.md +0 -0
  246. /package/{src → dist}/functions/templates/uv/pyproject.toml +0 -0
  247. /package/{src → dist}/functions/templates/uv/src/__init__.py +0 -0
  248. /package/{src → dist}/functions/templates/uv/src/context.py +0 -0
  249. /package/{src → dist}/functions/templates/uv/src/main.py +0 -0
  250. /package/{src/utils/index.ts → dist/utils/index.d.ts} +0 -0
@@ -0,0 +1,276 @@
1
+ import { AppwriteException, Databases, ID, Query, Users, } from "node-appwrite";
2
+ import { AuthUserSchema, } from "../schemas/authUser.js";
3
+ import { logger, MessageFormatter } from "appwrite-utils-helpers";
4
+ import { splitIntoBatches } from "../shared/migrationHelpers.js";
5
+ import { getAppwriteClient, tryAwaitWithRetry, } from "appwrite-utils-helpers";
6
+ import { isUndefined } from "es-toolkit/compat";
7
+ import { isEmpty } from "es-toolkit/compat";
8
+ export class UsersController {
9
+ config;
10
+ users;
11
+ static userFields = [
12
+ "email",
13
+ "name",
14
+ "password",
15
+ "phone",
16
+ "labels",
17
+ "prefs",
18
+ "userId",
19
+ "$createdAt",
20
+ "$updatedAt",
21
+ ];
22
+ constructor(config, db) {
23
+ this.config = config;
24
+ this.users = new Users(this.config.appwriteClient);
25
+ }
26
+ async wipeUsers() {
27
+ const allUsers = await this.getAllUsers();
28
+ MessageFormatter.progress("Deleting all users...", { prefix: "Users" });
29
+ const createBatches = (finalData, batchSize) => {
30
+ const finalBatches = [];
31
+ for (let i = 0; i < finalData.length; i += batchSize) {
32
+ finalBatches.push(finalData.slice(i, i + batchSize));
33
+ }
34
+ return finalBatches;
35
+ };
36
+ let usersDeleted = 0;
37
+ if (allUsers.length > 0) {
38
+ const batchedUserPromises = createBatches(allUsers, 25); // Batch size of 25
39
+ for (const batch of batchedUserPromises) {
40
+ MessageFormatter.progress(`Deleting ${batch.length} users...`, { prefix: "Users" });
41
+ await Promise.all(batch.map((user) => tryAwaitWithRetry(async () => await this.users.delete(user.$id))));
42
+ usersDeleted += batch.length;
43
+ if (usersDeleted % 100 === 0) {
44
+ MessageFormatter.progress(`Deleted ${usersDeleted} users...`, { prefix: "Users" });
45
+ }
46
+ }
47
+ }
48
+ else {
49
+ MessageFormatter.info("No users to delete", { prefix: "Users" });
50
+ }
51
+ }
52
+ async getAllUsers() {
53
+ const allUsers = [];
54
+ const users = await tryAwaitWithRetry(async () => await this.users.list([Query.limit(200)]));
55
+ if (users.users.length === 0) {
56
+ return [];
57
+ }
58
+ if (users.users.length === 200) {
59
+ let lastDocumentId = users.users[users.users.length - 1].$id;
60
+ allUsers.push(...users.users);
61
+ while (lastDocumentId) {
62
+ const moreUsers = await tryAwaitWithRetry(async () => await this.users.list([
63
+ Query.limit(200),
64
+ Query.cursorAfter(lastDocumentId),
65
+ ]));
66
+ allUsers.push(...moreUsers.users);
67
+ if (moreUsers.users.length < 200) {
68
+ break;
69
+ }
70
+ lastDocumentId = moreUsers.users[moreUsers.users.length - 1].$id;
71
+ }
72
+ }
73
+ else {
74
+ allUsers.push(...users.users);
75
+ }
76
+ return allUsers;
77
+ }
78
+ async createUsersAndReturn(items) {
79
+ const users = await Promise.all(items.map((item) => this.createUserAndReturn(item)));
80
+ return users;
81
+ }
82
+ async createUserAndReturn(item) {
83
+ try {
84
+ const user = await tryAwaitWithRetry(async () => {
85
+ const createdUser = await this.users.create(item.userId || ID.unique(), item.email || undefined, item.phone && item.phone.length < 15 && item.phone.startsWith("+")
86
+ ? item.phone
87
+ : undefined, `changeMe${item.email?.toLowerCase()}` || `changeMePlease`, item.name || undefined);
88
+ if (item.labels) {
89
+ await this.users.updateLabels(createdUser.$id, item.labels);
90
+ }
91
+ if (item.prefs) {
92
+ await this.users.updatePrefs(createdUser.$id, item.prefs);
93
+ }
94
+ return createdUser;
95
+ }); // Set throwError to true since we want to handle errors
96
+ return user;
97
+ }
98
+ catch (e) {
99
+ if (e instanceof Error) {
100
+ logger.error("FAILED CREATING USER: ", e.message, item);
101
+ }
102
+ }
103
+ }
104
+ async createAndCheckForUserAndReturn(item) {
105
+ let userToReturn = undefined;
106
+ try {
107
+ // Attempt to find an existing user by email or phone.
108
+ let foundUsers = [];
109
+ if (item.email) {
110
+ const foundUsersByEmail = await this.users.list([
111
+ Query.equal("email", item.email),
112
+ ]);
113
+ foundUsers = foundUsersByEmail.users;
114
+ }
115
+ if (item.phone) {
116
+ const foundUsersByPhone = await this.users.list([
117
+ Query.equal("phone", item.phone),
118
+ ]);
119
+ foundUsers = foundUsers.length
120
+ ? foundUsers.concat(foundUsersByPhone.users)
121
+ : foundUsersByPhone.users;
122
+ }
123
+ userToReturn = foundUsers[0] || undefined;
124
+ if (!userToReturn) {
125
+ userToReturn = await this.users.create(item.userId || ID.unique(), item.email || undefined, item.phone && item.phone.length < 15 && item.phone.startsWith("+")
126
+ ? item.phone
127
+ : undefined, item.password?.toLowerCase() ||
128
+ `changeMe${item.email?.toLowerCase()}` ||
129
+ `changeMePlease`, item.name || undefined);
130
+ }
131
+ else {
132
+ // Update user details as necessary, ensuring email uniqueness if attempting an update.
133
+ if (item.email &&
134
+ item.email !== userToReturn.email &&
135
+ !isEmpty(item.email) &&
136
+ !isUndefined(item.email)) {
137
+ const emailExists = await this.users.list([
138
+ Query.equal("email", item.email),
139
+ ]);
140
+ if (emailExists.users.length === 0) {
141
+ userToReturn = await this.users.updateEmail(userToReturn.$id, item.email);
142
+ }
143
+ else {
144
+ MessageFormatter.warning("Email update skipped: Email already exists.", { prefix: "Users" });
145
+ }
146
+ }
147
+ if (item.password) {
148
+ userToReturn = await this.users.updatePassword(userToReturn.$id, item.password.toLowerCase());
149
+ }
150
+ if (item.name && item.name !== userToReturn.name) {
151
+ userToReturn = await this.users.updateName(userToReturn.$id, item.name);
152
+ }
153
+ if (item.phone &&
154
+ item.phone !== userToReturn.phone &&
155
+ item.phone.length < 15 &&
156
+ item.phone.startsWith("+") &&
157
+ (isUndefined(userToReturn.phone) || isEmpty(userToReturn.phone))) {
158
+ const userFoundWithPhone = await this.users.list([
159
+ Query.equal("phone", item.phone),
160
+ ]);
161
+ if (userFoundWithPhone.total === 0) {
162
+ userToReturn = await this.users.updatePhone(userToReturn.$id, item.phone);
163
+ }
164
+ }
165
+ }
166
+ if (item.$createdAt && item.$updatedAt) {
167
+ MessageFormatter.warning("$createdAt and $updatedAt are not yet supported, sorry about that!", { prefix: "Users" });
168
+ }
169
+ if (item.labels && item.labels.length) {
170
+ userToReturn = await this.users.updateLabels(userToReturn.$id, item.labels);
171
+ }
172
+ if (item.prefs && Object.keys(item.prefs).length) {
173
+ await this.users.updatePrefs(userToReturn.$id, item.prefs);
174
+ userToReturn.prefs = item.prefs;
175
+ }
176
+ return userToReturn;
177
+ }
178
+ catch (error) {
179
+ return userToReturn;
180
+ }
181
+ }
182
+ async getUserIdByEmailOrPhone(email, phone) {
183
+ if (!email && !phone) {
184
+ return undefined;
185
+ }
186
+ if (email && phone) {
187
+ const foundUsersByEmail = await this.users.list([
188
+ // @ts-ignore
189
+ Query.or([Query.equal("email", email), Query.equal("phone", phone)]),
190
+ ]);
191
+ if (foundUsersByEmail.users.length > 0) {
192
+ return foundUsersByEmail.users[0]?.$id;
193
+ }
194
+ }
195
+ else if (email) {
196
+ const foundUsersByEmail = await this.users.list([
197
+ Query.equal("email", email),
198
+ ]);
199
+ if (foundUsersByEmail.users.length > 0) {
200
+ return foundUsersByEmail.users[0]?.$id;
201
+ }
202
+ else {
203
+ if (!phone) {
204
+ return undefined;
205
+ }
206
+ else {
207
+ const foundUsersByPhone = await this.users.list([
208
+ Query.equal("phone", phone),
209
+ ]);
210
+ if (foundUsersByPhone.users.length > 0) {
211
+ return foundUsersByPhone.users[0]?.$id;
212
+ }
213
+ else {
214
+ return undefined;
215
+ }
216
+ }
217
+ }
218
+ }
219
+ if (phone) {
220
+ const foundUsersByPhone = await this.users.list([
221
+ Query.equal("phone", phone),
222
+ ]);
223
+ if (foundUsersByPhone.users.length > 0) {
224
+ return foundUsersByPhone.users[0]?.$id;
225
+ }
226
+ else {
227
+ return undefined;
228
+ }
229
+ }
230
+ }
231
+ transferUsersBetweenDbsLocalToRemote = async (endpoint, projectId, apiKey) => {
232
+ const localUsers = this.users;
233
+ const client = getAppwriteClient(endpoint, projectId, apiKey);
234
+ const remoteUsers = new Users(client);
235
+ let fromUsers = await localUsers.list([Query.limit(50)]);
236
+ if (fromUsers.users.length === 0) {
237
+ MessageFormatter.info("No users found", { prefix: "Users" });
238
+ return;
239
+ }
240
+ else if (fromUsers.users.length < 50) {
241
+ MessageFormatter.progress(`Transferring ${fromUsers.users.length} users to remote`, { prefix: "Users" });
242
+ const batchedPromises = fromUsers.users.map((user) => {
243
+ return tryAwaitWithRetry(async () => {
244
+ const toCreateObject = {
245
+ ...user,
246
+ };
247
+ delete toCreateObject.$id;
248
+ delete toCreateObject.$createdAt;
249
+ delete toCreateObject.$updatedAt;
250
+ await remoteUsers.create(user.$id, user.email, user.phone, user.password, user.name);
251
+ });
252
+ });
253
+ await Promise.all(batchedPromises);
254
+ }
255
+ else {
256
+ while (fromUsers.users.length === 50) {
257
+ fromUsers = await localUsers.list([
258
+ Query.limit(50),
259
+ Query.cursorAfter(fromUsers.users[fromUsers.users.length - 1].$id),
260
+ ]);
261
+ const batchedPromises = fromUsers.users.map((user) => {
262
+ return tryAwaitWithRetry(async () => {
263
+ const toCreateObject = {
264
+ ...user,
265
+ };
266
+ delete toCreateObject.$id;
267
+ delete toCreateObject.$createdAt;
268
+ delete toCreateObject.$updatedAt;
269
+ await remoteUsers.create(user.$id, user.email, user.phone, user.password, user.name);
270
+ });
271
+ });
272
+ await Promise.all(batchedPromises);
273
+ }
274
+ }
275
+ };
276
+ }
@@ -0,0 +1 @@
1
+ export declare function migrateConfig(workingDir: string): Promise<void>;
@@ -0,0 +1,261 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import { existsSync } from "fs";
4
+ import { MessageFormatter } from "appwrite-utils-helpers";
5
+ import { ConfirmationDialogs } from "../shared/confirmationDialogs.js";
6
+ import yaml from "js-yaml";
7
+ export async function migrateConfig(workingDir) {
8
+ try {
9
+ // Look for appwriteConfig.ts files in the working directory and subdirectories
10
+ const configFiles = await findAppwriteConfigFiles(workingDir);
11
+ if (configFiles.length === 0) {
12
+ MessageFormatter.info("No appwriteConfig.ts files found to migrate", { prefix: "Migration" });
13
+ return;
14
+ }
15
+ MessageFormatter.info(`Found ${configFiles.length} appwriteConfig.ts file(s) to migrate`, { prefix: "Migration" });
16
+ for (const configFile of configFiles) {
17
+ await migrateConfigFile(configFile, workingDir);
18
+ }
19
+ MessageFormatter.success("Migration completed successfully", { prefix: "Migration" });
20
+ }
21
+ catch (error) {
22
+ MessageFormatter.error("Migration failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
23
+ throw error;
24
+ }
25
+ }
26
+ async function findAppwriteConfigFiles(dir) {
27
+ const configFiles = [];
28
+ const checkDir = async (currentDir) => {
29
+ try {
30
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
31
+ for (const entry of entries) {
32
+ const fullPath = path.join(currentDir, entry.name);
33
+ if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
34
+ await checkDir(fullPath);
35
+ }
36
+ else if (entry.isFile() && entry.name === 'appwriteConfig.ts') {
37
+ configFiles.push(fullPath);
38
+ }
39
+ }
40
+ }
41
+ catch (error) {
42
+ // Ignore directory access errors
43
+ }
44
+ };
45
+ await checkDir(dir);
46
+ return configFiles;
47
+ }
48
+ async function migrateConfigFile(configFilePath, workingDir) {
49
+ const configDir = path.dirname(configFilePath);
50
+ const appwriteDir = path.join(path.dirname(configDir), '.appwrite');
51
+ MessageFormatter.info(`Migrating ${path.relative(workingDir, configFilePath)}`, { prefix: "Migration" });
52
+ // Check if .appwrite directory already exists
53
+ if (existsSync(appwriteDir)) {
54
+ const shouldOverwrite = await ConfirmationDialogs.confirmOverwrite(`.appwrite directory already exists at ${path.relative(workingDir, appwriteDir)}`);
55
+ if (!shouldOverwrite) {
56
+ MessageFormatter.info("Skipping migration for this config", { prefix: "Migration" });
57
+ return;
58
+ }
59
+ }
60
+ // Load and parse the TypeScript config
61
+ const config = await parseTypeScriptConfig(configFilePath);
62
+ // Create .appwrite directory
63
+ await fs.mkdir(appwriteDir, { recursive: true });
64
+ // Convert config to YAML and save
65
+ const yamlConfig = convertToYAMLConfig(config);
66
+ const yamlContent = yaml.dump(yamlConfig, {
67
+ indent: 2,
68
+ lineWidth: 120,
69
+ noRefs: true
70
+ });
71
+ await fs.writeFile(path.join(appwriteDir, 'config.yaml'), yamlContent);
72
+ // Copy all directories except collections and schemas (we handle collections separately, skip schemas entirely)
73
+ const entries = await fs.readdir(configDir, { withFileTypes: true });
74
+ for (const entry of entries) {
75
+ if (entry.isDirectory() && entry.name !== 'collections' && entry.name !== 'schemas') {
76
+ const sourcePath = path.join(configDir, entry.name);
77
+ const targetPath = path.join(appwriteDir, entry.name);
78
+ await fs.cp(sourcePath, targetPath, { recursive: true });
79
+ MessageFormatter.info(`Copied ${entry.name}/ to .appwrite/${entry.name}/`, { prefix: "Migration" });
80
+ }
81
+ }
82
+ // Convert TypeScript collections to YAML collections
83
+ const collectionsPath = path.join(configDir, 'collections');
84
+ if (existsSync(collectionsPath)) {
85
+ const targetCollectionsPath = path.join(appwriteDir, 'collections');
86
+ await fs.mkdir(targetCollectionsPath, { recursive: true });
87
+ const collectionFiles = await fs.readdir(collectionsPath);
88
+ for (const file of collectionFiles) {
89
+ if (file.endsWith('.ts')) {
90
+ await convertCollectionToYaml(path.join(collectionsPath, file), targetCollectionsPath);
91
+ }
92
+ }
93
+ MessageFormatter.info(`Converted TypeScript collections to YAML in .appwrite/collections/`, { prefix: "Migration" });
94
+ }
95
+ // Keep original config file in place (no backup needed since we're not deleting it)
96
+ MessageFormatter.success(`Migration completed for ${path.relative(workingDir, configFilePath)}`, { prefix: "Migration" });
97
+ }
98
+ async function parseTypeScriptConfig(configFilePath) {
99
+ try {
100
+ // Use tsx to import the TypeScript config file directly
101
+ const { register } = await import("tsx/esm/api");
102
+ const { pathToFileURL } = await import("node:url");
103
+ const unregister = register();
104
+ try {
105
+ const configUrl = pathToFileURL(configFilePath).href;
106
+ const configModule = await import(configUrl);
107
+ const config = configModule.default?.default || configModule.default || configModule;
108
+ if (!config) {
109
+ throw new Error("Failed to load config from TypeScript file");
110
+ }
111
+ return config;
112
+ }
113
+ finally {
114
+ unregister();
115
+ }
116
+ }
117
+ catch (error) {
118
+ MessageFormatter.error("Could not load TypeScript config", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
119
+ throw new Error('Failed to load TypeScript configuration file. Please ensure it exports a valid config object.');
120
+ }
121
+ }
122
+ function convertToYAMLConfig(config) {
123
+ // Convert the config to the nested YAML structure
124
+ const yamlConfig = {
125
+ appwrite: {
126
+ endpoint: config.appwriteEndpoint,
127
+ project: config.appwriteProject,
128
+ key: config.appwriteKey
129
+ },
130
+ logging: {
131
+ enabled: config.logging?.enabled ?? false,
132
+ level: config.logging?.level ?? "info",
133
+ console: config.logging?.console ?? false,
134
+ logDirectory: "./logs"
135
+ },
136
+ backups: {
137
+ enabled: config.enableBackups ?? false,
138
+ interval: config.backupInterval ?? 3600,
139
+ retention: config.backupRetention ?? 30,
140
+ cleanup: config.enableBackupCleanup ?? false
141
+ },
142
+ data: {
143
+ enableMockData: config.enableMockData ?? false,
144
+ documentBucketId: config.documentBucketId ?? "documents",
145
+ usersCollectionName: config.usersCollectionName ?? "Users",
146
+ importDirectory: "importData"
147
+ },
148
+ schemas: {
149
+ outputDirectory: "schemas",
150
+ yamlSchemaDirectory: ".yaml_schemas"
151
+ },
152
+ migrations: {
153
+ enabled: true
154
+ },
155
+ databases: (config.databases || []).map(db => ({
156
+ id: db.$id,
157
+ name: db.name,
158
+ collections: [] // Collections will be handled separately
159
+ })),
160
+ buckets: (config.buckets || []).map(bucket => ({
161
+ id: bucket.$id,
162
+ name: bucket.name,
163
+ permissions: bucket.$permissions?.map((p) => ({
164
+ permission: p.permission,
165
+ target: p.target
166
+ })) || [],
167
+ fileSecurity: bucket.fileSecurity ?? false,
168
+ enabled: bucket.enabled ?? true,
169
+ maximumFileSize: bucket.maximumFileSize ?? 30000000,
170
+ allowedFileExtensions: bucket.allowedFileExtensions || [],
171
+ compression: bucket.compression || "gzip",
172
+ encryption: bucket.encryption ?? false,
173
+ antivirus: bucket.antivirus ?? false
174
+ })),
175
+ functions: (config.functions || []).map((func) => ({
176
+ id: func.$id,
177
+ name: func.name,
178
+ runtime: func.runtime,
179
+ execute: func.execute || [],
180
+ events: func.events || [],
181
+ schedule: func.schedule || "",
182
+ timeout: func.timeout ?? 15,
183
+ enabled: func.enabled ?? true,
184
+ logging: func.logging ?? false,
185
+ entrypoint: func.entrypoint || "src/main.js",
186
+ commands: func.commands || "",
187
+ scopes: func.scopes || [],
188
+ specification: func.specification || "s-1vcpu-512mb"
189
+ }))
190
+ };
191
+ return yamlConfig;
192
+ }
193
+ async function convertCollectionToYaml(tsFilePath, targetDir) {
194
+ try {
195
+ // Load the TypeScript collection using tsx
196
+ const { register } = await import("tsx/esm/api");
197
+ const { pathToFileURL } = await import("node:url");
198
+ const unregister = register();
199
+ try {
200
+ const configUrl = pathToFileURL(tsFilePath).href;
201
+ const collectionModule = await import(configUrl);
202
+ const collection = collectionModule.default?.default || collectionModule.default || collectionModule;
203
+ if (!collection) {
204
+ throw new Error("Failed to load collection from TypeScript file");
205
+ }
206
+ // Convert collection to YAML format
207
+ const yamlCollection = {
208
+ name: collection.name,
209
+ id: collection.$id,
210
+ documentSecurity: collection.documentSecurity ?? false,
211
+ enabled: collection.enabled ?? true,
212
+ permissions: (collection.permissions || collection.$permissions || []).map((p) => ({
213
+ permission: p.permission,
214
+ target: p.target
215
+ })),
216
+ attributes: (collection.attributes || []).map((attr) => ({
217
+ key: attr.key,
218
+ type: attr.type,
219
+ size: attr.size,
220
+ required: attr.required ?? false,
221
+ array: attr.array,
222
+ default: attr.xdefault || attr.default,
223
+ min: attr.min,
224
+ max: attr.max,
225
+ elements: attr.elements,
226
+ relatedCollection: attr.relatedCollection,
227
+ relationType: attr.relationType,
228
+ twoWay: attr.twoWay,
229
+ twoWayKey: attr.twoWayKey,
230
+ onDelete: attr.onDelete,
231
+ side: attr.side
232
+ })),
233
+ indexes: (collection.indexes || []).map((idx) => ({
234
+ key: idx.key,
235
+ type: idx.type,
236
+ attributes: idx.attributes,
237
+ orders: idx.orders
238
+ })),
239
+ importDefs: collection.importDefs || []
240
+ };
241
+ // Remove undefined values
242
+ const cleanYamlCollection = JSON.parse(JSON.stringify(yamlCollection, (key, value) => value === undefined ? undefined : value));
243
+ // Write YAML file
244
+ const fileName = path.basename(tsFilePath, '.ts') + '.yaml';
245
+ const targetPath = path.join(targetDir, fileName);
246
+ const yamlContent = yaml.dump(cleanYamlCollection, {
247
+ indent: 2,
248
+ lineWidth: 120,
249
+ noRefs: true
250
+ });
251
+ await fs.writeFile(targetPath, yamlContent);
252
+ MessageFormatter.info(`Converted ${path.basename(tsFilePath)} to ${fileName}`, { prefix: "Migration" });
253
+ }
254
+ finally {
255
+ unregister();
256
+ }
257
+ }
258
+ catch (error) {
259
+ MessageFormatter.error(`Failed to convert collection ${path.basename(tsFilePath)}`, error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
260
+ }
261
+ }
@@ -0,0 +1,2 @@
1
+ export * from 'appwrite-utils-helpers';
2
+ export * from "./setupFiles.js";
@@ -0,0 +1,50 @@
1
+ import { type AppwriteConfig } from "appwrite-utils";
2
+ import { type ValidationResult } from "appwrite-utils-helpers";
3
+ /**
4
+ * Session authentication preservation options for config loading
5
+ */
6
+ export interface SessionPreservationOptions {
7
+ sessionCookie?: string;
8
+ authMethod?: "session" | "apikey" | "auto";
9
+ sessionMetadata?: {
10
+ email?: string;
11
+ expiresAt?: string;
12
+ };
13
+ }
14
+ /**
15
+ * Configuration loading options
16
+ */
17
+ export interface ConfigLoadingOptions {
18
+ validate?: boolean;
19
+ strictMode?: boolean;
20
+ reportValidation?: boolean;
21
+ preserveAuth?: SessionPreservationOptions;
22
+ }
23
+ /**
24
+ * Helper function to create session preservation options from session data
25
+ * @param sessionCookie The session cookie string
26
+ * @param email Optional email associated with the session
27
+ * @param expiresAt Optional expiration timestamp
28
+ * @returns SessionPreservationOptions object
29
+ */
30
+ export declare function createSessionPreservation(sessionCookie: string, email?: string, expiresAt?: string): SessionPreservationOptions;
31
+ export { findAppwriteConfig, findFunctionsDir } from 'appwrite-utils-helpers';
32
+ /**
33
+ * Loads the Appwrite configuration and returns both config and the path where it was found.
34
+ * @param configDir The directory to search for config files.
35
+ * @param options Loading options including validation settings and session preservation.
36
+ * @returns Object containing the config, path, and validation results.
37
+ */
38
+ export declare const loadConfigWithPath: (configDir: string, options?: ConfigLoadingOptions) => Promise<{
39
+ config: AppwriteConfig;
40
+ actualConfigPath: string;
41
+ validation?: ValidationResult;
42
+ }>;
43
+ /**
44
+ * Loads the Appwrite configuration and all collection configurations from a specified directory.
45
+ * Supports both YAML and TypeScript config formats with backward compatibility.
46
+ * @param configDir The directory containing the config file and collections folder.
47
+ * @param options Loading options including validation settings and session preservation.
48
+ * @returns The loaded Appwrite configuration including collections.
49
+ */
50
+ export declare const loadConfig: (configDir: string, options?: ConfigLoadingOptions) => Promise<AppwriteConfig>;