appwrite-utils-cli 0.10.85 → 1.0.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 (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 +261 -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 +196 -52
  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 +156 -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 +377 -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 +230 -63
  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 +212 -0
  160. package/src/utils/loadConfigs.ts +414 -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
@@ -1,99 +1,461 @@
1
1
  import path from "path";
2
2
  import fs from "fs";
3
- import { type AppwriteConfig, type Collection } from "appwrite-utils";
3
+ import { type AppwriteConfig, type Collection, type CollectionCreate } from "appwrite-utils";
4
4
  import { register } from "tsx/esm/api"; // Import the register function
5
5
  import { pathToFileURL } from "node:url";
6
6
  import chalk from "chalk";
7
+ import { findYamlConfig, loadYamlConfig } from "../config/yamlConfig.js";
8
+ import yaml from "js-yaml";
9
+ import { z } from "zod";
7
10
 
8
11
  /**
9
- * Recursively searches for a file named 'appwriteConfig.ts' starting from the given directory.
12
+ * Recursively searches for configuration files starting from the given directory.
13
+ * Priority: 1) YAML configs in .appwrite directories, 2) appwriteConfig.ts files in subdirectories
10
14
  * @param dir The directory to start the search from.
11
- * @returns The path to the file if found, or null if not found.
15
+ * @returns The directory path where the config was found, suitable for passing to loadConfig().
12
16
  */
13
17
  export const findAppwriteConfig = (dir: string): string | null => {
14
- if (dir === "node_modules") {
18
+ // First try to find YAML config (already searches recursively for .appwrite dirs)
19
+ const yamlConfig = findYamlConfig(dir);
20
+ if (yamlConfig) {
21
+ // Return the directory containing the config file
22
+ return path.dirname(yamlConfig);
23
+ }
24
+
25
+ // Fall back to TypeScript config search
26
+ const tsConfigPath = findAppwriteConfigTS(dir);
27
+ if (tsConfigPath) {
28
+ return path.dirname(tsConfigPath);
29
+ }
30
+
31
+ return null;
32
+ };
33
+
34
+ const shouldIgnoreDirectory = (dirName: string): boolean => {
35
+ const ignoredDirs = [
36
+ 'node_modules',
37
+ 'dist',
38
+ 'build',
39
+ 'coverage',
40
+ '.next',
41
+ '.nuxt',
42
+ '.cache',
43
+ '.git',
44
+ '.svn',
45
+ '.hg',
46
+ '__pycache__',
47
+ '.pytest_cache',
48
+ '.mypy_cache',
49
+ 'venv',
50
+ '.venv',
51
+ 'env',
52
+ '.env',
53
+ 'target',
54
+ 'out',
55
+ 'bin',
56
+ 'obj',
57
+ '.vs',
58
+ '.vscode',
59
+ '.idea',
60
+ 'temp',
61
+ 'tmp',
62
+ '.tmp',
63
+ 'logs',
64
+ 'log',
65
+ '.DS_Store',
66
+ 'Thumbs.db'
67
+ ];
68
+
69
+ return ignoredDirs.includes(dirName) ||
70
+ dirName.startsWith('.git') ||
71
+ dirName.startsWith('node_modules') ||
72
+ dirName.startsWith('.');
73
+ };
74
+
75
+ const findAppwriteConfigTS = (dir: string, depth: number = 0): string | null => {
76
+ // Limit search depth to prevent infinite recursion
77
+ if (depth > 5) {
15
78
  return null;
16
79
  }
17
- const files = fs.readdirSync(dir, { withFileTypes: true });
18
80
 
19
- for (const file of files) {
20
- if (file.isDirectory() && file.name !== "node_modules") {
21
- const result = findAppwriteConfig(path.join(dir, file.name));
22
- if (result) return result;
23
- } else if (file.name === "appwriteConfig.ts") {
24
- return path.join(dir, file.name);
81
+ if (shouldIgnoreDirectory(path.basename(dir))) {
82
+ return null;
83
+ }
84
+
85
+ try {
86
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
87
+
88
+ // First check current directory for appwriteConfig.ts
89
+ for (const entry of entries) {
90
+ if (entry.isFile() && entry.name === "appwriteConfig.ts") {
91
+ return path.join(dir, entry.name);
92
+ }
25
93
  }
94
+
95
+ // Then search subdirectories
96
+ for (const entry of entries) {
97
+ if (entry.isDirectory() && !shouldIgnoreDirectory(entry.name)) {
98
+ const result = findAppwriteConfigTS(path.join(dir, entry.name), depth + 1);
99
+ if (result) return result;
100
+ }
101
+ }
102
+ } catch (error) {
103
+ // Ignore directory access errors
26
104
  }
27
105
 
28
106
  return null;
29
107
  };
30
108
 
109
+ /**
110
+ * Loads the Appwrite configuration and returns both config and the path where it was found.
111
+ * @param configDir The directory to search for config files.
112
+ * @returns Object containing the config and the actual path where it was found.
113
+ */
114
+ export const loadConfigWithPath = async (
115
+ configDir: string
116
+ ): Promise<{ config: AppwriteConfig; actualConfigPath: string }> => {
117
+ let config: AppwriteConfig | null = null;
118
+ let actualConfigPath: string | null = null;
119
+
120
+ // Check if we're given the .appwrite directory directly
121
+ if (configDir.endsWith('.appwrite')) {
122
+ // Look for config files directly in this directory
123
+ const possibleYamlFiles = ['config.yaml', 'config.yml', 'appwriteConfig.yaml', 'appwriteConfig.yml'];
124
+ for (const fileName of possibleYamlFiles) {
125
+ const yamlPath = path.join(configDir, fileName);
126
+ if (fs.existsSync(yamlPath)) {
127
+ config = await loadYamlConfig(yamlPath);
128
+ actualConfigPath = yamlPath;
129
+ break;
130
+ }
131
+ }
132
+ } else {
133
+ // Original logic: search for .appwrite directories
134
+ const yamlConfigPath = findYamlConfig(configDir);
135
+ if (yamlConfigPath) {
136
+ config = await loadYamlConfig(yamlConfigPath);
137
+ actualConfigPath = yamlConfigPath;
138
+ }
139
+ }
140
+
141
+ // Fall back to TypeScript config if YAML not found or failed to load
142
+ if (!config) {
143
+ const configPath = path.join(configDir, "appwriteConfig.ts");
144
+
145
+ // Only try to load TypeScript config if the file exists
146
+ if (fs.existsSync(configPath)) {
147
+ const unregister = register(); // Register tsx enhancement
148
+
149
+ try {
150
+ console.log(`Loading TypeScript config from: ${configPath}`);
151
+ const configUrl = pathToFileURL(configPath).href;
152
+ const configModule = (await import(configUrl));
153
+ config = configModule.default?.default || configModule.default || configModule;
154
+ if (!config) {
155
+ throw new Error("Failed to load config");
156
+ }
157
+ actualConfigPath = configPath;
158
+ } finally {
159
+ unregister(); // Unregister tsx when done
160
+ }
161
+ }
162
+ }
163
+
164
+ if (!config || !actualConfigPath) {
165
+ throw new Error("No valid configuration found");
166
+ }
167
+
168
+ // Determine collections directory based on actual config file location
169
+ let collectionsDir: string;
170
+ const configFileDir = path.dirname(actualConfigPath);
171
+
172
+ // Check if config is in .appwrite directory
173
+ if (configFileDir.endsWith('.appwrite')) {
174
+ collectionsDir = path.join(configFileDir, "collections");
175
+ } else {
176
+ // Config is in root or other directory
177
+ collectionsDir = path.join(configFileDir, "collections");
178
+ }
179
+
180
+ // Load collections if they exist
181
+ if (fs.existsSync(collectionsDir)) {
182
+ const unregister = register(); // Register tsx for collections
183
+
184
+ try {
185
+ const collectionFiles = fs.readdirSync(collectionsDir);
186
+ config.collections = [];
187
+
188
+ for (const file of collectionFiles) {
189
+ if (file === "index.ts") {
190
+ continue;
191
+ }
192
+ const filePath = path.join(collectionsDir, file);
193
+
194
+ // Handle YAML collections
195
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
196
+ const collection = loadYamlCollection(filePath);
197
+ if (collection) {
198
+ config.collections.push(collection);
199
+ }
200
+ continue;
201
+ }
202
+
203
+ // Handle TypeScript collections
204
+ if (file.endsWith('.ts')) {
205
+ const fileUrl = pathToFileURL(filePath).href;
206
+ const collectionModule = (await import(fileUrl));
207
+ const collection: Collection | undefined = collectionModule.default?.default || collectionModule.default || collectionModule;
208
+ if (collection) {
209
+ // Ensure importDefs are properly loaded
210
+ if (collectionModule.importDefs || collection.importDefs) {
211
+ collection.importDefs = collectionModule.importDefs || collection.importDefs;
212
+ }
213
+ config.collections.push(collection as CollectionCreate);
214
+ }
215
+ }
216
+ }
217
+ } finally {
218
+ unregister(); // Unregister tsx when done
219
+ }
220
+ }
221
+
222
+ return { config, actualConfigPath };
223
+ };
224
+
31
225
  /**
32
226
  * Loads the Appwrite configuration and all collection configurations from a specified directory.
33
- * @param configDir The directory containing the appwriteConfig.ts and collections folder.
227
+ * Supports both YAML and TypeScript config formats with backward compatibility.
228
+ * @param configDir The directory containing the config file and collections folder.
34
229
  * @returns The loaded Appwrite configuration including collections.
35
230
  */
36
231
  export const loadConfig = async (
37
232
  configDir: string
38
233
  ): Promise<AppwriteConfig> => {
39
- const unregister = register(); // Register tsx enhancement
234
+ let config: AppwriteConfig | null = null;
235
+ let actualConfigPath: string | null = null;
40
236
 
41
- try {
237
+ // First try to find and load YAML config
238
+ const yamlConfigPath = findYamlConfig(configDir);
239
+ if (yamlConfigPath) {
240
+ console.log(`Loading YAML config from: ${yamlConfigPath}`);
241
+ config = await loadYamlConfig(yamlConfigPath);
242
+ actualConfigPath = yamlConfigPath;
243
+ }
244
+
245
+ // Fall back to TypeScript config if YAML not found or failed to load
246
+ if (!config) {
42
247
  const configPath = path.join(configDir, "appwriteConfig.ts");
43
- console.log(`Loading config from: ${configPath}`);
44
- const configUrl = pathToFileURL(configPath).href;
45
- const configModule = (await import(configUrl));
46
- const config: AppwriteConfig | undefined = configModule.default?.default || configModule.default || configModule;
47
- if (!config) {
48
- throw new Error("Failed to load config");
248
+
249
+ // Only try to load TypeScript config if the file exists
250
+ if (fs.existsSync(configPath)) {
251
+ const unregister = register(); // Register tsx enhancement
252
+
253
+ try {
254
+ console.log(`Loading TypeScript config from: ${configPath}`);
255
+ const configUrl = pathToFileURL(configPath).href;
256
+ const configModule = (await import(configUrl));
257
+ config = configModule.default?.default || configModule.default || configModule;
258
+ if (!config) {
259
+ throw new Error("Failed to load config");
260
+ }
261
+ actualConfigPath = configPath;
262
+ } finally {
263
+ unregister(); // Unregister tsx when done
264
+ }
49
265
  }
50
- const collectionsDir = path.join(configDir, "collections");
51
- const collectionFiles = fs.readdirSync(collectionsDir);
266
+ }
52
267
 
53
- config.collections = [];
268
+ if (!config) {
269
+ throw new Error("No valid configuration found");
270
+ }
54
271
 
55
- for (const file of collectionFiles) {
56
- if (file === "index.ts") {
57
- continue;
58
- }
59
- const filePath = path.join(collectionsDir, file);
60
- const fileUrl = pathToFileURL(filePath).href;
61
- const collectionModule = (await import(fileUrl));
62
- const collection: Collection | undefined = collectionModule.default?.default || collectionModule.default || collectionModule;
63
- if (collection) {
64
- // Ensure importDefs are properly loaded
65
- if (collectionModule.importDefs || collection.importDefs) {
66
- collection.importDefs = collectionModule.importDefs || collection.importDefs;
272
+ // Determine collections directory based on actual config file location
273
+ let collectionsDir: string;
274
+ if (actualConfigPath) {
275
+ const configFileDir = path.dirname(actualConfigPath);
276
+
277
+ // Check if config is in .appwrite directory
278
+ if (configFileDir.endsWith('.appwrite')) {
279
+ collectionsDir = path.join(configFileDir, "collections");
280
+ } else {
281
+ // Config is in root or other directory
282
+ collectionsDir = path.join(configFileDir, "collections");
283
+ }
284
+ } else {
285
+ // Fallback to original behavior if no actual config path found
286
+ collectionsDir = path.join(configDir, "collections");
287
+ }
288
+
289
+ // Load collections if they exist
290
+ if (fs.existsSync(collectionsDir)) {
291
+ const unregister = register(); // Register tsx for collections
292
+
293
+ try {
294
+ const collectionFiles = fs.readdirSync(collectionsDir);
295
+ config.collections = [];
296
+
297
+ for (const file of collectionFiles) {
298
+ if (file === "index.ts") {
299
+ continue;
300
+ }
301
+ const filePath = path.join(collectionsDir, file);
302
+
303
+ // Handle YAML collections
304
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
305
+ const collection = loadYamlCollection(filePath);
306
+ if (collection) {
307
+ config.collections.push(collection);
308
+ }
309
+ continue;
310
+ }
311
+
312
+ // Handle TypeScript collections
313
+ if (file.endsWith('.ts')) {
314
+ const fileUrl = pathToFileURL(filePath).href;
315
+ const collectionModule = (await import(fileUrl));
316
+ const collection: Collection | undefined = collectionModule.default?.default || collectionModule.default || collectionModule;
317
+ if (collection) {
318
+ // Ensure importDefs are properly loaded
319
+ if (collectionModule.importDefs || collection.importDefs) {
320
+ collection.importDefs = collectionModule.importDefs || collection.importDefs;
321
+ }
322
+ config.collections.push(collection);
323
+ }
67
324
  }
68
- config.collections.push(collection);
69
325
  }
326
+ } finally {
327
+ unregister(); // Unregister tsx when done
70
328
  }
71
-
72
- return config;
73
- } finally {
74
- unregister(); // Unregister tsx when done
329
+ } else {
330
+ config.collections = config.collections || [];
75
331
  }
332
+
333
+ return config;
76
334
  };
77
335
 
78
- export const findFunctionsDir = (dir: string): string | null => {
79
- if (dir === "node_modules") {
336
+ export const findFunctionsDir = (dir: string, depth: number = 0): string | null => {
337
+ // Limit search depth to prevent infinite recursion
338
+ if (depth > 5) {
339
+ return null;
340
+ }
341
+
342
+ if (shouldIgnoreDirectory(path.basename(dir))) {
80
343
  return null;
81
344
  }
82
345
 
83
- const files = fs.readdirSync(dir, { withFileTypes: true });
346
+ try {
347
+ const files = fs.readdirSync(dir, { withFileTypes: true });
84
348
 
85
- for (const entry of files) {
86
- if (!entry.isDirectory() || entry.name === "node_modules") {
87
- continue;
88
- }
349
+ for (const entry of files) {
350
+ if (!entry.isDirectory() || shouldIgnoreDirectory(entry.name)) {
351
+ continue;
352
+ }
89
353
 
90
- if (entry.name === "functions") {
91
- return path.join(dir, entry.name);
92
- }
354
+ if (entry.name === "functions") {
355
+ return path.join(dir, entry.name);
356
+ }
93
357
 
94
- const result = findFunctionsDir(path.join(dir, entry.name));
95
- if (result) return result;
358
+ const result = findFunctionsDir(path.join(dir, entry.name), depth + 1);
359
+ if (result) return result;
360
+ }
361
+ } catch (error) {
362
+ // Ignore directory access errors
96
363
  }
97
364
 
98
365
  return null;
366
+ };
367
+
368
+ // YAML Collection Schema
369
+ const YamlCollectionSchema = z.object({
370
+ name: z.string(),
371
+ id: z.string().optional(),
372
+ documentSecurity: z.boolean().default(false),
373
+ enabled: z.boolean().default(true),
374
+ permissions: z.array(
375
+ z.object({
376
+ permission: z.string(),
377
+ target: z.string()
378
+ })
379
+ ).optional().default([]),
380
+ attributes: z.array(
381
+ z.object({
382
+ key: z.string(),
383
+ type: z.string(),
384
+ size: z.number().optional(),
385
+ required: z.boolean().default(false),
386
+ array: z.boolean().optional(),
387
+ default: z.any().optional(),
388
+ description: z.string().optional(),
389
+ min: z.number().optional(),
390
+ max: z.number().optional(),
391
+ elements: z.array(z.string()).optional(),
392
+ relatedCollection: z.string().optional(),
393
+ relationType: z.string().optional(),
394
+ twoWay: z.boolean().optional(),
395
+ twoWayKey: z.string().optional(),
396
+ onDelete: z.string().optional(),
397
+ side: z.string().optional()
398
+ })
399
+ ).optional().default([]),
400
+ indexes: z.array(
401
+ z.object({
402
+ key: z.string(),
403
+ type: z.string(),
404
+ attributes: z.array(z.string()),
405
+ orders: z.array(z.string()).optional()
406
+ })
407
+ ).optional().default([]),
408
+ importDefs: z.array(z.any()).optional().default([])
409
+ });
410
+
411
+ type YamlCollection = z.infer<typeof YamlCollectionSchema>;
412
+
413
+ const loadYamlCollection = (filePath: string): CollectionCreate | null => {
414
+ try {
415
+ const fileContent = fs.readFileSync(filePath, "utf8");
416
+ const yamlData = yaml.load(fileContent) as unknown;
417
+ const parsedCollection = YamlCollectionSchema.parse(yamlData);
418
+
419
+ // Convert YAML collection to CollectionCreate format
420
+ const collection: CollectionCreate = {
421
+ name: parsedCollection.name,
422
+ $id: parsedCollection.id || parsedCollection.name.toLowerCase().replace(/\s+/g, '_'),
423
+ documentSecurity: parsedCollection.documentSecurity,
424
+ enabled: parsedCollection.enabled,
425
+ $permissions: parsedCollection.permissions.map(p => ({
426
+ permission: p.permission as any,
427
+ target: p.target
428
+ })),
429
+ attributes: parsedCollection.attributes.map(attr => ({
430
+ key: attr.key,
431
+ type: attr.type as any,
432
+ size: attr.size,
433
+ required: attr.required,
434
+ array: attr.array,
435
+ xdefault: attr.default,
436
+ description: attr.description,
437
+ min: attr.min,
438
+ max: attr.max,
439
+ elements: attr.elements,
440
+ relatedCollection: attr.relatedCollection,
441
+ relationType: attr.relationType as any,
442
+ twoWay: attr.twoWay,
443
+ twoWayKey: attr.twoWayKey,
444
+ onDelete: attr.onDelete as any,
445
+ side: attr.side as any
446
+ })),
447
+ indexes: parsedCollection.indexes.map(idx => ({
448
+ key: idx.key,
449
+ type: idx.type as any,
450
+ attributes: idx.attributes,
451
+ orders: idx.orders as any
452
+ })),
453
+ importDefs: parsedCollection.importDefs
454
+ };
455
+
456
+ return collection;
457
+ } catch (error) {
458
+ console.error(`Error loading YAML collection from ${filePath}:`, error);
459
+ return null;
460
+ }
99
461
  };
@@ -410,7 +410,8 @@ export class SchemaGenerator {
410
410
  baseSchemaCode += ".nullish()";
411
411
  }
412
412
  break;
413
- case "float":
413
+ case "double":
414
+ case "float": // Backward compatibility
414
415
  baseSchemaCode = "z.number()";
415
416
  if (finalAttribute.min !== undefined) {
416
417
  baseSchemaCode += `.min(${finalAttribute.min}, "Minimum value of ${finalAttribute.min} not met")`;