appwrite-utils-cli 1.7.7 → 1.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/SELECTION_DIALOGS.md +146 -0
  2. package/dist/cli/commands/databaseCommands.js +89 -23
  3. package/dist/config/services/ConfigLoaderService.d.ts +7 -0
  4. package/dist/config/services/ConfigLoaderService.js +47 -1
  5. package/dist/functions/deployments.js +5 -23
  6. package/dist/functions/methods.js +4 -2
  7. package/dist/functions/pathResolution.d.ts +37 -0
  8. package/dist/functions/pathResolution.js +185 -0
  9. package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
  10. package/dist/functions/templates/count-docs-in-collection/package.json +25 -0
  11. package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
  12. package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
  13. package/dist/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
  14. package/dist/functions/templates/hono-typescript/README.md +286 -0
  15. package/dist/functions/templates/hono-typescript/package.json +26 -0
  16. package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  17. package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  18. package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
  19. package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
  20. package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
  21. package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  22. package/dist/functions/templates/hono-typescript/tsconfig.json +20 -0
  23. package/dist/functions/templates/typescript-node/README.md +32 -0
  24. package/dist/functions/templates/typescript-node/package.json +25 -0
  25. package/dist/functions/templates/typescript-node/src/context.ts +103 -0
  26. package/dist/functions/templates/typescript-node/src/index.ts +29 -0
  27. package/dist/functions/templates/typescript-node/tsconfig.json +28 -0
  28. package/dist/functions/templates/uv/README.md +31 -0
  29. package/dist/functions/templates/uv/pyproject.toml +30 -0
  30. package/dist/functions/templates/uv/src/__init__.py +0 -0
  31. package/dist/functions/templates/uv/src/context.py +125 -0
  32. package/dist/functions/templates/uv/src/index.py +46 -0
  33. package/dist/main.js +175 -4
  34. package/dist/migrations/appwriteToX.d.ts +27 -2
  35. package/dist/migrations/appwriteToX.js +293 -69
  36. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +1 -1
  37. package/dist/migrations/yaml/generateImportSchemas.js +23 -8
  38. package/dist/shared/schemaGenerator.js +25 -12
  39. package/dist/shared/selectionDialogs.d.ts +214 -0
  40. package/dist/shared/selectionDialogs.js +540 -0
  41. package/dist/utils/configDiscovery.d.ts +4 -4
  42. package/dist/utils/configDiscovery.js +66 -30
  43. package/dist/utils/yamlConverter.d.ts +1 -0
  44. package/dist/utils/yamlConverter.js +26 -3
  45. package/dist/utilsController.d.ts +7 -1
  46. package/dist/utilsController.js +198 -17
  47. package/package.json +4 -2
  48. package/scripts/copy-templates.ts +23 -0
  49. package/src/cli/commands/databaseCommands.ts +133 -34
  50. package/src/config/services/ConfigLoaderService.ts +62 -1
  51. package/src/functions/deployments.ts +10 -35
  52. package/src/functions/methods.ts +4 -2
  53. package/src/functions/pathResolution.ts +227 -0
  54. package/src/main.ts +276 -34
  55. package/src/migrations/appwriteToX.ts +385 -90
  56. package/src/migrations/yaml/generateImportSchemas.ts +26 -8
  57. package/src/shared/schemaGenerator.ts +29 -12
  58. package/src/shared/selectionDialogs.ts +745 -0
  59. package/src/utils/configDiscovery.ts +83 -39
  60. package/src/utils/yamlConverter.ts +29 -3
  61. package/src/utilsController.ts +250 -22
  62. package/dist/utils/schemaStrings.d.ts +0 -14
  63. package/dist/utils/schemaStrings.js +0 -428
  64. package/dist/utils/sessionPreservationExample.d.ts +0 -1666
  65. package/dist/utils/sessionPreservationExample.js +0 -101
@@ -1,428 +0,0 @@
1
- import { toCamelCase, toPascalCase } from "../utils/index.js";
2
- import { Databases } from "node-appwrite";
3
- import { z } from "zod";
4
- import fs from "fs";
5
- import path from "path";
6
- import { dump } from "js-yaml";
7
- import { findFunctionsDir } from "./loadConfigs.js";
8
- import { ulid } from "ulidx";
9
- export class SchemaGenerator {
10
- relationshipMap = new Map();
11
- config;
12
- appwriteFolderPath;
13
- constructor(config, appwriteFolderPath) {
14
- this.config = config;
15
- this.appwriteFolderPath = appwriteFolderPath;
16
- this.extractRelationships();
17
- }
18
- resolveCollectionName = (idOrName) => {
19
- const col = this.config.collections?.find((c) => c.$id === idOrName || c.name === idOrName);
20
- return col?.name ?? idOrName;
21
- };
22
- updateTsSchemas() {
23
- const collections = this.config.collections;
24
- const functions = this.config.functions || [];
25
- delete this.config.collections;
26
- delete this.config.functions;
27
- const configPath = path.join(this.appwriteFolderPath, "appwriteConfig.ts");
28
- const configContent = `import { type AppwriteConfig } from "appwrite-utils";
29
-
30
- const appwriteConfig: AppwriteConfig = {
31
- appwriteEndpoint: "${this.config.appwriteEndpoint}",
32
- appwriteProject: "${this.config.appwriteProject}",
33
- appwriteKey: "${this.config.appwriteKey}",
34
- enableBackups: ${this.config.enableBackups},
35
- backupInterval: ${this.config.backupInterval},
36
- backupRetention: ${this.config.backupRetention},
37
- enableBackupCleanup: ${this.config.enableBackupCleanup},
38
- enableMockData: ${this.config.enableMockData},
39
- documentBucketId: "${this.config.documentBucketId}",
40
- usersCollectionName: "${this.config.usersCollectionName}",
41
- databases: ${JSON.stringify(this.config.databases)},
42
- buckets: ${JSON.stringify(this.config.buckets)},
43
- functions: ${JSON.stringify(functions.map((func) => ({
44
- functionId: func.$id || ulid(),
45
- name: func.name,
46
- runtime: func.runtime,
47
- path: func.dirPath || `functions/${func.name}`,
48
- entrypoint: func.entrypoint || "src/index.ts",
49
- execute: func.execute,
50
- events: func.events || [],
51
- schedule: func.schedule || "",
52
- timeout: func.timeout || 15,
53
- enabled: func.enabled !== false,
54
- logging: func.logging !== false,
55
- commands: func.commands || "npm install",
56
- scopes: func.scopes || [],
57
- installationId: func.installationId,
58
- providerRepositoryId: func.providerRepositoryId,
59
- providerBranch: func.providerBranch,
60
- providerSilentMode: func.providerSilentMode,
61
- providerRootDirectory: func.providerRootDirectory,
62
- specification: func.specification,
63
- ...(func.predeployCommands
64
- ? { predeployCommands: func.predeployCommands }
65
- : {}),
66
- ...(func.deployDir ? { deployDir: func.deployDir } : {}),
67
- })), null, 2)}
68
- };
69
-
70
- export default appwriteConfig;
71
- `;
72
- fs.writeFileSync(configPath, configContent, { encoding: "utf-8" });
73
- const collectionsFolderPath = path.join(this.appwriteFolderPath, "collections");
74
- if (!fs.existsSync(collectionsFolderPath)) {
75
- fs.mkdirSync(collectionsFolderPath, { recursive: true });
76
- }
77
- collections?.forEach((collection) => {
78
- const { databaseId, ...collectionWithoutDbId } = collection; // Destructure to exclude databaseId
79
- const collectionFilePath = path.join(collectionsFolderPath, `${collection.name}.ts`);
80
- const collectionContent = `import { type CollectionCreate } from "appwrite-utils";
81
-
82
- const ${collection.name}Config: Partial<CollectionCreate> = {
83
- name: "${collection.name}",
84
- $id: "${collection.$id}",
85
- enabled: ${collection.enabled},
86
- documentSecurity: ${collection.documentSecurity},
87
- $permissions: [
88
- ${collection.$permissions
89
- .map((permission) => `{ permission: "${permission.permission}", target: "${permission.target}" }`)
90
- .join(",\n ")}
91
- ],
92
- attributes: [
93
- ${(collection.attributes || [])
94
- .map((attr) => {
95
- return `{ ${Object.entries(attr)
96
- .map(([key, value]) => {
97
- // Check the type of the value and format it accordingly
98
- if (typeof value === "string") {
99
- // If the value is a string, wrap it in quotes
100
- return `${key}: "${value.replace(/"/g, '\\"')}"`; // Escape existing quotes in the string
101
- }
102
- else if (Array.isArray(value)) {
103
- // If the value is an array, join it with commas
104
- if (value.length > 0) {
105
- return `${key}: [${value
106
- .map((item) => `"${item}"`)
107
- .join(", ")}]`;
108
- }
109
- else {
110
- return `${key}: []`;
111
- }
112
- }
113
- else {
114
- // If the value is not a string (e.g., boolean or number), output it directly
115
- return `${key}: ${value}`;
116
- }
117
- })
118
- .join(", ")} }`;
119
- })
120
- .join(",\n ")}
121
- ],
122
- indexes: [
123
- ${(collection.indexes?.map((index) => {
124
- // Map each attribute to ensure it is properly quoted
125
- const formattedAttributes = index.attributes.map((attr) => `"${attr}"`).join(", ") ?? "";
126
- return `{ key: "${index.key}", type: "${index.type}", attributes: [${formattedAttributes}], orders: [${index.orders
127
- ?.filter((order) => order !== null)
128
- .map((order) => `"${order}"`)
129
- .join(", ") ?? ""}] }`;
130
- }) ?? []).join(",\n ")}
131
- ]
132
- };
133
-
134
- export default ${collection.name}Config;
135
- `;
136
- fs.writeFileSync(collectionFilePath, collectionContent, {
137
- encoding: "utf-8",
138
- });
139
- console.log(`Collection schema written to ${collectionFilePath}`);
140
- });
141
- }
142
- extractRelationships() {
143
- if (!this.config.collections) {
144
- return;
145
- }
146
- this.config.collections.forEach((collection) => {
147
- if (!collection.attributes) {
148
- return;
149
- }
150
- collection.attributes.forEach((attr) => {
151
- if (attr.type === "relationship" && attr.twoWay && attr.twoWayKey) {
152
- const relationshipAttr = attr;
153
- let isArrayParent = false;
154
- let isArrayChild = false;
155
- switch (relationshipAttr.relationType) {
156
- case "oneToMany":
157
- isArrayParent = true;
158
- isArrayChild = false;
159
- break;
160
- case "manyToMany":
161
- isArrayParent = true;
162
- isArrayChild = true;
163
- break;
164
- case "oneToOne":
165
- isArrayParent = false;
166
- isArrayChild = false;
167
- break;
168
- case "manyToOne":
169
- isArrayParent = false;
170
- isArrayChild = true;
171
- break;
172
- default:
173
- break;
174
- }
175
- this.addRelationship(collection.name, this.resolveCollectionName(relationshipAttr.relatedCollection), attr.key, relationshipAttr.twoWayKey, isArrayParent, isArrayChild);
176
- console.log(`Extracted relationship: ${attr.key}\n\t${collection.name} -> ${relationshipAttr.relatedCollection}, databaseId: ${collection.databaseId}`);
177
- }
178
- });
179
- });
180
- }
181
- addRelationship(parentCollection, childCollection, parentKey, childKey, isArrayParent, isArrayChild) {
182
- const relationshipsChild = this.relationshipMap.get(childCollection) || [];
183
- const relationshipsParent = this.relationshipMap.get(parentCollection) || [];
184
- relationshipsParent.push({
185
- parentCollection,
186
- childCollection,
187
- parentKey,
188
- childKey,
189
- isArray: isArrayParent,
190
- isChild: false,
191
- });
192
- relationshipsChild.push({
193
- parentCollection,
194
- childCollection,
195
- parentKey,
196
- childKey,
197
- isArray: isArrayChild,
198
- isChild: true,
199
- });
200
- this.relationshipMap.set(childCollection, relationshipsChild);
201
- this.relationshipMap.set(parentCollection, relationshipsParent);
202
- }
203
- generateSchemas() {
204
- if (!this.config.collections) {
205
- return;
206
- }
207
- this.config.collections.forEach((collection) => {
208
- const schemaString = this.createSchemaStringV4(collection.name, collection.attributes);
209
- const camelCaseName = toCamelCase(collection.name);
210
- const schemaPath = path.join(this.appwriteFolderPath, "schemas", `${camelCaseName}.ts`);
211
- fs.writeFileSync(schemaPath, schemaString, { encoding: "utf-8" });
212
- console.log(`Schema written to ${schemaPath}`);
213
- });
214
- }
215
- // Zod v4 recursive getter-based schemas
216
- createSchemaStringV4 = (name, attributes) => {
217
- const pascalName = toPascalCase(name);
218
- let imports = `import { z } from "zod";\n`;
219
- // Use the relationshipMap to find related collections
220
- const relationshipDetails = this.relationshipMap.get(name) || [];
221
- let relatedCollections = relationshipDetails
222
- .filter((detail, index, self) => {
223
- const uniqueKey = `${detail.parentCollection}-${detail.childCollection}-${detail.parentKey}-${detail.childKey}`;
224
- return (index ===
225
- self.findIndex((obj) => `${obj.parentCollection}-${obj.childCollection}-${obj.parentKey}-${obj.childKey}` ===
226
- uniqueKey));
227
- })
228
- .map((detail) => {
229
- const relatedCollectionName = detail.isChild
230
- ? detail.parentCollection
231
- : detail.childCollection;
232
- const key = detail.isChild ? detail.childKey : detail.parentKey;
233
- const isArray = detail.isArray ? "array" : "";
234
- return [relatedCollectionName, key, isArray];
235
- });
236
- // Include one-way relationship attributes directly (no twoWayKey)
237
- const oneWayRels = [];
238
- for (const attr of attributes) {
239
- if (attr.type === "relationship" && attr.relatedCollection) {
240
- const relatedName = this.resolveCollectionName(attr.relatedCollection);
241
- const isArray = attr.relationType === "oneToMany" || attr.relationType === "manyToMany"
242
- ? "array"
243
- : "";
244
- oneWayRels.push([relatedName, attr.key, isArray]);
245
- }
246
- }
247
- // Merge and dedupe (by relatedName+key)
248
- relatedCollections = [...relatedCollections, ...oneWayRels].filter((item, idx, self) => idx === self.findIndex((o) => `${o[0]}::${o[1]}` === `${item[0]}::${item[1]}`));
249
- const hasRelationships = relatedCollections.length > 0;
250
- // Build imports for related collections
251
- if (hasRelationships) {
252
- const importLines = relatedCollections.map((rel) => {
253
- const relatedPascalName = toPascalCase(rel[0]);
254
- const relatedCamelName = toCamelCase(rel[0]);
255
- return `import { ${relatedPascalName}Schema } from "./${relatedCamelName}";`;
256
- });
257
- const unique = Array.from(new Set(importLines));
258
- imports += unique.join("\n") + (unique.length ? "\n" : "");
259
- }
260
- let schemaString = `${imports}\n`;
261
- // Single object schema with recursive getters (Zod v4)
262
- schemaString += `export const ${pascalName}Schema = z.object({\n`;
263
- schemaString += ` $id: z.string(),\n`;
264
- schemaString += ` $createdAt: z.string(),\n`;
265
- schemaString += ` $updatedAt: z.string(),\n`;
266
- schemaString += ` $permissions: z.array(z.string()),\n`;
267
- for (const attribute of attributes) {
268
- if (attribute.type === "relationship")
269
- continue;
270
- schemaString += ` ${attribute.key}: ${this.typeToZod(attribute)},\n`;
271
- }
272
- // Add recursive getters for relationships (respect required flag)
273
- relatedCollections.forEach((rel) => {
274
- const relatedPascalName = toPascalCase(rel[0]);
275
- const isArray = rel[2] === "array";
276
- const key = String(rel[1]);
277
- const attrMeta = attributes.find(a => a.key === key && a.type === "relationship");
278
- const isRequired = !!attrMeta?.required;
279
- let getterBody = "";
280
- if (isArray) {
281
- getterBody = isRequired
282
- ? `${relatedPascalName}Schema.array()`
283
- : `${relatedPascalName}Schema.array().nullish()`;
284
- }
285
- else {
286
- getterBody = isRequired
287
- ? `${relatedPascalName}Schema`
288
- : `${relatedPascalName}Schema.nullish()`;
289
- }
290
- schemaString += ` get ${key}(){\n return ${getterBody}\n },\n`;
291
- });
292
- schemaString += `});\n\n`;
293
- schemaString += `export type ${pascalName} = z.infer<typeof ${pascalName}Schema>;\n\n`;
294
- return schemaString;
295
- };
296
- typeToZod = (attribute) => {
297
- let baseSchemaCode = "";
298
- const finalAttribute = (attribute.type === "string" &&
299
- attribute.format &&
300
- attribute.format === "enum" &&
301
- attribute.type === "string"
302
- ? { ...attribute, type: attribute.format }
303
- : attribute);
304
- switch (finalAttribute.type) {
305
- case "string":
306
- baseSchemaCode = "z.string()";
307
- if (finalAttribute.size) {
308
- baseSchemaCode += `.max(${finalAttribute.size}, "Maximum length of ${finalAttribute.size} characters exceeded")`;
309
- }
310
- if (finalAttribute.xdefault !== undefined) {
311
- baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
312
- }
313
- if (!attribute.required && !attribute.array) {
314
- baseSchemaCode += ".nullish()";
315
- }
316
- break;
317
- case "integer":
318
- baseSchemaCode = "z.number().int()";
319
- if (finalAttribute.min !== undefined) {
320
- if (BigInt(finalAttribute.min) === BigInt(-9223372036854776000)) {
321
- finalAttribute.min = undefined;
322
- }
323
- else {
324
- baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
325
- }
326
- }
327
- if (finalAttribute.max !== undefined) {
328
- if (BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
329
- finalAttribute.max = undefined;
330
- }
331
- else {
332
- baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
333
- }
334
- }
335
- if (finalAttribute.xdefault !== undefined) {
336
- baseSchemaCode += `.default(${finalAttribute.xdefault})`;
337
- }
338
- if (!finalAttribute.required && !finalAttribute.array) {
339
- baseSchemaCode += ".nullish()";
340
- }
341
- break;
342
- case "double":
343
- case "float": // Backward compatibility
344
- baseSchemaCode = "z.number()";
345
- if (finalAttribute.min !== undefined) {
346
- baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;
347
- }
348
- if (finalAttribute.max !== undefined) {
349
- baseSchemaCode += `.max(${finalAttribute.max}, "Maximum value of ${finalAttribute.max} exceeded")`;
350
- }
351
- if (finalAttribute.xdefault !== undefined) {
352
- baseSchemaCode += `.default(${finalAttribute.xdefault})`;
353
- }
354
- if (!finalAttribute.required && !finalAttribute.array) {
355
- baseSchemaCode += ".nullish()";
356
- }
357
- break;
358
- case "boolean":
359
- baseSchemaCode = "z.boolean()";
360
- if (finalAttribute.xdefault !== undefined) {
361
- baseSchemaCode += `.default(${finalAttribute.xdefault})`;
362
- }
363
- if (!finalAttribute.required && !finalAttribute.array) {
364
- baseSchemaCode += ".nullish()";
365
- }
366
- break;
367
- case "datetime":
368
- baseSchemaCode = "z.date()";
369
- if (finalAttribute.xdefault !== undefined) {
370
- baseSchemaCode += `.default(new Date("${finalAttribute.xdefault}"))`;
371
- }
372
- if (!finalAttribute.required && !finalAttribute.array) {
373
- baseSchemaCode += ".nullish()";
374
- }
375
- break;
376
- case "email":
377
- baseSchemaCode = "z.string().email()";
378
- if (finalAttribute.xdefault !== undefined) {
379
- baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
380
- }
381
- if (!finalAttribute.required && !finalAttribute.array) {
382
- baseSchemaCode += ".nullish()";
383
- }
384
- break;
385
- case "ip":
386
- baseSchemaCode = "z.string()"; // Add custom validation as needed
387
- if (finalAttribute.xdefault !== undefined) {
388
- baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
389
- }
390
- if (!finalAttribute.required && !finalAttribute.array) {
391
- baseSchemaCode += ".nullish()";
392
- }
393
- break;
394
- case "url":
395
- baseSchemaCode = "z.string().url()";
396
- if (finalAttribute.xdefault !== undefined) {
397
- baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
398
- }
399
- if (!finalAttribute.required && !finalAttribute.array) {
400
- baseSchemaCode += ".nullish()";
401
- }
402
- break;
403
- case "enum":
404
- baseSchemaCode = `z.enum([${finalAttribute.elements
405
- .map((element) => `"${element}"`)
406
- .join(", ")}])`;
407
- if (finalAttribute.xdefault !== undefined) {
408
- baseSchemaCode += `.default("${finalAttribute.xdefault}")`;
409
- }
410
- if (!attribute.required && !attribute.array) {
411
- baseSchemaCode += ".nullish()";
412
- }
413
- break;
414
- case "relationship":
415
- break;
416
- default:
417
- baseSchemaCode = "z.any()";
418
- }
419
- // Handle arrays
420
- if (attribute.array) {
421
- baseSchemaCode = `z.array(${baseSchemaCode})`;
422
- }
423
- if (attribute.array && !attribute.required) {
424
- baseSchemaCode += ".nullish()";
425
- }
426
- return baseSchemaCode;
427
- };
428
- }