appwrite-utils-cli 0.10.82 → 0.10.83

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.
package/README.md CHANGED
@@ -150,6 +150,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
150
150
 
151
151
  ## Changelog
152
152
 
153
+ - 0.10.83: Actually fixed the import oops
153
154
  - 0.10.82: Fixed the `lodash` import, replaced with `es-toolkit`
154
155
  - 0.10.81: Fixed `wipeCollection` -- it wasn't properly deleting all files in a loop
155
156
  - 0.10.80: Updated `appwrite-utils` req
@@ -1,28 +1,27 @@
1
1
  import { Query } from "node-appwrite";
2
2
  import { attributeSchema, parseAttribute, } from "appwrite-utils";
3
3
  import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
4
- import _ from "lodash";
5
4
  import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
5
  import chalk from "chalk";
7
6
  const attributesSame = (databaseAttribute, configAttribute) => {
8
7
  const attributesToCheck = [
9
- 'key',
10
- 'type',
11
- 'array',
12
- 'encrypted',
13
- 'required',
14
- 'size',
15
- 'min',
16
- 'max',
17
- 'xdefault',
18
- 'elements',
19
- 'relationType',
20
- 'twoWay',
21
- 'twoWayKey',
22
- 'onDelete',
23
- 'relatedCollection'
8
+ "key",
9
+ "type",
10
+ "array",
11
+ "encrypted",
12
+ "required",
13
+ "size",
14
+ "min",
15
+ "max",
16
+ "xdefault",
17
+ "elements",
18
+ "relationType",
19
+ "twoWay",
20
+ "twoWayKey",
21
+ "onDelete",
22
+ "relatedCollection",
24
23
  ];
25
- return attributesToCheck.every(attr => {
24
+ return attributesToCheck.every((attr) => {
26
25
  // Check if both objects have the attribute
27
26
  const dbHasAttr = attr in databaseAttribute;
28
27
  const configHasAttr = attr in configAttribute;
@@ -31,7 +30,8 @@ const attributesSame = (databaseAttribute, configAttribute) => {
31
30
  const dbValue = databaseAttribute[attr];
32
31
  const configValue = configAttribute[attr];
33
32
  // Consider undefined and null as equivalent
34
- if ((dbValue === undefined || dbValue === null) && (configValue === undefined || configValue === null)) {
33
+ if ((dbValue === undefined || dbValue === null) &&
34
+ (configValue === undefined || configValue === null)) {
35
35
  return true;
36
36
  }
37
37
  return dbValue === configValue;
@@ -68,11 +68,15 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
68
68
  catch (error) {
69
69
  foundAttribute = undefined;
70
70
  }
71
- if (foundAttribute && attributesSame(foundAttribute, attribute) && updateEnabled) {
71
+ if (foundAttribute &&
72
+ attributesSame(foundAttribute, attribute) &&
73
+ updateEnabled) {
72
74
  // No need to do anything, they are the same
73
75
  return;
74
76
  }
75
- else if (foundAttribute && !attributesSame(foundAttribute, attribute) && updateEnabled) {
77
+ else if (foundAttribute &&
78
+ !attributesSame(foundAttribute, attribute) &&
79
+ updateEnabled) {
76
80
  // console.log(
77
81
  // `Updating attribute with same key ${attribute.key} but different values`
78
82
  // );
@@ -82,7 +86,9 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
82
86
  };
83
87
  action = "update";
84
88
  }
85
- else if (!updateEnabled && foundAttribute && !attributesSame(foundAttribute, attribute)) {
89
+ else if (!updateEnabled &&
90
+ foundAttribute &&
91
+ !attributesSame(foundAttribute, attribute)) {
86
92
  await db.deleteAttribute(dbId, collection.$id, attribute.key);
87
93
  console.log(`Deleted attribute: ${attribute.key} to recreate it because they diff (update disabled temporarily)`);
88
94
  return;
@@ -131,10 +137,14 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
131
137
  switch (finalAttribute.type) {
132
138
  case "string":
133
139
  if (action === "create") {
134
- await tryAwaitWithRetry(async () => await db.createStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.size, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false, finalAttribute.encrypted));
140
+ await tryAwaitWithRetry(async () => await db.createStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.size, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
141
+ ? finalAttribute.xdefault
142
+ : null, finalAttribute.array || false, finalAttribute.encrypted));
135
143
  }
136
144
  else {
137
- await tryAwaitWithRetry(async () => await db.updateStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
145
+ await tryAwaitWithRetry(async () => await db.updateStringAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
146
+ ? finalAttribute.xdefault
147
+ : null));
138
148
  }
139
149
  break;
140
150
  case "integer":
@@ -147,7 +157,9 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
147
157
  BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
148
158
  delete finalAttribute.max;
149
159
  }
150
- await tryAwaitWithRetry(async () => await db.createIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
160
+ await tryAwaitWithRetry(async () => await db.createIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required
161
+ ? finalAttribute.xdefault
162
+ : null, finalAttribute.array || false));
151
163
  }
152
164
  else {
153
165
  if (finalAttribute.min &&
@@ -158,63 +170,93 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
158
170
  BigInt(finalAttribute.max) === BigInt(9223372036854776000)) {
159
171
  delete finalAttribute.max;
160
172
  }
161
- await tryAwaitWithRetry(async () => await db.updateIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
173
+ await tryAwaitWithRetry(async () => await db.updateIntegerAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required
174
+ ? finalAttribute.xdefault
175
+ : null));
162
176
  }
163
177
  break;
164
178
  case "float":
165
179
  if (action === "create") {
166
- await tryAwaitWithRetry(async () => await db.createFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
180
+ await tryAwaitWithRetry(async () => await db.createFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required
181
+ ? finalAttribute.xdefault
182
+ : null, finalAttribute.array || false));
167
183
  }
168
184
  else {
169
- await tryAwaitWithRetry(async () => await db.updateFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
185
+ await tryAwaitWithRetry(async () => await db.updateFloatAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.min || -2147483647, finalAttribute.max || 2147483647, finalAttribute.xdefault !== undefined && !finalAttribute.required
186
+ ? finalAttribute.xdefault
187
+ : null));
170
188
  }
171
189
  break;
172
190
  case "boolean":
173
191
  if (action === "create") {
174
- await tryAwaitWithRetry(async () => await db.createBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
192
+ await tryAwaitWithRetry(async () => await db.createBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
193
+ ? finalAttribute.xdefault
194
+ : null, finalAttribute.array || false));
175
195
  }
176
196
  else {
177
- await tryAwaitWithRetry(async () => await db.updateBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
197
+ await tryAwaitWithRetry(async () => await db.updateBooleanAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
198
+ ? finalAttribute.xdefault
199
+ : null));
178
200
  }
179
201
  break;
180
202
  case "datetime":
181
203
  if (action === "create") {
182
- await tryAwaitWithRetry(async () => await db.createDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
204
+ await tryAwaitWithRetry(async () => await db.createDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
205
+ ? finalAttribute.xdefault
206
+ : null, finalAttribute.array || false));
183
207
  }
184
208
  else {
185
- await tryAwaitWithRetry(async () => await db.updateDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
209
+ await tryAwaitWithRetry(async () => await db.updateDatetimeAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
210
+ ? finalAttribute.xdefault
211
+ : null));
186
212
  }
187
213
  break;
188
214
  case "email":
189
215
  if (action === "create") {
190
- await tryAwaitWithRetry(async () => await db.createEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
216
+ await tryAwaitWithRetry(async () => await db.createEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
217
+ ? finalAttribute.xdefault
218
+ : null, finalAttribute.array || false));
191
219
  }
192
220
  else {
193
- await tryAwaitWithRetry(async () => await db.updateEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
221
+ await tryAwaitWithRetry(async () => await db.updateEmailAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
222
+ ? finalAttribute.xdefault
223
+ : null));
194
224
  }
195
225
  break;
196
226
  case "ip":
197
227
  if (action === "create") {
198
- await tryAwaitWithRetry(async () => await db.createIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
228
+ await tryAwaitWithRetry(async () => await db.createIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
229
+ ? finalAttribute.xdefault
230
+ : null, finalAttribute.array || false));
199
231
  }
200
232
  else {
201
- await tryAwaitWithRetry(async () => await db.updateIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
233
+ await tryAwaitWithRetry(async () => await db.updateIpAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
234
+ ? finalAttribute.xdefault
235
+ : null));
202
236
  }
203
237
  break;
204
238
  case "url":
205
239
  if (action === "create") {
206
- await tryAwaitWithRetry(async () => await db.createUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
240
+ await tryAwaitWithRetry(async () => await db.createUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
241
+ ? finalAttribute.xdefault
242
+ : null, finalAttribute.array || false));
207
243
  }
208
244
  else {
209
- await tryAwaitWithRetry(async () => await db.updateUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
245
+ await tryAwaitWithRetry(async () => await db.updateUrlAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
246
+ ? finalAttribute.xdefault
247
+ : null));
210
248
  }
211
249
  break;
212
250
  case "enum":
213
251
  if (action === "create") {
214
- await tryAwaitWithRetry(async () => await db.createEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null, finalAttribute.array || false));
252
+ await tryAwaitWithRetry(async () => await db.createEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
253
+ ? finalAttribute.xdefault
254
+ : null, finalAttribute.array || false));
215
255
  }
216
256
  else {
217
- await tryAwaitWithRetry(async () => await db.updateEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required ? finalAttribute.xdefault : null));
257
+ await tryAwaitWithRetry(async () => await db.updateEnumAttribute(dbId, collection.$id, finalAttribute.key, finalAttribute.elements, finalAttribute.required || false, finalAttribute.xdefault !== undefined && !finalAttribute.required
258
+ ? finalAttribute.xdefault
259
+ : null));
218
260
  }
219
261
  break;
220
262
  case "relationship":
@@ -232,13 +274,16 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
232
274
  };
233
275
  export const createUpdateCollectionAttributes = async (db, dbId, collection, attributes) => {
234
276
  console.log(chalk.green(`Creating/Updating attributes for collection: ${collection.name}`));
277
+ const existingAttributes =
235
278
  // @ts-expect-error
236
- const existingAttributes = collection.attributes.map((attr) => parseAttribute(attr)) || [];
279
+ collection.attributes.map((attr) => parseAttribute(attr)) || [];
237
280
  const attributesToRemove = existingAttributes.filter((attr) => !attributes.some((a) => a.key === attr.key));
238
281
  const indexesToRemove = collection.indexes.filter((index) => attributesToRemove.some((attr) => index.attributes.includes(attr.key)));
239
282
  if (attributesToRemove.length > 0) {
240
283
  if (indexesToRemove.length > 0) {
241
- console.log(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove.map((index) => index.key).join(", ")}`));
284
+ console.log(chalk.red(`Removing indexes as they rely on an attribute that is being removed: ${indexesToRemove
285
+ .map((index) => index.key)
286
+ .join(", ")}`));
242
287
  for (const index of indexesToRemove) {
243
288
  await tryAwaitWithRetry(async () => await db.deleteIndex(dbId, collection.$id, index.key));
244
289
  await delay(100);
@@ -15,3 +15,9 @@ export declare const createOrUpdateCollections: (database: Databases, databaseId
15
15
  }[], selectedCollections?: Models.Collection[]) => Promise<void>;
16
16
  export declare const generateMockData: (database: Databases, databaseId: string, configCollections: any[]) => Promise<void>;
17
17
  export declare const fetchAllCollections: (dbId: string, database: Databases) => Promise<Models.Collection[]>;
18
+ /**
19
+ * Transfers all documents from one collection to another in a different database
20
+ * within the same Appwrite Project
21
+ */
22
+ export declare const transferDocumentsBetweenDbsLocalToLocal: (db: Databases, fromDbId: string, toDbId: string, fromCollId: string, toCollId: string) => Promise<void>;
23
+ export declare const transferDocumentsBetweenDbsLocalToRemote: (localDb: Databases, endpoint: string, projectId: string, apiKey: string, fromDbId: string, toDbId: string, fromCollId: string, toCollId: string) => Promise<void>;
@@ -243,8 +243,12 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
243
243
  attributes);
244
244
  // Add delay after creating attributes
245
245
  await delay(250);
246
+ const indexesToUse = indexes.length > 0
247
+ ? indexes
248
+ : config.collections?.find((c) => c.$id === collectionToUse.$id)
249
+ ?.indexes ?? [];
246
250
  console.log("Creating Indexes");
247
- await createOrUpdateIndexes(databaseId, database, collectionToUse.$id, (indexes ?? []));
251
+ await createOrUpdateIndexes(databaseId, database, collectionToUse.$id, indexesToUse);
248
252
  // Add delay after creating indexes
249
253
  await delay(250);
250
254
  }
@@ -283,3 +287,134 @@ export const fetchAllCollections = async (dbId, database) => {
283
287
  console.log(`Fetched a total of ${collections.length} collections.`);
284
288
  return collections;
285
289
  };
290
+ /**
291
+ * Transfers all documents from one collection to another in a different database
292
+ * within the same Appwrite Project
293
+ */
294
+ export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDbId, fromCollId, toCollId) => {
295
+ let fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
296
+ let totalDocumentsTransferred = 0;
297
+ if (fromCollDocs.documents.length === 0) {
298
+ console.log(`No documents found in collection ${fromCollId}`);
299
+ return;
300
+ }
301
+ else if (fromCollDocs.documents.length < 50) {
302
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
303
+ const toCreateObject = {
304
+ ...doc,
305
+ };
306
+ delete toCreateObject.$databaseId;
307
+ delete toCreateObject.$collectionId;
308
+ delete toCreateObject.$createdAt;
309
+ delete toCreateObject.$updatedAt;
310
+ delete toCreateObject.$id;
311
+ delete toCreateObject.$permissions;
312
+ return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
313
+ });
314
+ await Promise.all(batchedPromises);
315
+ totalDocumentsTransferred += fromCollDocs.documents.length;
316
+ }
317
+ else {
318
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
319
+ const toCreateObject = {
320
+ ...doc,
321
+ };
322
+ delete toCreateObject.$databaseId;
323
+ delete toCreateObject.$collectionId;
324
+ delete toCreateObject.$createdAt;
325
+ delete toCreateObject.$updatedAt;
326
+ delete toCreateObject.$id;
327
+ delete toCreateObject.$permissions;
328
+ return tryAwaitWithRetry(async () => db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
329
+ });
330
+ await Promise.all(batchedPromises);
331
+ totalDocumentsTransferred += fromCollDocs.documents.length;
332
+ while (fromCollDocs.documents.length === 50) {
333
+ fromCollDocs = await tryAwaitWithRetry(async () => await db.listDocuments(fromDbId, fromCollId, [
334
+ Query.limit(50),
335
+ Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
336
+ ]));
337
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
338
+ const toCreateObject = {
339
+ ...doc,
340
+ };
341
+ delete toCreateObject.$databaseId;
342
+ delete toCreateObject.$collectionId;
343
+ delete toCreateObject.$createdAt;
344
+ delete toCreateObject.$updatedAt;
345
+ delete toCreateObject.$id;
346
+ delete toCreateObject.$permissions;
347
+ return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
348
+ });
349
+ await Promise.all(batchedPromises);
350
+ totalDocumentsTransferred += fromCollDocs.documents.length;
351
+ }
352
+ }
353
+ console.log(`Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`);
354
+ };
355
+ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint, projectId, apiKey, fromDbId, toDbId, fromCollId, toCollId) => {
356
+ const client = new Client()
357
+ .setEndpoint(endpoint)
358
+ .setProject(projectId)
359
+ .setKey(apiKey);
360
+ let totalDocumentsTransferred = 0;
361
+ const remoteDb = new Databases(client);
362
+ let fromCollDocs = await tryAwaitWithRetry(async () => localDb.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
363
+ if (fromCollDocs.documents.length === 0) {
364
+ console.log(`No documents found in collection ${fromCollId}`);
365
+ return;
366
+ }
367
+ else if (fromCollDocs.documents.length < 50) {
368
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
369
+ const toCreateObject = {
370
+ ...doc,
371
+ };
372
+ delete toCreateObject.$databaseId;
373
+ delete toCreateObject.$collectionId;
374
+ delete toCreateObject.$createdAt;
375
+ delete toCreateObject.$updatedAt;
376
+ delete toCreateObject.$id;
377
+ delete toCreateObject.$permissions;
378
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
379
+ });
380
+ await Promise.all(batchedPromises);
381
+ totalDocumentsTransferred += fromCollDocs.documents.length;
382
+ }
383
+ else {
384
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
385
+ const toCreateObject = {
386
+ ...doc,
387
+ };
388
+ delete toCreateObject.$databaseId;
389
+ delete toCreateObject.$collectionId;
390
+ delete toCreateObject.$createdAt;
391
+ delete toCreateObject.$updatedAt;
392
+ delete toCreateObject.$id;
393
+ delete toCreateObject.$permissions;
394
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
395
+ });
396
+ await Promise.all(batchedPromises);
397
+ totalDocumentsTransferred += fromCollDocs.documents.length;
398
+ while (fromCollDocs.documents.length === 50) {
399
+ fromCollDocs = await tryAwaitWithRetry(async () => localDb.listDocuments(fromDbId, fromCollId, [
400
+ Query.limit(50),
401
+ Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
402
+ ]));
403
+ const batchedPromises = fromCollDocs.documents.map((doc) => {
404
+ const toCreateObject = {
405
+ ...doc,
406
+ };
407
+ delete toCreateObject.$databaseId;
408
+ delete toCreateObject.$collectionId;
409
+ delete toCreateObject.$createdAt;
410
+ delete toCreateObject.$updatedAt;
411
+ delete toCreateObject.$id;
412
+ delete toCreateObject.$permissions;
413
+ return tryAwaitWithRetry(async () => remoteDb.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
414
+ });
415
+ await Promise.all(batchedPromises);
416
+ totalDocumentsTransferred += fromCollDocs.documents.length;
417
+ }
418
+ }
419
+ console.log(`Total documents transferred from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}: ${totalDocumentsTransferred}`);
420
+ };
@@ -1,6 +1,6 @@
1
1
  import { SchemaGenerator } from "./schemaStrings.js";
2
2
  import { Client, Compression, Databases, Query, Storage, } from "node-appwrite";
3
- import { fetchAllCollections } from "./collections.js";
3
+ import { fetchAllCollections } from "../collections/methods.js";
4
4
  import { fetchAllDatabases } from "./databases.js";
5
5
  import { CollectionSchema, attributeSchema, AppwriteConfigSchema, permissionsSchema, attributesSchema, indexesSchema, parseAttribute, } from "appwrite-utils";
6
6
  import { getDatabaseFromConfig } from "./afterImportActions.js";
@@ -1,7 +1,6 @@
1
1
  import { Query } from "node-appwrite";
2
2
  import { attributeSchema, parseAttribute, } from "appwrite-utils";
3
3
  import { nameToIdMapping, enqueueOperation } from "./queue.js";
4
- import _ from "lodash";
5
4
  import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
5
  const attributesSame = (databaseAttribute, configAttribute) => {
7
6
  const attributesToCheck = [
@@ -1,6 +1,5 @@
1
- import _ from "lodash";
2
1
  import { converterFunctions } from "appwrite-utils";
3
- const { cloneDeep, isObject } = _;
2
+ import { cloneDeep, isPlainObject } from "es-toolkit";
4
3
  /**
5
4
  * Deeply converts all properties of an object (or array) to strings.
6
5
  * @param data The input data to convert.
@@ -10,7 +9,7 @@ export const deepAnyToString = (data) => {
10
9
  if (Array.isArray(data)) {
11
10
  return data.map((item) => deepAnyToString(item));
12
11
  }
13
- else if (isObject(data)) {
12
+ else if (isPlainObject(data)) {
14
13
  return Object.keys(data).reduce((acc, key) => {
15
14
  acc[key] = deepAnyToString(data[key]);
16
15
  return acc;
@@ -31,7 +30,7 @@ export const deepConvert = (data, convertFn) => {
31
30
  if (Array.isArray(data)) {
32
31
  return data.map((item) => deepConvert(item, convertFn));
33
32
  }
34
- else if (isObject(data)) {
33
+ else if (isPlainObject(data)) {
35
34
  return Object.keys(data).reduce((acc, key) => {
36
35
  acc[key] = deepConvert(data[key], convertFn);
37
36
  return acc;
@@ -3,14 +3,14 @@ import path from "path";
3
3
  import fs from "fs";
4
4
  import { convertObjectByAttributeMappings } from "./converters.js";
5
5
  import { z } from "zod";
6
- import { checkForCollection } from "./collections.js";
6
+ import { checkForCollection } from "../collections/methods.js";
7
7
  import { ID, Users } from "node-appwrite";
8
8
  import { logger } from "./logging.js";
9
9
  import { findOrCreateOperation, updateOperation } from "./migrationHelper.js";
10
10
  import { AuthUserCreateSchema } from "../schemas/authUser.js";
11
- import _ from "lodash";
12
11
  import { UsersController } from "./users.js";
13
12
  import { finalizeByAttributeMap } from "../utils/helperFunctions.js";
13
+ import { isEmpty } from "es-toolkit/compat";
14
14
  // Define a schema for the structure of collection import data using Zod for validation
15
15
  export const CollectionImportDataSchema = z.object({
16
16
  // Optional collection creation schema
@@ -447,7 +447,7 @@ export class DataLoader {
447
447
  const sourceValue = this.getValueFromData(collectionData.data[i].finalData, collectionData.data[i].context, idMapping.sourceField);
448
448
  // Skip if value to match is missing or empty
449
449
  if (!sourceValue ||
450
- _.isEmpty(sourceValue) ||
450
+ isEmpty(sourceValue) ||
451
451
  sourceValue === null)
452
452
  continue;
453
453
  const isFieldToSetArray = collectionConfig.attributes.find((attribute) => attribute.key === fieldToSetKey)?.array;
@@ -1,5 +1,4 @@
1
1
  import { AppwriteException, ID, Query, } from "node-appwrite";
2
- import _ from "lodash";
3
2
  import { areCollectionNamesSame, tryAwaitWithRetry } from "../utils/index.js";
4
3
  import { resolveAndUpdateRelationships } from "./relationships.js";
5
4
  import { UsersController } from "./users.js";
@@ -1,7 +1,6 @@
1
1
  import { Query } from "node-appwrite";
2
2
  import { createOrUpdateAttribute } from "./attributes.js";
3
- import _ from "lodash";
4
- import { fetchAndCacheCollectionByName } from "./collections.js";
3
+ import { fetchAndCacheCollectionByName } from "../collections/methods.js";
5
4
  import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
5
  export const queuedOperations = [];
7
6
  export const nameToIdMapping = new Map();
@@ -1,5 +1,5 @@
1
1
  import { Databases, Query } from "node-appwrite";
2
- import { fetchAllCollections } from "./collections.js";
2
+ import { fetchAllCollections } from "../collections/methods.js";
3
3
  import { logger } from "./logging.js";
4
4
  /**
5
5
  * Finds collections that have defined relationship attributes.
@@ -1,9 +1,10 @@
1
1
  import { AppwriteException, Databases, ID, Query, Users, } from "node-appwrite";
2
2
  import { AuthUserSchema, } from "../schemas/authUser.js";
3
- import _ from "lodash";
4
3
  import { logger } from "./logging.js";
5
4
  import { splitIntoBatches } from "./migrationHelper.js";
6
5
  import { getAppwriteClient, tryAwaitWithRetry, } from "../utils/helperFunctions.js";
6
+ import { isUndefined } from "es-toolkit/compat";
7
+ import { isEmpty } from "es-toolkit/compat";
7
8
  export class UsersController {
8
9
  config;
9
10
  users;
@@ -131,8 +132,8 @@ export class UsersController {
131
132
  // Update user details as necessary, ensuring email uniqueness if attempting an update.
132
133
  if (item.email &&
133
134
  item.email !== userToReturn.email &&
134
- !_.isEmpty(item.email) &&
135
- !_.isUndefined(item.email)) {
135
+ !isEmpty(item.email) &&
136
+ !isUndefined(item.email)) {
136
137
  const emailExists = await this.users.list([
137
138
  Query.equal("email", item.email),
138
139
  ]);
@@ -153,7 +154,7 @@ export class UsersController {
153
154
  item.phone !== userToReturn.phone &&
154
155
  item.phone.length < 15 &&
155
156
  item.phone.startsWith("+") &&
156
- (_.isUndefined(userToReturn.phone) || _.isEmpty(userToReturn.phone))) {
157
+ (isUndefined(userToReturn.phone) || isEmpty(userToReturn.phone))) {
157
158
  const userFoundWithPhone = await this.users.list([
158
159
  Query.equal("phone", item.phone),
159
160
  ]);
@@ -1,11 +1,11 @@
1
- import _ from "lodash";
1
+ import { isNumber, isString, isBoolean, isArray, isPlainObject, isNull, isUndefined, isDate, isEmpty, isInteger, isArrayLike, isArrayLikeObject, isFunction, isLength, isMap, isSet, isRegExp, isSymbol, isObjectLike, isSafeInteger, isTypedArray, isEqual, isMatch, has, get, } from "es-toolkit/compat";
2
2
  export const validationRules = {
3
- isNumber: (value) => _.isNumber(value),
4
- isString: (value) => _.isString(value),
5
- isBoolean: (value) => _.isBoolean(value),
6
- isArray: (value) => _.isArray(value),
7
- isObject: (value) => _.isObject(value) && !_.isArray(value) && !_.isFunction(value),
8
- isNull: (value) => _.isNull(value),
3
+ isNumber: (value) => isNumber(value),
4
+ isString: (value) => isString(value),
5
+ isBoolean: (value) => isBoolean(value),
6
+ isArray: (value) => isArray(value),
7
+ isObject: (value) => isPlainObject(value) && !isArray(value) && !isFunction(value),
8
+ isNull: (value) => isNull(value),
9
9
  isValidEmail: (value) => value.match(/^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$/) !== null,
10
10
  isValidPhone: (value) => value.match(/^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/im) !==
11
11
  null,
@@ -16,27 +16,27 @@ export const validationRules = {
16
16
  isValidHexAlpha: (value) => value.match(/^#([a-f0-9]{8}|[a-f0-9]{4})$/i) !== null,
17
17
  isValidDate: (value) => value.match(/^\d{4}-\d{2}-\d{2}$/) !== null,
18
18
  isValidTime: (value) => value.match(/^\d{2}:\d{2}(:\d{2})?$/) !== null,
19
- isNullish: (value) => _.isNull(value) || _.isUndefined(value),
20
- isUndefined: (value) => _.isUndefined(value),
21
- isDefined: (value) => !_.isUndefined(value) && !_.isNull(value) && !_.isEmpty(value),
22
- isDate: (value) => _.isDate(value),
23
- isEmpty: (value) => _.isEmpty(value),
24
- isInteger: (value) => _.isInteger(value),
25
- isFloat: (value) => _.isNumber(value) && !_.isInteger(value),
26
- isArrayLike: (value) => _.isArrayLike(value),
27
- isArrayLikeObject: (value) => _.isArrayLikeObject(value),
28
- isFunction: (value) => _.isFunction(value),
29
- isLength: (value) => _.isLength(value),
30
- isMap: (value) => _.isMap(value),
31
- isSet: (value) => _.isSet(value),
32
- isRegExp: (value) => _.isRegExp(value),
33
- isSymbol: (value) => _.isSymbol(value),
34
- isObjectLike: (value) => _.isObjectLike(value),
35
- isPlainObject: (value) => _.isPlainObject(value),
36
- isSafeInteger: (value) => _.isSafeInteger(value),
37
- isTypedArray: (value) => _.isTypedArray(value),
38
- isEqual: (value, other) => _.isEqual(value, other),
39
- isMatch: (object, source) => _.isMatch(object, source),
40
- has: (object, path) => _.has(object, path),
41
- get: (object, path, defaultValue) => _.get(object, path, defaultValue),
19
+ isNullish: (value) => isNull(value) || isUndefined(value),
20
+ isUndefined: (value) => isUndefined(value),
21
+ isDefined: (value) => !isUndefined(value) && !isNull(value) && !isEmpty(value),
22
+ isDate: (value) => isDate(value),
23
+ isEmpty: (value) => isEmpty(value),
24
+ isInteger: (value) => isInteger(value),
25
+ isFloat: (value) => isNumber(value) && !isInteger(value),
26
+ isArrayLike: (value) => isArrayLike(value),
27
+ isArrayLikeObject: (value) => isArrayLikeObject(value),
28
+ isFunction: (value) => isFunction(value),
29
+ isLength: (value) => isLength(value),
30
+ isMap: (value) => isMap(value),
31
+ isSet: (value) => isSet(value),
32
+ isRegExp: (value) => isRegExp(value),
33
+ isSymbol: (value) => isSymbol(value),
34
+ isObjectLike: (value) => isObjectLike(value),
35
+ isPlainObject: (value) => isPlainObject(value),
36
+ isSafeInteger: (value) => isSafeInteger(value),
37
+ isTypedArray: (value) => isTypedArray(value),
38
+ isEqual: (value, other) => isEqual(value, other),
39
+ isMatch: (object, source) => isMatch(object, source),
40
+ has: (object, path) => has(object, path),
41
+ get: (object, path, defaultValue) => get(object, path, defaultValue),
42
42
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appwrite-utils-cli",
3
3
  "description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
4
- "version": "0.10.82",
4
+ "version": "0.10.83",
5
5
  "main": "src/main.ts",
6
6
  "type": "module",
7
7
  "repository": {