appwrite-utils-cli 0.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 (86) hide show
  1. package/README.md +80 -0
  2. package/dist/main.d.ts +2 -0
  3. package/dist/main.js +74 -0
  4. package/dist/migrations/afterImportActions.d.ts +12 -0
  5. package/dist/migrations/afterImportActions.js +196 -0
  6. package/dist/migrations/attributes.d.ts +4 -0
  7. package/dist/migrations/attributes.js +158 -0
  8. package/dist/migrations/backup.d.ts +621 -0
  9. package/dist/migrations/backup.js +159 -0
  10. package/dist/migrations/collections.d.ts +16 -0
  11. package/dist/migrations/collections.js +207 -0
  12. package/dist/migrations/converters.d.ts +179 -0
  13. package/dist/migrations/converters.js +575 -0
  14. package/dist/migrations/dbHelpers.d.ts +5 -0
  15. package/dist/migrations/dbHelpers.js +54 -0
  16. package/dist/migrations/importController.d.ts +44 -0
  17. package/dist/migrations/importController.js +312 -0
  18. package/dist/migrations/importDataActions.d.ts +44 -0
  19. package/dist/migrations/importDataActions.js +219 -0
  20. package/dist/migrations/indexes.d.ts +4 -0
  21. package/dist/migrations/indexes.js +18 -0
  22. package/dist/migrations/logging.d.ts +2 -0
  23. package/dist/migrations/logging.js +14 -0
  24. package/dist/migrations/migrationHelper.d.ts +18 -0
  25. package/dist/migrations/migrationHelper.js +66 -0
  26. package/dist/migrations/queue.d.ts +13 -0
  27. package/dist/migrations/queue.js +79 -0
  28. package/dist/migrations/relationships.d.ts +90 -0
  29. package/dist/migrations/relationships.js +209 -0
  30. package/dist/migrations/schema.d.ts +3142 -0
  31. package/dist/migrations/schema.js +485 -0
  32. package/dist/migrations/schemaStrings.d.ts +12 -0
  33. package/dist/migrations/schemaStrings.js +261 -0
  34. package/dist/migrations/setupDatabase.d.ts +7 -0
  35. package/dist/migrations/setupDatabase.js +151 -0
  36. package/dist/migrations/storage.d.ts +8 -0
  37. package/dist/migrations/storage.js +241 -0
  38. package/dist/migrations/users.d.ts +11 -0
  39. package/dist/migrations/users.js +114 -0
  40. package/dist/migrations/validationRules.d.ts +43 -0
  41. package/dist/migrations/validationRules.js +42 -0
  42. package/dist/schemas/authUser.d.ts +62 -0
  43. package/dist/schemas/authUser.js +17 -0
  44. package/dist/setup.d.ts +2 -0
  45. package/dist/setup.js +5 -0
  46. package/dist/types.d.ts +9 -0
  47. package/dist/types.js +5 -0
  48. package/dist/utils/configSchema.json +742 -0
  49. package/dist/utils/helperFunctions.d.ts +34 -0
  50. package/dist/utils/helperFunctions.js +72 -0
  51. package/dist/utils/index.d.ts +2 -0
  52. package/dist/utils/index.js +2 -0
  53. package/dist/utils/setupFiles.d.ts +2 -0
  54. package/dist/utils/setupFiles.js +276 -0
  55. package/dist/utilsController.d.ts +30 -0
  56. package/dist/utilsController.js +106 -0
  57. package/package.json +34 -0
  58. package/src/main.ts +77 -0
  59. package/src/migrations/afterImportActions.ts +300 -0
  60. package/src/migrations/attributes.ts +315 -0
  61. package/src/migrations/backup.ts +189 -0
  62. package/src/migrations/collections.ts +303 -0
  63. package/src/migrations/converters.ts +628 -0
  64. package/src/migrations/dbHelpers.ts +89 -0
  65. package/src/migrations/importController.ts +509 -0
  66. package/src/migrations/importDataActions.ts +313 -0
  67. package/src/migrations/indexes.ts +37 -0
  68. package/src/migrations/logging.ts +15 -0
  69. package/src/migrations/migrationHelper.ts +100 -0
  70. package/src/migrations/queue.ts +119 -0
  71. package/src/migrations/relationships.ts +336 -0
  72. package/src/migrations/schema.ts +590 -0
  73. package/src/migrations/schemaStrings.ts +310 -0
  74. package/src/migrations/setupDatabase.ts +219 -0
  75. package/src/migrations/storage.ts +351 -0
  76. package/src/migrations/users.ts +148 -0
  77. package/src/migrations/validationRules.ts +63 -0
  78. package/src/schemas/authUser.ts +23 -0
  79. package/src/setup.ts +8 -0
  80. package/src/types.ts +14 -0
  81. package/src/utils/configSchema.json +742 -0
  82. package/src/utils/helperFunctions.ts +111 -0
  83. package/src/utils/index.ts +2 -0
  84. package/src/utils/setupFiles.ts +295 -0
  85. package/src/utilsController.ts +173 -0
  86. package/tsconfig.json +37 -0
@@ -0,0 +1,351 @@
1
+ import {
2
+ Storage,
3
+ Databases,
4
+ Query,
5
+ InputFile,
6
+ type Models,
7
+ ID,
8
+ } from "node-appwrite";
9
+ import { type OperationCreate, type BackupCreate } from "./backup.js";
10
+ import { splitIntoBatches } from "./migrationHelper.js";
11
+ import type { AppwriteConfig } from "./schema.js";
12
+
13
+ export const logOperation = async (
14
+ db: Databases,
15
+ dbId: string,
16
+ operationDetails: OperationCreate,
17
+ operationId?: string
18
+ ): Promise<Models.Document> => {
19
+ try {
20
+ let operation;
21
+ if (operationId) {
22
+ // Update existing operation log
23
+ operation = await db.updateDocument(
24
+ "migrations",
25
+ "currentOperations",
26
+ operationId,
27
+ operationDetails
28
+ );
29
+ } else {
30
+ // Create new operation log
31
+ operation = await db.createDocument(
32
+ "migrations",
33
+ "currentOperations",
34
+ ID.unique(),
35
+ operationDetails
36
+ );
37
+ }
38
+ console.log(`Operation logged: ${operation.$id}`);
39
+ return operation;
40
+ } catch (error) {
41
+ console.error(`Error logging operation: ${error}`);
42
+ throw error;
43
+ }
44
+ };
45
+
46
+ export const initOrGetBackupStorage = async (storage: Storage) => {
47
+ try {
48
+ const backupStorage = await storage.getBucket("backupStorage");
49
+ return backupStorage;
50
+ } catch (e) {
51
+ // ID backupStorage
52
+ // Name Backups Storage
53
+ const backupStorage = await storage.createBucket(
54
+ "backupStorage",
55
+ "Backups Storage"
56
+ );
57
+ return backupStorage;
58
+ }
59
+ };
60
+
61
+ export const initOrGetDocumentStorage = async (
62
+ storage: Storage,
63
+ config: AppwriteConfig,
64
+ dbName: string
65
+ ) => {
66
+ try {
67
+ await storage.getBucket(
68
+ `${config.documentBucketId}_${dbName.toLowerCase().replace(" ", "")}`
69
+ );
70
+ } catch (e) {
71
+ // ID documentStorage
72
+ // Name Document Storage
73
+ const documentStorage = await storage.createBucket(
74
+ `${config.documentBucketId}_${dbName.toLowerCase().replace(" ", "")}`,
75
+ "Document Storage"
76
+ );
77
+ return documentStorage;
78
+ }
79
+ };
80
+
81
+ export const wipeDocumentStorage = async (
82
+ storage: Storage,
83
+ config: AppwriteConfig,
84
+ dbName: string
85
+ ): Promise<void> => {
86
+ const bucketId = `${config.documentBucketId
87
+ .toLowerCase()
88
+ .replace(" ", "")}_${dbName.toLowerCase().replace(" ", "")}`;
89
+ console.log(`Wiping storage for bucket ID: ${bucketId}`);
90
+ let moreFiles = true;
91
+ let lastFileId: string | undefined;
92
+ const allFiles: string[] = [];
93
+ while (moreFiles) {
94
+ const queries = [Query.limit(100)]; // Adjust the limit as needed
95
+ if (lastFileId) {
96
+ queries.push(Query.cursorAfter(lastFileId));
97
+ }
98
+ const filesPulled = await storage.listFiles(bucketId, queries);
99
+ if (filesPulled.files.length === 0) {
100
+ console.log("No files found, done!");
101
+ moreFiles = false;
102
+ break;
103
+ } else if (filesPulled.files.length > 0) {
104
+ const fileIds = filesPulled.files.map((file) => file.$id);
105
+ allFiles.push(...fileIds);
106
+ }
107
+ moreFiles = filesPulled.files.length > 100; // Adjust based on the limit
108
+ if (moreFiles) {
109
+ lastFileId = filesPulled.files[filesPulled.files.length - 1].$id;
110
+ }
111
+ }
112
+
113
+ for (const fileId of allFiles) {
114
+ console.log(`Deleting file: ${fileId}`);
115
+ await storage.deleteFile(bucketId, fileId);
116
+ }
117
+ console.log(`All files in bucket ${bucketId} have been deleted.`);
118
+ };
119
+
120
+ async function retryFailedPromises(
121
+ batch: Promise<Models.Document>[],
122
+ maxRetries = 3
123
+ ): Promise<PromiseSettledResult<Models.Document>[]> {
124
+ const results = await Promise.allSettled(batch);
125
+ const toRetry: Promise<any>[] = [];
126
+
127
+ results.forEach((result, index) => {
128
+ if (result.status === "rejected") {
129
+ console.error("Promise rejected with reason:", result.reason);
130
+ if (maxRetries > 0) {
131
+ toRetry.push(batch[index]);
132
+ }
133
+ }
134
+ });
135
+
136
+ if (toRetry.length > 0) {
137
+ console.log(`Retrying ${toRetry.length} promises`);
138
+ return retryFailedPromises(toRetry, maxRetries - 1);
139
+ } else {
140
+ return results
141
+ .filter((result) => result.status === "fulfilled")
142
+ .map((result) => result);
143
+ }
144
+ }
145
+
146
+ export const backupDatabase = async (
147
+ database: Databases,
148
+ databaseId: string,
149
+ storage: Storage
150
+ ): Promise<void> => {
151
+ console.log("---------------------------------");
152
+ console.log("Starting Database Backup of " + databaseId);
153
+ console.log("---------------------------------");
154
+ let data: BackupCreate = {
155
+ database: "",
156
+ collections: [],
157
+ documents: [],
158
+ };
159
+
160
+ const backupOperation = await logOperation(database, databaseId, {
161
+ operationType: "backup",
162
+ collectionId: "",
163
+ data: "Starting backup...",
164
+ progress: 0,
165
+ total: 100, // This will be dynamically updated later
166
+ error: "",
167
+ status: "in_progress",
168
+ });
169
+
170
+ // Fetch and backup the database details
171
+ let db: Models.Database;
172
+ try {
173
+ db = await database.get(databaseId);
174
+ } catch (e) {
175
+ console.error(`Error fetching database: ${e}`);
176
+ await logOperation(
177
+ database,
178
+ databaseId,
179
+ {
180
+ operationType: "backup",
181
+ collectionId: "",
182
+ data: "Error fetching database, skipping...",
183
+ progress: 0,
184
+ total: 100, // This will be dynamically updated later
185
+ error: `Error fetching database: ${e}`,
186
+ status: "error",
187
+ },
188
+ backupOperation.$id
189
+ );
190
+ return;
191
+ }
192
+ data.database = JSON.stringify(db);
193
+
194
+ // Initialize pagination for collections
195
+ let lastCollectionId = "";
196
+ let moreCollections = true;
197
+ let progress = 0;
198
+ let total = 0; // Initialize total to 0, will be updated dynamically
199
+
200
+ while (moreCollections) {
201
+ const collectionResponse = await database.listCollections(databaseId, [
202
+ Query.limit(500), // Adjust the limit as needed
203
+ ...(lastCollectionId ? [Query.cursorAfter(lastCollectionId)] : []),
204
+ ]);
205
+
206
+ total += collectionResponse.collections.length; // Update total with number of collections
207
+
208
+ for (const {
209
+ $id: collectionId,
210
+ name: collectionName,
211
+ } of collectionResponse.collections) {
212
+ let collectionDocumentCount = 0; // Initialize document count for the current collection
213
+ try {
214
+ const collection = await database.getCollection(
215
+ databaseId,
216
+ collectionId
217
+ );
218
+ progress++;
219
+ data.collections.push(JSON.stringify(collection));
220
+
221
+ // Initialize pagination for documents within the current collection
222
+ let lastDocumentId = "";
223
+ let moreDocuments = true;
224
+
225
+ while (moreDocuments) {
226
+ const documentResponse = await database.listDocuments(
227
+ databaseId,
228
+ collectionId,
229
+ [
230
+ Query.limit(500), // Adjust the limit as needed
231
+ ...(lastDocumentId ? [Query.cursorAfter(lastDocumentId)] : []),
232
+ ]
233
+ );
234
+
235
+ total += documentResponse.documents.length; // Update total with number of documents
236
+ collectionDocumentCount += documentResponse.documents.length; // Update document count for the current collection
237
+ let documentPromises: Promise<Models.Document>[] = [];
238
+ for (const { $id: documentId } of documentResponse.documents) {
239
+ documentPromises.push(
240
+ database.getDocument(databaseId, collectionId, documentId)
241
+ );
242
+ }
243
+ const promiseBatches = splitIntoBatches(documentPromises);
244
+ const documentsPulled = [];
245
+ for (const batch of promiseBatches) {
246
+ const successfulDocuments = await retryFailedPromises(batch);
247
+ documentsPulled.push(...successfulDocuments);
248
+ }
249
+ const documents = documentsPulled;
250
+ data.documents.push({
251
+ collectionId: collectionId,
252
+ data: JSON.stringify(documents),
253
+ });
254
+ progress += documents.length;
255
+
256
+ console.log(
257
+ `Collection ${collectionName} backed up ${collectionDocumentCount} documents (so far)`
258
+ );
259
+
260
+ // Update the operation log with the current progress
261
+ await logOperation(
262
+ database,
263
+ databaseId,
264
+ {
265
+ operationType: "backup",
266
+ collectionId: collectionId,
267
+ data: `Still backing up, ${data.collections.length} collections so far`,
268
+ progress: progress,
269
+ total: total,
270
+ error: "",
271
+ status: "in_progress",
272
+ },
273
+ backupOperation.$id
274
+ );
275
+
276
+ // Check if there are more documents to fetch
277
+ moreDocuments = documentResponse.documents.length === 500;
278
+ if (moreDocuments) {
279
+ lastDocumentId =
280
+ documentResponse.documents[documentResponse.documents.length - 1]
281
+ .$id;
282
+ }
283
+ }
284
+ console.log(
285
+ `Collection ${collectionName} backed up with ${collectionDocumentCount} documents.`
286
+ );
287
+ } catch (error) {
288
+ console.log(
289
+ `Collection ${collectionName} must not exist, continuing...`
290
+ );
291
+ continue;
292
+ }
293
+ }
294
+
295
+ // Check if there are more collections to fetch
296
+ moreCollections = collectionResponse.collections.length === 500;
297
+ if (moreCollections) {
298
+ lastCollectionId =
299
+ collectionResponse.collections[
300
+ collectionResponse.collections.length - 1
301
+ ].$id;
302
+ }
303
+ }
304
+
305
+ // Update the backup operation with the current progress and total
306
+ await logOperation(
307
+ database,
308
+ databaseId,
309
+ {
310
+ operationType: "backup",
311
+ collectionId: "",
312
+ data: `Still backing up, ${data.collections.length} collections so far`,
313
+ progress: progress,
314
+ total: total,
315
+ error: "",
316
+ status: "in_progress",
317
+ },
318
+ backupOperation.$id
319
+ );
320
+
321
+ // Create the backup with the accumulated data
322
+ const bucket = await initOrGetBackupStorage(storage);
323
+ const inputFile = InputFile.fromPlainText(
324
+ JSON.stringify(data),
325
+ `${new Date().toISOString()}-${databaseId}.json`
326
+ );
327
+ const fileCreated = await storage.createFile(
328
+ bucket.$id,
329
+ ID.unique(),
330
+ inputFile
331
+ );
332
+
333
+ // Final update to the backup operation marking it as completed
334
+ await logOperation(
335
+ database,
336
+ databaseId,
337
+ {
338
+ operationType: "backup",
339
+ collectionId: "",
340
+ data: fileCreated.$id,
341
+ progress: 100,
342
+ total: total, // Ensure the total reflects the actual total processed
343
+ error: "",
344
+ status: "completed",
345
+ },
346
+ backupOperation.$id
347
+ );
348
+ console.log("---------------------------------");
349
+ console.log("Database Backup Complete");
350
+ console.log("---------------------------------");
351
+ };
@@ -0,0 +1,148 @@
1
+ import type { AppwriteConfig, ConfigCollection } from "./schema.js";
2
+ import { Databases, ID, Query, Users, type Models } from "node-appwrite";
3
+ import {
4
+ AuthUserSchema,
5
+ type AuthUser,
6
+ type AuthUserCreate,
7
+ } from "../schemas/authUser.js";
8
+ import _ from "lodash";
9
+
10
+ export class UsersController {
11
+ private config: AppwriteConfig;
12
+ private users: Users;
13
+ static userFields = [
14
+ "email",
15
+ "name",
16
+ "password",
17
+ "phone",
18
+ "labels",
19
+ "prefs",
20
+ "userId",
21
+ "$createdAt",
22
+ "$updatedAt",
23
+ ];
24
+
25
+ constructor(config: AppwriteConfig, db: Databases) {
26
+ this.config = config;
27
+ this.users = new Users(this.config.appwriteClient!);
28
+ }
29
+
30
+ async wipeUsers() {
31
+ const users = await this.users.list([Query.limit(25)]);
32
+ const allUsers = users.users;
33
+ let lastDocumentId: string | undefined;
34
+ if (users.total > 25) {
35
+ lastDocumentId = users.users[users.users.length - 1].$id;
36
+ }
37
+ while (lastDocumentId) {
38
+ const moreUsers = await this.users.list([
39
+ Query.limit(25),
40
+ Query.cursorAfter(lastDocumentId),
41
+ ]);
42
+ allUsers.push(...moreUsers.users);
43
+ lastDocumentId = moreUsers.users[moreUsers.users.length - 1].$id;
44
+ }
45
+ console.log("Deleting all users");
46
+ for (const user of allUsers) {
47
+ await this.users.delete(user.$id);
48
+ }
49
+ }
50
+
51
+ async createUserAndReturn(item: AuthUserCreate) {
52
+ console.log("Creating user with item", item);
53
+
54
+ // Attempt to find an existing user by email or phone.
55
+ let foundUsers: Models.User<Models.Preferences>[] = [];
56
+ if (item.email) {
57
+ const foundUsersByEmail = await this.users.list([
58
+ Query.equal("email", item.email),
59
+ ]);
60
+ foundUsers = foundUsersByEmail.users;
61
+ }
62
+ if (item.phone) {
63
+ const foundUsersByPhone = await this.users.list([
64
+ Query.equal("phone", item.phone),
65
+ ]);
66
+ foundUsers = foundUsers.length
67
+ ? foundUsers.concat(foundUsersByPhone.users)
68
+ : foundUsersByPhone.users;
69
+ }
70
+
71
+ let userToReturn = foundUsers[0] || undefined;
72
+
73
+ if (!userToReturn) {
74
+ console.log("Creating user cause not found");
75
+ userToReturn = await this.users.create(
76
+ item.userId || ID.unique(),
77
+ item.email || undefined,
78
+ item.phone && item.phone.length < 15 && item.phone.startsWith("+")
79
+ ? item.phone
80
+ : undefined,
81
+ item.password?.toLowerCase() || `changeMe${item.email}`.toLowerCase(),
82
+ item.name || undefined
83
+ );
84
+ } else {
85
+ console.log("Updating user cause found");
86
+ // Update user details as necessary, ensuring email uniqueness if attempting an update.
87
+ if (
88
+ item.email &&
89
+ item.email !== userToReturn.email &&
90
+ !_.isEmpty(item.email) &&
91
+ !_.isUndefined(item.email)
92
+ ) {
93
+ const emailExists = await this.users.list([
94
+ Query.equal("email", item.email),
95
+ ]);
96
+ if (emailExists.users.length === 0) {
97
+ userToReturn = await this.users.updateEmail(
98
+ userToReturn.$id,
99
+ item.email
100
+ );
101
+ } else {
102
+ console.log("Email update skipped: Email already exists.");
103
+ }
104
+ }
105
+ if (item.password) {
106
+ userToReturn = await this.users.updatePassword(
107
+ userToReturn.$id,
108
+ item.password.toLowerCase()
109
+ );
110
+ }
111
+ if (item.name && item.name !== userToReturn.name) {
112
+ userToReturn = await this.users.updateName(userToReturn.$id, item.name);
113
+ }
114
+ if (
115
+ item.phone &&
116
+ item.phone !== userToReturn.phone &&
117
+ item.phone.length < 15 &&
118
+ item.phone.startsWith("+") &&
119
+ (_.isUndefined(userToReturn.phone) || _.isEmpty(userToReturn.phone))
120
+ ) {
121
+ const userFoundWithPhone = await this.users.list([
122
+ Query.equal("phone", item.phone),
123
+ ]);
124
+ if (userFoundWithPhone.total === 0) {
125
+ userToReturn = await this.users.updatePhone(
126
+ userToReturn.$id,
127
+ item.phone
128
+ );
129
+ }
130
+ }
131
+ }
132
+ if (item.$createdAt && item.$updatedAt) {
133
+ console.log(
134
+ "$createdAt and $updatedAt are not yet supported, sorry about that!"
135
+ );
136
+ }
137
+ if (item.labels && item.labels.length) {
138
+ userToReturn = await this.users.updateLabels(
139
+ userToReturn.$id,
140
+ item.labels
141
+ );
142
+ }
143
+ if (item.prefs && Object.keys(item.prefs).length) {
144
+ userToReturn = await this.users.updatePrefs(userToReturn.$id, item.prefs);
145
+ }
146
+ return userToReturn;
147
+ }
148
+ }
@@ -0,0 +1,63 @@
1
+ import _ from "lodash";
2
+
3
+ export interface ValidationRules {
4
+ [key: string]: (value: any, ...args: any[]) => boolean;
5
+ }
6
+
7
+ export const validationRules = {
8
+ isNumber: (value: any): boolean => _.isNumber(value),
9
+ isString: (value: any): boolean => _.isString(value),
10
+ isBoolean: (value: any): boolean => _.isBoolean(value),
11
+ isArray: (value: any): boolean => _.isArray(value),
12
+ isObject: (value: any): boolean =>
13
+ _.isObject(value) && !_.isArray(value) && !_.isFunction(value),
14
+ isNull: (value: any): boolean => _.isNull(value),
15
+ isValidEmail: (value: string): boolean =>
16
+ value.match(/^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$/) !== null,
17
+ isValidPhone: (value: string): boolean =>
18
+ value.match(/^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im) !==
19
+ null,
20
+ isValidPassword: (value: string): boolean =>
21
+ value.match(
22
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/
23
+ ) !== null,
24
+ isValidUrl: (value: string): boolean =>
25
+ value.match(
26
+ /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
27
+ ) !== null,
28
+ isValidHex: (value: string): boolean =>
29
+ value.match(/^#([a-f0-9]{6}|[a-f0-9]{3})$/i) !== null,
30
+ isValidHexColor: (value: string): boolean =>
31
+ value.match(/^#([a-f0-9]{6}|[a-f0-9]{3})$/i) !== null,
32
+ isValidHexAlpha: (value: string): boolean =>
33
+ value.match(/^#([a-f0-9]{8}|[a-f0-9]{4})$/i) !== null,
34
+ isValidDate: (value: string): boolean =>
35
+ value.match(/^\d{4}-\d{2}-\d{2}$/) !== null,
36
+ isValidTime: (value: string): boolean =>
37
+ value.match(/^\d{2}:\d{2}(:\d{2})?$/) !== null,
38
+ isNullish: (value: any): boolean => _.isNull(value) || _.isUndefined(value),
39
+ isUndefined: (value: any): boolean => _.isUndefined(value),
40
+ isDefined: (value: any): boolean =>
41
+ !_.isUndefined(value) && !_.isNull(value) && !_.isEmpty(value),
42
+ isDate: (value: any): boolean => _.isDate(value),
43
+ isEmpty: (value: any): boolean => _.isEmpty(value),
44
+ isInteger: (value: any): boolean => _.isInteger(value),
45
+ isFloat: (value: any): boolean => _.isNumber(value) && !_.isInteger(value),
46
+ isArrayLike: (value: any): boolean => _.isArrayLike(value),
47
+ isArrayLikeObject: (value: any): boolean => _.isArrayLikeObject(value),
48
+ isFunction: (value: any): boolean => _.isFunction(value),
49
+ isLength: (value: any): boolean => _.isLength(value),
50
+ isMap: (value: any): boolean => _.isMap(value),
51
+ isSet: (value: any): boolean => _.isSet(value),
52
+ isRegExp: (value: any): boolean => _.isRegExp(value),
53
+ isSymbol: (value: any): boolean => _.isSymbol(value),
54
+ isObjectLike: (value: any): boolean => _.isObjectLike(value),
55
+ isPlainObject: (value: any): boolean => _.isPlainObject(value),
56
+ isSafeInteger: (value: any): boolean => _.isSafeInteger(value),
57
+ isTypedArray: (value: any): boolean => _.isTypedArray(value),
58
+ isEqual: (value: any, other: any): boolean => _.isEqual(value, other),
59
+ isMatch: (object: any, source: any): boolean => _.isMatch(object, source),
60
+ has: (object: any, path: string): boolean => _.has(object, path),
61
+ get: (object: any, path: string, defaultValue: any): any =>
62
+ _.get(object, path, defaultValue),
63
+ };
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+
3
+ export const AuthUserSchema = z.object({
4
+ $id: z.string(),
5
+ $createdAt: z.string().optional(),
6
+ $updatedAt: z.string().optional(),
7
+ name: z.string().nullish(),
8
+ email: z.string().nullish(),
9
+ phone: z.string().nullish(),
10
+ prefs: z.record(z.string()).optional().default({}),
11
+ labels: z.array(z.string()).optional().default([]),
12
+ });
13
+
14
+ export type AuthUser = z.infer<typeof AuthUserSchema>;
15
+
16
+ export const AuthUserCreateSchema = AuthUserSchema.omit({
17
+ $id: true,
18
+ }).extend({
19
+ userId: z.string().optional(),
20
+ password: z.string().optional(),
21
+ });
22
+
23
+ export type AuthUserCreate = z.infer<typeof AuthUserCreateSchema>;
package/src/setup.ts ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { setupDirsFiles } from "./utils/setupFiles.js";
3
+
4
+ const args = process.argv.slice(2);
5
+
6
+ const genExample = args.includes("--example");
7
+
8
+ setupDirsFiles(genExample);
package/src/types.ts ADDED
@@ -0,0 +1,14 @@
1
+ export type { AppwriteConfig } from "./migrations/schema.js";
2
+ export type { ConverterFunctions } from "./migrations/converters.js";
3
+ export type { ValidationRules } from "./migrations/validationRules.js";
4
+ export type { AfterImportActions } from "./migrations/afterImportActions.js";
5
+ export {
6
+ type AuthUserCreate,
7
+ AuthUserCreateSchema,
8
+ type AuthUser,
9
+ AuthUserSchema,
10
+ } from "./schemas/authUser.js";
11
+ export { getFileViewUrl, getFileDownloadUrl } from "./utils/helperFunctions.js";
12
+ export { converterFunctions } from "./migrations/converters.js";
13
+ export { validationRules } from "./migrations/validationRules.js";
14
+ export { afterImportActions } from "./migrations/afterImportActions.js";