appwrite-utils-cli 1.5.2 → 1.6.1

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