appwrite-cli 13.0.0-rc.5 → 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.
@@ -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 */
@@ -17,7 +17,8 @@ import {
17
17
  KeysCollection,
18
18
  KeysTable,
19
19
  } from "../config.js";
20
- 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";
21
22
  import { createSettingsObject } from "../utils.js";
22
23
  import { Spinner, SPINNER_DOTS } from "../spinner.js";
23
24
  import { paginate } from "../paginate.js";
@@ -811,7 +812,9 @@ export class Push {
811
812
  }
812
813
 
813
814
  if (!func.path) {
814
- errors.push(new Error(`Function '${func.name}' has no path configured`));
815
+ errors.push(
816
+ new Error(`Function '${func.name}' has no path configured`),
817
+ );
815
818
  updaterRow.fail({
816
819
  errorMessage: `No path configured for function`,
817
820
  });
@@ -1671,6 +1674,13 @@ const pushResources = async ({
1671
1674
  topics: localConfig.getMessagingTopics(),
1672
1675
  };
1673
1676
 
1677
+ parseWithBetterErrors<ConfigType>(
1678
+ ConfigSchema,
1679
+ config,
1680
+ "Configuration validation failed",
1681
+ config,
1682
+ );
1683
+
1674
1684
  await pushInstance.pushResources(config, {
1675
1685
  all: cliConfig.all,
1676
1686
  skipDeprecated,
package/lib/config.ts CHANGED
@@ -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.5';
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/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.5",
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.5",
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.5/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.5/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",