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.
- package/CHANGELOG.md +4 -0
- package/README.md +2 -2
- package/cli.ts +3 -3
- package/dist/bundle-win-arm64.mjs +336 -212
- package/dist/cli.cjs +336 -212
- package/dist/index.cjs +156 -104
- package/dist/index.js +156 -104
- package/dist/lib/commands/config-validations.d.ts +1 -1
- package/dist/lib/commands/config-validations.d.ts.map +1 -1
- package/dist/lib/commands/errors.d.ts +4 -4
- package/dist/lib/commands/errors.d.ts.map +1 -1
- package/dist/lib/commands/generate.d.ts.map +1 -1
- package/dist/lib/commands/generators/base.d.ts +5 -0
- package/dist/lib/commands/generators/base.d.ts.map +1 -1
- package/dist/lib/commands/generic.d.ts.map +1 -1
- package/dist/lib/commands/init.d.ts.map +1 -1
- package/dist/lib/commands/run.d.ts.map +1 -1
- package/dist/lib/commands/types.d.ts.map +1 -1
- package/dist/lib/commands/update.d.ts.map +1 -1
- package/dist/lib/commands/utils/change-approval.d.ts +3 -3
- package/dist/lib/commands/utils/change-approval.d.ts.map +1 -1
- package/dist/lib/commands/utils/database-sync.d.ts.map +1 -1
- package/dist/lib/commands/utils/deployment.d.ts +16 -4
- package/dist/lib/commands/utils/deployment.d.ts.map +1 -1
- package/dist/lib/commands/utils/pools.d.ts.map +1 -1
- package/dist/lib/constants.d.ts +1 -1
- package/dist/lib/emulation/docker.d.ts.map +1 -1
- package/dist/lib/json.d.ts +1 -1
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/paginate.d.ts +5 -6
- package/dist/lib/paginate.d.ts.map +1 -1
- package/dist/lib/parser.d.ts +5 -4
- package/dist/lib/parser.d.ts.map +1 -1
- package/dist/lib/spinner.d.ts +1 -1
- package/dist/lib/spinner.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +6 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/validations.d.ts +1 -1
- package/dist/lib/validations.d.ts.map +1 -1
- package/eslint.config.js +45 -0
- package/install.ps1 +2 -2
- package/install.sh +1 -1
- package/lib/client.ts +3 -3
- package/lib/commands/config-validations.ts +1 -1
- package/lib/commands/config.ts +2 -2
- package/lib/commands/errors.ts +2 -2
- package/lib/commands/generate.ts +8 -6
- package/lib/commands/generators/base.ts +6 -0
- package/lib/commands/generators/typescript/databases.ts +9 -9
- package/lib/commands/generators/typescript/templates/databases.ts.hbs +16 -16
- package/lib/commands/generic.ts +21 -16
- package/lib/commands/init.ts +147 -61
- package/lib/commands/pull.ts +1 -1
- package/lib/commands/push.ts +19 -19
- package/lib/commands/run.ts +15 -9
- package/lib/commands/types.ts +18 -8
- package/lib/commands/update.ts +24 -16
- package/lib/commands/utils/attributes.ts +6 -6
- package/lib/commands/utils/change-approval.ts +26 -19
- package/lib/commands/utils/database-sync.ts +58 -18
- package/lib/commands/utils/deployment.ts +22 -5
- package/lib/commands/utils/pools.ts +11 -5
- package/lib/config.ts +1 -1
- package/lib/constants.ts +1 -1
- package/lib/emulation/docker.ts +5 -6
- package/lib/emulation/utils.ts +2 -2
- package/lib/json.ts +15 -7
- package/lib/paginate.ts +30 -20
- package/lib/parser.ts +46 -15
- package/lib/questions.ts +38 -38
- package/lib/spinner.ts +5 -1
- package/lib/utils.ts +15 -3
- package/lib/validations.ts +1 -1
- package/package.json +7 -1
- package/scoop/appwrite.config.json +3 -3
package/lib/commands/push.ts
CHANGED
|
@@ -439,7 +439,7 @@ export class Push {
|
|
|
439
439
|
|
|
440
440
|
if (settings.services) {
|
|
441
441
|
this.log("Applying service statuses ...");
|
|
442
|
-
for (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2586
|
+
const topics: any[] = [];
|
|
2587
2587
|
|
|
2588
2588
|
for (const topicId of topicsIds) {
|
|
2589
2589
|
const idTopic = configTopics.filter((b: any) => b.$id === topicId);
|
package/lib/commands/run.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
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
|
|
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
|
|
187
|
+
remoteVariables.forEach((v) => {
|
|
182
188
|
allVariables[v.key] = v.value;
|
|
183
189
|
userVariables[v.key] = v.value;
|
|
184
190
|
});
|
|
185
|
-
} catch (err:
|
|
191
|
+
} catch (err: unknown) {
|
|
186
192
|
warn(
|
|
187
193
|
"Remote variables not fetched. Production environment variables will not be available. Reason: " +
|
|
188
|
-
err
|
|
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:
|
|
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
|
|
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:
|
|
403
|
+
actionRunner(async (_options: unknown, command: Command) => {
|
|
398
404
|
command.help();
|
|
399
405
|
}),
|
|
400
406
|
);
|
package/lib/commands/types.ts
CHANGED
|
@@ -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
|
-
|
|
130
|
-
let collections:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
186
|
+
(count: number, item: TypeDataItem) =>
|
|
187
|
+
count + (item.attributes || []).length,
|
|
178
188
|
0,
|
|
179
189
|
);
|
|
180
190
|
log(`Found ${totalAttributes} ${resourceType} across all ${itemType}`);
|
package/lib/commands/update.ts
CHANGED
|
@@ -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 {
|
|
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 (
|
|
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 (
|
|
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:
|
|
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:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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: ${
|
|
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:
|
|
127
|
+
} catch (e: unknown) {
|
|
128
|
+
const message = getErrorMessage(e);
|
|
129
|
+
|
|
123
130
|
if (
|
|
124
|
-
|
|
125
|
-
|
|
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: ${
|
|
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:
|
|
240
|
+
} catch (e: unknown) {
|
|
241
|
+
const message = getErrorMessage(e);
|
|
234
242
|
console.log("");
|
|
235
|
-
error(`Failed to check for updates: ${
|
|
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
|
-
|
|
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
|
-
|
|
151
|
+
const attribute = recreating ? remote : local;
|
|
152
152
|
|
|
153
|
-
for (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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 |
|
|
61
|
+
type ComparableValue = boolean | number | string | unknown[] | undefined;
|
|
61
62
|
|
|
62
|
-
export const getObjectChanges = <T extends Record<string,
|
|
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:
|
|
114
|
-
resourceGetFunction:
|
|
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:
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
150
|
-
|
|
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(
|
|
160
|
+
local: chalk.green(
|
|
161
|
+
localValue.map((entry) => String(entry)).join("\n"),
|
|
162
|
+
),
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
|
-
} else if (value !==
|
|
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(
|
|
169
|
+
remote: chalk.red(String(value ?? "")),
|
|
170
|
+
local: chalk.green(String(localValue ?? "")),
|
|
164
171
|
});
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
|
-
} catch (e:
|
|
168
|
-
|
|
169
|
-
|
|
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
|
);
|