appwrite-utils-cli 0.9.5 → 0.9.52

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 (38) hide show
  1. package/README.md +2 -0
  2. package/dist/collections/attributes.js +4 -4
  3. package/dist/collections/indexes.js +3 -1
  4. package/dist/collections/methods.js +13 -7
  5. package/dist/interactiveCLI.js +28 -14
  6. package/dist/migrations/transfer.js +86 -52
  7. package/dist/utils/helperFunctions.d.ts +1 -0
  8. package/dist/utils/helperFunctions.js +1 -0
  9. package/package.json +53 -53
  10. package/src/collections/attributes.ts +5 -4
  11. package/src/collections/indexes.ts +3 -1
  12. package/src/collections/methods.ts +17 -7
  13. package/src/interactiveCLI.ts +41 -15
  14. package/src/migrations/transfer.ts +160 -95
  15. package/src/utils/helperFunctions.ts +3 -0
  16. package/zlogs/announcements.json +397 -0
  17. package/zlogs/announcementscomments.json +36 -0
  18. package/zlogs/articles.json +138 -0
  19. package/zlogs/articlescomments.json +4 -0
  20. package/zlogs/businesscategories.json +7097 -0
  21. package/zlogs/contacts.json +517063 -0
  22. package/zlogs/contactscouncils.json +61905 -0
  23. package/zlogs/contactssociallinks.json +13776 -0
  24. package/zlogs/councils.json +5076 -0
  25. package/zlogs/documents.json +917 -0
  26. package/zlogs/emails.json +4 -0
  27. package/zlogs/events.json +132625 -0
  28. package/zlogs/knowledgebase.json +333 -0
  29. package/zlogs/knowledgebasecomments.json +4 -0
  30. package/zlogs/linkcategories.json +180 -0
  31. package/zlogs/links.json +4364 -0
  32. package/zlogs/memberrequests.json +83 -0
  33. package/zlogs/memberrequestscomments.json +65 -0
  34. package/zlogs/mergedUserMap.json +56 -0
  35. package/zlogs/oldIdToNewIdPerCollectionMap.json +27663 -0
  36. package/zlogs/regions.json +145 -0
  37. package/zlogs/testimonials.json +335 -0
  38. package/zlogs/users.json +25516 -0
package/README.md CHANGED
@@ -124,6 +124,8 @@ This updated CLI ensures that developers have robust tools at their fingertips t
124
124
 
125
125
  ## Changelog
126
126
 
127
+ - 0.9.52: Add delay after creating indexes, attributes, and others to prevent `fetch failed` errors during large-scale collection creation
128
+ - 0.9.51: Fix transfer databases, remove "ensure duplicates" check
127
129
  - 0.9.5: Fixed not checking for storage bucket for each database (checking the creation status) when importing data
128
130
  - 0.9.4: Fixed migrations database ensuring it has the required collections
129
131
  - 0.9.3: Fixed deployment error && fix lack of existing `appwriteConfig.ts` file from causing error (you want to be able to setup yeah? haha)
@@ -2,7 +2,7 @@ import { Query } from "node-appwrite";
2
2
  import { attributeSchema, parseAttribute, } from "appwrite-utils";
3
3
  import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
4
4
  import _ from "lodash";
5
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
5
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
6
6
  const attributesSame = (databaseAttribute, configAttribute) => {
7
7
  return (databaseAttribute.key == configAttribute.key &&
8
8
  databaseAttribute.type == configAttribute.type &&
@@ -207,18 +207,18 @@ export const createOrUpdateAttribute = async (db, dbId, collection, attribute) =
207
207
  };
208
208
  export const createUpdateCollectionAttributes = async (db, dbId, collection, attributes) => {
209
209
  console.log(`Creating/Updating attributes for collection: ${collection.name}`);
210
- const batchSize = 3; // Size of each batch
210
+ const batchSize = 3;
211
211
  for (let i = 0; i < attributes.length; i += batchSize) {
212
- // Slice the attributes array to get a batch of at most batchSize elements
213
212
  const batch = attributes.slice(i, i + batchSize);
214
213
  const attributePromises = batch.map((attribute) => createOrUpdateAttribute(db, dbId, collection, attribute));
215
- // Await the completion of all promises in the current batch
216
214
  const results = await Promise.allSettled(attributePromises);
217
215
  results.forEach((result) => {
218
216
  if (result.status === "rejected") {
219
217
  console.error("An attribute promise was rejected:", result.reason);
220
218
  }
221
219
  });
220
+ // Add delay after each batch
221
+ await delay(2000);
222
222
  }
223
223
  console.log(`Finished creating/updating attributes for collection: ${collection.name}`);
224
224
  };
@@ -1,6 +1,6 @@
1
1
  import { indexSchema } from "appwrite-utils";
2
2
  import { Databases, IndexType, Query } from "node-appwrite";
3
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
3
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
4
4
  export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
5
5
  const existingIndex = await db.listIndexes(dbId, collectionId, [
6
6
  Query.equal("key", index.key),
@@ -23,5 +23,7 @@ export const createOrUpdateIndex = async (dbId, db, collectionId, index) => {
23
23
  export const createOrUpdateIndexes = async (dbId, db, collectionId, indexes) => {
24
24
  for (const index of indexes) {
25
25
  await tryAwaitWithRetry(async () => await createOrUpdateIndex(dbId, db, collectionId, index));
26
+ // Add delay after each index creation/update
27
+ await delay(1000);
26
28
  }
27
29
  };
@@ -4,7 +4,7 @@ import { createUpdateCollectionAttributes } from "./attributes.js";
4
4
  import { createOrUpdateIndexes } from "./indexes.js";
5
5
  import _ from "lodash";
6
6
  import { SchemaGenerator } from "../utils/schemaStrings.js";
7
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
7
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
8
8
  export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
9
9
  // Had to do this because kept running into issues with type checking arrays so, sorry 40ms
10
10
  const collection = await db.getCollection(dbId, targetCollectionId);
@@ -109,7 +109,7 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
109
109
  if (!configCollections) {
110
110
  return;
111
111
  }
112
- const usedIds = new Set(); // To track IDs used in this operation
112
+ const usedIds = new Set();
113
113
  for (const { attributes, indexes, ...collection } of configCollections) {
114
114
  // Prepare permissions for the collection
115
115
  const permissions = [];
@@ -149,15 +149,15 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
149
149
  let foundColl = deletedCollections?.find((coll) => coll.collectionName.toLowerCase().trim().replace(" ", "") ===
150
150
  collection.name.toLowerCase().trim().replace(" ", ""));
151
151
  if (collection.$id) {
152
- collectionId = collection.$id; // Always use the provided $id if present
152
+ collectionId = collection.$id;
153
153
  }
154
154
  else if (foundColl && !usedIds.has(foundColl.collectionId)) {
155
- collectionId = foundColl.collectionId; // Use ID from deleted collection if not already used
155
+ collectionId = foundColl.collectionId;
156
156
  }
157
157
  else {
158
- collectionId = ID.unique(); // Generate a new unique ID
158
+ collectionId = ID.unique();
159
159
  }
160
- usedIds.add(collectionId); // Mark this ID as used
160
+ usedIds.add(collectionId);
161
161
  // Create the collection with the determined ID
162
162
  try {
163
163
  collectionToUse = await tryAwaitWithRetry(async () => await database.createCollection(databaseId, collectionId, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
@@ -166,18 +166,24 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
166
166
  }
167
167
  catch (error) {
168
168
  console.error(`Failed to create collection ${collection.name} with ID ${collectionId}: ${error}`);
169
- continue; // Skip to the next collection on failure
169
+ continue;
170
170
  }
171
171
  }
172
172
  else {
173
173
  console.log(`Collection ${collection.name} exists, updating it`);
174
174
  await tryAwaitWithRetry(async () => await database.updateCollection(databaseId, collectionToUse.$id, collection.name, permissions, collection.documentSecurity ?? false, collection.enabled ?? true));
175
175
  }
176
+ // Add delay after creating/updating collection
177
+ await delay(1000);
176
178
  // Update attributes and indexes for the collection
177
179
  console.log("Creating Attributes");
178
180
  await createUpdateCollectionAttributes(database, databaseId, collectionToUse, attributes);
181
+ // Add delay after creating attributes
182
+ await delay(1000);
179
183
  console.log("Creating Indexes");
180
184
  await createOrUpdateIndexes(databaseId, database, collectionToUse.$id, indexes ?? []);
185
+ // Add delay after creating indexes
186
+ await delay(1000);
181
187
  }
182
188
  // Process any remaining tasks in the queue
183
189
  await processQueue(database, databaseId);
@@ -416,14 +416,14 @@ export class InteractiveCLI {
416
416
  default: false,
417
417
  },
418
418
  ]);
419
- const { checkDuplicates } = await inquirer.prompt([
420
- {
421
- type: "confirm",
422
- name: "checkDuplicates",
423
- message: "Do you want to check for duplicates during import?",
424
- default: true,
425
- },
426
- ]);
419
+ // const { checkDuplicates } = await inquirer.prompt([
420
+ // {
421
+ // type: "confirm",
422
+ // name: "checkDuplicates",
423
+ // message: "Do you want to check for duplicates during import?",
424
+ // default: true,
425
+ // },
426
+ // ]);
427
427
  console.log("Importing data...");
428
428
  await this.controller.importData({
429
429
  databases: selectedDatabases,
@@ -431,7 +431,8 @@ export class InteractiveCLI {
431
431
  ? selectedCollections.map((c) => c.$id)
432
432
  : undefined,
433
433
  shouldWriteFile,
434
- checkDuplicates,
434
+ checkDuplicates: false,
435
+ // checkDuplicates,
435
436
  });
436
437
  }
437
438
  async transferData() {
@@ -476,10 +477,21 @@ export class InteractiveCLI {
476
477
  }
477
478
  else {
478
479
  targetClient = sourceClient;
479
- sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient);
480
+ const allDatabases = await fetchAllDatabases(sourceClient);
481
+ sourceDatabases = targetDatabases = allDatabases;
482
+ }
483
+ const fromDbs = await this.selectDatabases(sourceDatabases, "Select the source database:", false);
484
+ console.log(fromDbs);
485
+ const fromDb = fromDbs;
486
+ if (!fromDb) {
487
+ throw new Error("No source database selected");
488
+ }
489
+ const availableDbs = targetDatabases.filter((db) => db.$id !== fromDb.$id);
490
+ const targetDbs = await this.selectDatabases(availableDbs, "Select the target database:", false);
491
+ const targetDb = targetDbs;
492
+ if (!targetDb) {
493
+ throw new Error("No target database selected");
480
494
  }
481
- const [fromDb] = await this.selectDatabases(sourceDatabases, "Select the source database:", false);
482
- const [targetDb] = await this.selectDatabases(targetDatabases.filter((db) => db.$id !== fromDb.$id), "Select the target database:", false);
483
495
  const selectedCollections = await this.selectCollections(fromDb, sourceClient, "Select collections to transfer):");
484
496
  const { transferStorage } = await inquirer.prompt([
485
497
  {
@@ -499,8 +511,10 @@ export class InteractiveCLI {
499
511
  const targetBuckets = isRemote
500
512
  ? await listBuckets(targetStorage)
501
513
  : sourceBuckets;
502
- [sourceBucket] = await this.selectBuckets(sourceBuckets.buckets, "Select the source bucket:", false);
503
- [targetBucket] = await this.selectBuckets(targetBuckets.buckets, "Select the target bucket:", false);
514
+ const sourceBucketPicked = await this.selectBuckets(sourceBuckets.buckets, "Select the source bucket:", false);
515
+ const targetBucketPicked = await this.selectBuckets(targetBuckets.buckets, "Select the target bucket:", false);
516
+ sourceBucket = sourceBucketPicked;
517
+ targetBucket = targetBucketPicked;
504
518
  }
505
519
  let transferOptions = {
506
520
  fromDb,
@@ -100,14 +100,26 @@ export const transferStorageLocalToRemote = async (localStorage, endpoint, proje
100
100
  * within the same Appwrite Project
101
101
  */
102
102
  export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDbId, fromCollId, toCollId) => {
103
- let fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, [Query.limit(50)]));
104
103
  let totalDocumentsTransferred = 0;
105
- if (fromCollDocs.documents.length === 0) {
106
- console.log(`No documents found in collection ${fromCollId}`);
107
- return;
108
- }
109
- else if (fromCollDocs.documents.length < 50) {
110
- const batchedPromises = fromCollDocs.documents.map((doc) => {
104
+ let lastDocumentId;
105
+ let hasMoreDocuments = true;
106
+ while (hasMoreDocuments) {
107
+ const queryParams = [Query.limit(50)];
108
+ if (lastDocumentId) {
109
+ queryParams.push(Query.cursorAfter(lastDocumentId));
110
+ }
111
+ const fromCollDocs = await tryAwaitWithRetry(async () => db.listDocuments(fromDbId, fromCollId, queryParams));
112
+ if (fromCollDocs.documents.length === 0) {
113
+ if (totalDocumentsTransferred === 0) {
114
+ console.log(`No documents found in collection ${fromCollId}`);
115
+ }
116
+ break;
117
+ }
118
+ const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await db.listDocuments(toDbId, toCollId, [
119
+ Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
120
+ ]));
121
+ const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
122
+ const batchedPromises = docsToCreate.map((doc) => {
111
123
  const toCreateObject = {
112
124
  ...doc,
113
125
  };
@@ -120,42 +132,13 @@ export const transferDocumentsBetweenDbsLocalToLocal = async (db, fromDbId, toDb
120
132
  return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
121
133
  });
122
134
  await Promise.all(batchedPromises);
123
- totalDocumentsTransferred += fromCollDocs.documents.length;
124
- }
125
- else {
126
- const batchedPromises = fromCollDocs.documents.map((doc) => {
127
- const toCreateObject = {
128
- ...doc,
129
- };
130
- delete toCreateObject.$databaseId;
131
- delete toCreateObject.$collectionId;
132
- delete toCreateObject.$createdAt;
133
- delete toCreateObject.$updatedAt;
134
- delete toCreateObject.$id;
135
- delete toCreateObject.$permissions;
136
- return tryAwaitWithRetry(async () => db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
137
- });
138
- await Promise.all(batchedPromises);
139
- totalDocumentsTransferred += fromCollDocs.documents.length;
140
- while (fromCollDocs.documents.length === 50) {
141
- fromCollDocs = await tryAwaitWithRetry(async () => await db.listDocuments(fromDbId, fromCollId, [
142
- Query.limit(50),
143
- Query.cursorAfter(fromCollDocs.documents[fromCollDocs.documents.length - 1].$id),
144
- ]));
145
- const batchedPromises = fromCollDocs.documents.map((doc) => {
146
- const toCreateObject = {
147
- ...doc,
148
- };
149
- delete toCreateObject.$databaseId;
150
- delete toCreateObject.$collectionId;
151
- delete toCreateObject.$createdAt;
152
- delete toCreateObject.$updatedAt;
153
- delete toCreateObject.$id;
154
- delete toCreateObject.$permissions;
155
- return tryAwaitWithRetry(async () => await db.createDocument(toDbId, toCollId, doc.$id, toCreateObject, doc.$permissions));
156
- });
157
- await Promise.all(batchedPromises);
158
- totalDocumentsTransferred += fromCollDocs.documents.length;
135
+ totalDocumentsTransferred += docsToCreate.length;
136
+ if (fromCollDocs.documents.length < 50) {
137
+ hasMoreDocuments = false;
138
+ }
139
+ else {
140
+ lastDocumentId =
141
+ fromCollDocs.documents[fromCollDocs.documents.length - 1].$id;
159
142
  }
160
143
  }
161
144
  console.log(`Transferred ${totalDocumentsTransferred} documents from database ${fromDbId} to database ${toDbId} -- collection ${fromCollId} to collection ${toCollId}`);
@@ -173,7 +156,11 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint
173
156
  return;
174
157
  }
175
158
  else if (fromCollDocs.documents.length < 50) {
176
- const batchedPromises = fromCollDocs.documents.map((doc) => {
159
+ const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await remoteDb.listDocuments(toDbId, toCollId, [
160
+ Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
161
+ ]));
162
+ const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
163
+ const batchedPromises = docsToCreate.map((doc) => {
177
164
  const toCreateObject = {
178
165
  ...doc,
179
166
  };
@@ -189,7 +176,11 @@ export const transferDocumentsBetweenDbsLocalToRemote = async (localDb, endpoint
189
176
  totalDocumentsTransferred += fromCollDocs.documents.length;
190
177
  }
191
178
  else {
192
- const batchedPromises = fromCollDocs.documents.map((doc) => {
179
+ const allDocsToCreateCheck = await tryAwaitWithRetry(async () => await remoteDb.listDocuments(toDbId, toCollId, [
180
+ Query.equal("$id", fromCollDocs.documents.map((doc) => doc.$id)),
181
+ ]));
182
+ const docsToCreate = fromCollDocs.documents.filter((doc) => !allDocsToCreateCheck.documents.some((d) => d.$id === doc.$id));
183
+ const batchedPromises = docsToCreate.map((doc) => {
193
184
  const toCreateObject = {
194
185
  ...doc,
195
186
  };
@@ -305,9 +296,7 @@ export const transferDatabaseLocalToRemote = async (localDb, endpoint, projectId
305
296
  let lastCollectionId;
306
297
  let fromCollections = await tryAwaitWithRetry(async () => await localDb.listCollections(fromDbId, [Query.limit(50)]));
307
298
  const allFromCollections = fromCollections.collections;
308
- if (fromCollections.collections.length < 50) {
309
- }
310
- else {
299
+ if (fromCollections.collections.length >= 50) {
311
300
  lastCollectionId =
312
301
  fromCollections.collections[fromCollections.collections.length - 1].$id;
313
302
  while (lastCollectionId) {
@@ -324,13 +313,58 @@ export const transferDatabaseLocalToRemote = async (localDb, endpoint, projectId
324
313
  }
325
314
  }
326
315
  for (const collection of allFromCollections) {
327
- const toCollection = await tryAwaitWithRetry(async () => await remoteDb.createCollection(toDbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled));
328
- console.log(`Collection ${toCollection.name} created`);
316
+ let toCollection;
317
+ const toCollectionExists = await tryAwaitWithRetry(async () => await remoteDb.listCollections(toDbId, [
318
+ Query.equal("$id", collection.$id),
319
+ ]));
320
+ if (toCollectionExists.collections.length > 0) {
321
+ console.log(`Collection ${collection.name} already exists. Updating...`);
322
+ toCollection = toCollectionExists.collections[0];
323
+ // Update collection if needed
324
+ if (toCollection.name !== collection.name ||
325
+ toCollection.$permissions !== collection.$permissions ||
326
+ toCollection.documentSecurity !== collection.documentSecurity ||
327
+ toCollection.enabled !== collection.enabled) {
328
+ toCollection = await tryAwaitWithRetry(async () => await remoteDb.updateCollection(toDbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled));
329
+ console.log(`Collection ${toCollection.name} updated`);
330
+ }
331
+ }
332
+ else {
333
+ toCollection = await tryAwaitWithRetry(async () => await remoteDb.createCollection(toDbId, collection.$id, collection.name, collection.$permissions, collection.documentSecurity, collection.enabled));
334
+ console.log(`Collection ${toCollection.name} created`);
335
+ }
336
+ // Check and update attributes
337
+ const existingAttributes = await tryAwaitWithRetry(async () => await remoteDb.listAttributes(toDbId, toCollection.$id));
329
338
  for (const attribute of collection.attributes) {
330
- await tryAwaitWithRetry(async () => await createOrUpdateAttribute(remoteDb, toDbId, toCollection, parseAttribute(attribute)));
339
+ const parsedAttribute = parseAttribute(attribute);
340
+ const existingAttribute = existingAttributes.attributes.find(
341
+ // @ts-expect-error
342
+ (attr) => attr.key === parsedAttribute.key);
343
+ if (!existingAttribute) {
344
+ await tryAwaitWithRetry(async () => await createOrUpdateAttribute(remoteDb, toDbId, toCollection, parsedAttribute));
345
+ console.log(`Attribute ${parsedAttribute.key} created`);
346
+ }
347
+ else {
348
+ // Check if attribute needs updating
349
+ // Note: Appwrite doesn't allow updating most attribute properties
350
+ // You might need to delete and recreate the attribute if significant changes are needed
351
+ console.log(`Attribute ${parsedAttribute.key} already exists`);
352
+ }
331
353
  }
354
+ // Check and update indexes
355
+ const existingIndexes = await tryAwaitWithRetry(async () => await remoteDb.listIndexes(toDbId, toCollection.$id));
332
356
  for (const index of collection.indexes) {
333
- await tryAwaitWithRetry(async () => await remoteDb.createIndex(toDbId, toCollection.$id, index.key, index.type, index.attributes, index.orders));
357
+ const existingIndex = existingIndexes.indexes.find((idx) => idx.key === index.key);
358
+ if (!existingIndex) {
359
+ await tryAwaitWithRetry(async () => await remoteDb.createIndex(toDbId, toCollection.$id, index.key, index.type, index.attributes, index.orders));
360
+ console.log(`Index ${index.key} created`);
361
+ }
362
+ else {
363
+ // Check if index needs updating
364
+ // Note: Appwrite doesn't allow updating indexes
365
+ // You might need to delete and recreate the index if changes are needed
366
+ console.log(`Index ${index.key} already exists`);
367
+ }
334
368
  }
335
369
  await transferDocumentsBetweenDbsLocalToRemote(localDb, endpoint, projectId, apiKey, fromDbId, toDbId, collection.$id, toCollection.$id);
336
370
  }
@@ -45,3 +45,4 @@ export declare let numTimesFailedTotal: number;
45
45
  */
46
46
  export declare const tryAwaitWithRetry: <T>(createFunction: () => Promise<T>, attemptNum?: number, throwError?: boolean) => Promise<T>;
47
47
  export declare const getAppwriteClient: (endpoint: string, projectId: string, apiKey: string) => Client;
48
+ export declare const delay: (ms: number) => Promise<unknown>;
@@ -116,3 +116,4 @@ export const getAppwriteClient = (endpoint, projectId, apiKey) => {
116
116
  .setProject(projectId)
117
117
  .setKey(apiKey);
118
118
  };
119
+ export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
- {
2
- "name": "appwrite-utils-cli",
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.9.5",
5
- "main": "src/main.ts",
6
- "type": "module",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/zachhandley/AppwriteUtils"
10
- },
11
- "author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
12
- "keywords": [
13
- "appwrite",
14
- "cli",
15
- "utils",
16
- "migrations",
17
- "data",
18
- "database",
19
- "import",
20
- "migration",
21
- "utility"
22
- ],
23
- "bin": {
24
- "appwrite-migrate": "./dist/main.js"
25
- },
26
- "scripts": {
27
- "build": "bun run tsc",
28
- "start": "tsx --no-cache src/main.ts",
29
- "deploy": "bun run build && npm publish --access public",
30
- "postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
31
- },
32
- "dependencies": {
33
- "@types/inquirer": "^9.0.7",
34
- "appwrite-utils": "^0.3.8",
35
- "commander": "^12.1.0",
36
- "inquirer": "^9.3.6",
37
- "js-yaml": "^4.1.0",
38
- "lodash": "^4.17.21",
39
- "luxon": "^3.5.0",
40
- "nanostores": "^0.10.3",
41
- "node-appwrite": "^13.0.0",
42
- "tsx": "^4.17.0",
43
- "ulidx": "^2.4.0",
44
- "winston": "^3.14.1",
45
- "zod": "^3.23.8"
46
- },
47
- "devDependencies": {
48
- "@types/js-yaml": "^4.0.9",
49
- "@types/lodash": "^4.17.7",
50
- "@types/luxon": "^3.4.2",
51
- "typescript": "^5.5.4"
52
- }
53
- }
1
+ {
2
+ "name": "appwrite-utils-cli",
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.9.52",
5
+ "main": "src/main.ts",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/zachhandley/AppwriteUtils"
10
+ },
11
+ "author": "Zach Handley <zach@blackleafdigital.com> (https://zachhandley.com)",
12
+ "keywords": [
13
+ "appwrite",
14
+ "cli",
15
+ "utils",
16
+ "migrations",
17
+ "data",
18
+ "database",
19
+ "import",
20
+ "migration",
21
+ "utility"
22
+ ],
23
+ "bin": {
24
+ "appwrite-migrate": "./dist/main.js"
25
+ },
26
+ "scripts": {
27
+ "build": "bun run tsc",
28
+ "start": "tsx --no-cache src/main.ts",
29
+ "deploy": "bun run build && npm publish --access public",
30
+ "postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
31
+ },
32
+ "dependencies": {
33
+ "@types/inquirer": "^9.0.7",
34
+ "appwrite-utils": "^0.3.8",
35
+ "commander": "^12.1.0",
36
+ "inquirer": "^9.3.6",
37
+ "js-yaml": "^4.1.0",
38
+ "lodash": "^4.17.21",
39
+ "luxon": "^3.5.0",
40
+ "nanostores": "^0.10.3",
41
+ "node-appwrite": "^13.0.0",
42
+ "tsx": "^4.17.0",
43
+ "ulidx": "^2.4.0",
44
+ "winston": "^3.14.2",
45
+ "zod": "^3.23.8"
46
+ },
47
+ "devDependencies": {
48
+ "@types/js-yaml": "^4.0.9",
49
+ "@types/lodash": "^4.17.7",
50
+ "@types/luxon": "^3.4.2",
51
+ "typescript": "^5.5.4"
52
+ }
53
+ }
@@ -6,7 +6,7 @@ import {
6
6
  } from "appwrite-utils";
7
7
  import { nameToIdMapping, enqueueOperation } from "../migrations/queue.js";
8
8
  import _ from "lodash";
9
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
9
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
10
10
 
11
11
  const attributesSame = (
12
12
  databaseAttribute: Attribute,
@@ -461,21 +461,22 @@ export const createUpdateCollectionAttributes = async (
461
461
  `Creating/Updating attributes for collection: ${collection.name}`
462
462
  );
463
463
 
464
- const batchSize = 3; // Size of each batch
464
+ const batchSize = 3;
465
465
  for (let i = 0; i < attributes.length; i += batchSize) {
466
- // Slice the attributes array to get a batch of at most batchSize elements
467
466
  const batch = attributes.slice(i, i + batchSize);
468
467
  const attributePromises = batch.map((attribute) =>
469
468
  createOrUpdateAttribute(db, dbId, collection, attribute)
470
469
  );
471
470
 
472
- // Await the completion of all promises in the current batch
473
471
  const results = await Promise.allSettled(attributePromises);
474
472
  results.forEach((result) => {
475
473
  if (result.status === "rejected") {
476
474
  console.error("An attribute promise was rejected:", result.reason);
477
475
  }
478
476
  });
477
+
478
+ // Add delay after each batch
479
+ await delay(2000);
479
480
  }
480
481
  console.log(
481
482
  `Finished creating/updating attributes for collection: ${collection.name}`
@@ -1,6 +1,6 @@
1
1
  import { indexSchema, type Index } from "appwrite-utils";
2
2
  import { Databases, IndexType, Query, type Models } from "node-appwrite";
3
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
3
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
4
4
 
5
5
  export const createOrUpdateIndex = async (
6
6
  dbId: string,
@@ -49,5 +49,7 @@ export const createOrUpdateIndexes = async (
49
49
  await tryAwaitWithRetry(
50
50
  async () => await createOrUpdateIndex(dbId, db, collectionId, index)
51
51
  );
52
+ // Add delay after each index creation/update
53
+ await delay(1000);
52
54
  }
53
55
  };
@@ -12,7 +12,7 @@ import { createUpdateCollectionAttributes } from "./attributes.js";
12
12
  import { createOrUpdateIndexes } from "./indexes.js";
13
13
  import _ from "lodash";
14
14
  import { SchemaGenerator } from "../utils/schemaStrings.js";
15
- import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
15
+ import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
16
16
 
17
17
  export const documentExists = async (
18
18
  db: Databases,
@@ -163,7 +163,7 @@ export const createOrUpdateCollections = async (
163
163
  if (!configCollections) {
164
164
  return;
165
165
  }
166
- const usedIds = new Set(); // To track IDs used in this operation
166
+ const usedIds = new Set();
167
167
 
168
168
  for (const { attributes, indexes, ...collection } of configCollections) {
169
169
  // Prepare permissions for the collection
@@ -215,14 +215,14 @@ export const createOrUpdateCollections = async (
215
215
  );
216
216
 
217
217
  if (collection.$id) {
218
- collectionId = collection.$id; // Always use the provided $id if present
218
+ collectionId = collection.$id;
219
219
  } else if (foundColl && !usedIds.has(foundColl.collectionId)) {
220
- collectionId = foundColl.collectionId; // Use ID from deleted collection if not already used
220
+ collectionId = foundColl.collectionId;
221
221
  } else {
222
- collectionId = ID.unique(); // Generate a new unique ID
222
+ collectionId = ID.unique();
223
223
  }
224
224
 
225
- usedIds.add(collectionId); // Mark this ID as used
225
+ usedIds.add(collectionId);
226
226
 
227
227
  // Create the collection with the determined ID
228
228
  try {
@@ -243,7 +243,7 @@ export const createOrUpdateCollections = async (
243
243
  console.error(
244
244
  `Failed to create collection ${collection.name} with ID ${collectionId}: ${error}`
245
245
  );
246
- continue; // Skip to the next collection on failure
246
+ continue;
247
247
  }
248
248
  } else {
249
249
  console.log(`Collection ${collection.name} exists, updating it`);
@@ -260,6 +260,9 @@ export const createOrUpdateCollections = async (
260
260
  );
261
261
  }
262
262
 
263
+ // Add delay after creating/updating collection
264
+ await delay(1000);
265
+
263
266
  // Update attributes and indexes for the collection
264
267
  console.log("Creating Attributes");
265
268
  await createUpdateCollectionAttributes(
@@ -268,6 +271,10 @@ export const createOrUpdateCollections = async (
268
271
  collectionToUse!,
269
272
  attributes
270
273
  );
274
+
275
+ // Add delay after creating attributes
276
+ await delay(1000);
277
+
271
278
  console.log("Creating Indexes");
272
279
  await createOrUpdateIndexes(
273
280
  databaseId,
@@ -275,6 +282,9 @@ export const createOrUpdateCollections = async (
275
282
  collectionToUse!.$id,
276
283
  indexes ?? []
277
284
  );
285
+
286
+ // Add delay after creating indexes
287
+ await delay(1000);
278
288
  }
279
289
  // Process any remaining tasks in the queue
280
290
  await processQueue(database, databaseId);