appwrite-cli 13.6.0 → 13.6.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 (75) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +2 -2
  3. package/cli.ts +3 -3
  4. package/dist/bundle-win-arm64.mjs +336 -212
  5. package/dist/cli.cjs +336 -212
  6. package/dist/index.cjs +156 -104
  7. package/dist/index.js +156 -104
  8. package/dist/lib/commands/config-validations.d.ts +1 -1
  9. package/dist/lib/commands/config-validations.d.ts.map +1 -1
  10. package/dist/lib/commands/errors.d.ts +4 -4
  11. package/dist/lib/commands/errors.d.ts.map +1 -1
  12. package/dist/lib/commands/generate.d.ts.map +1 -1
  13. package/dist/lib/commands/generators/base.d.ts +5 -0
  14. package/dist/lib/commands/generators/base.d.ts.map +1 -1
  15. package/dist/lib/commands/generic.d.ts.map +1 -1
  16. package/dist/lib/commands/init.d.ts.map +1 -1
  17. package/dist/lib/commands/run.d.ts.map +1 -1
  18. package/dist/lib/commands/types.d.ts.map +1 -1
  19. package/dist/lib/commands/update.d.ts.map +1 -1
  20. package/dist/lib/commands/utils/change-approval.d.ts +3 -3
  21. package/dist/lib/commands/utils/change-approval.d.ts.map +1 -1
  22. package/dist/lib/commands/utils/database-sync.d.ts.map +1 -1
  23. package/dist/lib/commands/utils/deployment.d.ts +16 -4
  24. package/dist/lib/commands/utils/deployment.d.ts.map +1 -1
  25. package/dist/lib/commands/utils/pools.d.ts.map +1 -1
  26. package/dist/lib/constants.d.ts +1 -1
  27. package/dist/lib/emulation/docker.d.ts.map +1 -1
  28. package/dist/lib/json.d.ts +1 -1
  29. package/dist/lib/json.d.ts.map +1 -1
  30. package/dist/lib/paginate.d.ts +5 -6
  31. package/dist/lib/paginate.d.ts.map +1 -1
  32. package/dist/lib/parser.d.ts +5 -4
  33. package/dist/lib/parser.d.ts.map +1 -1
  34. package/dist/lib/spinner.d.ts +1 -1
  35. package/dist/lib/spinner.d.ts.map +1 -1
  36. package/dist/lib/utils.d.ts +6 -1
  37. package/dist/lib/utils.d.ts.map +1 -1
  38. package/dist/lib/validations.d.ts +1 -1
  39. package/dist/lib/validations.d.ts.map +1 -1
  40. package/eslint.config.js +45 -0
  41. package/install.ps1 +2 -2
  42. package/install.sh +1 -1
  43. package/lib/client.ts +3 -3
  44. package/lib/commands/config-validations.ts +1 -1
  45. package/lib/commands/config.ts +2 -2
  46. package/lib/commands/errors.ts +2 -2
  47. package/lib/commands/generate.ts +8 -6
  48. package/lib/commands/generators/base.ts +6 -0
  49. package/lib/commands/generators/typescript/databases.ts +9 -9
  50. package/lib/commands/generators/typescript/templates/databases.ts.hbs +16 -16
  51. package/lib/commands/generic.ts +21 -16
  52. package/lib/commands/init.ts +147 -61
  53. package/lib/commands/pull.ts +1 -1
  54. package/lib/commands/push.ts +19 -19
  55. package/lib/commands/run.ts +15 -9
  56. package/lib/commands/types.ts +18 -8
  57. package/lib/commands/update.ts +24 -16
  58. package/lib/commands/utils/attributes.ts +6 -6
  59. package/lib/commands/utils/change-approval.ts +26 -19
  60. package/lib/commands/utils/database-sync.ts +58 -18
  61. package/lib/commands/utils/deployment.ts +22 -5
  62. package/lib/commands/utils/pools.ts +11 -5
  63. package/lib/config.ts +1 -1
  64. package/lib/constants.ts +1 -1
  65. package/lib/emulation/docker.ts +5 -6
  66. package/lib/emulation/utils.ts +2 -2
  67. package/lib/json.ts +15 -7
  68. package/lib/paginate.ts +30 -20
  69. package/lib/parser.ts +46 -15
  70. package/lib/questions.ts +38 -38
  71. package/lib/spinner.ts +5 -1
  72. package/lib/utils.ts +15 -3
  73. package/lib/validations.ts +1 -1
  74. package/package.json +7 -1
  75. package/scoop/appwrite.config.json +3 -3
@@ -439,7 +439,7 @@ export class Push {
439
439
 
440
440
  if (settings.services) {
441
441
  this.log("Applying service statuses ...");
442
- for (let [service, status] of Object.entries(settings.services)) {
442
+ for (const [service, status] of Object.entries(settings.services)) {
443
443
  await projectsService.updateServiceStatus({
444
444
  projectId: projectId,
445
445
  service: service as ApiService,
@@ -487,7 +487,7 @@ export class Push {
487
487
 
488
488
  if (settings.auth.methods) {
489
489
  this.log("Applying auth methods statuses ...");
490
- for (let [method, status] of Object.entries(settings.auth.methods)) {
490
+ for (const [method, status] of Object.entries(settings.auth.methods)) {
491
491
  await projectsService.updateAuthStatus({
492
492
  projectId,
493
493
  method: method as AuthMethod,
@@ -861,7 +861,7 @@ export class Push {
861
861
  ([key, value]) => ({ key, value }),
862
862
  );
863
863
  }
864
- } catch (error) {
864
+ } catch (_error) {
865
865
  envVariables = [];
866
866
  }
867
867
  await Promise.all(
@@ -1223,7 +1223,7 @@ export class Push {
1223
1223
  ([key, value]) => ({ key, value }),
1224
1224
  );
1225
1225
  }
1226
- } catch (error) {
1226
+ } catch (_error) {
1227
1227
  envVariables = [];
1228
1228
  }
1229
1229
  await Promise.all(
@@ -1424,7 +1424,7 @@ export class Push {
1424
1424
  this.projectClient,
1425
1425
  );
1426
1426
 
1427
- let tablesChanged = new Set();
1427
+ const tablesChanged = new Set();
1428
1428
  const errors: any[] = [];
1429
1429
 
1430
1430
  // Parallel tables actions
@@ -1492,7 +1492,7 @@ export class Push {
1492
1492
  );
1493
1493
 
1494
1494
  // Serialize attribute actions
1495
- for (let table of tables) {
1495
+ for (const table of tables) {
1496
1496
  let columns = table.columns;
1497
1497
  let indexes = table.indexes;
1498
1498
  let hadChanges = false;
@@ -1843,7 +1843,7 @@ const pushSettings = async (): Promise<void> => {
1843
1843
 
1844
1844
  try {
1845
1845
  const projectsService = await getProjectsService();
1846
- let response = await projectsService.get(
1846
+ const response = await projectsService.get(
1847
1847
  localConfig.getProject().projectId,
1848
1848
  );
1849
1849
 
@@ -1880,7 +1880,7 @@ const pushSettings = async (): Promise<void> => {
1880
1880
  return;
1881
1881
  }
1882
1882
  }
1883
- } catch (e) {}
1883
+ } catch (_e) {}
1884
1884
 
1885
1885
  try {
1886
1886
  log("Pushing project settings ...");
@@ -1939,7 +1939,7 @@ const pushSite = async ({
1939
1939
  return;
1940
1940
  }
1941
1941
 
1942
- let sites = siteIds.map((id: string) => {
1942
+ const sites = siteIds.map((id: string) => {
1943
1943
  const sites = localConfig.getSites();
1944
1944
  const site = sites.find((s: any) => s.$id === id);
1945
1945
 
@@ -1952,7 +1952,7 @@ const pushSite = async ({
1952
1952
 
1953
1953
  log("Validating sites ...");
1954
1954
  // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1955
- for (let site of sites) {
1955
+ for (const site of sites) {
1956
1956
  if (!site.buildCommand) {
1957
1957
  log(`Site ${site.name} is missing build command.`);
1958
1958
  const answers = await inquirer.prompt(questionsGetEntrypoint);
@@ -2081,7 +2081,7 @@ const pushFunction = async ({
2081
2081
  return;
2082
2082
  }
2083
2083
 
2084
- let functions = functionIds.map((id: string) => {
2084
+ const functions = functionIds.map((id: string) => {
2085
2085
  const functions = localConfig.getFunctions();
2086
2086
  const func = functions.find((f: any) => f.$id === id);
2087
2087
 
@@ -2093,7 +2093,7 @@ const pushFunction = async ({
2093
2093
  });
2094
2094
 
2095
2095
  log("Validating functions ...");
2096
- for (let func of functions) {
2096
+ for (const func of functions) {
2097
2097
  if (!func.entrypoint) {
2098
2098
  log(`Function ${func.name} is missing an entrypoint.`);
2099
2099
  const answers = await inquirer.prompt(questionsGetEntrypoint);
@@ -2258,7 +2258,7 @@ const pushTable = async ({
2258
2258
  });
2259
2259
  }
2260
2260
  }
2261
- } catch (e) {
2261
+ } catch (_e) {
2262
2262
  // Skip if database doesn't exist or other errors
2263
2263
  }
2264
2264
  }
@@ -2432,7 +2432,7 @@ const pushCollection = async (): Promise<void> => {
2432
2432
  };
2433
2433
 
2434
2434
  const pushBucket = async (): Promise<void> => {
2435
- let bucketIds: string[] = [];
2435
+ const bucketIds: string[] = [];
2436
2436
  const configBuckets = localConfig.getBuckets();
2437
2437
 
2438
2438
  if (cliConfig.all) {
@@ -2455,7 +2455,7 @@ const pushBucket = async (): Promise<void> => {
2455
2455
  return;
2456
2456
  }
2457
2457
 
2458
- let buckets: any[] = [];
2458
+ const buckets: any[] = [];
2459
2459
 
2460
2460
  for (const bucketId of bucketIds) {
2461
2461
  const idBuckets = configBuckets.filter((b: any) => b.$id === bucketId);
@@ -2496,7 +2496,7 @@ const pushBucket = async (): Promise<void> => {
2496
2496
  };
2497
2497
 
2498
2498
  const pushTeam = async (): Promise<void> => {
2499
- let teamIds: string[] = [];
2499
+ const teamIds: string[] = [];
2500
2500
  const configTeams = localConfig.getTeams();
2501
2501
 
2502
2502
  if (cliConfig.all) {
@@ -2519,7 +2519,7 @@ const pushTeam = async (): Promise<void> => {
2519
2519
  return;
2520
2520
  }
2521
2521
 
2522
- let teams: any[] = [];
2522
+ const teams: any[] = [];
2523
2523
 
2524
2524
  for (const teamId of teamIds) {
2525
2525
  const idTeams = configTeams.filter((t: any) => t.$id === teamId);
@@ -2560,7 +2560,7 @@ const pushTeam = async (): Promise<void> => {
2560
2560
  };
2561
2561
 
2562
2562
  const pushMessagingTopic = async (): Promise<void> => {
2563
- let topicsIds: string[] = [];
2563
+ const topicsIds: string[] = [];
2564
2564
  const configTopics = localConfig.getMessagingTopics();
2565
2565
 
2566
2566
  if (cliConfig.all) {
@@ -2583,7 +2583,7 @@ const pushMessagingTopic = async (): Promise<void> => {
2583
2583
  return;
2584
2584
  }
2585
2585
 
2586
- let topics: any[] = [];
2586
+ const topics: any[] = [];
2587
2587
 
2588
2588
  for (const topicId of topicsIds) {
2589
2589
  const idTopic = configTopics.filter((b: any) => b.$id === topicId);
@@ -3,7 +3,8 @@ import { parse as parseDotenv } from "dotenv";
3
3
  import chalk from "chalk";
4
4
  import ignoreModule from "ignore";
5
5
  const ignore: typeof ignoreModule =
6
- (ignoreModule as any).default ?? ignoreModule;
6
+ (ignoreModule as unknown as { default?: typeof ignoreModule }).default ??
7
+ ignoreModule;
7
8
  import tar from "tar";
8
9
  import fs from "fs";
9
10
  import chokidar from "chokidar";
@@ -24,7 +25,12 @@ import {
24
25
  commandDescriptions,
25
26
  drawTable,
26
27
  } from "../parser.js";
27
- import { systemHasCommand, isPortTaken, getAllFiles } from "../utils.js";
28
+ import {
29
+ systemHasCommand,
30
+ isPortTaken,
31
+ getAllFiles,
32
+ getErrorMessage,
33
+ } from "../utils.js";
28
34
  import {
29
35
  runtimeNames,
30
36
  systemTools,
@@ -62,7 +68,7 @@ const runFunction = async ({
62
68
  }
63
69
 
64
70
  const functions = localConfig.getFunctions();
65
- const func = functions.find((f: any) => f.$id === functionId);
71
+ const func = functions.find((f) => f.$id === functionId);
66
72
  if (!func) {
67
73
  throw new Error("Function '" + functionId + "' not found.");
68
74
  }
@@ -178,14 +184,14 @@ const runFunction = async ({
178
184
  "variables",
179
185
  );
180
186
 
181
- remoteVariables.forEach((v: any) => {
187
+ remoteVariables.forEach((v) => {
182
188
  allVariables[v.key] = v.value;
183
189
  userVariables[v.key] = v.value;
184
190
  });
185
- } catch (err: any) {
191
+ } catch (err: unknown) {
186
192
  warn(
187
193
  "Remote variables not fetched. Production environment variables will not be available. Reason: " +
188
- err.message,
194
+ getErrorMessage(err),
189
195
  );
190
196
  }
191
197
  }
@@ -214,10 +220,10 @@ const runFunction = async ({
214
220
 
215
221
  try {
216
222
  await JwtManager.setup(userId, (func.scopes as Scopes[]) ?? []);
217
- } catch (err: any) {
223
+ } catch (err: unknown) {
218
224
  warn(
219
225
  "Dynamic API key not generated. Header x-appwrite-key will not be set. Reason: " +
220
- err.message,
226
+ getErrorMessage(err),
221
227
  );
222
228
  }
223
229
 
@@ -394,7 +400,7 @@ export const run = new Command("run")
394
400
  helpWidth: process.stdout.columns || 80,
395
401
  })
396
402
  .action(
397
- actionRunner(async (_options: any, command: Command) => {
403
+ actionRunner(async (_options: unknown, command: Command) => {
398
404
  command.help();
399
405
  }),
400
406
  );
@@ -83,6 +83,16 @@ interface TypesOptions {
83
83
  strict: boolean;
84
84
  }
85
85
 
86
+ type TypeAttribute = Record<string, unknown> & {
87
+ relatedTable?: string;
88
+ };
89
+
90
+ type TypeDataItem = Record<string, unknown> & {
91
+ name: string;
92
+ attributes?: TypeAttribute[];
93
+ columns?: TypeAttribute[];
94
+ };
95
+
86
96
  const typesCommand = actionRunner(
87
97
  async (rawOutputDirectory: string, { language, strict }: TypesOptions) => {
88
98
  if (language === "auto") {
@@ -126,13 +136,11 @@ const typesCommand = actionRunner(
126
136
  }
127
137
 
128
138
  // Try tables first, fallback to collections
129
- let tables = localConfig.getTables();
130
- let collections: any[] = [];
131
- let dataSource = "tables";
139
+ const tables = localConfig.getTables();
140
+ let collections: TypeDataItem[] = [];
132
141
 
133
142
  if (tables.length === 0) {
134
143
  collections = localConfig.getCollections();
135
- dataSource = "collections";
136
144
 
137
145
  if (collections.length === 0) {
138
146
  const configFileName = path.basename(localConfig.path);
@@ -143,7 +151,8 @@ const typesCommand = actionRunner(
143
151
  }
144
152
 
145
153
  // Use tables if available, otherwise use collections
146
- let dataItems: any[] = tables.length > 0 ? tables : collections;
154
+ let dataItems: TypeDataItem[] =
155
+ tables.length > 0 ? (tables as TypeDataItem[]) : collections;
147
156
  const itemType = tables.length > 0 ? "tables" : "collections";
148
157
 
149
158
  // Normalize tables data: rename 'columns' to 'attributes' for template compatibility
@@ -152,7 +161,7 @@ const typesCommand = actionRunner(
152
161
  const { columns, ...rest } = table;
153
162
  return {
154
163
  ...rest,
155
- attributes: (columns || []).map((column: any) => {
164
+ attributes: (columns || []).map((column: TypeAttribute) => {
156
165
  if (column.relatedTable) {
157
166
  const { relatedTable, ...columnRest } = column;
158
167
  return {
@@ -167,14 +176,15 @@ const typesCommand = actionRunner(
167
176
  }
168
177
 
169
178
  log(
170
- `Found ${dataItems.length} ${itemType}: ${dataItems.map((c: any) => c.name).join(", ")}`,
179
+ `Found ${dataItems.length} ${itemType}: ${dataItems.map((c) => c.name).join(", ")}`,
171
180
  );
172
181
 
173
182
  // Use columns if available, otherwise use attributes
174
183
  const resourceType = tables.length > 0 ? "columns" : "attributes";
175
184
 
176
185
  const totalAttributes = dataItems.reduce(
177
- (count: number, item: any) => count + (item.attributes || []).length,
186
+ (count: number, item: TypeDataItem) =>
187
+ count + (item.attributes || []).length,
178
188
  0,
179
189
  );
180
190
  log(`Found ${totalAttributes} ${resourceType} across all ${itemType}`);
@@ -3,7 +3,11 @@ import { Command } from "commander";
3
3
  import chalk from "chalk";
4
4
  import inquirer from "inquirer";
5
5
  import { success, log, warn, error, hint, actionRunner } from "../parser.js";
6
- import { getLatestVersion, compareVersions } from "../utils.js";
6
+ import {
7
+ getLatestVersion,
8
+ compareVersions,
9
+ getErrorMessage,
10
+ } from "../utils.js";
7
11
  import {
8
12
  GITHUB_RELEASES_URL,
9
13
  NPM_PACKAGE_NAME,
@@ -12,6 +16,8 @@ import {
12
16
  import packageJson from "../../package.json" with { type: "json" };
13
17
  const { version } = packageJson;
14
18
 
19
+ type ExecCommandOptions = Exclude<Parameters<typeof spawn>[2], undefined>;
20
+
15
21
  /**
16
22
  * Check if the CLI was installed via npm
17
23
  */
@@ -36,7 +42,7 @@ const isInstalledViaNpm = (): boolean => {
36
42
  }
37
43
 
38
44
  return false;
39
- } catch (e) {
45
+ } catch (_e) {
40
46
  return false;
41
47
  }
42
48
  };
@@ -51,7 +57,7 @@ const isInstalledViaHomebrew = (): boolean => {
51
57
  scriptPath.includes("/opt/homebrew/") ||
52
58
  scriptPath.includes("/usr/local/Cellar/")
53
59
  );
54
- } catch (e) {
60
+ } catch (_e) {
55
61
  return false;
56
62
  }
57
63
  };
@@ -62,7 +68,7 @@ const isInstalledViaHomebrew = (): boolean => {
62
68
  const execCommand = (
63
69
  command: string,
64
70
  args: string[] = [],
65
- options: any = {},
71
+ options: ExecCommandOptions = {},
66
72
  ): Promise<void> => {
67
73
  return new Promise((resolve, reject) => {
68
74
  const child = spawn(command, args, {
@@ -94,17 +100,16 @@ const updateViaNpm = async (): Promise<void> => {
94
100
  console.log("");
95
101
  success("Updated to latest version via npm!");
96
102
  hint("Run 'appwrite --version' to verify the new version.");
97
- } catch (e: any) {
98
- if (
99
- e.message.includes("EEXIST") ||
100
- e.message.includes("file already exists")
101
- ) {
103
+ } catch (e: unknown) {
104
+ const message = getErrorMessage(e);
105
+
106
+ if (message.includes("EEXIST") || message.includes("file already exists")) {
102
107
  console.log("");
103
108
  success("Latest version is already installed via npm!");
104
109
  hint("The CLI is up to date. Run 'appwrite --version' to verify.");
105
110
  } else {
106
111
  console.log("");
107
- error(`Failed to update via npm: ${e.message}`);
112
+ error(`Failed to update via npm: ${message}`);
108
113
  hint(`Try running: npm install -g ${NPM_PACKAGE_NAME}@latest --force`);
109
114
  }
110
115
  }
@@ -119,17 +124,19 @@ const updateViaHomebrew = async (): Promise<void> => {
119
124
  console.log("");
120
125
  success("Updated to latest version via Homebrew!");
121
126
  hint("Run 'appwrite --version' to verify the new version.");
122
- } catch (e: any) {
127
+ } catch (e: unknown) {
128
+ const message = getErrorMessage(e);
129
+
123
130
  if (
124
- e.message.includes("already installed") ||
125
- e.message.includes("up-to-date")
131
+ message.includes("already installed") ||
132
+ message.includes("up-to-date")
126
133
  ) {
127
134
  console.log("");
128
135
  success("Latest version is already installed via Homebrew!");
129
136
  hint("The CLI is up to date. Run 'appwrite --version' to verify.");
130
137
  } else {
131
138
  console.log("");
132
- error(`Failed to update via Homebrew: ${e.message}`);
139
+ error(`Failed to update via Homebrew: ${message}`);
133
140
  hint("Try running: brew upgrade appwrite");
134
141
  }
135
142
  }
@@ -230,9 +237,10 @@ const updateCli = async ({ manual }: UpdateOptions = {}): Promise<void> => {
230
237
  } else {
231
238
  await chooseUpdateMethod(latestVersion);
232
239
  }
233
- } catch (e: any) {
240
+ } catch (e: unknown) {
241
+ const message = getErrorMessage(e);
234
242
  console.log("");
235
- error(`Failed to check for updates: ${e.message}`);
243
+ error(`Failed to check for updates: ${message}`);
236
244
  hint(`You can manually check for updates at: ${GITHUB_RELEASES_URL}`);
237
245
  }
238
246
  };
@@ -76,7 +76,7 @@ export class Attributes {
76
76
  return answers.changes;
77
77
  }
78
78
 
79
- let answers = await inquirer.prompt(questionPushChanges);
79
+ const answers = await inquirer.prompt(questionPushChanges);
80
80
 
81
81
  if (answers.changes !== "YES" && answers.changes !== "NO") {
82
82
  answers.changes = await fixConfirmation();
@@ -148,9 +148,9 @@ export class Attributes {
148
148
  const keyName = `${chalk.yellow(local.key)} in ${collection.name} (${collection["$id"]})`;
149
149
  const action = chalk.cyan(recreating ? "recreating" : "changing");
150
150
  let reason = "";
151
- let attribute = recreating ? remote : local;
151
+ const attribute = recreating ? remote : local;
152
152
 
153
- for (let key of Object.keys(remote)) {
153
+ for (const key of Object.keys(remote)) {
154
154
  if (!KeysAttributes.has(key)) {
155
155
  continue;
156
156
  }
@@ -737,7 +737,7 @@ export class Attributes {
737
737
  log(`Creating indexes ...`);
738
738
 
739
739
  const databasesService = await getDatabasesService(this.client);
740
- for (let index of indexes) {
740
+ for (const index of indexes) {
741
741
  await databasesService.createIndex({
742
742
  databaseId: collection["databaseId"],
743
743
  collectionId: collection["$id"],
@@ -769,7 +769,7 @@ export class Attributes {
769
769
  ): Promise<void> => {
770
770
  log(`Creating attributes ...`);
771
771
 
772
- for (let attribute of attributes) {
772
+ for (const attribute of attributes) {
773
773
  if (attribute.side !== "child") {
774
774
  await this.createAttribute(
775
775
  collection["databaseId"],
@@ -803,7 +803,7 @@ export class Attributes {
803
803
  ): Promise<void> => {
804
804
  log(`Creating columns ...`);
805
805
 
806
- for (let column of columns) {
806
+ for (const column of columns) {
807
807
  if (column.side !== "child") {
808
808
  await this.createAttribute(table["databaseId"], table["$id"], column);
809
809
  }
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
+ import { AppwriteException } from "@appwrite.io/console";
3
4
  import { cliConfig, success, warn, log, drawTable } from "../../parser.js";
4
5
  import { whitelistKeys } from "../../config.js";
5
6
  import {
@@ -10,7 +11,7 @@ import {
10
11
  /**
11
12
  * Check if a value is considered empty
12
13
  */
13
- export const isEmpty = (value: any): boolean =>
14
+ export const isEmpty = (value: unknown): boolean =>
14
15
  value === null ||
15
16
  value === undefined ||
16
17
  (typeof value === "string" && value.trim().length === 0) ||
@@ -33,7 +34,7 @@ export const getConfirmation = async (): Promise<boolean> => {
33
34
  return answers.changes;
34
35
  }
35
36
 
36
- let answers = await inquirer.prompt(questionPushChanges);
37
+ const answers = await inquirer.prompt(questionPushChanges);
37
38
 
38
39
  if (answers.changes !== "YES" && answers.changes !== "NO") {
39
40
  answers.changes = await fixConfirmation();
@@ -57,9 +58,9 @@ interface ObjectChange {
57
58
  local: string;
58
59
  }
59
60
 
60
- type ComparableValue = boolean | number | string | any[] | undefined;
61
+ type ComparableValue = boolean | number | string | unknown[] | undefined;
61
62
 
62
- export const getObjectChanges = <T extends Record<string, any>>(
63
+ export const getObjectChanges = <T extends Record<string, unknown>>(
63
64
  remote: T,
64
65
  local: T,
65
66
  index: keyof T,
@@ -110,8 +111,10 @@ export const getObjectChanges = <T extends Record<string, any>>(
110
111
  * Compares local resources with remote resources and prompts user for confirmation
111
112
  */
112
113
  export const approveChanges = async (
113
- resource: any[],
114
- resourceGetFunction: Function,
114
+ resource: Array<Record<string, unknown>>,
115
+ resourceGetFunction: (
116
+ options: Record<string, unknown>,
117
+ ) => Promise<Record<string, unknown>>,
115
118
  keys: Set<string>,
116
119
  resourceName: string,
117
120
  resourcePlural: string,
@@ -120,12 +123,12 @@ export const approveChanges = async (
120
123
  secondResourceName: string = "",
121
124
  ): Promise<boolean> => {
122
125
  log("Checking for changes ...");
123
- const changes: any[] = [];
126
+ const changes: Array<Record<string, unknown>> = [];
124
127
 
125
128
  await Promise.all(
126
129
  resource.map(async (localResource) => {
127
130
  try {
128
- const options: Record<string, any> = {
131
+ const options: Record<string, unknown> = {
129
132
  [resourceName]: localResource["$id"],
130
133
  };
131
134
 
@@ -135,7 +138,7 @@ export const approveChanges = async (
135
138
 
136
139
  const remoteResource = await resourceGetFunction(options);
137
140
 
138
- for (let [key, value] of Object.entries(
141
+ for (const [key, value] of Object.entries(
139
142
  whitelistKeys(remoteResource, keys),
140
143
  )) {
141
144
  if (skipKeys.includes(key)) {
@@ -146,28 +149,32 @@ export const approveChanges = async (
146
149
  continue;
147
150
  }
148
151
 
149
- if (Array.isArray(value) && Array.isArray(localResource[key])) {
150
- if (JSON.stringify(value) !== JSON.stringify(localResource[key])) {
152
+ const localValue = localResource[key];
153
+
154
+ if (Array.isArray(value) && Array.isArray(localValue)) {
155
+ if (JSON.stringify(value) !== JSON.stringify(localValue)) {
151
156
  changes.push({
152
157
  id: localResource["$id"],
153
158
  key,
154
159
  remote: chalk.red((value as string[]).join("\n")),
155
- local: chalk.green(localResource[key].join("\n")),
160
+ local: chalk.green(
161
+ localValue.map((entry) => String(entry)).join("\n"),
162
+ ),
156
163
  });
157
164
  }
158
- } else if (value !== localResource[key]) {
165
+ } else if (value !== localValue) {
159
166
  changes.push({
160
167
  id: localResource["$id"],
161
168
  key,
162
- remote: chalk.red(value),
163
- local: chalk.green(localResource[key]),
169
+ remote: chalk.red(String(value ?? "")),
170
+ local: chalk.green(String(localValue ?? "")),
164
171
  });
165
172
  }
166
173
  }
167
- } catch (e: any) {
168
- if (Number(e.code) !== 404) {
169
- throw e;
170
- }
174
+ } catch (e: unknown) {
175
+ const isNotFound =
176
+ e instanceof AppwriteException && Number(e.code) === 404;
177
+ if (!isNotFound) throw e;
171
178
  }
172
179
  }),
173
180
  );