appwrite-utils-cli 0.9.993 → 0.9.994

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 (46) hide show
  1. package/README.md +1 -2
  2. package/dist/collections/methods.js +6 -4
  3. package/dist/functions/deployments.d.ts +4 -0
  4. package/dist/functions/deployments.js +114 -0
  5. package/dist/functions/methods.d.ts +13 -8
  6. package/dist/functions/methods.js +74 -20
  7. package/dist/functions/templates/count-docs-in-collection/src/main.d.ts +21 -0
  8. package/dist/functions/templates/count-docs-in-collection/src/main.js +114 -0
  9. package/dist/functions/templates/count-docs-in-collection/src/request.d.ts +15 -0
  10. package/dist/functions/templates/count-docs-in-collection/src/request.js +6 -0
  11. package/dist/functions/templates/typescript-node/src/index.d.ts +11 -0
  12. package/dist/functions/templates/typescript-node/src/index.js +11 -0
  13. package/dist/interactiveCLI.d.ts +6 -0
  14. package/dist/interactiveCLI.js +437 -32
  15. package/dist/main.js +0 -0
  16. package/dist/migrations/appwriteToX.js +32 -1
  17. package/dist/migrations/schemaStrings.d.ts +1 -0
  18. package/dist/migrations/schemaStrings.js +74 -2
  19. package/dist/migrations/transfer.js +112 -123
  20. package/dist/utils/loadConfigs.d.ts +1 -0
  21. package/dist/utils/loadConfigs.js +19 -0
  22. package/dist/utils/schemaStrings.js +27 -0
  23. package/dist/utilsController.d.ts +5 -1
  24. package/dist/utilsController.js +54 -2
  25. package/package.json +57 -55
  26. package/src/collections/methods.ts +29 -10
  27. package/src/functions/deployments.ts +190 -0
  28. package/src/functions/methods.ts +295 -235
  29. package/src/functions/templates/count-docs-in-collection/README.md +54 -0
  30. package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
  31. package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
  32. package/src/functions/templates/poetry/README.md +30 -0
  33. package/src/functions/templates/poetry/pyproject.toml +16 -0
  34. package/src/functions/templates/poetry/src/__init__.py +0 -0
  35. package/src/functions/templates/poetry/src/index.py +16 -0
  36. package/src/functions/templates/typescript-node/README.md +32 -0
  37. package/src/functions/templates/typescript-node/src/index.ts +23 -0
  38. package/src/interactiveCLI.ts +606 -47
  39. package/src/migrations/appwriteToX.ts +44 -1
  40. package/src/migrations/schemaStrings.ts +83 -2
  41. package/src/migrations/transfer.ts +280 -207
  42. package/src/setupController.ts +41 -41
  43. package/src/utils/loadConfigs.ts +24 -0
  44. package/src/utils/schemaStrings.ts +27 -0
  45. package/src/utilsController.ts +63 -3
  46. package/tsconfig.json +8 -1
package/README.md CHANGED
@@ -86,8 +86,6 @@ Available options:
86
86
  - `--functionId`: Function ID to update
87
87
  - `--specification`: New function specification (one of: s-0.5vcpu-512mb, s-1vcpu-1gb, s-2vcpu-2gb, s-2vcpu-4gb, s-4vcpu-4gb, s-4vcpu-8gb, s-8vcpu-4gb, s-8vcpu-8gb)
88
88
 
89
-
90
-
91
89
  ## Examples
92
90
 
93
91
  ### Transfer Databases
@@ -149,6 +147,7 @@ This updated CLI ensures that developers have robust tools at their fingertips t
149
147
 
150
148
  ## Changelog
151
149
 
150
+ - 0.9.994: Added function deployment management, in BETA, and fixed document transfer between databases
152
151
  - 0.9.993: Fixed `updateFunctionSpecifications` resetting functions to default with undefined values (oops)
153
152
  - 0.9.992: Added `updateFunctionSpecifications` which lists functions and specifications to allow you to update your functions max CPU and RAM usage per-function
154
153
  - 0.9.990: Fixed `transferFilesLocalToLocal` and `remote` if a document exists with that `$id`, also fixed wipe `"all"` option also wiping the associated buckets
@@ -3,7 +3,7 @@ import { nameToIdMapping, processQueue } from "../migrations/queue.js";
3
3
  import { createUpdateCollectionAttributes } from "./attributes.js";
4
4
  import { createOrUpdateIndexes } from "./indexes.js";
5
5
  import _ from "lodash";
6
- import { SchemaGenerator } from "../utils/schemaStrings.js";
6
+ import { SchemaGenerator } from "../migrations/schemaStrings.js";
7
7
  import { delay, tryAwaitWithRetry } from "../utils/helperFunctions.js";
8
8
  export const documentExists = async (db, dbId, targetCollectionId, toCreateObject) => {
9
9
  // Had to do this because kept running into issues with type checking arrays so, sorry 40ms
@@ -93,7 +93,7 @@ async function wipeDocumentsFromCollection(database, databaseId, collectionId) {
93
93
  const docsResponse = await database.listDocuments(databaseId, collectionId, [Query.limit(1000)]);
94
94
  documents = documents.concat(docsResponse.documents);
95
95
  }
96
- const batchDeletePromises = documents.map(doc => database.deleteDocument(databaseId, collectionId, doc.$id));
96
+ const batchDeletePromises = documents.map((doc) => database.deleteDocument(databaseId, collectionId, doc.$id));
97
97
  const maxStackSize = 100;
98
98
  for (let i = 0; i < batchDeletePromises.length; i += maxStackSize) {
99
99
  await Promise.all(batchDeletePromises.slice(i, i + maxStackSize));
@@ -117,7 +117,9 @@ export const wipeDatabase = async (database, databaseId) => {
117
117
  return collectionsDeleted;
118
118
  };
119
119
  export const wipeCollection = async (database, databaseId, collectionId) => {
120
- const collections = await database.listCollections(databaseId, [Query.equal("$id", collectionId)]);
120
+ const collections = await database.listCollections(databaseId, [
121
+ Query.equal("$id", collectionId),
122
+ ]);
121
123
  if (collections.total === 0) {
122
124
  console.log(`Collection ${collectionId} not found`);
123
125
  return;
@@ -141,7 +143,7 @@ export const createOrUpdateCollections = async (database, databaseId, config, de
141
143
  const permissions = [];
142
144
  if (collection.$permissions && collection.$permissions.length > 0) {
143
145
  for (const permission of collection.$permissions) {
144
- if (typeof permission === 'string') {
146
+ if (typeof permission === "string") {
145
147
  permissions.push(permission);
146
148
  }
147
149
  else {
@@ -0,0 +1,4 @@
1
+ import { Client } from "node-appwrite";
2
+ import { type AppwriteFunction } from "appwrite-utils";
3
+ export declare const deployFunction: (client: Client, functionId: string, codePath: string, activate?: boolean, entrypoint?: string, commands?: string) => Promise<import("node-appwrite").Models.Deployment>;
4
+ export declare const deployLocalFunction: (client: Client, functionName: string, functionConfig: AppwriteFunction, functionPath?: string) => Promise<import("node-appwrite").Models.Deployment>;
@@ -0,0 +1,114 @@
1
+ import { Client, Functions, Runtime } from "node-appwrite";
2
+ import { InputFile } from "node-appwrite/file";
3
+ import { create as createTarball } from "tar";
4
+ import { join } from "node:path";
5
+ import fs from "node:fs";
6
+ import {} from "appwrite-utils";
7
+ import chalk from "chalk";
8
+ import cliProgress from "cli-progress";
9
+ import { execSync } from "child_process";
10
+ import { createFunction, getFunction, updateFunctionSpecifications, } from "./methods.js";
11
+ const findFunctionDirectory = (basePath, functionName) => {
12
+ const normalizedName = functionName.toLowerCase().replace(/\s+/g, "-");
13
+ const dirs = fs.readdirSync(basePath, { withFileTypes: true });
14
+ for (const dir of dirs) {
15
+ if (dir.isDirectory()) {
16
+ const fullPath = join(basePath, dir.name);
17
+ if (dir.name.toLowerCase() === normalizedName) {
18
+ return fullPath;
19
+ }
20
+ const nestedResult = findFunctionDirectory(fullPath, functionName);
21
+ if (nestedResult) {
22
+ return nestedResult;
23
+ }
24
+ }
25
+ }
26
+ return undefined;
27
+ };
28
+ export const deployFunction = async (client, functionId, codePath, activate = true, entrypoint = "index.js", commands = "npm install") => {
29
+ const functions = new Functions(client);
30
+ console.log(chalk.blue("📦 Preparing function deployment..."));
31
+ const progressBar = new cliProgress.SingleBar({
32
+ format: "Uploading |" +
33
+ chalk.cyan("{bar}") +
34
+ "| {percentage}% | {value}/{total} Chunks",
35
+ barCompleteChar: "█",
36
+ barIncompleteChar: "░",
37
+ hideCursor: true,
38
+ });
39
+ const tarPath = join(process.cwd(), `function-${functionId}.tar.gz`);
40
+ await createTarball({
41
+ gzip: true,
42
+ file: tarPath,
43
+ cwd: codePath,
44
+ }, ["."]);
45
+ const fileBuffer = await fs.promises.readFile(tarPath);
46
+ const fileObject = InputFile.fromBuffer(new Uint8Array(fileBuffer), `function-${functionId}.tar.gz`);
47
+ try {
48
+ console.log(chalk.blue("🚀 Creating deployment..."));
49
+ const functionResponse = await functions.createDeployment(functionId, fileObject, activate, entrypoint, commands, (progress) => {
50
+ const chunks = progress.chunksUploaded;
51
+ const total = progress.chunksTotal;
52
+ if (chunks && total) {
53
+ if (chunks === 0) {
54
+ progressBar.start(total, 0);
55
+ }
56
+ else if (chunks === total) {
57
+ progressBar.update(total);
58
+ progressBar.stop();
59
+ console.log(chalk.green("✅ Upload complete!"));
60
+ }
61
+ else {
62
+ progressBar.update(chunks);
63
+ }
64
+ }
65
+ });
66
+ await fs.promises.unlink(tarPath);
67
+ console.log(chalk.green("✨ Function deployed successfully!"));
68
+ return functionResponse;
69
+ }
70
+ catch (error) {
71
+ progressBar.stop();
72
+ console.error(chalk.red("❌ Deployment failed:"), error);
73
+ throw error;
74
+ }
75
+ };
76
+ export const deployLocalFunction = async (client, functionName, functionConfig, functionPath) => {
77
+ let functionExists = true;
78
+ try {
79
+ await getFunction(client, functionConfig.$id);
80
+ }
81
+ catch (error) {
82
+ functionExists = false;
83
+ }
84
+ const resolvedPath = functionPath ||
85
+ functionConfig.dirPath ||
86
+ findFunctionDirectory(process.cwd(), functionName) ||
87
+ join(process.cwd(), "functions", functionName.toLowerCase().replace(/\s+/g, "-"));
88
+ if (functionConfig.predeployCommands?.length) {
89
+ console.log(chalk.blue("Executing predeploy commands..."));
90
+ for (const command of functionConfig.predeployCommands) {
91
+ try {
92
+ console.log(chalk.gray(`Executing: ${command}`));
93
+ execSync(command, {
94
+ cwd: resolvedPath,
95
+ stdio: "inherit",
96
+ });
97
+ }
98
+ catch (error) {
99
+ console.error(chalk.red(`Failed to execute predeploy command: ${command}`));
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ if (!functionExists) {
105
+ await createFunction(client, functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute, functionConfig.events, functionConfig.schedule, functionConfig.timeout, functionConfig.enabled, functionConfig.logging, functionConfig.entrypoint, functionConfig.commands);
106
+ }
107
+ if (functionConfig.specification) {
108
+ await updateFunctionSpecifications(client, functionConfig.$id, functionConfig.specification);
109
+ }
110
+ const deployPath = functionConfig.deployDir
111
+ ? join(resolvedPath, functionConfig.deployDir)
112
+ : resolvedPath;
113
+ return deployFunction(client, functionConfig.$id, deployPath, true, functionConfig.entrypoint, functionConfig.commands);
114
+ };
@@ -1,10 +1,15 @@
1
- import { Client, Runtime, type Models } from "node-appwrite";
1
+ import { Client, Runtime } from "node-appwrite";
2
2
  import { type Specification } from "appwrite-utils";
3
- export declare const listFunctions: (client: Client, queries?: string[], search?: string) => Promise<Models.FunctionList>;
4
- export declare const getFunction: (client: Client, functionId: string) => Promise<Models.Function>;
3
+ export declare const listFunctions: (client: Client, queries?: string[], search?: string) => Promise<import("node-appwrite").Models.FunctionList>;
4
+ export declare const getFunction: (client: Client, functionId: string) => Promise<import("node-appwrite").Models.Function>;
5
+ export declare const downloadLatestFunctionDeployment: (client: Client, functionId: string, basePath?: string) => Promise<{
6
+ path: string;
7
+ function: import("node-appwrite").Models.Function;
8
+ deployment: import("node-appwrite").Models.Deployment;
9
+ }>;
5
10
  export declare const deleteFunction: (client: Client, functionId: string) => Promise<{}>;
6
- export declare const createFunction: (client: Client, functionId: string, name: string, runtime: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, templateRepository?: string, templateOwner?: string, templateRootDirectory?: string, templateVersion?: string, specification?: string) => Promise<Models.Function>;
7
- export declare const updateFunctionSpecifications: (client: Client, functionId: string, specification: Specification) => Promise<Models.Function | undefined>;
8
- export declare const listSpecifications: (client: Client) => Promise<Models.SpecificationList>;
9
- export declare const updateFunction: (client: Client, functionId: string, name: string, runtime?: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, specification?: Specification) => Promise<Models.Function>;
10
- export declare const deployFunction: (client: Client, functionId: string, codePath: string, activate?: boolean, entrypoint?: string, commands?: string) => Promise<Models.Deployment>;
11
+ export declare const createFunction: (client: Client, functionId: string, name: string, runtime: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, templateRepository?: string, templateOwner?: string, templateRootDirectory?: string, templateVersion?: string, specification?: string) => Promise<import("node-appwrite").Models.Function>;
12
+ export declare const updateFunctionSpecifications: (client: Client, functionId: string, specification: Specification) => Promise<import("node-appwrite").Models.Function | undefined>;
13
+ export declare const listSpecifications: (client: Client) => Promise<import("node-appwrite").Models.SpecificationList>;
14
+ export declare const updateFunction: (client: Client, functionId: string, name: string, runtime?: Runtime, execute?: string[], events?: string[], schedule?: string, timeout?: number, enabled?: boolean, logging?: boolean, entrypoint?: string, commands?: string, scopes?: string[], installationId?: string, providerRepositoryId?: string, providerBranch?: string, providerSilentMode?: boolean, providerRootDirectory?: string, specification?: Specification) => Promise<import("node-appwrite").Models.Function>;
15
+ export declare const createFunctionTemplate: (templateType: "typescript-node" | "poetry" | "count-docs-in-collection", functionName: string, basePath?: string) => Promise<string>;
@@ -1,10 +1,9 @@
1
1
  import { AppwriteException, Client, Functions, Query, Runtime, } from "node-appwrite";
2
- import { InputFile } from "node-appwrite/file";
3
- import { create as createTarball } from "tar";
4
2
  import { join } from "node:path";
5
3
  import fs from "node:fs";
6
4
  import {} from "appwrite-utils";
7
5
  import chalk from "chalk";
6
+ import { extract as extractTar } from "tar";
8
7
  export const listFunctions = async (client, queries, search) => {
9
8
  const functions = new Functions(client);
10
9
  const functionsList = await functions.list(queries, search);
@@ -15,6 +14,42 @@ export const getFunction = async (client, functionId) => {
15
14
  const functionResponse = await functions.get(functionId);
16
15
  return functionResponse;
17
16
  };
17
+ export const downloadLatestFunctionDeployment = async (client, functionId, basePath = process.cwd()) => {
18
+ const functions = new Functions(client);
19
+ const functionInfo = await getFunction(client, functionId);
20
+ const functionDeployments = await functions.listDeployments(functionId, [
21
+ Query.orderDesc("$createdAt"),
22
+ ]);
23
+ if (functionDeployments.deployments.length === 0) {
24
+ throw new Error("No deployments found for function");
25
+ }
26
+ const latestDeployment = functionDeployments.deployments[0];
27
+ const deploymentData = await functions.getDeploymentDownload(functionId, latestDeployment.$id);
28
+ // Create function directory using provided basePath
29
+ const functionDir = join(basePath, functionInfo.name.toLowerCase().replace(/\s+/g, "-"));
30
+ await fs.promises.mkdir(functionDir, { recursive: true });
31
+ // Create temporary file for tar extraction
32
+ const tarPath = join(functionDir, "temp.tar.gz");
33
+ const uint8Array = new Uint8Array(deploymentData);
34
+ await fs.promises.writeFile(tarPath, uint8Array);
35
+ try {
36
+ // Extract tar file
37
+ extractTar({
38
+ C: functionDir,
39
+ file: tarPath,
40
+ sync: true,
41
+ });
42
+ return {
43
+ path: functionDir,
44
+ function: functionInfo,
45
+ deployment: latestDeployment,
46
+ };
47
+ }
48
+ finally {
49
+ // Clean up tar file
50
+ await fs.promises.unlink(tarPath).catch(() => { });
51
+ }
52
+ };
18
53
  export const deleteFunction = async (client, functionId) => {
19
54
  const functions = new Functions(client);
20
55
  const functionResponse = await functions.delete(functionId);
@@ -58,22 +93,41 @@ export const updateFunction = async (client, functionId, name, runtime, execute,
58
93
  const functionResponse = await functions.update(functionId, name, runtime, execute, events, schedule, timeout, enabled, logging, entrypoint, commands, scopes, installationId, providerRepositoryId, providerBranch, providerSilentMode, providerRootDirectory, specification);
59
94
  return functionResponse;
60
95
  };
61
- export const deployFunction = async (client, functionId, codePath, activate = true, entrypoint = "index.js", commands = "npm install") => {
62
- const functions = new Functions(client);
63
- // Create temporary tar.gz file
64
- const tarPath = join(process.cwd(), `function-${functionId}.tar.gz`);
65
- await createTarball({
66
- gzip: true,
67
- file: tarPath,
68
- cwd: codePath,
69
- }, ["."] // Include all files from codePath
70
- );
71
- // Read the tar.gz file and create a File object
72
- const fileBuffer = await fs.promises.readFile(tarPath);
73
- const fileObject = InputFile.fromBuffer(new Uint8Array(fileBuffer), `function-${functionId}.tar.gz`);
74
- // Create deployment with the File object
75
- const functionResponse = await functions.createDeployment(functionId, fileObject, activate, entrypoint, commands);
76
- // Clean up the temporary tar file
77
- await fs.promises.unlink(tarPath);
78
- return functionResponse;
96
+ export const createFunctionTemplate = async (templateType, functionName, basePath = "./functions") => {
97
+ const functionPath = join(basePath, functionName);
98
+ const templatesPath = join(__dirname, "templates", templateType);
99
+ // Create function directory
100
+ await fs.promises.mkdir(functionPath, { recursive: true });
101
+ // Copy template files recursively
102
+ const copyTemplateFiles = async (sourcePath, targetPath) => {
103
+ const entries = await fs.promises.readdir(sourcePath, {
104
+ withFileTypes: true,
105
+ });
106
+ for (const entry of entries) {
107
+ const srcPath = join(sourcePath, entry.name);
108
+ const destPath = join(targetPath, entry.name);
109
+ if (entry.isDirectory()) {
110
+ await fs.promises.mkdir(destPath, { recursive: true });
111
+ await copyTemplateFiles(srcPath, destPath);
112
+ }
113
+ else {
114
+ let content = await fs.promises.readFile(srcPath, "utf-8");
115
+ // Replace template variables
116
+ content = content
117
+ .replace(/\{\{functionName\}\}/g, functionName)
118
+ .replace(/\{\{databaseId\}\}/g, "{{databaseId}}")
119
+ .replace(/\{\{collectionId\}\}/g, "{{collectionId}}");
120
+ await fs.promises.writeFile(destPath, content);
121
+ }
122
+ }
123
+ };
124
+ try {
125
+ await copyTemplateFiles(templatesPath, functionPath);
126
+ console.log(chalk.green(`✨ Created ${templateType} function template at ${functionPath}`));
127
+ }
128
+ catch (error) {
129
+ console.error(chalk.red(`Failed to create function template: ${error}`));
130
+ throw error;
131
+ }
132
+ return functionPath;
79
133
  };
@@ -0,0 +1,21 @@
1
+ import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
2
+ /**
3
+ * Main function to handle document counting requests.
4
+ * @param {Object} params - The function parameters.
5
+ * @param {Object} params.req - The request object.
6
+ * @param {Object} params.res - The response object.
7
+ * @param {Function} params.log - Logging function.
8
+ * @param {Function} params.error - Error logging function.
9
+ * @returns {Promise<Object>} JSON response with count or error message.
10
+ */
11
+ declare const _default: ({ req, res, log, error }: {
12
+ req: AppwriteRequest;
13
+ res: AppwriteResponse;
14
+ log: (message: string) => void;
15
+ error: (message: string) => void;
16
+ }) => Promise<{
17
+ body: Uint8Array;
18
+ statusCode: number;
19
+ headers: Record<string, string>;
20
+ }>;
21
+ export default _default;
@@ -0,0 +1,114 @@
1
+ import { Client, Databases, Query } from "node-appwrite";
2
+ import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
3
+ import { requestSchema } from "./request.js";
4
+ /**
5
+ * Main function to handle document counting requests.
6
+ * @param {Object} params - The function parameters.
7
+ * @param {Object} params.req - The request object.
8
+ * @param {Object} params.res - The response object.
9
+ * @param {Function} params.log - Logging function.
10
+ * @param {Function} params.error - Error logging function.
11
+ * @returns {Promise<Object>} JSON response with count or error message.
12
+ */
13
+ export default async ({ req, res, log, error }) => {
14
+ // Initialize Appwrite client
15
+ const client = new Client()
16
+ .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"])
17
+ .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"])
18
+ .setKey(req.headers["x-appwrite-key"] || "");
19
+ const databases = new Databases(client);
20
+ try {
21
+ if (req.method === "POST") {
22
+ // Parse request body
23
+ const body = requestSchema.safeParse(typeof req.body === "string" ? JSON.parse(req.body) : req.body);
24
+ if (!body.success) {
25
+ return res.json({ success: false, error: body.error }, 400);
26
+ }
27
+ const { databaseId, collectionId, queries = [] } = body.data;
28
+ log(`Queries: ${JSON.stringify(queries)}`);
29
+ // Count documents in the specified collection
30
+ const count = await countAllDocuments(log, databases, databaseId, collectionId, queries);
31
+ // Return successful response with document count
32
+ return res.json({
33
+ success: true,
34
+ count: count,
35
+ });
36
+ }
37
+ else {
38
+ // Return error for non-POST requests
39
+ return res.json({ success: false, error: "Method not allowed" }, 405);
40
+ }
41
+ }
42
+ catch (err) {
43
+ // Log and return any errors
44
+ error(`Error processing request: ${err}`);
45
+ return res.json({ success: false, error: err.message }, 500);
46
+ }
47
+ };
48
+ /**
49
+ * Counts all documents in a collection, handling large collections efficiently.
50
+ * @param {Function} log - Logging function.
51
+ * @param {Databases} databases - Appwrite Databases instance.
52
+ * @param {string} databaseId - ID of the database.
53
+ * @param {string} collectionId - ID of the collection.
54
+ * @param {string[]} queries - Array of query strings to filter documents.
55
+ * @param {number} batchSize - Size of batches for processing (default: 1000).
56
+ * @returns {Promise<number>} Total count of documents.
57
+ */
58
+ async function countAllDocuments(log, databases, databaseId, collectionId, queries = [], batchSize = 1000) {
59
+ // Filter out limit and offset queries
60
+ const initialQueries = queries.filter((q) => !(q.includes("limit") || q.includes("offset")));
61
+ // Initial query to check if total is less than 5000
62
+ const initialResponse = await databases.listDocuments(databaseId, collectionId, [...initialQueries, Query.limit(1)]);
63
+ if (initialResponse.total < 5000) {
64
+ log(`Total documents (from initial response): ${initialResponse.total}`);
65
+ return initialResponse.total;
66
+ }
67
+ // If total is 5000 or more, we need to count manually
68
+ let bound = 5000;
69
+ // Exponential search to find an upper bound
70
+ while (true) {
71
+ log(`Querying for offset ${bound}`);
72
+ try {
73
+ const response = await databases.listDocuments(databaseId, collectionId, [
74
+ ...initialQueries,
75
+ Query.limit(1),
76
+ Query.offset(bound),
77
+ ]);
78
+ if (response.documents.length === 0) {
79
+ break;
80
+ }
81
+ bound *= 2;
82
+ }
83
+ catch (error) {
84
+ break;
85
+ }
86
+ }
87
+ // Binary search to find the exact count
88
+ let low = Math.floor(bound / 2);
89
+ let high = bound;
90
+ let lastValidCount = low;
91
+ while (low <= high) {
92
+ const mid = Math.floor((low + high) / 2);
93
+ log(`Binary search: Querying for offset ${mid}`);
94
+ try {
95
+ const response = await databases.listDocuments(databaseId, collectionId, [
96
+ ...initialQueries,
97
+ Query.limit(1),
98
+ Query.offset(mid),
99
+ ]);
100
+ if (response.documents.length > 0) {
101
+ lastValidCount = mid + 1; // +1 because offset is 0-based
102
+ low = mid + 1;
103
+ }
104
+ else {
105
+ high = mid - 1;
106
+ }
107
+ }
108
+ catch (error) {
109
+ high = mid - 1;
110
+ }
111
+ }
112
+ log(`Total documents: ${lastValidCount}`);
113
+ return lastValidCount;
114
+ }
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ export declare const requestSchema: z.ZodObject<{
3
+ databaseId: z.ZodString;
4
+ collectionId: z.ZodString;
5
+ queries: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ collectionId: string;
8
+ databaseId: string;
9
+ queries?: string[] | undefined;
10
+ }, {
11
+ collectionId: string;
12
+ databaseId: string;
13
+ queries?: string[] | undefined;
14
+ }>;
15
+ export type Request = z.infer<typeof requestSchema>;
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ export const requestSchema = z.object({
3
+ databaseId: z.string(),
4
+ collectionId: z.string(),
5
+ queries: z.array(z.string()).optional(),
6
+ });
@@ -0,0 +1,11 @@
1
+ import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
2
+ export default function ({ req, res, log, error, }: {
3
+ req: AppwriteRequest;
4
+ res: AppwriteResponse;
5
+ log: (message: string) => void;
6
+ error: (message: string) => void;
7
+ }): Promise<{
8
+ body: Uint8Array;
9
+ statusCode: number;
10
+ headers: Record<string, string>;
11
+ }>;
@@ -0,0 +1,11 @@
1
+ import { Client } from "node-appwrite";
2
+ import { AppwriteRequest, AppwriteResponse } from "appwrite-utils";
3
+ export default async function ({ req, res, log, error, }) {
4
+ const client = new Client()
5
+ .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"])
6
+ .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"])
7
+ .setKey(req.headers["x-appwrite-key"] || "");
8
+ return res.json({
9
+ message: "Hello from TypeScript function!",
10
+ });
11
+ }
@@ -6,6 +6,12 @@ export declare class InteractiveCLI {
6
6
  private initControllerIfNeeded;
7
7
  private selectDatabases;
8
8
  private selectCollections;
9
+ private createFunction;
10
+ private findFunctionInSubdirectories;
11
+ private deployFunction;
12
+ private deleteFunction;
13
+ private selectFunctions;
14
+ private getLocalFunctions;
9
15
  private selectBuckets;
10
16
  private createCollectionConfig;
11
17
  private configureBuckets;