appwrite-utils-cli 0.10.86 → 1.0.2

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 (178) hide show
  1. package/.appwrite/.yaml_schemas/appwrite-config.schema.json +380 -0
  2. package/.appwrite/.yaml_schemas/collection.schema.json +255 -0
  3. package/.appwrite/collections/Categories.yaml +182 -0
  4. package/.appwrite/collections/ExampleCollection.yaml +36 -0
  5. package/.appwrite/collections/Posts.yaml +227 -0
  6. package/.appwrite/collections/Users.yaml +149 -0
  7. package/.appwrite/config.yaml +109 -0
  8. package/.appwrite/import/README.md +148 -0
  9. package/.appwrite/import/categories-import.yaml +129 -0
  10. package/.appwrite/import/posts-import.yaml +208 -0
  11. package/.appwrite/import/users-import.yaml +130 -0
  12. package/.appwrite/importData/categories.json +194 -0
  13. package/.appwrite/importData/posts.json +270 -0
  14. package/.appwrite/importData/users.json +220 -0
  15. package/.appwrite/schemas/categories.json +128 -0
  16. package/.appwrite/schemas/exampleCollection.json +52 -0
  17. package/.appwrite/schemas/posts.json +173 -0
  18. package/.appwrite/schemas/users.json +125 -0
  19. package/README.md +264 -33
  20. package/dist/collections/attributes.js +3 -2
  21. package/dist/collections/methods.js +56 -38
  22. package/dist/config/yamlConfig.d.ts +501 -0
  23. package/dist/config/yamlConfig.js +452 -0
  24. package/dist/databases/setup.d.ts +6 -0
  25. package/dist/databases/setup.js +119 -0
  26. package/dist/functions/methods.d.ts +1 -1
  27. package/dist/functions/methods.js +5 -2
  28. package/dist/functions/openapi.d.ts +4 -0
  29. package/dist/functions/openapi.js +60 -0
  30. package/dist/interactiveCLI.d.ts +5 -0
  31. package/dist/interactiveCLI.js +194 -49
  32. package/dist/main.js +91 -30
  33. package/dist/migrations/afterImportActions.js +2 -2
  34. package/dist/migrations/appwriteToX.d.ts +10 -0
  35. package/dist/migrations/appwriteToX.js +15 -4
  36. package/dist/migrations/backup.d.ts +16 -16
  37. package/dist/migrations/dataLoader.d.ts +83 -1
  38. package/dist/migrations/dataLoader.js +4 -4
  39. package/dist/migrations/importController.js +25 -18
  40. package/dist/migrations/importDataActions.js +2 -2
  41. package/dist/migrations/logging.d.ts +9 -1
  42. package/dist/migrations/logging.js +41 -22
  43. package/dist/migrations/migrationHelper.d.ts +4 -4
  44. package/dist/migrations/relationships.js +1 -1
  45. package/dist/migrations/services/DataTransformationService.d.ts +55 -0
  46. package/dist/migrations/services/DataTransformationService.js +158 -0
  47. package/dist/migrations/services/FileHandlerService.d.ts +75 -0
  48. package/dist/migrations/services/FileHandlerService.js +236 -0
  49. package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
  50. package/dist/migrations/services/ImportOrchestrator.js +488 -0
  51. package/dist/migrations/services/RateLimitManager.d.ts +138 -0
  52. package/dist/migrations/services/RateLimitManager.js +279 -0
  53. package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
  54. package/dist/migrations/services/RelationshipResolver.js +332 -0
  55. package/dist/migrations/services/UserMappingService.d.ts +109 -0
  56. package/dist/migrations/services/UserMappingService.js +277 -0
  57. package/dist/migrations/services/ValidationService.d.ts +74 -0
  58. package/dist/migrations/services/ValidationService.js +260 -0
  59. package/dist/migrations/transfer.d.ts +0 -6
  60. package/dist/migrations/transfer.js +16 -132
  61. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +384 -0
  62. package/dist/migrations/yaml/YamlImportConfigLoader.js +375 -0
  63. package/dist/migrations/yaml/YamlImportIntegration.d.ts +87 -0
  64. package/dist/migrations/yaml/YamlImportIntegration.js +330 -0
  65. package/dist/migrations/yaml/generateImportSchemas.d.ts +17 -0
  66. package/dist/migrations/yaml/generateImportSchemas.js +575 -0
  67. package/dist/schemas/authUser.d.ts +9 -9
  68. package/dist/shared/attributeManager.d.ts +17 -0
  69. package/dist/shared/attributeManager.js +273 -0
  70. package/dist/shared/confirmationDialogs.d.ts +75 -0
  71. package/dist/shared/confirmationDialogs.js +236 -0
  72. package/dist/shared/functionManager.d.ts +48 -0
  73. package/dist/shared/functionManager.js +322 -0
  74. package/dist/shared/indexManager.d.ts +24 -0
  75. package/dist/shared/indexManager.js +150 -0
  76. package/dist/shared/jsonSchemaGenerator.d.ts +51 -0
  77. package/dist/shared/jsonSchemaGenerator.js +313 -0
  78. package/dist/shared/logging.d.ts +10 -0
  79. package/dist/shared/logging.js +46 -0
  80. package/dist/shared/messageFormatter.d.ts +37 -0
  81. package/dist/shared/messageFormatter.js +152 -0
  82. package/dist/shared/migrationHelpers.d.ts +173 -0
  83. package/dist/shared/migrationHelpers.js +142 -0
  84. package/dist/shared/operationLogger.d.ts +3 -0
  85. package/dist/shared/operationLogger.js +25 -0
  86. package/dist/shared/operationQueue.d.ts +13 -0
  87. package/dist/shared/operationQueue.js +79 -0
  88. package/dist/shared/progressManager.d.ts +62 -0
  89. package/dist/shared/progressManager.js +215 -0
  90. package/dist/shared/schemaGenerator.d.ts +18 -0
  91. package/dist/shared/schemaGenerator.js +523 -0
  92. package/dist/storage/methods.d.ts +3 -1
  93. package/dist/storage/methods.js +144 -55
  94. package/dist/storage/schemas.d.ts +56 -16
  95. package/dist/types.d.ts +2 -2
  96. package/dist/types.js +1 -1
  97. package/dist/users/methods.d.ts +16 -0
  98. package/dist/users/methods.js +276 -0
  99. package/dist/utils/configMigration.d.ts +1 -0
  100. package/dist/utils/configMigration.js +262 -0
  101. package/dist/utils/dataConverters.d.ts +46 -0
  102. package/dist/utils/dataConverters.js +139 -0
  103. package/dist/utils/loadConfigs.d.ts +15 -4
  104. package/dist/utils/loadConfigs.js +379 -51
  105. package/dist/utils/schemaStrings.js +2 -1
  106. package/dist/utils/setupFiles.d.ts +2 -1
  107. package/dist/utils/setupFiles.js +723 -28
  108. package/dist/utils/validationRules.d.ts +43 -0
  109. package/dist/utils/validationRules.js +42 -0
  110. package/dist/utils/yamlConverter.d.ts +48 -0
  111. package/dist/utils/yamlConverter.js +98 -0
  112. package/dist/utilsController.js +65 -43
  113. package/package.json +19 -15
  114. package/src/collections/attributes.ts +3 -2
  115. package/src/collections/methods.ts +85 -51
  116. package/src/config/yamlConfig.ts +488 -0
  117. package/src/{migrations/setupDatabase.ts → databases/setup.ts} +11 -5
  118. package/src/functions/methods.ts +8 -4
  119. package/src/functions/templates/count-docs-in-collection/package.json +25 -0
  120. package/src/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
  121. package/src/functions/templates/typescript-node/package.json +24 -0
  122. package/src/functions/templates/typescript-node/tsconfig.json +28 -0
  123. package/src/functions/templates/uv/README.md +31 -0
  124. package/src/functions/templates/uv/pyproject.toml +29 -0
  125. package/src/interactiveCLI.ts +226 -61
  126. package/src/main.ts +111 -37
  127. package/src/migrations/afterImportActions.ts +2 -2
  128. package/src/migrations/appwriteToX.ts +17 -4
  129. package/src/migrations/dataLoader.ts +4 -4
  130. package/src/migrations/importController.ts +30 -22
  131. package/src/migrations/importDataActions.ts +2 -2
  132. package/src/migrations/relationships.ts +1 -1
  133. package/src/migrations/services/DataTransformationService.ts +196 -0
  134. package/src/migrations/services/FileHandlerService.ts +311 -0
  135. package/src/migrations/services/ImportOrchestrator.ts +669 -0
  136. package/src/migrations/services/RateLimitManager.ts +363 -0
  137. package/src/migrations/services/RelationshipResolver.ts +461 -0
  138. package/src/migrations/services/UserMappingService.ts +345 -0
  139. package/src/migrations/services/ValidationService.ts +349 -0
  140. package/src/migrations/transfer.ts +22 -228
  141. package/src/migrations/yaml/YamlImportConfigLoader.ts +427 -0
  142. package/src/migrations/yaml/YamlImportIntegration.ts +419 -0
  143. package/src/migrations/yaml/generateImportSchemas.ts +589 -0
  144. package/src/shared/attributeManager.ts +429 -0
  145. package/src/shared/confirmationDialogs.ts +327 -0
  146. package/src/shared/functionManager.ts +515 -0
  147. package/src/shared/indexManager.ts +253 -0
  148. package/src/shared/jsonSchemaGenerator.ts +403 -0
  149. package/src/shared/logging.ts +74 -0
  150. package/src/shared/messageFormatter.ts +195 -0
  151. package/src/{migrations/migrationHelper.ts → shared/migrationHelpers.ts} +22 -4
  152. package/src/{migrations/helper.ts → shared/operationLogger.ts} +7 -2
  153. package/src/{migrations/queue.ts → shared/operationQueue.ts} +1 -1
  154. package/src/shared/progressManager.ts +278 -0
  155. package/src/{migrations/schemaStrings.ts → shared/schemaGenerator.ts} +71 -17
  156. package/src/storage/methods.ts +199 -78
  157. package/src/types.ts +2 -2
  158. package/src/{migrations/users.ts → users/methods.ts} +2 -2
  159. package/src/utils/configMigration.ts +349 -0
  160. package/src/utils/loadConfigs.ts +416 -52
  161. package/src/utils/schemaStrings.ts +2 -1
  162. package/src/utils/setupFiles.ts +742 -40
  163. package/src/{migrations → utils}/validationRules.ts +1 -1
  164. package/src/utils/yamlConverter.ts +131 -0
  165. package/src/utilsController.ts +75 -54
  166. package/src/functions/templates/poetry/README.md +0 -30
  167. package/src/functions/templates/poetry/pyproject.toml +0 -16
  168. package/src/migrations/attributes.ts +0 -561
  169. package/src/migrations/backup.ts +0 -205
  170. package/src/migrations/databases.ts +0 -39
  171. package/src/migrations/dbHelpers.ts +0 -92
  172. package/src/migrations/indexes.ts +0 -40
  173. package/src/migrations/logging.ts +0 -29
  174. package/src/migrations/storage.ts +0 -538
  175. /package/src/{migrations → functions}/openapi.ts +0 -0
  176. /package/src/functions/templates/{poetry → uv}/src/__init__.py +0 -0
  177. /package/src/functions/templates/{poetry → uv}/src/index.py +0 -0
  178. /package/src/{migrations/converters.ts → utils/dataConverters.ts} +0 -0
@@ -0,0 +1,452 @@
1
+ import { z } from "zod";
2
+ import yaml from "js-yaml";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { AppwriteConfigSchema, RuntimeSchema, FunctionScopes, FunctionSpecifications, permissionsSchema, PermissionToAppwritePermission } from "appwrite-utils";
6
+ const YamlConfigSchema = z.object({
7
+ appwrite: z.object({
8
+ endpoint: z.string().default("https://cloud.appwrite.io/v1"),
9
+ project: z.string(),
10
+ key: z.string(),
11
+ }),
12
+ logging: z
13
+ .object({
14
+ enabled: z.boolean().default(false),
15
+ level: z.enum(["error", "warn", "info", "debug"]).default("info"),
16
+ directory: z.string().optional(),
17
+ console: z.boolean().default(false),
18
+ })
19
+ .optional()
20
+ .default({ enabled: false, level: "info", console: false }),
21
+ backups: z
22
+ .object({
23
+ enabled: z.boolean().default(true),
24
+ interval: z.number().default(3600),
25
+ retention: z.number().default(30),
26
+ cleanup: z.boolean().default(true),
27
+ })
28
+ .optional()
29
+ .default({ enabled: true, interval: 3600, retention: 30, cleanup: true }),
30
+ data: z
31
+ .object({
32
+ enableMockData: z.boolean().default(false),
33
+ documentBucketId: z.string().default("documents"),
34
+ usersCollectionName: z.string().default("Members"),
35
+ importDirectory: z.string().default("importData"),
36
+ })
37
+ .optional()
38
+ .default({
39
+ enableMockData: false,
40
+ documentBucketId: "documents",
41
+ usersCollectionName: "Members",
42
+ importDirectory: "importData",
43
+ }),
44
+ schemas: z
45
+ .object({
46
+ outputDirectory: z.string().default("schemas"),
47
+ yamlSchemaDirectory: z.string().default(".yaml_schemas"),
48
+ })
49
+ .optional()
50
+ .default({
51
+ outputDirectory: "schemas",
52
+ yamlSchemaDirectory: ".yaml_schemas",
53
+ }),
54
+ migrations: z
55
+ .object({
56
+ enabled: z.boolean().default(true),
57
+ })
58
+ .optional()
59
+ .default({
60
+ enabled: true,
61
+ }),
62
+ databases: z
63
+ .array(z.object({
64
+ id: z.string(),
65
+ name: z.string(),
66
+ bucket: z
67
+ .object({
68
+ id: z.string(),
69
+ name: z.string(),
70
+ permissions: permissionsSchema,
71
+ fileSecurity: z.boolean().optional(),
72
+ enabled: z.boolean().optional(),
73
+ maximumFileSize: z.number().optional(),
74
+ allowedFileExtensions: z.array(z.string()).optional(),
75
+ compression: z.enum(["none", "gzip", "zstd"]).optional(),
76
+ encryption: z.boolean().optional(),
77
+ antivirus: z.boolean().optional(),
78
+ })
79
+ .optional(),
80
+ }))
81
+ .optional()
82
+ .default([
83
+ { id: "dev", name: "Development" },
84
+ { id: "main", name: "Main" },
85
+ { id: "staging", name: "Staging" },
86
+ ]),
87
+ buckets: z
88
+ .array(z.object({
89
+ id: z.string(),
90
+ name: z.string(),
91
+ permissions: permissionsSchema,
92
+ fileSecurity: z.boolean().optional(),
93
+ enabled: z.boolean().optional(),
94
+ maximumFileSize: z.number().optional(),
95
+ allowedFileExtensions: z.array(z.string()).optional(),
96
+ compression: z.enum(["none", "gzip", "zstd"]).optional(),
97
+ encryption: z.boolean().optional(),
98
+ antivirus: z.boolean().optional(),
99
+ }))
100
+ .optional()
101
+ .default([]),
102
+ functions: z
103
+ .array(z.object({
104
+ id: z.string(),
105
+ name: z.string(),
106
+ runtime: RuntimeSchema,
107
+ execute: z.array(z.string()).optional(),
108
+ events: z.array(z.string()).optional(),
109
+ schedule: z.string().optional(),
110
+ timeout: z.number().optional(),
111
+ enabled: z.boolean().optional(),
112
+ logging: z.boolean().optional(),
113
+ entrypoint: z.string().optional(),
114
+ commands: z.string().optional(),
115
+ scopes: z.array(FunctionScopes).optional(),
116
+ installationId: z.string().optional(),
117
+ providerRepositoryId: z.string().optional(),
118
+ providerBranch: z.string().optional(),
119
+ providerSilentMode: z.boolean().optional(),
120
+ providerRootDirectory: z.string().optional(),
121
+ templateRepository: z.string().optional(),
122
+ templateOwner: z.string().optional(),
123
+ templateRootDirectory: z.string().optional(),
124
+ templateBranch: z.string().optional(),
125
+ specification: FunctionSpecifications.optional(),
126
+ }))
127
+ .optional()
128
+ .default([]),
129
+ });
130
+ export const convertYamlToAppwriteConfig = (yamlConfig) => {
131
+ const appwriteConfig = {
132
+ appwriteEndpoint: yamlConfig.appwrite.endpoint,
133
+ appwriteProject: yamlConfig.appwrite.project,
134
+ appwriteKey: yamlConfig.appwrite.key,
135
+ appwriteClient: null,
136
+ logging: {
137
+ enabled: yamlConfig.logging.enabled,
138
+ level: yamlConfig.logging.level,
139
+ logDirectory: yamlConfig.logging.directory,
140
+ console: yamlConfig.logging.console,
141
+ },
142
+ enableBackups: yamlConfig.backups.enabled,
143
+ backupInterval: yamlConfig.backups.interval,
144
+ backupRetention: yamlConfig.backups.retention,
145
+ enableBackupCleanup: yamlConfig.backups.cleanup,
146
+ enableMockData: yamlConfig.data.enableMockData,
147
+ documentBucketId: yamlConfig.data.documentBucketId,
148
+ usersCollectionName: yamlConfig.data.usersCollectionName,
149
+ useMigrations: yamlConfig.migrations.enabled,
150
+ schemaConfig: {
151
+ outputDirectory: yamlConfig.schemas.outputDirectory,
152
+ yamlSchemaDirectory: yamlConfig.schemas.yamlSchemaDirectory,
153
+ importDirectory: yamlConfig.data.importDirectory,
154
+ },
155
+ databases: yamlConfig.databases.map((db) => ({
156
+ $id: db.id,
157
+ name: db.name,
158
+ bucket: db.bucket
159
+ ? {
160
+ $id: db.bucket.id,
161
+ name: db.bucket.name,
162
+ $permissions: PermissionToAppwritePermission(db.bucket.permissions),
163
+ fileSecurity: db.bucket.fileSecurity || false,
164
+ enabled: db.bucket.enabled || true,
165
+ maximumFileSize: db.bucket.maximumFileSize || 30000000,
166
+ allowedFileExtensions: db.bucket.allowedFileExtensions || [],
167
+ compression: db.bucket.compression || "none",
168
+ encryption: db.bucket.encryption || false,
169
+ antivirus: db.bucket.antivirus || false,
170
+ }
171
+ : undefined,
172
+ })),
173
+ buckets: yamlConfig.buckets.map((bucket) => ({
174
+ $id: bucket.id,
175
+ name: bucket.name,
176
+ $permissions: PermissionToAppwritePermission(bucket.permissions),
177
+ fileSecurity: bucket.fileSecurity || false,
178
+ enabled: bucket.enabled || true,
179
+ maximumFileSize: bucket.maximumFileSize || 30000000,
180
+ allowedFileExtensions: bucket.allowedFileExtensions || [],
181
+ compression: bucket.compression || "none",
182
+ encryption: bucket.encryption || false,
183
+ antivirus: bucket.antivirus || false,
184
+ })),
185
+ functions: yamlConfig.functions?.map((func) => ({
186
+ $id: func.id,
187
+ name: func.name,
188
+ runtime: func.runtime,
189
+ execute: func.execute || [],
190
+ events: func.events || [],
191
+ schedule: func.schedule || "",
192
+ timeout: func.timeout || 15,
193
+ enabled: func.enabled || true,
194
+ logging: func.logging || true,
195
+ entrypoint: func.entrypoint || "",
196
+ commands: func.commands || "",
197
+ scopes: func.scopes || [],
198
+ installationId: func.installationId || "",
199
+ providerRepositoryId: func.providerRepositoryId || "",
200
+ providerBranch: func.providerBranch || "",
201
+ providerSilentMode: func.providerSilentMode || false,
202
+ providerRootDirectory: func.providerRootDirectory || "",
203
+ templateRepository: func.templateRepository || "",
204
+ templateOwner: func.templateOwner || "",
205
+ templateRootDirectory: func.templateRootDirectory || "",
206
+ templateBranch: func.templateBranch || "",
207
+ specification: func.specification || "s-0.5vcpu-512mb",
208
+ })),
209
+ collections: [],
210
+ };
211
+ return appwriteConfig;
212
+ };
213
+ export const loadYamlConfig = async (configPath) => {
214
+ try {
215
+ const fileContent = fs.readFileSync(configPath, "utf8");
216
+ const yamlData = yaml.load(fileContent);
217
+ const yamlConfig = YamlConfigSchema.parse(yamlData);
218
+ return convertYamlToAppwriteConfig(yamlConfig);
219
+ }
220
+ catch (error) {
221
+ if (error instanceof z.ZodError) {
222
+ console.error("❌ YAML config validation failed:");
223
+ error.errors.forEach((err) => {
224
+ console.error(` ${err.path.join('.')} → ${err.message}`);
225
+ });
226
+ }
227
+ else {
228
+ console.error("❌ Error loading YAML config:", error instanceof Error ? error.message : error);
229
+ if (error instanceof Error && error.stack) {
230
+ console.error("Stack trace:", error.stack);
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+ };
236
+ export const findYamlConfig = (startDir) => {
237
+ // First check current directory for YAML configs
238
+ const possiblePaths = [
239
+ path.join(startDir, ".appwrite", "appwriteConfig.yaml"),
240
+ path.join(startDir, ".appwrite", "appwriteConfig.yml"),
241
+ path.join(startDir, ".appwrite", "config.yaml"),
242
+ path.join(startDir, ".appwrite", "config.yml"),
243
+ path.join(startDir, "appwrite.yaml"),
244
+ path.join(startDir, "appwrite.yml"),
245
+ ];
246
+ for (const configPath of possiblePaths) {
247
+ if (fs.existsSync(configPath)) {
248
+ return configPath;
249
+ }
250
+ }
251
+ // Recursively search subdirectories for .appwrite folders
252
+ const yamlConfigInSubdirs = findYamlConfigRecursive(startDir);
253
+ if (yamlConfigInSubdirs) {
254
+ return yamlConfigInSubdirs;
255
+ }
256
+ // Only check one level up to avoid infinite traversal
257
+ const parentDir = path.dirname(startDir);
258
+ if (parentDir !== startDir && path.basename(parentDir) !== 'node_modules') {
259
+ const parentPossiblePaths = [
260
+ path.join(parentDir, ".appwrite", "appwriteConfig.yaml"),
261
+ path.join(parentDir, ".appwrite", "appwriteConfig.yml"),
262
+ path.join(parentDir, "appwrite.yaml"),
263
+ path.join(parentDir, "appwrite.yml"),
264
+ ];
265
+ for (const configPath of parentPossiblePaths) {
266
+ if (fs.existsSync(configPath)) {
267
+ return configPath;
268
+ }
269
+ }
270
+ }
271
+ return null;
272
+ };
273
+ const shouldIgnoreDirectory = (dirName) => {
274
+ const ignoredDirs = [
275
+ 'node_modules',
276
+ 'dist',
277
+ 'build',
278
+ 'coverage',
279
+ '.next',
280
+ '.nuxt',
281
+ '.cache',
282
+ '.git',
283
+ '.svn',
284
+ '.hg',
285
+ '__pycache__',
286
+ '.pytest_cache',
287
+ '.mypy_cache',
288
+ 'venv',
289
+ '.venv',
290
+ 'env',
291
+ '.env',
292
+ 'target',
293
+ 'out',
294
+ 'bin',
295
+ 'obj',
296
+ '.vs',
297
+ '.vscode',
298
+ '.idea',
299
+ 'temp',
300
+ 'tmp',
301
+ '.tmp',
302
+ 'logs',
303
+ 'log',
304
+ '.DS_Store',
305
+ 'Thumbs.db'
306
+ ];
307
+ return ignoredDirs.includes(dirName) ||
308
+ dirName.startsWith('.git') ||
309
+ dirName.startsWith('node_modules') ||
310
+ dirName.startsWith('.');
311
+ };
312
+ const findYamlConfigRecursive = (dir, depth = 0) => {
313
+ // Limit search depth to prevent infinite recursion
314
+ if (depth > 5) {
315
+ return null;
316
+ }
317
+ if (shouldIgnoreDirectory(path.basename(dir))) {
318
+ return null;
319
+ }
320
+ try {
321
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
322
+ for (const entry of entries) {
323
+ if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
324
+ const fullPath = path.join(dir, entry.name);
325
+ // Check if this is an .appwrite directory
326
+ if (entry.name === ".appwrite") {
327
+ const configPaths = [
328
+ path.join(fullPath, "appwriteConfig.yaml"),
329
+ path.join(fullPath, "appwriteConfig.yml"),
330
+ path.join(fullPath, "config.yaml"),
331
+ path.join(fullPath, "config.yml"),
332
+ ];
333
+ for (const configPath of configPaths) {
334
+ if (fs.existsSync(configPath)) {
335
+ return configPath;
336
+ }
337
+ }
338
+ }
339
+ // Recurse into other directories with increased depth
340
+ const result = findYamlConfigRecursive(fullPath, depth + 1);
341
+ if (result)
342
+ return result;
343
+ }
344
+ }
345
+ }
346
+ catch (error) {
347
+ // Ignore directory access errors
348
+ }
349
+ return null;
350
+ };
351
+ export const generateYamlConfigTemplate = (outputPath) => {
352
+ const template = {
353
+ appwrite: {
354
+ endpoint: "https://cloud.appwrite.io/v1",
355
+ project: "YOUR_PROJECT_ID",
356
+ key: "YOUR_API_KEY",
357
+ },
358
+ logging: {
359
+ enabled: false,
360
+ level: "info",
361
+ console: false,
362
+ },
363
+ backups: {
364
+ enabled: true,
365
+ interval: 3600,
366
+ retention: 30,
367
+ cleanup: true,
368
+ },
369
+ data: {
370
+ enableMockData: false,
371
+ documentBucketId: "documents",
372
+ usersCollectionName: "Members",
373
+ importDirectory: "importData",
374
+ },
375
+ schemas: {
376
+ outputDirectory: "schemas",
377
+ yamlSchemaDirectory: ".yaml_schemas",
378
+ },
379
+ migrations: {
380
+ enabled: true,
381
+ },
382
+ databases: [
383
+ { id: "dev", name: "Development" },
384
+ { id: "main", name: "Main" },
385
+ { id: "staging", name: "Staging" },
386
+ ],
387
+ buckets: [],
388
+ functions: [],
389
+ };
390
+ const yamlContent = yaml.dump(template, {
391
+ indent: 2,
392
+ lineWidth: 120,
393
+ sortKeys: false,
394
+ });
395
+ // Add schema reference header
396
+ const schemaReference = "# yaml-language-server: $schema=./.yaml_schemas/appwrite-config.schema.json\n";
397
+ const finalContent = schemaReference + "# Appwrite Project Configuration\n" + yamlContent;
398
+ fs.writeFileSync(outputPath, finalContent, "utf8");
399
+ };
400
+ /**
401
+ * Adds a new function to the YAML config file
402
+ * @param configPath Path to the YAML config file
403
+ * @param newFunction The function configuration to add
404
+ */
405
+ export const addFunctionToYamlConfig = async (configPath, newFunction) => {
406
+ try {
407
+ // Read current config
408
+ const fileContent = fs.readFileSync(configPath, "utf8");
409
+ const yamlData = yaml.load(fileContent);
410
+ // Initialize functions array if it doesn't exist
411
+ if (!yamlData.functions) {
412
+ yamlData.functions = [];
413
+ }
414
+ // Convert AppwriteFunction to YAML format
415
+ const yamlFunction = {
416
+ id: newFunction.$id,
417
+ name: newFunction.name,
418
+ runtime: newFunction.runtime,
419
+ execute: newFunction.execute || ["any"],
420
+ events: newFunction.events || [],
421
+ schedule: newFunction.schedule || "",
422
+ timeout: newFunction.timeout || 15,
423
+ enabled: newFunction.enabled !== false,
424
+ logging: newFunction.logging !== false,
425
+ entrypoint: newFunction.entrypoint || "",
426
+ commands: newFunction.commands || "",
427
+ scopes: newFunction.scopes || [],
428
+ specification: newFunction.specification || "s-0.5vcpu-512mb"
429
+ };
430
+ // Add new function
431
+ yamlData.functions.push(yamlFunction);
432
+ // Write back to file
433
+ const updatedYamlContent = yaml.dump(yamlData, {
434
+ indent: 2,
435
+ lineWidth: 120,
436
+ sortKeys: false,
437
+ });
438
+ // Preserve schema reference if it exists
439
+ const lines = fileContent.split('\n');
440
+ const schemaLine = lines.find(line => line.startsWith('# yaml-language-server:'));
441
+ let finalContent = updatedYamlContent;
442
+ if (schemaLine) {
443
+ finalContent = schemaLine + '\n' + updatedYamlContent;
444
+ }
445
+ fs.writeFileSync(configPath, finalContent, "utf8");
446
+ console.log(`✅ Added function "${newFunction.name}" to YAML config`);
447
+ }
448
+ catch (error) {
449
+ console.error("❌ Error adding function to YAML config:", error instanceof Error ? error.message : error);
450
+ throw error;
451
+ }
452
+ };
@@ -0,0 +1,6 @@
1
+ import { Databases, type Models } from "node-appwrite";
2
+ import { type AppwriteConfig } from "appwrite-utils";
3
+ export declare const setupMigrationDatabase: (config: AppwriteConfig) => Promise<void>;
4
+ export declare const ensureDatabasesExist: (config: AppwriteConfig, databasesToEnsure?: Models.Database[]) => Promise<void>;
5
+ export declare const wipeOtherDatabases: (database: Databases, databasesToKeep: Models.Database[], useMigrations?: boolean) => Promise<void>;
6
+ export declare const ensureCollectionsExist: (config: AppwriteConfig, database: Models.Database, collectionsToEnsure?: Models.Collection[]) => Promise<void>;
@@ -0,0 +1,119 @@
1
+ import { Databases, Query } from "node-appwrite";
2
+ import { createOrUpdateAttribute } from "../collections/attributes.js";
3
+ import { getMigrationCollectionSchemas } from "../storage/schemas.js";
4
+ import { areCollectionNamesSame, delay, toCamelCase, tryAwaitWithRetry, } from "../utils/index.js";
5
+ import {} from "appwrite-utils";
6
+ import { ulid } from "ulidx";
7
+ export const setupMigrationDatabase = async (config) => {
8
+ if (!config.useMigrations) {
9
+ console.log("Migrations database disabled, skipping setup");
10
+ return;
11
+ }
12
+ console.log("---------------------------------");
13
+ console.log("Starting Migrations Setup");
14
+ console.log("---------------------------------");
15
+ const database = new Databases(config.appwriteClient);
16
+ if (!config.appwriteClient) {
17
+ throw new Error("Appwrite client is not initialized in the config");
18
+ }
19
+ let db;
20
+ const migrationCollectionsSetup = getMigrationCollectionSchemas();
21
+ try {
22
+ db = await tryAwaitWithRetry(async () => await database.get("migrations"), undefined, true);
23
+ console.log("Migrations database found");
24
+ }
25
+ catch (e) {
26
+ db = await tryAwaitWithRetry(async () => await database.create("migrations", "Migrations", true));
27
+ console.log("Migrations database created");
28
+ }
29
+ if (!db) {
30
+ console.error("Failed to create or retrieve the migrations database");
31
+ return;
32
+ }
33
+ for (const [collectionName, { collection, attributes }] of Object.entries(migrationCollectionsSetup)) {
34
+ const collectionId = toCamelCase(collectionName);
35
+ let collectionFound;
36
+ try {
37
+ collectionFound = await tryAwaitWithRetry(async () => await database.getCollection(db.$id, collectionId), undefined, true);
38
+ console.log(`Collection found: ${collectionId}`);
39
+ }
40
+ catch (e) {
41
+ console.log(`Collection not found: ${collectionId}`);
42
+ try {
43
+ collectionFound = await tryAwaitWithRetry(async () => await database.createCollection(db.$id, collectionId, collectionName, undefined, collection.documentSecurity, collection.enabled), undefined, true);
44
+ console.log(`Collection created: ${collectionId}`);
45
+ }
46
+ catch (createError) {
47
+ console.error(`Failed to create collection: ${collectionId}`, createError);
48
+ continue;
49
+ }
50
+ }
51
+ if (!collectionFound) {
52
+ console.error(`Failed to create or retrieve collection: ${collectionId}`);
53
+ continue;
54
+ }
55
+ for (const attribute of attributes) {
56
+ try {
57
+ await createOrUpdateAttribute(database, db.$id, collectionFound, attribute);
58
+ await delay(100);
59
+ console.log(`Attribute created/updated: ${attribute.key}`);
60
+ }
61
+ catch (attrError) {
62
+ console.error(`Failed to create/update attribute: ${attribute.key}`, attrError);
63
+ }
64
+ }
65
+ }
66
+ console.log("---------------------------------");
67
+ console.log("Migrations Setup Complete");
68
+ console.log("---------------------------------");
69
+ };
70
+ export const ensureDatabasesExist = async (config, databasesToEnsure) => {
71
+ if (!config.appwriteClient) {
72
+ throw new Error("Appwrite client is not initialized in the config");
73
+ }
74
+ const database = new Databases(config.appwriteClient);
75
+ const databasesToCreate = databasesToEnsure || config.databases || [];
76
+ if (!databasesToCreate.length) {
77
+ console.log("No databases to create");
78
+ return;
79
+ }
80
+ const existingDatabases = await tryAwaitWithRetry(async () => await database.list([Query.limit(500)]));
81
+ const migrationsDatabase = existingDatabases.databases.find((d) => d.name.toLowerCase().trim().replace(" ", "") === "migrations");
82
+ if (config.useMigrations && existingDatabases.databases.length !== 0 && migrationsDatabase) {
83
+ console.log("Creating all databases including migrations");
84
+ databasesToCreate.push(migrationsDatabase);
85
+ }
86
+ for (const db of databasesToCreate) {
87
+ if (!existingDatabases.databases.some((d) => d.name === db.name)) {
88
+ await tryAwaitWithRetry(async () => await database.create(db.$id || ulid(), db.name, true));
89
+ console.log(`${db.name} database created`);
90
+ }
91
+ }
92
+ };
93
+ export const wipeOtherDatabases = async (database, databasesToKeep, useMigrations = true) => {
94
+ console.log(`Databases to keep: ${databasesToKeep.map(db => db.name).join(", ")}`);
95
+ const allDatabases = await tryAwaitWithRetry(async () => await database.list([Query.limit(500)]));
96
+ const migrationsDatabase = allDatabases.databases.find((d) => d.name.toLowerCase().trim().replace(" ", "") === "migrations");
97
+ if (useMigrations && allDatabases.databases.length !== 0 && migrationsDatabase) {
98
+ console.log("Wiping all databases except migrations");
99
+ databasesToKeep.push(migrationsDatabase);
100
+ }
101
+ for (const db of allDatabases.databases) {
102
+ if (!databasesToKeep.some((d) => d.name === db.name)) {
103
+ await tryAwaitWithRetry(async () => await database.delete(db.$id));
104
+ console.log(`Deleted database: ${db.name}`);
105
+ }
106
+ }
107
+ };
108
+ export const ensureCollectionsExist = async (config, database, collectionsToEnsure) => {
109
+ const databaseClient = new Databases(config.appwriteClient);
110
+ const collectionsToCreate = collectionsToEnsure ||
111
+ (config.collections ? config.collections : []);
112
+ const existingCollections = await tryAwaitWithRetry(async () => await databaseClient.listCollections(database.$id, [Query.limit(500)]));
113
+ for (const collection of collectionsToCreate) {
114
+ if (!existingCollections.collections.some((c) => c.name === collection.name)) {
115
+ await tryAwaitWithRetry(async () => await databaseClient.createCollection(database.$id, ulid(), collection.name, undefined, true, true));
116
+ console.log(`${collection.name} collection created in ${database.name}`);
117
+ }
118
+ }
119
+ };
@@ -12,4 +12,4 @@ export declare const createFunction: (client: Client, functionConfig: AppwriteFu
12
12
  export declare const updateFunctionSpecifications: (client: Client, functionId: string, specification: Specification) => Promise<import("node-appwrite").Models.Function | undefined>;
13
13
  export declare const listSpecifications: (client: Client) => Promise<import("node-appwrite").Models.SpecificationList>;
14
14
  export declare const updateFunction: (client: Client, functionConfig: AppwriteFunction) => Promise<import("node-appwrite").Models.Function>;
15
- export declare const createFunctionTemplate: (templateType: "typescript-node" | "poetry" | "count-docs-in-collection", functionName: string, basePath?: string) => Promise<string>;
15
+ export declare const createFunctionTemplate: (templateType: "typescript-node" | "uv" | "count-docs-in-collection", functionName: string, basePath?: string) => Promise<string>;
@@ -1,5 +1,6 @@
1
1
  import { AppwriteException, Client, Functions, Query, Runtime, } from "node-appwrite";
2
- import { join } from "node:path";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import fs from "node:fs";
4
5
  import {} from "appwrite-utils";
5
6
  import chalk from "chalk";
@@ -100,7 +101,9 @@ export const updateFunction = async (client, functionConfig) => {
100
101
  };
101
102
  export const createFunctionTemplate = async (templateType, functionName, basePath = "./functions") => {
102
103
  const functionPath = join(basePath, functionName);
103
- const templatesPath = join(__dirname, "templates", templateType);
104
+ const currentFileUrl = import.meta.url;
105
+ const currentDir = dirname(fileURLToPath(currentFileUrl));
106
+ const templatesPath = join(currentDir, "templates", templateType);
104
107
  // Create function directory
105
108
  await fs.promises.mkdir(functionPath, { recursive: true });
106
109
  // Copy template files recursively
@@ -0,0 +1,4 @@
1
+ import { type AppwriteConfig } from "appwrite-utils";
2
+ import { z } from "zod";
3
+ export declare const generateOpenApi: (config: AppwriteConfig) => Promise<void>;
4
+ export declare function transformTypeToOpenApi<T extends z.ZodTypeAny>(schema: T, description?: string | Record<string, any> | null | undefined): T;
@@ -0,0 +1,60 @@
1
+ import { OpenAPIRegistry, OpenApiGeneratorV3, OpenApiGeneratorV31, } from "@asteasolutions/zod-to-openapi";
2
+ import { attributeSchema, CollectionSchema, } from "appwrite-utils";
3
+ import { z } from "zod";
4
+ import { writeFileSync } from "fs";
5
+ const registry = new OpenAPIRegistry();
6
+ export const generateOpenApi = async (config) => {
7
+ if (!config.collections) {
8
+ return;
9
+ }
10
+ for (const collection of config.collections) {
11
+ // Transform and register each attribute schema
12
+ const attributeSchemas = collection.attributes.map((attribute) => {
13
+ return transformTypeToOpenApi(attributeSchema, attribute.description);
14
+ });
15
+ // Create and register the collection schema with descriptions
16
+ const updatedCollectionSchema = CollectionSchema.extend({
17
+ // @ts-ignore
18
+ attributes: attributeSchemas,
19
+ }).openapi(collection.description ?? "No description");
20
+ // Register the updated collection schema under the collection name
21
+ registry.register(collection.name, updatedCollectionSchema);
22
+ }
23
+ // Convert the registry to OpenAPI JSON
24
+ const generator = new OpenApiGeneratorV31(registry.definitions);
25
+ const openApiSpec = generator.generateComponents();
26
+ // Output the OpenAPI spec to a file
27
+ writeFileSync("./appwrite/openapi/openapi.json", JSON.stringify(openApiSpec, null, 2));
28
+ };
29
+ export function transformTypeToOpenApi(schema, description) {
30
+ // Check if description is an object (OpenAPI properties) or a string
31
+ let updatedSchema;
32
+ if (!description) {
33
+ return schema;
34
+ }
35
+ if (typeof description === "string") {
36
+ updatedSchema = schema.openapi(description);
37
+ }
38
+ else if (typeof description === "object") {
39
+ updatedSchema = schema.openapi(description);
40
+ }
41
+ else {
42
+ updatedSchema = schema;
43
+ }
44
+ // Check and transform attributes if they exist
45
+ if (schema._def && schema._def.shape) {
46
+ const shape = schema._def.shape();
47
+ for (const key in shape) {
48
+ const attributeDesc = shape[key].description;
49
+ if (attributeDesc) {
50
+ if (typeof attributeDesc === "string") {
51
+ shape[key] = shape[key].openapi(attributeDesc);
52
+ }
53
+ else if (typeof attributeDesc === "object") {
54
+ shape[key] = shape[key].openapi(attributeDesc);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ return updatedSchema;
60
+ }