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,273 @@
1
+ import {} from "node-appwrite";
2
+ import { parseAttribute, } from "appwrite-utils";
3
+ import { nameToIdMapping, enqueueOperation } from "./operationQueue.js";
4
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
5
+ import chalk from "chalk";
6
+ import pLimit from "p-limit";
7
+ // Concurrency limits for different operations
8
+ const attributeLimit = pLimit(3); // Low limit for attribute operations
9
+ const queryLimit = pLimit(25); // Higher limit for read operations
10
+ export const attributesSame = (databaseAttribute, configAttribute) => {
11
+ const attributesToCheck = [
12
+ "key",
13
+ "type",
14
+ "array",
15
+ "encrypted",
16
+ "required",
17
+ "size",
18
+ "min",
19
+ "max",
20
+ "xdefault",
21
+ "elements",
22
+ "relationType",
23
+ "twoWay",
24
+ "twoWayKey",
25
+ "onDelete",
26
+ "relatedCollection",
27
+ ];
28
+ return attributesToCheck.every((attr) => {
29
+ // Special handling for min/max values
30
+ if (attr === "min" || attr === "max") {
31
+ const dbValue = databaseAttribute[attr];
32
+ const configValue = configAttribute[attr];
33
+ // Use type-specific default values when comparing
34
+ if (databaseAttribute.type === "integer") {
35
+ const defaultMin = attr === "min" ? -2147483647 : undefined;
36
+ const defaultMax = attr === "max" ? 2147483647 : undefined;
37
+ return (dbValue ?? defaultMin) === (configValue ?? defaultMax);
38
+ }
39
+ if (databaseAttribute.type === "double" || databaseAttribute.type === "float") {
40
+ const defaultMin = attr === "min" ? -2147483647 : undefined;
41
+ const defaultMax = attr === "max" ? 2147483647 : undefined;
42
+ return (dbValue ?? defaultMin) === (configValue ?? defaultMax);
43
+ }
44
+ }
45
+ // Check if both objects have the attribute
46
+ const dbHasAttr = attr in databaseAttribute;
47
+ const configHasAttr = attr in configAttribute;
48
+ // If both have the attribute, compare values
49
+ if (dbHasAttr && configHasAttr) {
50
+ const dbValue = databaseAttribute[attr];
51
+ const configValue = configAttribute[attr];
52
+ // Consider undefined and null as equivalent
53
+ if ((dbValue === undefined || dbValue === null) &&
54
+ (configValue === undefined || configValue === null)) {
55
+ return true;
56
+ }
57
+ return dbValue === configValue;
58
+ }
59
+ // If neither has the attribute, consider it the same
60
+ if (!dbHasAttr && !configHasAttr) {
61
+ return true;
62
+ }
63
+ // If one has the attribute and the other doesn't, check if it's undefined or null
64
+ if (dbHasAttr && !configHasAttr) {
65
+ const dbValue = databaseAttribute[attr];
66
+ return dbValue === undefined || dbValue === null;
67
+ }
68
+ if (!dbHasAttr && configHasAttr) {
69
+ const configValue = configAttribute[attr];
70
+ return configValue === undefined || configValue === null;
71
+ }
72
+ // If we reach here, the attributes are different
73
+ return false;
74
+ });
75
+ };
76
+ export const createOrUpdateAttribute = async (db, dbId, collection, attribute, options = {}) => {
77
+ const { updateEnabled = true, useQueue = true, verbose = false } = options;
78
+ let action = "create";
79
+ let foundAttribute;
80
+ let finalAttribute = attribute;
81
+ try {
82
+ const collectionAttr = collection.attributes.find(
83
+ // @ts-expect-error - Appwrite type issues
84
+ (attr) => attr.key === attribute.key);
85
+ foundAttribute = parseAttribute(collectionAttr);
86
+ if (verbose) {
87
+ console.log(`Found attribute: ${JSON.stringify(foundAttribute)}`);
88
+ }
89
+ }
90
+ catch (error) {
91
+ foundAttribute = undefined;
92
+ }
93
+ if (foundAttribute &&
94
+ attributesSame(foundAttribute, attribute) &&
95
+ updateEnabled) {
96
+ if (verbose) {
97
+ console.log(chalk.green(`✓ Attribute ${attribute.key} is up to date`));
98
+ }
99
+ return;
100
+ }
101
+ if (foundAttribute) {
102
+ action = "update";
103
+ if (verbose) {
104
+ console.log(chalk.yellow(`⚠ Updating attribute ${attribute.key}`));
105
+ }
106
+ }
107
+ else {
108
+ if (verbose) {
109
+ console.log(chalk.blue(`+ Creating attribute ${attribute.key}`));
110
+ }
111
+ }
112
+ // Handle relationship attributes with nameToIdMapping
113
+ if (attribute.type === "relationship" && attribute.relatedCollection) {
114
+ const relatedCollectionId = nameToIdMapping.get(attribute.relatedCollection);
115
+ if (relatedCollectionId) {
116
+ finalAttribute = {
117
+ ...attribute,
118
+ relatedCollection: relatedCollectionId,
119
+ };
120
+ }
121
+ }
122
+ // Handle BigInt values for integer, double and float types
123
+ if (attribute.type === "integer" || attribute.type === "double" || attribute.type === "float") {
124
+ if (typeof finalAttribute.min === "bigint") {
125
+ finalAttribute.min = Number(finalAttribute.min);
126
+ }
127
+ if (typeof finalAttribute.max === "bigint") {
128
+ finalAttribute.max = Number(finalAttribute.max);
129
+ }
130
+ }
131
+ const queuedOperation = {
132
+ type: "attribute",
133
+ collectionId: collection.$id,
134
+ attribute: finalAttribute,
135
+ collection,
136
+ };
137
+ const executeOperation = async () => {
138
+ await attributeLimit(async () => {
139
+ if (action === "update" && foundAttribute) {
140
+ // Delete existing attribute first
141
+ await tryAwaitWithRetry(async () => {
142
+ await db.deleteAttribute(dbId, collection.$id, attribute.key);
143
+ });
144
+ await delay(250);
145
+ }
146
+ // Create attribute based on type
147
+ switch (finalAttribute.type) {
148
+ case "string":
149
+ await tryAwaitWithRetry(async () => {
150
+ await db.createStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.size, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array, finalAttribute.encrypted);
151
+ });
152
+ break;
153
+ case "integer":
154
+ await tryAwaitWithRetry(async () => {
155
+ await db.createIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.min, finalAttribute.max, finalAttribute.xdefault, finalAttribute.array);
156
+ });
157
+ break;
158
+ case "double":
159
+ case "float": // Backward compatibility
160
+ await tryAwaitWithRetry(async () => {
161
+ await db.createFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.min, finalAttribute.max, finalAttribute.xdefault, finalAttribute.array);
162
+ });
163
+ break;
164
+ case "boolean":
165
+ await tryAwaitWithRetry(async () => {
166
+ await db.createBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
167
+ });
168
+ break;
169
+ case "datetime":
170
+ await tryAwaitWithRetry(async () => {
171
+ await db.createDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
172
+ });
173
+ break;
174
+ case "email":
175
+ await tryAwaitWithRetry(async () => {
176
+ await db.createEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
177
+ });
178
+ break;
179
+ case "ip":
180
+ await tryAwaitWithRetry(async () => {
181
+ await db.createIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
182
+ });
183
+ break;
184
+ case "url":
185
+ await tryAwaitWithRetry(async () => {
186
+ await db.createUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
187
+ });
188
+ break;
189
+ case "enum":
190
+ await tryAwaitWithRetry(async () => {
191
+ await db.createEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required, finalAttribute.xdefault, finalAttribute.array);
192
+ });
193
+ break;
194
+ case "relationship":
195
+ await tryAwaitWithRetry(async () => {
196
+ await db.createRelationshipAttribute(dbId, collection.$id, finalAttribute.relatedCollection, finalAttribute.relationType, finalAttribute.twoWay, finalAttribute.key, finalAttribute.twoWayKey, finalAttribute.onDelete);
197
+ });
198
+ break;
199
+ default:
200
+ throw new Error(`Unknown attribute type: ${finalAttribute.type}`);
201
+ }
202
+ });
203
+ };
204
+ if (useQueue) {
205
+ enqueueOperation(queuedOperation);
206
+ }
207
+ else {
208
+ await executeOperation();
209
+ }
210
+ };
211
+ export const createUpdateCollectionAttributes = async (db, dbId, collection, collectionConfig, options = {}) => {
212
+ if (!collectionConfig.attributes)
213
+ return;
214
+ const { verbose = false } = options;
215
+ if (verbose) {
216
+ console.log(chalk.blue(`Processing ${collectionConfig.attributes.length} attributes for collection ${collection.name}`));
217
+ }
218
+ for (const attribute of collectionConfig.attributes) {
219
+ try {
220
+ await createOrUpdateAttribute(db, dbId, collection, attribute, options);
221
+ if (verbose) {
222
+ console.log(chalk.green(`✓ Processed attribute ${attribute.key}`));
223
+ }
224
+ // Add delay between attribute operations to prevent rate limiting
225
+ await delay(250);
226
+ }
227
+ catch (error) {
228
+ console.error(chalk.red(`❌ Failed to process attribute ${attribute.key}:`), error);
229
+ throw error;
230
+ }
231
+ }
232
+ };
233
+ export const deleteObsoleteAttributes = async (db, dbId, collection, collectionConfig, options = {}) => {
234
+ const { useQueue = true, verbose = false } = options;
235
+ const configAttributes = collectionConfig.attributes || [];
236
+ const configAttributeKeys = new Set(configAttributes.map(attr => attr.key));
237
+ // Find attributes that exist in the database but not in the config
238
+ const obsoleteAttributes = collection.attributes.filter(
239
+ // @ts-expect-error - Appwrite type issues
240
+ (attr) => !configAttributeKeys.has(attr.key));
241
+ if (obsoleteAttributes.length === 0) {
242
+ return;
243
+ }
244
+ if (verbose) {
245
+ console.log(chalk.yellow(`🗑️ Removing ${obsoleteAttributes.length} obsolete attributes from collection ${collection.name}`));
246
+ }
247
+ for (const attr of obsoleteAttributes) {
248
+ const queuedOperation = {
249
+ type: "attribute",
250
+ collectionId: collection.$id,
251
+ // @ts-expect-error - Appwrite type issues
252
+ attribute: { key: attr.key, type: "delete" },
253
+ collection,
254
+ };
255
+ const executeOperation = async () => {
256
+ await attributeLimit(() => tryAwaitWithRetry(async () => {
257
+ // @ts-expect-error - Appwrite type issues
258
+ await db.deleteAttribute(dbId, collection.$id, attr.key);
259
+ }));
260
+ };
261
+ if (useQueue) {
262
+ enqueueOperation(queuedOperation);
263
+ }
264
+ else {
265
+ await executeOperation();
266
+ await delay(250);
267
+ }
268
+ if (verbose) {
269
+ // @ts-expect-error - Appwrite type issues
270
+ console.log(chalk.gray(`🗑️ Deleted obsolete attribute ${attr.key}`));
271
+ }
272
+ }
273
+ };
@@ -0,0 +1,75 @@
1
+ export interface DestructiveOperationOptions {
2
+ operation: string;
3
+ targets: string[];
4
+ consequences?: string[];
5
+ requireExplicitConfirmation?: boolean;
6
+ confirmationText?: string;
7
+ skipConfirmation?: boolean;
8
+ }
9
+ export interface BackupPromptOptions {
10
+ operation: string;
11
+ targets: string[];
12
+ recommendBackup?: boolean;
13
+ backupMessage?: string;
14
+ }
15
+ export declare class ConfirmationDialogs {
16
+ /**
17
+ * Shows a confirmation dialog for destructive operations
18
+ */
19
+ static confirmDestructiveOperation(options: DestructiveOperationOptions): Promise<boolean>;
20
+ /**
21
+ * Prompts user about creating a backup before a destructive operation
22
+ */
23
+ static promptForBackup(options: BackupPromptOptions): Promise<'yes' | 'no' | 'skip'>;
24
+ /**
25
+ * Shows a final confirmation before proceeding with an operation
26
+ */
27
+ static finalConfirmation(operation: string, details?: string[]): Promise<boolean>;
28
+ /**
29
+ * Specialized confirmation for database wiping
30
+ */
31
+ static confirmDatabaseWipe(databaseNames: string[], options?: {
32
+ includeStorage?: boolean;
33
+ includeUsers?: boolean;
34
+ skipConfirmation?: boolean;
35
+ }): Promise<boolean>;
36
+ /**
37
+ * Specialized confirmation for collection wiping
38
+ */
39
+ static confirmCollectionWipe(databaseName: string, collectionNames: string[], options?: {
40
+ skipConfirmation?: boolean;
41
+ }): Promise<boolean>;
42
+ /**
43
+ * Specialized confirmation for function deployment
44
+ */
45
+ static confirmFunctionDeployment(functionNames: string[], options?: {
46
+ isProduction?: boolean;
47
+ hasBreakingChanges?: boolean;
48
+ skipConfirmation?: boolean;
49
+ }): Promise<boolean>;
50
+ /**
51
+ * Shows operation summary and asks for final confirmation
52
+ */
53
+ static showOperationSummary(title: string, summary: Record<string, string | number | string[]>, options?: {
54
+ confirmationRequired?: boolean;
55
+ warningMessage?: string;
56
+ }): Promise<boolean>;
57
+ /**
58
+ * Interactive selection with confirmation
59
+ */
60
+ static selectWithConfirmation<T>(items: T[], options: {
61
+ message: string;
62
+ displayProperty?: keyof T;
63
+ multiSelect?: boolean;
64
+ confirmMessage?: string;
65
+ validator?: (selection: T[]) => string | true;
66
+ }): Promise<T[]>;
67
+ /**
68
+ * Confirms overwriting an existing file or directory
69
+ */
70
+ static confirmOverwrite(target: string): Promise<boolean>;
71
+ /**
72
+ * Confirms removal of a file
73
+ */
74
+ static confirmRemoval(target: string): Promise<boolean>;
75
+ }
@@ -0,0 +1,236 @@
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
3
+ import { MessageFormatter } from "./messageFormatter.js";
4
+ export class ConfirmationDialogs {
5
+ /**
6
+ * Shows a confirmation dialog for destructive operations
7
+ */
8
+ static async confirmDestructiveOperation(options) {
9
+ if (options.skipConfirmation) {
10
+ return true;
11
+ }
12
+ MessageFormatter.warning(`You are about to perform a destructive operation:`);
13
+ console.log(chalk.red.bold(` Operation: ${options.operation}`));
14
+ console.log(chalk.yellow(` Targets: ${options.targets.join(", ")}`));
15
+ if (options.consequences && options.consequences.length > 0) {
16
+ console.log(chalk.red("\n This will:"));
17
+ options.consequences.forEach(consequence => {
18
+ console.log(chalk.red(` • ${consequence}`));
19
+ });
20
+ }
21
+ console.log(chalk.red("\n ⚠️ THIS ACTION CANNOT BE UNDONE!"));
22
+ if (options.requireExplicitConfirmation && options.confirmationText) {
23
+ const { confirmation } = await inquirer.prompt([{
24
+ type: 'input',
25
+ name: 'confirmation',
26
+ message: chalk.red(`Type "${options.confirmationText}" to confirm:`),
27
+ validate: (input) => {
28
+ return input === options.confirmationText ||
29
+ chalk.red(`Please type exactly: ${options.confirmationText}`);
30
+ }
31
+ }]);
32
+ return confirmation === options.confirmationText;
33
+ }
34
+ else {
35
+ const { confirmed } = await inquirer.prompt([{
36
+ type: 'confirm',
37
+ name: 'confirmed',
38
+ message: chalk.red('Are you absolutely sure you want to continue?'),
39
+ default: false
40
+ }]);
41
+ return confirmed;
42
+ }
43
+ }
44
+ /**
45
+ * Prompts user about creating a backup before a destructive operation
46
+ */
47
+ static async promptForBackup(options) {
48
+ const message = options.backupMessage ||
49
+ `Create a backup before performing ${options.operation} on: ${options.targets.join(", ")}?`;
50
+ console.log(chalk.blue("\n🛡️ Backup Recommendation"));
51
+ if (options.recommendBackup !== false) {
52
+ console.log(chalk.yellow(" It's strongly recommended to create a backup before proceeding."));
53
+ }
54
+ const { choice } = await inquirer.prompt([{
55
+ type: 'list',
56
+ name: 'choice',
57
+ message,
58
+ choices: [
59
+ { name: '🛡️ Yes, create backup first', value: 'yes' },
60
+ { name: '⚠️ No, proceed without backup', value: 'no' },
61
+ { name: '❌ Cancel operation', value: 'skip' }
62
+ ],
63
+ default: 'yes'
64
+ }]);
65
+ return choice;
66
+ }
67
+ /**
68
+ * Shows a final confirmation before proceeding with an operation
69
+ */
70
+ static async finalConfirmation(operation, details) {
71
+ console.log(chalk.green(`\n✅ Ready to perform: ${chalk.bold(operation)}`));
72
+ if (details && details.length > 0) {
73
+ console.log(chalk.gray(" Details:"));
74
+ details.forEach(detail => {
75
+ console.log(chalk.gray(` • ${detail}`));
76
+ });
77
+ }
78
+ const { proceed } = await inquirer.prompt([{
79
+ type: 'confirm',
80
+ name: 'proceed',
81
+ message: 'Proceed with this operation?',
82
+ default: true
83
+ }]);
84
+ return proceed;
85
+ }
86
+ /**
87
+ * Specialized confirmation for database wiping
88
+ */
89
+ static async confirmDatabaseWipe(databaseNames, options = {}) {
90
+ const consequences = [
91
+ "Delete all documents in the specified databases",
92
+ "Remove all collections and their data",
93
+ ];
94
+ if (options.includeStorage) {
95
+ consequences.push("Delete all files in associated storage buckets");
96
+ }
97
+ if (options.includeUsers) {
98
+ consequences.push("Delete all user accounts");
99
+ }
100
+ return this.confirmDestructiveOperation({
101
+ operation: "Database Wipe",
102
+ targets: databaseNames,
103
+ consequences,
104
+ requireExplicitConfirmation: true,
105
+ confirmationText: "DELETE ALL DATA",
106
+ skipConfirmation: options.skipConfirmation,
107
+ });
108
+ }
109
+ /**
110
+ * Specialized confirmation for collection wiping
111
+ */
112
+ static async confirmCollectionWipe(databaseName, collectionNames, options = {}) {
113
+ return this.confirmDestructiveOperation({
114
+ operation: "Collection Wipe",
115
+ targets: collectionNames.map(name => `${databaseName}.${name}`),
116
+ consequences: [
117
+ "Delete all documents in the specified collections",
118
+ "Keep the collection structure intact",
119
+ ],
120
+ requireExplicitConfirmation: collectionNames.length > 5,
121
+ confirmationText: "DELETE DOCUMENTS",
122
+ skipConfirmation: options.skipConfirmation,
123
+ });
124
+ }
125
+ /**
126
+ * Specialized confirmation for function deployment
127
+ */
128
+ static async confirmFunctionDeployment(functionNames, options = {}) {
129
+ if (options.skipConfirmation) {
130
+ return true;
131
+ }
132
+ const consequences = ["Replace existing function code"];
133
+ if (options.isProduction) {
134
+ consequences.push("Affect production environment");
135
+ }
136
+ if (options.hasBreakingChanges) {
137
+ consequences.push("Potentially break existing integrations");
138
+ }
139
+ return this.confirmDestructiveOperation({
140
+ operation: "Function Deployment",
141
+ targets: functionNames,
142
+ consequences: consequences.length > 1 ? consequences : undefined,
143
+ requireExplicitConfirmation: options.isProduction || options.hasBreakingChanges,
144
+ confirmationText: options.isProduction ? "DEPLOY TO PRODUCTION" : "DEPLOY",
145
+ });
146
+ }
147
+ /**
148
+ * Shows operation summary and asks for final confirmation
149
+ */
150
+ static async showOperationSummary(title, summary, options = {}) {
151
+ MessageFormatter.section(`${title} Summary`);
152
+ Object.entries(summary).forEach(([key, value]) => {
153
+ const formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
154
+ if (Array.isArray(value)) {
155
+ console.log(`${chalk.gray("●")} ${formattedKey}:`);
156
+ value.forEach(item => {
157
+ console.log(` ${chalk.gray("•")} ${item}`);
158
+ });
159
+ }
160
+ else {
161
+ console.log(`${chalk.gray("●")} ${formattedKey}: ${chalk.cyan(String(value))}`);
162
+ }
163
+ });
164
+ if (options.warningMessage) {
165
+ console.log(chalk.yellow(`\n⚠️ ${options.warningMessage}`));
166
+ }
167
+ if (options.confirmationRequired !== false) {
168
+ const { confirmed } = await inquirer.prompt([{
169
+ type: 'confirm',
170
+ name: 'confirmed',
171
+ message: 'Continue with this operation?',
172
+ default: true
173
+ }]);
174
+ return confirmed;
175
+ }
176
+ return true;
177
+ }
178
+ /**
179
+ * Interactive selection with confirmation
180
+ */
181
+ static async selectWithConfirmation(items, options) {
182
+ const choices = items.map((item, index) => ({
183
+ name: options.displayProperty ? String(item[options.displayProperty]) : String(item),
184
+ value: item,
185
+ }));
186
+ const prompt = options.multiSelect ? 'checkbox' : 'list';
187
+ const { selection } = await inquirer.prompt([{
188
+ type: prompt,
189
+ name: 'selection',
190
+ message: options.message,
191
+ choices,
192
+ validate: options.validator ? (input) => {
193
+ const result = options.validator(Array.isArray(input) ? input : [input]);
194
+ return result;
195
+ } : undefined,
196
+ }]);
197
+ const selectedItems = Array.isArray(selection) ? selection : [selection];
198
+ if (options.confirmMessage) {
199
+ const confirmMessage = options.confirmMessage.replace('{count}', selectedItems.length.toString());
200
+ const { confirmed } = await inquirer.prompt([{
201
+ type: 'confirm',
202
+ name: 'confirmed',
203
+ message: confirmMessage,
204
+ default: true
205
+ }]);
206
+ if (!confirmed) {
207
+ return [];
208
+ }
209
+ }
210
+ return selectedItems;
211
+ }
212
+ /**
213
+ * Confirms overwriting an existing file or directory
214
+ */
215
+ static async confirmOverwrite(target) {
216
+ const { confirmed } = await inquirer.prompt([{
217
+ type: 'confirm',
218
+ name: 'confirmed',
219
+ message: chalk.yellow(`${target} already exists. Overwrite?`),
220
+ default: false
221
+ }]);
222
+ return confirmed;
223
+ }
224
+ /**
225
+ * Confirms removal of a file
226
+ */
227
+ static async confirmRemoval(target) {
228
+ const { confirmed } = await inquirer.prompt([{
229
+ type: 'confirm',
230
+ name: 'confirmed',
231
+ message: chalk.red(target),
232
+ default: false
233
+ }]);
234
+ return confirmed;
235
+ }
236
+ }
@@ -0,0 +1,48 @@
1
+ import { Client, type Models } from "node-appwrite";
2
+ import { type AppwriteFunction } from "appwrite-utils";
3
+ export interface FunctionSearchOptions {
4
+ searchPaths?: string[];
5
+ caseSensitive?: boolean;
6
+ allowFuzzyMatch?: boolean;
7
+ verbose?: boolean;
8
+ }
9
+ export interface FunctionDeploymentOptions {
10
+ activate?: boolean;
11
+ entrypoint?: string;
12
+ commands?: string;
13
+ ignored?: string[];
14
+ verbose?: boolean;
15
+ forceRedeploy?: boolean;
16
+ }
17
+ export declare class FunctionManager {
18
+ private client;
19
+ private functions;
20
+ constructor(client: Client);
21
+ /**
22
+ * Improved function directory detection with multiple strategies
23
+ */
24
+ findFunctionDirectory(functionName: string, options?: FunctionSearchOptions): Promise<string | null>;
25
+ private generateNameVariations;
26
+ private getStandardFunctionPaths;
27
+ private recursiveDirectorySearch;
28
+ private shouldSkipDirectory;
29
+ private isValidFunctionDirectory;
30
+ /**
31
+ * Enhanced function deployment with better error handling and validation
32
+ */
33
+ deployFunction(functionConfig: AppwriteFunction, functionPath: string, options?: FunctionDeploymentOptions): Promise<Models.Deployment>;
34
+ private createFunction;
35
+ private updateFunction;
36
+ private executePredeployCommands;
37
+ private createDeployment;
38
+ getFunction(functionId: string): Promise<Models.Function>;
39
+ listFunctions(): Promise<Models.FunctionList>;
40
+ deleteFunction(functionId: string): Promise<void>;
41
+ /**
42
+ * Validate function configuration
43
+ */
44
+ validateFunctionConfig(functionConfig: AppwriteFunction): {
45
+ valid: boolean;
46
+ errors: string[];
47
+ };
48
+ }