appwrite-cli 13.0.0-rc.4 → 13.0.0

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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/dist/bundle.cjs +17258 -2711
  4. package/dist/lib/client.js +1 -1
  5. package/dist/lib/client.js.map +1 -1
  6. package/dist/lib/commands/config.d.ts +12 -12
  7. package/dist/lib/commands/config.d.ts.map +1 -1
  8. package/dist/lib/commands/config.js +15 -6
  9. package/dist/lib/commands/config.js.map +1 -1
  10. package/dist/lib/commands/pull.d.ts.map +1 -1
  11. package/dist/lib/commands/pull.js +53 -16
  12. package/dist/lib/commands/pull.js.map +1 -1
  13. package/dist/lib/commands/push.d.ts +1 -1
  14. package/dist/lib/commands/push.d.ts.map +1 -1
  15. package/dist/lib/commands/push.js +88 -12
  16. package/dist/lib/commands/push.js.map +1 -1
  17. package/dist/lib/commands/schema.js +1 -1
  18. package/dist/lib/commands/schema.js.map +1 -1
  19. package/dist/lib/commands/utils/attributes.d.ts.map +1 -1
  20. package/dist/lib/commands/utils/attributes.js +7 -2
  21. package/dist/lib/commands/utils/attributes.js.map +1 -1
  22. package/dist/lib/commands/utils/pools.js +2 -2
  23. package/dist/lib/commands/utils/pools.js.map +1 -1
  24. package/dist/lib/config.d.ts +1 -0
  25. package/dist/lib/config.d.ts.map +1 -1
  26. package/dist/lib/config.js +41 -1
  27. package/dist/lib/config.js.map +1 -1
  28. package/dist/lib/constants.d.ts +1 -1
  29. package/dist/lib/constants.d.ts.map +1 -1
  30. package/dist/lib/constants.js +1 -1
  31. package/dist/lib/constants.js.map +1 -1
  32. package/dist/lib/questions.d.ts +2 -0
  33. package/dist/lib/questions.d.ts.map +1 -1
  34. package/dist/lib/questions.js +14 -0
  35. package/dist/lib/questions.js.map +1 -1
  36. package/dist/package.json +1 -1
  37. package/install.ps1 +2 -2
  38. package/install.sh +1 -1
  39. package/lib/client.ts +1 -1
  40. package/lib/commands/config.ts +20 -6
  41. package/lib/commands/pull.ts +81 -16
  42. package/lib/commands/push.ts +117 -12
  43. package/lib/commands/schema.ts +1 -1
  44. package/lib/commands/utils/attributes.ts +15 -9
  45. package/lib/commands/utils/pools.ts +2 -2
  46. package/lib/config.ts +47 -1
  47. package/lib/constants.ts +1 -1
  48. package/lib/questions.ts +16 -0
  49. package/package.json +1 -1
  50. package/scoop/appwrite.config.json +3 -3
package/install.sh CHANGED
@@ -96,7 +96,7 @@ printSuccess() {
96
96
  downloadBinary() {
97
97
  echo "[2/4] Downloading executable for $OS ($ARCH) ..."
98
98
 
99
- GITHUB_LATEST_VERSION="13.0.0-rc.4"
99
+ GITHUB_LATEST_VERSION="13.0.0"
100
100
  GITHUB_FILE="appwrite-cli-${OS}-${ARCH}"
101
101
  GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE"
102
102
 
package/lib/client.ts CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  SDK_TITLE,
20
20
  } from "./constants.js";
21
21
 
22
- const JSONBigInt = JSONbig({ storeAsString: false });
22
+ const JSONBigInt = JSONbig({ useNativeBigInt: true });
23
23
 
24
24
  class Client {
25
25
  private endpoint: string;
@@ -127,10 +127,10 @@ const SettingsSchema = z
127
127
  .optional(),
128
128
  security: z
129
129
  .object({
130
- duration: z.number().optional(),
131
- limit: z.number().optional(),
132
- sessionsLimit: z.number().optional(),
133
- passwordHistory: z.number().optional(),
130
+ duration: z.union([z.number(), z.bigint()]).optional(),
131
+ limit: z.union([z.number(), z.bigint()]).optional(),
132
+ sessionsLimit: z.union([z.number(), z.bigint()]).optional(),
133
+ passwordHistory: z.union([z.number(), z.bigint()]).optional(),
134
134
  passwordDictionary: z.boolean().optional(),
135
135
  personalDataCheck: z.boolean().optional(),
136
136
  sessionAlerts: z.boolean().optional(),
@@ -155,7 +155,7 @@ const SiteSchema = z
155
155
  name: z.string(),
156
156
  enabled: z.boolean().optional(),
157
157
  logging: z.boolean().optional(),
158
- timeout: z.number().optional(),
158
+ timeout: z.union([z.number(), z.bigint()]).optional(),
159
159
  framework: z.string().optional(),
160
160
  buildRuntime: z.string().optional(),
161
161
  adapter: z.string().optional(),
@@ -182,7 +182,7 @@ const FunctionSchema = z
182
182
  scopes: z.array(z.string()).optional(),
183
183
  events: z.array(z.string()).optional(),
184
184
  schedule: z.string().optional(),
185
- timeout: z.number().optional(),
185
+ timeout: z.union([z.number(), z.bigint()]).optional(),
186
186
  entrypoint: z.string().optional(),
187
187
  commands: z.string().optional(),
188
188
  vars: z.record(z.string(), z.string()).optional(),
@@ -256,6 +256,20 @@ const AttributeSchema = AttributeSchemaBase.refine(
256
256
  message: "When 'required' is true, 'default' must be null",
257
257
  path: ["default"],
258
258
  },
259
+ ).refine(
260
+ (data) => {
261
+ if (
262
+ data.type === "string" &&
263
+ (data.size === undefined || data.size === null)
264
+ ) {
265
+ return false;
266
+ }
267
+ return true;
268
+ },
269
+ {
270
+ message: "When 'type' is 'string', 'size' must be defined",
271
+ path: ["size"],
272
+ },
259
273
  );
260
274
 
261
275
  const IndexSchema = z
@@ -41,6 +41,7 @@ import { createSettingsObject } from "../utils.js";
41
41
  import { ProjectNotInitializedError } from "./errors.js";
42
42
  import type { SettingsType, FunctionType, SiteType } from "./config.js";
43
43
  import { downloadDeploymentCode } from "./utils/deployment.js";
44
+ import { getConfirmation } from "./utils/change-approval.js";
44
45
 
45
46
  export interface PullOptions {
46
47
  all?: boolean;
@@ -804,53 +805,117 @@ const pullCollection = async (): Promise<void> => {
804
805
  const pullInstance = await createPullInstance();
805
806
  const { databases, collections } = await pullInstance.pullCollections();
806
807
 
807
- for (const database of databases) {
808
- localConfig.addDatabase(database);
809
- }
808
+ const localCollections = localConfig.getCollections();
809
+ const remoteCollectionIds = new Set(collections.map((c: any) => c.$id));
810
+ const collectionsToRemove = localCollections.filter(
811
+ (c: any) => !remoteCollectionIds.has(c.$id),
812
+ );
810
813
 
811
- for (const collection of collections) {
812
- localConfig.addCollection(collection);
814
+ if (collectionsToRemove.length > 0) {
815
+ warn(
816
+ `The following collections exist locally but not remotely and will be removed: ${collectionsToRemove.map((c: any) => c.name || c.$id).join(", ")}`,
817
+ );
818
+ if ((await getConfirmation()) !== true) {
819
+ log("Pull cancelled.");
820
+ return;
821
+ }
813
822
  }
823
+
824
+ localConfig.set("databases", databases);
825
+ localConfig.set("collections", collections);
814
826
  };
815
827
 
816
828
  const pullTable = async (): Promise<void> => {
817
829
  const pullInstance = await createPullInstance();
818
830
  const { databases, tables } = await pullInstance.pullTables();
819
831
 
820
- for (const database of databases) {
821
- localConfig.addTablesDB(database);
822
- }
832
+ const localTables = localConfig.getTables();
833
+ const remoteTableIds = new Set(tables.map((t: any) => t.$id));
834
+ const tablesToRemove = localTables.filter(
835
+ (t: any) => !remoteTableIds.has(t.$id),
836
+ );
823
837
 
824
- for (const table of tables) {
825
- localConfig.addTable(table);
838
+ if (tablesToRemove.length > 0) {
839
+ warn(
840
+ `The following tables exist locally but not remotely and will be removed: ${tablesToRemove.map((t: any) => t.name || t.$id).join(", ")}`,
841
+ );
842
+ if ((await getConfirmation()) !== true) {
843
+ log("Pull cancelled.");
844
+ return;
845
+ }
826
846
  }
847
+
848
+ localConfig.set("tablesDB", databases);
849
+ localConfig.set("tables", tables);
827
850
  };
828
851
 
829
852
  const pullBucket = async (): Promise<void> => {
830
853
  const pullInstance = await createPullInstance();
831
854
  const buckets = await pullInstance.pullBuckets();
832
855
 
833
- for (const bucket of buckets) {
834
- localConfig.addBucket(bucket);
856
+ const localBuckets = localConfig.getBuckets();
857
+ const remoteBucketIds = new Set(buckets.map((b: any) => b.$id));
858
+ const bucketsToRemove = localBuckets.filter(
859
+ (b: any) => !remoteBucketIds.has(b.$id),
860
+ );
861
+
862
+ if (bucketsToRemove.length > 0) {
863
+ warn(
864
+ `The following buckets exist locally but not remotely and will be removed: ${bucketsToRemove.map((b: any) => b.name || b.$id).join(", ")}`,
865
+ );
866
+ if ((await getConfirmation()) !== true) {
867
+ log("Pull cancelled.");
868
+ return;
869
+ }
835
870
  }
871
+
872
+ localConfig.set("buckets", buckets);
836
873
  };
837
874
 
838
875
  const pullTeam = async (): Promise<void> => {
839
876
  const pullInstance = await createPullInstance();
840
877
  const teams = await pullInstance.pullTeams();
841
878
 
842
- for (const team of teams) {
843
- localConfig.addTeam(team);
879
+ const localTeams = localConfig.getTeams();
880
+ const remoteTeamIds = new Set(teams.map((t: any) => t.$id));
881
+ const teamsToRemove = localTeams.filter(
882
+ (t: any) => !remoteTeamIds.has(t.$id),
883
+ );
884
+
885
+ if (teamsToRemove.length > 0) {
886
+ warn(
887
+ `The following teams exist locally but not remotely and will be removed: ${teamsToRemove.map((t: any) => t.name || t.$id).join(", ")}`,
888
+ );
889
+ if ((await getConfirmation()) !== true) {
890
+ log("Pull cancelled.");
891
+ return;
892
+ }
844
893
  }
894
+
895
+ localConfig.set("teams", teams);
845
896
  };
846
897
 
847
898
  const pullMessagingTopic = async (): Promise<void> => {
848
899
  const pullInstance = await createPullInstance();
849
900
  const topics = await pullInstance.pullMessagingTopics();
850
901
 
851
- for (const topic of topics) {
852
- localConfig.addMessagingTopic(topic);
902
+ const localTopics = localConfig.getMessagingTopics();
903
+ const remoteTopicIds = new Set(topics.map((t: any) => t.$id));
904
+ const topicsToRemove = localTopics.filter(
905
+ (t: any) => !remoteTopicIds.has(t.$id),
906
+ );
907
+
908
+ if (topicsToRemove.length > 0) {
909
+ warn(
910
+ `The following topics exist locally but not remotely and will be removed: ${topicsToRemove.map((t: any) => t.name || t.$id).join(", ")}`,
911
+ );
912
+ if ((await getConfirmation()) !== true) {
913
+ log("Pull cancelled.");
914
+ return;
915
+ }
853
916
  }
917
+
918
+ localConfig.set("topics", topics);
854
919
  };
855
920
 
856
921
  /** Commander.js exports */
@@ -1,4 +1,5 @@
1
1
  import fs from "fs";
2
+ import path from "path";
2
3
  import { parse as parseDotenv } from "dotenv";
3
4
  import chalk from "chalk";
4
5
  import inquirer from "inquirer";
@@ -16,7 +17,8 @@ import {
16
17
  KeysCollection,
17
18
  KeysTable,
18
19
  } from "../config.js";
19
- import type { SettingsType, ConfigType } from "./config.js";
20
+ import { ConfigSchema, type SettingsType, type ConfigType } from "./config.js";
21
+ import { parseWithBetterErrors } from "./utils/error-formatter.js";
20
22
  import { createSettingsObject } from "../utils.js";
21
23
  import { Spinner, SPINNER_DOTS } from "../spinner.js";
22
24
  import { paginate } from "../paginate.js";
@@ -25,7 +27,9 @@ import {
25
27
  questionsPushBuckets,
26
28
  questionsPushTeams,
27
29
  questionsPushFunctions,
30
+ questionsPushFunctionsCode,
28
31
  questionsPushSites,
32
+ questionsPushSitesCode,
29
33
  questionsGetEntrypoint,
30
34
  questionsPushCollections,
31
35
  questionsPushTables,
@@ -414,15 +418,15 @@ export class Push {
414
418
  this.log("Applying auth security settings ...");
415
419
  await projectsService.updateAuthDuration({
416
420
  projectId,
417
- duration: settings.auth.security.duration,
421
+ duration: Number(settings.auth.security.duration),
418
422
  });
419
423
  await projectsService.updateAuthLimit({
420
424
  projectId,
421
- limit: settings.auth.security.limit,
425
+ limit: Number(settings.auth.security.limit),
422
426
  });
423
427
  await projectsService.updateAuthSessionsLimit({
424
428
  projectId,
425
- limit: settings.auth.security.sessionsLimit,
429
+ limit: Number(settings.auth.security.sessionsLimit),
426
430
  });
427
431
  await projectsService.updateAuthPasswordDictionary({
428
432
  projectId,
@@ -430,7 +434,7 @@ export class Push {
430
434
  });
431
435
  await projectsService.updateAuthPasswordHistory({
432
436
  projectId,
433
- limit: settings.auth.security.passwordHistory,
437
+ limit: Number(settings.auth.security.passwordHistory),
434
438
  });
435
439
  await projectsService.updatePersonalDataCheck({
436
440
  projectId,
@@ -807,6 +811,29 @@ export class Push {
807
811
  return;
808
812
  }
809
813
 
814
+ if (!func.path) {
815
+ errors.push(
816
+ new Error(`Function '${func.name}' has no path configured`),
817
+ );
818
+ updaterRow.fail({
819
+ errorMessage: `No path configured for function`,
820
+ });
821
+ return;
822
+ }
823
+
824
+ if (
825
+ !fs.existsSync(func.path) ||
826
+ fs.readdirSync(func.path).length === 0
827
+ ) {
828
+ errors.push(
829
+ new Error(`Deployment not found or empty at path: ${func.path}`),
830
+ );
831
+ updaterRow.fail({
832
+ errorMessage: `path not found or empty: ${path.relative(process.cwd(), path.resolve(func.path))}`,
833
+ });
834
+ return;
835
+ }
836
+
810
837
  try {
811
838
  updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_DOTS);
812
839
  const functionsServiceDeploy = await getFunctionsService(
@@ -838,7 +865,7 @@ export class Push {
838
865
  switch (e.code) {
839
866
  case "ENOENT":
840
867
  updaterRow.fail({
841
- errorMessage: "Not found in the current directory. Skipping...",
868
+ errorMessage: `Deployment not found at path: ${path.resolve(func.path)}`,
842
869
  });
843
870
  break;
844
871
  default:
@@ -1145,6 +1172,27 @@ export class Push {
1145
1172
  return;
1146
1173
  }
1147
1174
 
1175
+ if (!site.path) {
1176
+ errors.push(new Error(`Site '${site.name}' has no path configured`));
1177
+ updaterRow.fail({
1178
+ errorMessage: `No path configured for site`,
1179
+ });
1180
+ return;
1181
+ }
1182
+
1183
+ if (
1184
+ !fs.existsSync(site.path) ||
1185
+ fs.readdirSync(site.path).length === 0
1186
+ ) {
1187
+ errors.push(
1188
+ new Error(`Deployment not found or empty at path: ${site.path}`),
1189
+ );
1190
+ updaterRow.fail({
1191
+ errorMessage: `path not found or empty: ${path.relative(process.cwd(), path.resolve(site.path))}`,
1192
+ });
1193
+ return;
1194
+ }
1195
+
1148
1196
  try {
1149
1197
  updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_DOTS);
1150
1198
  const sitesServiceDeploy = await getSitesService(this.projectClient);
@@ -1174,7 +1222,7 @@ export class Push {
1174
1222
  switch (e.code) {
1175
1223
  case "ENOENT":
1176
1224
  updaterRow.fail({
1177
- errorMessage: "Not found in the current directory. Skipping...",
1225
+ errorMessage: `Deployment not found at path: ${path.resolve(site.path)}`,
1178
1226
  });
1179
1227
  break;
1180
1228
  default:
@@ -1593,13 +1641,54 @@ const pushResources = async ({
1593
1641
  if (cliConfig.all) {
1594
1642
  checkDeployConditions(localConfig);
1595
1643
 
1644
+ const functions = localConfig.getFunctions();
1645
+ let allowFunctionsCodePush: boolean | null =
1646
+ cliConfig.force === true ? true : null;
1647
+ if (functions.length > 0 && allowFunctionsCodePush === null) {
1648
+ const codeAnswer = await inquirer.prompt(questionsPushFunctionsCode);
1649
+ allowFunctionsCodePush = codeAnswer.override;
1650
+ }
1651
+
1652
+ const sites = localConfig.getSites();
1653
+ let allowSitesCodePush: boolean | null =
1654
+ cliConfig.force === true ? true : null;
1655
+ if (sites.length > 0 && allowSitesCodePush === null) {
1656
+ const codeAnswer = await inquirer.prompt(questionsPushSitesCode);
1657
+ allowSitesCodePush = codeAnswer.override;
1658
+ }
1659
+
1596
1660
  const pushInstance = await createPushInstance();
1597
- const config = localConfig.getProject() as ConfigType;
1661
+ const project = localConfig.getProject();
1662
+ const config: ConfigType = {
1663
+ projectId: project.projectId ?? "",
1664
+ projectName: project.projectName,
1665
+ settings: project.projectSettings,
1666
+ functions,
1667
+ sites,
1668
+ collections: localConfig.getCollections(),
1669
+ databases: localConfig.getDatabases(),
1670
+ tables: localConfig.getTables(),
1671
+ tablesDB: localConfig.getTablesDBs(),
1672
+ buckets: localConfig.getBuckets(),
1673
+ teams: localConfig.getTeams(),
1674
+ topics: localConfig.getMessagingTopics(),
1675
+ };
1676
+
1677
+ parseWithBetterErrors<ConfigType>(
1678
+ ConfigSchema,
1679
+ config,
1680
+ "Configuration validation failed",
1681
+ config,
1682
+ );
1598
1683
 
1599
1684
  await pushInstance.pushResources(config, {
1685
+ all: cliConfig.all,
1600
1686
  skipDeprecated,
1601
- functionOptions: { code: true, withVariables: false },
1602
- siteOptions: { code: true, withVariables: false },
1687
+ functionOptions: {
1688
+ code: allowFunctionsCodePush === true,
1689
+ withVariables: false,
1690
+ },
1691
+ siteOptions: { code: allowSitesCodePush === true, withVariables: false },
1603
1692
  });
1604
1693
  } else {
1605
1694
  const actions: Record<string, (options?: any) => Promise<void>> = {
@@ -1763,12 +1852,20 @@ const pushSite = async ({
1763
1852
  return;
1764
1853
  }
1765
1854
 
1855
+ let allowCodePush: boolean | null = cliConfig.force === true ? true : null;
1856
+ if (code !== false && allowCodePush === null) {
1857
+ const codeAnswer = await inquirer.prompt(questionsPushSitesCode);
1858
+ allowCodePush = codeAnswer.override;
1859
+ }
1860
+
1861
+ const shouldPushCode = code !== false && allowCodePush === true;
1862
+
1766
1863
  log("Pushing sites ...");
1767
1864
 
1768
1865
  const pushInstance = await createPushInstance();
1769
1866
  const result = await pushInstance.pushSites(sites, {
1770
1867
  async: asyncDeploy,
1771
- code,
1868
+ code: shouldPushCode,
1772
1869
  withVariables,
1773
1870
  });
1774
1871
 
@@ -1883,12 +1980,20 @@ const pushFunction = async ({
1883
1980
  return;
1884
1981
  }
1885
1982
 
1983
+ let allowCodePush: boolean | null = cliConfig.force === true ? true : null;
1984
+ if (code !== false && allowCodePush === null) {
1985
+ const codeAnswer = await inquirer.prompt(questionsPushFunctionsCode);
1986
+ allowCodePush = codeAnswer.override;
1987
+ }
1988
+
1989
+ const shouldPushCode = code !== false && allowCodePush === true;
1990
+
1886
1991
  log("Pushing functions ...");
1887
1992
 
1888
1993
  const pushInstance = await createPushInstance();
1889
1994
  const result = await pushInstance.pushFunctions(functions, {
1890
1995
  async: asyncDeploy,
1891
- code,
1996
+ code: shouldPushCode,
1892
1997
  withVariables,
1893
1998
  });
1894
1999
 
@@ -9,7 +9,7 @@ import * as fs from "fs";
9
9
  import * as path from "path";
10
10
  import { Db } from "./db.js";
11
11
 
12
- const JSONBig = JSONbig({ storeAsString: false });
12
+ const JSONBig = JSONbig({ useNativeBigInt: true });
13
13
 
14
14
  export class Schema {
15
15
  private pullCommand: Pull;
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { getDatabasesService } from "../../services.js";
3
3
  import { KeysAttributes } from "../../config.js";
4
- import { log, success, cliConfig, drawTable } from "../../parser.js";
4
+ import { log, success, error, cliConfig, drawTable } from "../../parser.js";
5
5
  import { Pools } from "./pools.js";
6
6
  import inquirer from "inquirer";
7
7
 
@@ -591,15 +591,21 @@ export class Attributes {
591
591
 
592
592
  if (changes.length > 0) {
593
593
  changedAttributes = changes.map((change) => change.attribute);
594
- await Promise.all(
595
- changedAttributes.map((changed) =>
596
- this.updateAttribute(
597
- collection["databaseId"],
598
- collection["$id"],
599
- changed,
594
+ try {
595
+ await Promise.all(
596
+ changedAttributes.map((changed) =>
597
+ this.updateAttribute(
598
+ collection["databaseId"],
599
+ collection["$id"],
600
+ changed,
601
+ ),
600
602
  ),
601
- ),
602
- );
603
+ );
604
+ } catch (err) {
605
+ error(
606
+ `Error updating attribute for ${collection["$id"]}: ${String(err)}`,
607
+ );
608
+ }
603
609
  }
604
610
 
605
611
  const deletingAttributes = deleting.map((change) => change.attribute);
@@ -44,7 +44,7 @@ export class Pools {
44
44
  }
45
45
 
46
46
  if (this.pollMaxDebounces === this.POLL_DEFAULT_VALUE) {
47
- let steps = Math.max(1, Math.ceil(total / this.STEP_SIZE));
47
+ let steps = Math.max(1, Math.ceil(Number(total) / this.STEP_SIZE));
48
48
  if (steps > 1 && iteration === 1) {
49
49
  this.pollMaxDebounces *= steps;
50
50
 
@@ -83,7 +83,7 @@ export class Pools {
83
83
  }
84
84
 
85
85
  if (this.pollMaxDebounces === this.POLL_DEFAULT_VALUE) {
86
- let steps = Math.max(1, Math.ceil(total / this.STEP_SIZE));
86
+ let steps = Math.max(1, Math.ceil(Number(total) / this.STEP_SIZE));
87
87
  if (steps > 1 && iteration === 1) {
88
88
  this.pollMaxDebounces *= steps;
89
89
 
package/lib/config.ts CHANGED
@@ -24,7 +24,7 @@ import type {
24
24
  import { createSettingsObject } from "./utils.js";
25
25
  import { SDK_TITLE_LOWER } from "./constants.js";
26
26
 
27
- const JSONBigInt = JSONbig({ storeAsString: false });
27
+ const JSONBigInt = JSONbig({ useNativeBigInt: true });
28
28
 
29
29
  const KeysVars = new Set(["key", "value"]);
30
30
  const KeysSite = new Set([
@@ -159,6 +159,41 @@ const KeyIndexesColumns = new Set([
159
159
  "orders",
160
160
  ]);
161
161
 
162
+ const CONFIG_KEY_ORDER = [
163
+ "projectId",
164
+ "projectName",
165
+ "endpoint",
166
+ "settings",
167
+ "functions",
168
+ "sites",
169
+ "databases",
170
+ "collections",
171
+ "tablesDB",
172
+ "tables",
173
+ "buckets",
174
+ "teams",
175
+ "topics",
176
+ "messages",
177
+ ];
178
+
179
+ function orderConfigKeys<T extends Record<string, any>>(data: T): T {
180
+ const ordered: Record<string, any> = {};
181
+
182
+ for (const key of CONFIG_KEY_ORDER) {
183
+ if (key in data) {
184
+ ordered[key] = data[key];
185
+ }
186
+ }
187
+
188
+ for (const key of Object.keys(data)) {
189
+ if (!(key in ordered)) {
190
+ ordered[key] = data[key];
191
+ }
192
+ }
193
+
194
+ return ordered as T;
195
+ }
196
+
162
197
  function whitelistKeys<T = any>(
163
198
  value: T,
164
199
  keys: Set<string>,
@@ -326,6 +361,17 @@ class Local extends Config<ConfigType> {
326
361
  this.configDirectoryPath = _path.dirname(absolutePath);
327
362
  }
328
363
 
364
+ write(): void {
365
+ const dir = _path.dirname(this.path);
366
+ if (!fs.existsSync(dir)) {
367
+ fs.mkdirSync(dir, { recursive: true });
368
+ }
369
+ const orderedData = orderConfigKeys(this.data);
370
+ fs.writeFileSync(this.path, JSONBigInt.stringify(orderedData, null, 4), {
371
+ mode: 0o600,
372
+ });
373
+ }
374
+
329
375
  static findConfigFile(filename: string): string | null {
330
376
  let currentPath = process.cwd();
331
377
 
package/lib/constants.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // SDK
2
2
  export const SDK_TITLE = 'Appwrite';
3
3
  export const SDK_TITLE_LOWER = 'appwrite';
4
- export const SDK_VERSION = '13.0.0-rc.4';
4
+ export const SDK_VERSION = '13.0.0';
5
5
  export const SDK_NAME = 'Command Line';
6
6
  export const SDK_PLATFORM = 'console';
7
7
  export const SDK_LANGUAGE = 'cli';
package/lib/questions.ts CHANGED
@@ -368,6 +368,14 @@ export const questionsPullFunctionsCode: Question[] = [
368
368
  },
369
369
  ];
370
370
 
371
+ export const questionsPushFunctionsCode: Question[] = [
372
+ {
373
+ type: "confirm",
374
+ name: "override",
375
+ message: "Do you want to create a deployment for your functions?",
376
+ },
377
+ ];
378
+
371
379
  export const questionsPullSites: Question[] = [
372
380
  {
373
381
  type: "checkbox",
@@ -403,6 +411,14 @@ export const questionsPullSitesCode: Question[] = [
403
411
  },
404
412
  ];
405
413
 
414
+ export const questionsPushSitesCode: Question[] = [
415
+ {
416
+ type: "confirm",
417
+ name: "override",
418
+ message: "Do you want to create a deployment for your sites?",
419
+ },
420
+ ];
421
+
406
422
  export const questionsCreateFunction: Question[] = [
407
423
  {
408
424
  type: "input",
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "homepage": "https://appwrite.io/support",
5
5
  "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API",
6
- "version": "13.0.0-rc.4",
6
+ "version": "13.0.0",
7
7
  "license": "BSD-3-Clause",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/ScoopInstaller/Scoop/master/schema.json",
3
- "version": "13.0.0-rc.4",
3
+ "version": "13.0.0",
4
4
  "description": "The Appwrite CLI is a command-line application that allows you to interact with Appwrite and perform server-side tasks using your terminal.",
5
5
  "homepage": "https://github.com/appwrite/sdk-for-cli",
6
6
  "license": "BSD-3-Clause",
7
7
  "architecture": {
8
8
  "64bit": {
9
- "url": "https://github.com/appwrite/sdk-for-cli/releases/download/13.0.0-rc.4/appwrite-cli-win-x64.exe",
9
+ "url": "https://github.com/appwrite/sdk-for-cli/releases/download/13.0.0/appwrite-cli-win-x64.exe",
10
10
  "bin": [
11
11
  [
12
12
  "appwrite-cli-win-x64.exe",
@@ -15,7 +15,7 @@
15
15
  ]
16
16
  },
17
17
  "arm64": {
18
- "url": "https://github.com/appwrite/sdk-for-cli/releases/download/13.0.0-rc.4/appwrite-cli-win-arm64.exe",
18
+ "url": "https://github.com/appwrite/sdk-for-cli/releases/download/13.0.0/appwrite-cli-win-arm64.exe",
19
19
  "bin": [
20
20
  [
21
21
  "appwrite-cli-win-arm64.exe",