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,575 @@
1
+ import { DateTime } from "luxon";
2
+ import _ from "lodash";
3
+ const { cloneDeep, isObject } = _;
4
+ export const converterFunctions = {
5
+ /**
6
+ * Converts any value to a string. Handles null and undefined explicitly.
7
+ * @param {any} value The value to convert.
8
+ * @return {string | null} The converted string or null if the value is null or undefined.
9
+ */
10
+ anyToString(value) {
11
+ if (value == null)
12
+ return null;
13
+ return typeof value === "string" ? value : JSON.stringify(value);
14
+ },
15
+ /**
16
+ * Converts any value to a number. Returns null for non-numeric values, null, or undefined.
17
+ * @param {any} value The value to convert.
18
+ * @return {number | null} The converted number or null.
19
+ */
20
+ anyToNumber(value) {
21
+ if (value == null)
22
+ return null;
23
+ const number = Number(value);
24
+ return isNaN(number) ? null : number;
25
+ },
26
+ /**
27
+ * Converts any value to a boolean. Specifically handles string representations.
28
+ * @param {any} value The value to convert.
29
+ * @return {boolean | null} The converted boolean or null if conversion is not possible.
30
+ */
31
+ anyToBoolean(value) {
32
+ if (value == null)
33
+ return null;
34
+ if (typeof value === "string") {
35
+ const trimmedValue = value.trim().toLowerCase();
36
+ if (["true", "yes", "1"].includes(trimmedValue))
37
+ return true;
38
+ if (["false", "no", "0"].includes(trimmedValue))
39
+ return false;
40
+ return null; // Return null for strings that don't explicitly match
41
+ }
42
+ return Boolean(value);
43
+ },
44
+ /**
45
+ * Converts any value to an array, attempting to split strings by a specified separator.
46
+ * @param {any} value The value to convert.
47
+ * @param {string | undefined} separator The separator to use when splitting strings.
48
+ * @return {any[]} The resulting array after conversion.
49
+ */
50
+ anyToAnyArray(value) {
51
+ if (Array.isArray(value)) {
52
+ return value;
53
+ }
54
+ else if (typeof value === "string") {
55
+ // Let's try a few
56
+ return converterFunctions.trySplitByDifferentSeparators(value);
57
+ }
58
+ return [value];
59
+ },
60
+ /**
61
+ * Converts any value to an array of strings. If the input is already an array, returns it as is.
62
+ * Otherwise, if the input is a string, returns an array with the string as the only element.
63
+ * Otherwise, returns an empty array.
64
+ * @param {any} value The value to convert.
65
+ * @return {string[]} The resulting array after conversion.
66
+ */
67
+ anyToStringArray(value) {
68
+ if (Array.isArray(value)) {
69
+ return value.map((item) => String(item));
70
+ }
71
+ else if (typeof value === "string" && value.length > 0) {
72
+ return [value];
73
+ }
74
+ return [];
75
+ },
76
+ /**
77
+ * A function that converts any type of value to an array of numbers.
78
+ *
79
+ * @param {any} value - the value to be converted
80
+ * @return {number[]} an array of numbers
81
+ */
82
+ anyToNumberArray(value) {
83
+ if (Array.isArray(value)) {
84
+ return value.map((item) => Number(item));
85
+ }
86
+ else if (typeof value === "string") {
87
+ return [Number(value)];
88
+ }
89
+ return [];
90
+ },
91
+ trim(value) {
92
+ try {
93
+ return value.trim();
94
+ }
95
+ catch (error) {
96
+ return value;
97
+ }
98
+ },
99
+ /**
100
+ * Removes the start and end quotes from a string.
101
+ * @param value The string to remove quotes from.
102
+ * @return The string with quotes removed.
103
+ **/
104
+ removeStartEndQuotes(value) {
105
+ return value.replace(/^["']|["']$/g, "");
106
+ },
107
+ /**
108
+ * Tries to split a string by different separators and returns the split that has the most uniform segment lengths.
109
+ * This can be particularly useful for structured data like phone numbers.
110
+ * @param value The string to split.
111
+ * @return The split string array that has the most uniform segment lengths.
112
+ */
113
+ trySplitByDifferentSeparators(value) {
114
+ const separators = [",", ";", "|", ":", "/", "\\"];
115
+ let bestSplit = [];
116
+ let bestScore = -Infinity;
117
+ for (const separator of separators) {
118
+ const split = value.split(separator).map((s) => s.trim()); // Ensure we trim spaces
119
+ if (split.length <= 1)
120
+ continue; // Skip if no actual splitting occurred
121
+ // Calculate uniformity in segment length
122
+ const lengths = split.map((segment) => segment.length);
123
+ const averageLength = lengths.reduce((a, b) => a + b, 0) / lengths.length;
124
+ const lengthVariance = lengths.reduce((total, length) => total + Math.pow(length - averageLength, 2), 0) / lengths.length;
125
+ // Adjust scoring to prioritize splits with lower variance and/or specific segment count if needed
126
+ const score = split.length / (1 + lengthVariance); // Adjusted to prioritize lower variance
127
+ // Update bestSplit if this split has a better score
128
+ if (score > bestScore) {
129
+ bestSplit = split;
130
+ bestScore = score;
131
+ }
132
+ }
133
+ // If no suitable split was found, return the original value as a single-element array
134
+ if (bestSplit.length === 0) {
135
+ return [value];
136
+ }
137
+ return bestSplit;
138
+ },
139
+ joinValues(values) {
140
+ try {
141
+ return values.join("");
142
+ }
143
+ catch (error) {
144
+ return values;
145
+ }
146
+ },
147
+ joinBySpace(values) {
148
+ try {
149
+ return values.join(" ");
150
+ }
151
+ catch (error) {
152
+ return values;
153
+ }
154
+ },
155
+ joinByComma(values) {
156
+ try {
157
+ return values.join(",");
158
+ }
159
+ catch (error) {
160
+ return values;
161
+ }
162
+ },
163
+ joinByPipe(values) {
164
+ try {
165
+ return values.join("|");
166
+ }
167
+ catch (error) {
168
+ return values;
169
+ }
170
+ },
171
+ joinBySemicolon(values) {
172
+ try {
173
+ return values.join(";");
174
+ }
175
+ catch (error) {
176
+ return values;
177
+ }
178
+ },
179
+ joinByColon(values) {
180
+ try {
181
+ return values.join(":");
182
+ }
183
+ catch (error) {
184
+ return values;
185
+ }
186
+ },
187
+ joinBySlash(values) {
188
+ try {
189
+ return values.join("/");
190
+ }
191
+ catch (error) {
192
+ return values;
193
+ }
194
+ },
195
+ joinByHyphen(values) {
196
+ try {
197
+ return values.join("-");
198
+ }
199
+ catch (error) {
200
+ return values;
201
+ }
202
+ },
203
+ splitByComma(value) {
204
+ try {
205
+ return value.split(",");
206
+ }
207
+ catch (error) {
208
+ return value;
209
+ }
210
+ },
211
+ splitByPipe(value) {
212
+ try {
213
+ return value.split("|");
214
+ }
215
+ catch (error) {
216
+ return value;
217
+ }
218
+ },
219
+ splitBySemicolon(value) {
220
+ try {
221
+ return value.split(";");
222
+ }
223
+ catch (error) {
224
+ return value;
225
+ }
226
+ },
227
+ splitByColon(value) {
228
+ try {
229
+ return value.split(":");
230
+ }
231
+ catch (error) {
232
+ return value;
233
+ }
234
+ },
235
+ splitBySlash(value) {
236
+ try {
237
+ return value.split("/");
238
+ }
239
+ catch (error) {
240
+ return value;
241
+ }
242
+ },
243
+ splitByBackslash(value) {
244
+ try {
245
+ return value.split("\\");
246
+ }
247
+ catch (error) {
248
+ return value;
249
+ }
250
+ },
251
+ splitBySpace(value) {
252
+ try {
253
+ return value.split(" ");
254
+ }
255
+ catch (error) {
256
+ return value;
257
+ }
258
+ },
259
+ splitByDot(value) {
260
+ try {
261
+ return value.split(".");
262
+ }
263
+ catch (error) {
264
+ return value;
265
+ }
266
+ },
267
+ splitByUnderscore(value) {
268
+ try {
269
+ return value.split("_");
270
+ }
271
+ catch (error) {
272
+ return value;
273
+ }
274
+ },
275
+ splitByHyphen(value) {
276
+ try {
277
+ return value.split("-");
278
+ }
279
+ catch (error) {
280
+ return value;
281
+ }
282
+ },
283
+ /**
284
+ * Takes the first element of an array and returns it.
285
+ * @param {any[]} value The array to take the first element from.
286
+ * @return {any} The first element of the array.
287
+ */
288
+ pickFirstElement(value) {
289
+ try {
290
+ return value[0];
291
+ }
292
+ catch (error) {
293
+ return value;
294
+ }
295
+ },
296
+ /**
297
+ * Takes the last element of an array and returns it.
298
+ * @param {any[]} value The array to take the last element from.
299
+ * @return {any} The last element of the array.
300
+ */
301
+ pickLastElement(value) {
302
+ try {
303
+ return value[value.length - 1];
304
+ }
305
+ catch (error) {
306
+ return value;
307
+ }
308
+ },
309
+ /**
310
+ * Converts an object to a JSON string.
311
+ * @param {any} object The object to convert.
312
+ * @return {string} The JSON string representation of the object.
313
+ */
314
+ stringifyObject(object) {
315
+ return JSON.stringify(object);
316
+ },
317
+ /**
318
+ * Converts a JSON string to an object.
319
+ * @param {string} jsonString The JSON string to convert.
320
+ * @return {any} The object representation of the JSON string.
321
+ */
322
+ parseObject(jsonString) {
323
+ return JSON.parse(jsonString);
324
+ },
325
+ convertPhoneStringToUSInternational(value) {
326
+ // Normalize input: Remove all non-digit characters except the leading +
327
+ const normalizedValue = value.startsWith("+")
328
+ ? "+" + value.slice(1).replace(/\D/g, "")
329
+ : value.replace(/\D/g, "");
330
+ // Check if the value is not a string or doesn't contain digits, return as is
331
+ if (typeof normalizedValue !== "string" || !/\d/.test(normalizedValue))
332
+ return value;
333
+ // Handle numbers with a leading + (indicating an international format)
334
+ if (normalizedValue.startsWith("+")) {
335
+ // If the number is already in a valid international format, return as is
336
+ if (normalizedValue.length > 11 && normalizedValue.length <= 15) {
337
+ return normalizedValue;
338
+ }
339
+ }
340
+ else {
341
+ // For numbers without a leading +, check the length and format
342
+ if (normalizedValue.length === 10) {
343
+ // US numbers without country code, prepend +1
344
+ return `+1${normalizedValue}`;
345
+ }
346
+ else if (normalizedValue.length === 11 &&
347
+ normalizedValue.startsWith("1")) {
348
+ // US numbers with country code but missing +, prepend +
349
+ return `+${normalizedValue}`;
350
+ }
351
+ }
352
+ // For numbers that don't fit expected formats, return the original value
353
+ return value;
354
+ },
355
+ convertEmptyToNull(value) {
356
+ if (Array.isArray(value)) {
357
+ return value.map((item) => this.convertEmptyToNull(item));
358
+ }
359
+ if (_.isEmpty(value))
360
+ return null;
361
+ return value;
362
+ },
363
+ /**
364
+ * A function that removes invalid elements from an array
365
+ *
366
+ * @param {any[]} array - the input array
367
+ * @return {any[]} the filtered array without invalid elements
368
+ */
369
+ removeInvalidElements(array) {
370
+ if (!Array.isArray(array))
371
+ return array;
372
+ return _.filter(array, (element) => element !== null &&
373
+ element !== undefined &&
374
+ element !== "" &&
375
+ element !== "undefined" &&
376
+ element !== "null" &&
377
+ !_.isEmpty(element));
378
+ },
379
+ validateOrNullEmail(email) {
380
+ if (!email)
381
+ return null;
382
+ const emailRegex = /^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$/;
383
+ return emailRegex.test(email) ? email : null;
384
+ },
385
+ /**
386
+ * Tries to parse a date from various formats using Luxon with enhanced error reporting.
387
+ * @param {string | number} input The input date as a string or timestamp.
388
+ * @return {string | null} The parsed date in ISO 8601 format or null if parsing failed.
389
+ */
390
+ safeParseDate(input) {
391
+ const formats = [
392
+ "M/d/yyyy HH:mm:ss", // U.S. style with time
393
+ "d/M/yyyy HH:mm:ss", // Rest of the world style with time
394
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", // ISO 8601 format
395
+ "yyyy-MM-dd'T'HH:mm:ss", // ISO 8601 without milliseconds
396
+ "yyyy-MM-dd HH:mm:ss", // SQL datetime format (common log format)
397
+ "M/d/yyyy", // U.S. style without time
398
+ "d/M/yyyy", // Rest of the world style without time
399
+ "yyyy-MM-dd", // ISO date format
400
+ "dd-MM-yyyy",
401
+ "MM-dd-yyyy",
402
+ "dd/MM/yyyy",
403
+ "MM/dd/yyyy",
404
+ "dd.MM.yyyy",
405
+ "MM.dd.yyyy",
406
+ "yyyy.MM.dd",
407
+ "yyyy/MM/dd",
408
+ "yyyy/MM/dd HH:mm",
409
+ "yyyy-MM-dd HH:mm",
410
+ "M/d/yyyy h:mm:ss tt", // U.S. style with 12-hour clock
411
+ "d/M/yyyy h:mm:ss tt", // Rest of the world style with 12-hour clock
412
+ "h:mm tt", // Time only with 12-hour clock
413
+ "HH:mm:ss", // Time only with 24-hour clock
414
+ "HH:mm", // Time only without seconds, 24-hour clock
415
+ "h:mm tt M/d/yyyy", // 12-hour clock time followed by U.S. style date
416
+ "h:mm tt d/M/yyyy", // 12-hour clock time followed by Rest of the world style date
417
+ "yyyy-MM-dd'T'HH:mm:ss.SSSZ", // ISO 8601 with timezone offset
418
+ "yyyy-MM-dd'T'HH:mm:ssZ", // ISO 8601 without milliseconds but with timezone offset
419
+ "E, dd MMM yyyy HH:mm:ss z", // RFC 2822 format
420
+ "EEEE, MMMM d, yyyy", // Full textual date
421
+ "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", // ISO 8601 with extended timezone offset
422
+ "yyyy-MM-dd'T'HH:mm:ssXXX", // ISO 8601 without milliseconds but with extended timezone offset
423
+ "dd-MMM-yyyy", // Textual month with day and year
424
+ ];
425
+ // Attempt to parse as a timestamp first if input is a number
426
+ if (typeof input === "number") {
427
+ const dateFromMillis = DateTime.fromMillis(input);
428
+ if (dateFromMillis.isValid) {
429
+ return dateFromMillis.toISO();
430
+ }
431
+ }
432
+ // Attempt to parse as an ISO string or SQL string
433
+ let date = DateTime.fromISO(String(input));
434
+ if (!date.isValid)
435
+ date = DateTime.fromSQL(String(input));
436
+ // Try each custom format if still not valid
437
+ for (const format of formats) {
438
+ if (!date.isValid) {
439
+ date = DateTime.fromFormat(String(input), format);
440
+ }
441
+ }
442
+ // Return null if no valid date could be parsed
443
+ if (!date.isValid) {
444
+ return null;
445
+ }
446
+ return date.toISO();
447
+ },
448
+ };
449
+ /**
450
+ * Deeply converts all properties of an object (or array) to strings.
451
+ * @param data The input data to convert.
452
+ * @returns The data with all its properties converted to strings.
453
+ */
454
+ export const deepAnyToString = (data) => {
455
+ if (Array.isArray(data)) {
456
+ return data.map((item) => deepAnyToString(item));
457
+ }
458
+ else if (isObject(data)) {
459
+ return Object.keys(data).reduce((acc, key) => {
460
+ acc[key] = deepAnyToString(data[key]);
461
+ return acc;
462
+ }, {});
463
+ }
464
+ else {
465
+ return converterFunctions.anyToString(data);
466
+ }
467
+ };
468
+ /**
469
+ * Performs a deep conversion of all values in a nested structure to the specified type.
470
+ * Uses a conversion function like anyToString, anyToNumber, etc.
471
+ * @param data The data to convert.
472
+ * @param convertFn The conversion function to apply.
473
+ * @returns The converted data.
474
+ */
475
+ export const deepConvert = (data, convertFn) => {
476
+ if (Array.isArray(data)) {
477
+ return data.map((item) => deepConvert(item, convertFn));
478
+ }
479
+ else if (isObject(data)) {
480
+ return Object.keys(data).reduce((acc, key) => {
481
+ acc[key] = deepConvert(data[key], convertFn);
482
+ return acc;
483
+ }, {});
484
+ }
485
+ else {
486
+ return convertFn(data);
487
+ }
488
+ };
489
+ /**
490
+ * Converts an entire object's properties to different types based on a provided schema.
491
+ * @param obj The object to convert.
492
+ * @param schema A mapping of object keys to conversion functions.
493
+ * @returns The converted object.
494
+ */
495
+ export const convertObjectBySchema = (obj, schema) => {
496
+ return Object.keys(obj).reduce((acc, key) => {
497
+ const convertFn = schema[key];
498
+ acc[key] = convertFn ? convertFn(obj[key]) : obj[key];
499
+ return acc;
500
+ }, {});
501
+ };
502
+ /**
503
+ * Converts the keys of an object based on a provided attributeMappings.
504
+ * Each key in the object is checked against attributeMappings; if a matching entry is found,
505
+ * the key is renamed to the targetKey specified in attributeMappings.
506
+ *
507
+ * @param obj The object to convert.
508
+ * @param attributeMappings The attributeMappings defining how keys in the object should be converted.
509
+ * @returns The converted object with keys renamed according to attributeMappings.
510
+ */
511
+ export const convertObjectByAttributeMappings = (obj, attributeMappings) => {
512
+ const result = {};
513
+ // Correctly handle [any] notation by mapping or aggregating over all elements or keys
514
+ const resolveValue = (obj, path) => {
515
+ const parts = path.split(".");
516
+ let current = obj;
517
+ for (let i = 0; i < parts.length; i++) {
518
+ if (parts[i] === "[any]") {
519
+ if (Array.isArray(current)) {
520
+ // If current is an array, apply resolution to each item
521
+ return current.map((item) => resolveValue(item, parts.slice(i + 1).join(".")));
522
+ }
523
+ else if (typeof current === "object" && current !== null) {
524
+ // If current is an object, aggregate values from all keys
525
+ return Object.values(current).map((value) => resolveValue(value, parts.slice(i + 1).join(".")));
526
+ }
527
+ }
528
+ else {
529
+ current = current[parts[i]];
530
+ if (current === undefined)
531
+ return undefined;
532
+ }
533
+ }
534
+ return current;
535
+ };
536
+ for (const mapping of attributeMappings) {
537
+ if (Array.isArray(mapping.oldKeys)) {
538
+ // Collect and flatten values from multiple oldKeys
539
+ const values = mapping.oldKeys
540
+ .map((oldKey) => resolveValue(obj, oldKey))
541
+ .flat(Infinity);
542
+ result[mapping.targetKey] = values.filter((value) => value !== undefined);
543
+ }
544
+ else if (mapping.oldKey) {
545
+ // Resolve single oldKey
546
+ const value = resolveValue(obj, mapping.oldKey);
547
+ if (value !== undefined) {
548
+ result[mapping.targetKey] = Array.isArray(value)
549
+ ? value.flat(Infinity)
550
+ : value;
551
+ }
552
+ }
553
+ }
554
+ console.log("Resolved object:", result);
555
+ return result;
556
+ };
557
+ /**
558
+ * Ensures data conversion without mutating the original input.
559
+ * @param data The data to convert.
560
+ * @param convertFn The conversion function to apply.
561
+ * @returns The converted data.
562
+ */
563
+ export const immutableConvert = (data, convertFn) => {
564
+ const clonedData = cloneDeep(data);
565
+ return convertFn(clonedData);
566
+ };
567
+ /**
568
+ * Validates a string against a regular expression and returns the string if valid, or null.
569
+ * @param value The string to validate.
570
+ * @param pattern The regex pattern to validate against.
571
+ * @returns The original string if valid, otherwise null.
572
+ */
573
+ export const validateString = (value, pattern) => {
574
+ return pattern.test(value) ? value : null;
575
+ };
@@ -0,0 +1,5 @@
1
+ import type { AppwriteConfig, Attribute } from "./schema.js";
2
+ export declare const categorizeCollectionByRelationshipSide: (attributes: Attribute[]) => "parent" | "mixed" | "child";
3
+ export declare const getDependencies: (attributes: Attribute[]) => string[];
4
+ export declare const sortCollections: (configCollections: AppwriteConfig["collections"]) => AppwriteConfig["collections"];
5
+ export declare const sortAttributesByRelationshipSide: (attributes: Attribute[]) => Attribute[];
@@ -0,0 +1,54 @@
1
+ // Helper function to categorize collections based on relationship sides
2
+ export const categorizeCollectionByRelationshipSide = (attributes) => {
3
+ let hasParent = false;
4
+ let hasChild = false;
5
+ for (const attr of attributes) {
6
+ if (attr.type === "relationship") {
7
+ if (attr.side === "parent") {
8
+ hasParent = true;
9
+ }
10
+ else if (attr.side === "child") {
11
+ hasChild = true;
12
+ }
13
+ }
14
+ }
15
+ if (hasParent && hasChild)
16
+ return "mixed";
17
+ if (hasParent)
18
+ return "parent";
19
+ return "child";
20
+ };
21
+ // Helper function to get all dependencies of a collection
22
+ export const getDependencies = (attributes) => {
23
+ return attributes
24
+ .filter((attr) => attr.type === "relationship" && attr.relatedCollection !== undefined)
25
+ .map((attr) => attr.relatedCollection);
26
+ };
27
+ // Function to sort collections based on dependencies and relationship sides
28
+ export const sortCollections = (configCollections) => {
29
+ // Categorize collections based on their relationship sides
30
+ const parentCollections = configCollections.filter(({ attributes }) => categorizeCollectionByRelationshipSide(attributes) === "parent");
31
+ const mixedCollections = configCollections.filter(({ attributes }) => categorizeCollectionByRelationshipSide(attributes) === "mixed");
32
+ const childCollections = configCollections.filter(({ attributes }) => categorizeCollectionByRelationshipSide(attributes) === "child");
33
+ // Sort mixedCollections to ensure parents are processed before children within the mixed category
34
+ // This might involve more sophisticated logic if you need to order mixed collections based on specific parent-child relationships
35
+ mixedCollections.sort((a, b) => {
36
+ // Example sorting logic for mixed collections; adjust based on your specific needs
37
+ const aDependencies = getDependencies(a.attributes).length;
38
+ const bDependencies = getDependencies(b.attributes).length;
39
+ return aDependencies - bDependencies;
40
+ });
41
+ // Combine them back into a single array with the desired order
42
+ // Children first because they have no dependencies and parents will create the relationship if it's twoWay
43
+ return [...childCollections, ...parentCollections, ...mixedCollections];
44
+ };
45
+ // Function to sort attributes within a collection based on relationship sides
46
+ export const sortAttributesByRelationshipSide = (attributes) => {
47
+ // Separate attributes into parent and child based on their relationship side
48
+ const parentAttributes = attributes.filter((attr) => attr.type === "relationship" && attr.side === "parent");
49
+ const childAttributes = attributes.filter((attr) => attr.type === "relationship" && attr.side === "child");
50
+ const otherAttributes = attributes.filter((attr) => attr.type !== "relationship");
51
+ // Combine them back into a single array with child attributes first, then other attributes, and parent attributes last
52
+ // as parent attributes will create the relationship with the child if needed
53
+ return [...childAttributes, ...otherAttributes, ...parentAttributes];
54
+ };
@@ -0,0 +1,44 @@
1
+ import { type Databases, type Storage } from "node-appwrite";
2
+ import type { AppwriteConfig, ConfigCollection, ConfigDatabase, ImportDef, AttributeMappings } from "./schema.js";
3
+ import type { ImportDataActions } from "./importDataActions.js";
4
+ import type { SetupOptions } from "../utilsController.js";
5
+ export declare class ImportController {
6
+ private config;
7
+ private database;
8
+ private storage;
9
+ private appwriteFolderPath;
10
+ private importDataActions;
11
+ private setupOptions;
12
+ private documentCache;
13
+ private batchLimit;
14
+ private postImportActionsQueue;
15
+ constructor(config: AppwriteConfig, database: Databases, storage: Storage, appwriteFolderPath: string, importDataActions: ImportDataActions, setupOptions: SetupOptions);
16
+ run(): Promise<void>;
17
+ importCollections(db: ConfigDatabase): Promise<void>;
18
+ processImportDefinitions(db: ConfigDatabase, collection: ConfigCollection, isMembersCollection?: boolean): Promise<void>;
19
+ loadData(importDef: ImportDef): Promise<any[]>;
20
+ createContext(db: ConfigDatabase, collection: ConfigCollection, item: any): any;
21
+ transformData(item: any, attributeMappings: AttributeMappings): Promise<any>;
22
+ processBatch(db: ConfigDatabase, collection: ConfigCollection, importDef: ImportDef, dataToImport: any[], updateDefs?: ImportDef[], isMembersCollection?: boolean): Promise<void>;
23
+ handleCreate(context: any, finalItem: any, updateDefs?: ImportDef[], id?: string): Promise<any>;
24
+ handleUpdate(context: any, finalItem: any, importDef: ImportDef): Promise<any>;
25
+ getAttributeMappingsWithActions(attributeMappings: AttributeMappings, context: any, item: any): {
26
+ postImportActions: {
27
+ action: string;
28
+ params: any[];
29
+ }[];
30
+ targetKey: string;
31
+ converters: string[];
32
+ validationActions: {
33
+ params: string[];
34
+ action: string;
35
+ }[];
36
+ oldKey?: string | undefined;
37
+ oldKeys?: string[] | undefined;
38
+ fileData?: {
39
+ path: string;
40
+ name: string;
41
+ } | undefined;
42
+ }[];
43
+ executePostImportActions(): Promise<void>;
44
+ }