@njdamstra/appwrite-utils-cli 1.8.9
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 +19 -0
- package/README.md +1133 -0
- package/dist/adapters/AdapterFactory.d.ts +94 -0
- package/dist/adapters/AdapterFactory.js +405 -0
- package/dist/adapters/DatabaseAdapter.d.ts +233 -0
- package/dist/adapters/DatabaseAdapter.js +50 -0
- package/dist/adapters/LegacyAdapter.d.ts +50 -0
- package/dist/adapters/LegacyAdapter.js +612 -0
- package/dist/adapters/TablesDBAdapter.d.ts +45 -0
- package/dist/adapters/TablesDBAdapter.js +571 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +166 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +554 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +330 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +169 -0
- package/dist/cli/commands/storageCommands.d.ts +5 -0
- package/dist/cli/commands/storageCommands.js +143 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +13 -0
- package/dist/collections/attributes.js +1364 -0
- package/dist/collections/indexes.d.ts +12 -0
- package/dist/collections/indexes.js +217 -0
- package/dist/collections/methods.d.ts +19 -0
- package/dist/collections/methods.js +682 -0
- package/dist/collections/tableOperations.d.ts +86 -0
- package/dist/collections/tableOperations.js +434 -0
- package/dist/collections/transferOperations.d.ts +8 -0
- package/dist/collections/transferOperations.js +412 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +233 -0
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +129 -0
- package/dist/config/services/ConfigLoaderService.js +540 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +308 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/config/yamlConfig.d.ts +722 -0
- package/dist/config/yamlConfig.js +702 -0
- package/dist/databases/methods.d.ts +6 -0
- package/dist/databases/methods.js +35 -0
- package/dist/databases/setup.d.ts +5 -0
- package/dist/databases/setup.js +45 -0
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +272 -0
- package/dist/functions/deployments.d.ts +4 -0
- package/dist/functions/deployments.js +146 -0
- package/dist/functions/fnConfigDiscovery.d.ts +3 -0
- package/dist/functions/fnConfigDiscovery.js +108 -0
- package/dist/functions/methods.d.ts +16 -0
- package/dist/functions/methods.js +162 -0
- package/dist/functions/pathResolution.d.ts +37 -0
- package/dist/functions/pathResolution.js +185 -0
- package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
- package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/dist/functions/templates/hono-typescript/README.md +286 -0
- package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
- package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
- package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
- package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/dist/functions/templates/typescript-node/README.md +32 -0
- package/dist/functions/templates/typescript-node/src/context.ts +103 -0
- package/dist/functions/templates/typescript-node/src/index.ts +29 -0
- package/dist/functions/templates/uv/README.md +31 -0
- package/dist/functions/templates/uv/pyproject.toml +30 -0
- package/dist/functions/templates/uv/src/__init__.py +0 -0
- package/dist/functions/templates/uv/src/context.py +125 -0
- package/dist/functions/templates/uv/src/index.py +46 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +57 -0
- package/dist/interactiveCLI.d.ts +31 -0
- package/dist/interactiveCLI.js +898 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +1172 -0
- package/dist/migrations/afterImportActions.d.ts +17 -0
- package/dist/migrations/afterImportActions.js +306 -0
- package/dist/migrations/appwriteToX.d.ts +211 -0
- package/dist/migrations/appwriteToX.js +491 -0
- package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
- package/dist/migrations/comprehensiveTransfer.js +1317 -0
- package/dist/migrations/dataLoader.d.ts +753 -0
- package/dist/migrations/dataLoader.js +1250 -0
- package/dist/migrations/importController.d.ts +23 -0
- package/dist/migrations/importController.js +268 -0
- package/dist/migrations/importDataActions.d.ts +50 -0
- package/dist/migrations/importDataActions.js +230 -0
- package/dist/migrations/relationships.d.ts +29 -0
- package/dist/migrations/relationships.js +204 -0
- package/dist/migrations/services/DataTransformationService.d.ts +55 -0
- package/dist/migrations/services/DataTransformationService.js +158 -0
- package/dist/migrations/services/FileHandlerService.d.ts +75 -0
- package/dist/migrations/services/FileHandlerService.js +236 -0
- package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
- package/dist/migrations/services/ImportOrchestrator.js +485 -0
- package/dist/migrations/services/RateLimitManager.d.ts +138 -0
- package/dist/migrations/services/RateLimitManager.js +279 -0
- package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
- package/dist/migrations/services/RelationshipResolver.js +332 -0
- package/dist/migrations/services/UserMappingService.d.ts +109 -0
- package/dist/migrations/services/UserMappingService.js +277 -0
- package/dist/migrations/services/ValidationService.d.ts +74 -0
- package/dist/migrations/services/ValidationService.js +260 -0
- package/dist/migrations/transfer.d.ts +26 -0
- package/dist/migrations/transfer.js +608 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
- package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
- package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
- package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
- package/dist/schemas/authUser.d.ts +24 -0
- package/dist/schemas/authUser.js +17 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +5 -0
- package/dist/setupCommands.d.ts +58 -0
- package/dist/setupCommands.js +490 -0
- package/dist/setupController.d.ts +9 -0
- package/dist/setupController.js +34 -0
- package/dist/shared/attributeMapper.d.ts +20 -0
- package/dist/shared/attributeMapper.js +203 -0
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.d.ts +75 -0
- package/dist/shared/confirmationDialogs.js +236 -0
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.d.ts +48 -0
- package/dist/shared/functionManager.js +336 -0
- package/dist/shared/indexManager.d.ts +24 -0
- package/dist/shared/indexManager.js +151 -0
- package/dist/shared/jsonSchemaGenerator.d.ts +50 -0
- package/dist/shared/jsonSchemaGenerator.js +290 -0
- package/dist/shared/logging.d.ts +61 -0
- package/dist/shared/logging.js +116 -0
- package/dist/shared/messageFormatter.d.ts +39 -0
- package/dist/shared/messageFormatter.js +162 -0
- package/dist/shared/migrationHelpers.d.ts +61 -0
- package/dist/shared/migrationHelpers.js +145 -0
- package/dist/shared/operationLogger.d.ts +10 -0
- package/dist/shared/operationLogger.js +12 -0
- package/dist/shared/operationQueue.d.ts +40 -0
- package/dist/shared/operationQueue.js +311 -0
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/progressManager.d.ts +62 -0
- package/dist/shared/progressManager.js +215 -0
- package/dist/shared/pydanticModelGenerator.d.ts +17 -0
- package/dist/shared/pydanticModelGenerator.js +615 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +40 -0
- package/dist/shared/schemaGenerator.js +556 -0
- package/dist/shared/selectionDialogs.d.ts +214 -0
- package/dist/shared/selectionDialogs.js +544 -0
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +32 -0
- package/dist/storage/methods.js +472 -0
- package/dist/storage/schemas.d.ts +842 -0
- package/dist/storage/schemas.js +175 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +3 -0
- package/dist/users/methods.d.ts +16 -0
- package/dist/users/methods.js +277 -0
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +212 -0
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +472 -0
- package/dist/utils/configMigration.d.ts +1 -0
- package/dist/utils/configMigration.js +261 -0
- package/dist/utils/constantsGenerator.d.ts +31 -0
- package/dist/utils/constantsGenerator.js +321 -0
- package/dist/utils/dataConverters.d.ts +46 -0
- package/dist/utils/dataConverters.js +139 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +39 -0
- package/dist/utils/getClientFromConfig.js +199 -0
- package/dist/utils/helperFunctions.d.ts +63 -0
- package/dist/utils/helperFunctions.js +156 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/loadConfigs.d.ts +50 -0
- package/dist/utils/loadConfigs.js +358 -0
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.d.ts +2 -0
- package/dist/utils/retryFailedPromises.js +23 -0
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/setupFiles.d.ts +4 -0
- package/dist/utils/setupFiles.js +1192 -0
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/validationRules.d.ts +43 -0
- package/dist/utils/validationRules.js +42 -0
- package/dist/utils/versionDetection.d.ts +58 -0
- package/dist/utils/versionDetection.js +251 -0
- package/dist/utils/yamlConverter.d.ts +100 -0
- package/dist/utils/yamlConverter.js +428 -0
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +267 -0
- package/dist/utilsController.d.ts +106 -0
- package/dist/utilsController.js +863 -0
- package/package.json +75 -0
- package/scripts/copy-templates.ts +23 -0
- package/src/adapters/AdapterFactory.ts +510 -0
- package/src/adapters/DatabaseAdapter.ts +306 -0
- package/src/adapters/LegacyAdapter.ts +841 -0
- package/src/adapters/TablesDBAdapter.ts +773 -0
- package/src/adapters/index.ts +37 -0
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +201 -0
- package/src/cli/commands/databaseCommands.ts +749 -0
- package/src/cli/commands/functionCommands.ts +418 -0
- package/src/cli/commands/schemaCommands.ts +200 -0
- package/src/cli/commands/storageCommands.ts +152 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +2054 -0
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +352 -0
- package/src/collections/methods.ts +745 -0
- package/src/collections/tableOperations.ts +506 -0
- package/src/collections/transferOperations.ts +590 -0
- package/src/collections/wipeOperations.ts +346 -0
- package/src/config/ConfigManager.ts +808 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +740 -0
- package/src/config/services/ConfigMergeService.ts +388 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/config/yamlConfig.ts +761 -0
- package/src/databases/methods.ts +49 -0
- package/src/databases/setup.ts +77 -0
- package/src/examples/yamlTerminologyExample.ts +346 -0
- package/src/functions/deployments.ts +220 -0
- package/src/functions/fnConfigDiscovery.ts +103 -0
- package/src/functions/methods.ts +271 -0
- package/src/functions/pathResolution.ts +227 -0
- package/src/functions/templates/count-docs-in-collection/README.md +54 -0
- package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/typescript-node/README.md +32 -0
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +29 -0
- package/src/functions/templates/uv/README.md +31 -0
- package/src/functions/templates/uv/pyproject.toml +30 -0
- package/src/functions/templates/uv/src/__init__.py +0 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +46 -0
- package/src/init.ts +62 -0
- package/src/interactiveCLI.ts +1136 -0
- package/src/main.ts +1661 -0
- package/src/migrations/afterImportActions.ts +580 -0
- package/src/migrations/appwriteToX.ts +664 -0
- package/src/migrations/comprehensiveTransfer.ts +2285 -0
- package/src/migrations/dataLoader.ts +1702 -0
- package/src/migrations/importController.ts +428 -0
- package/src/migrations/importDataActions.ts +315 -0
- package/src/migrations/relationships.ts +334 -0
- package/src/migrations/services/DataTransformationService.ts +196 -0
- package/src/migrations/services/FileHandlerService.ts +311 -0
- package/src/migrations/services/ImportOrchestrator.ts +666 -0
- package/src/migrations/services/RateLimitManager.ts +363 -0
- package/src/migrations/services/RelationshipResolver.ts +461 -0
- package/src/migrations/services/UserMappingService.ts +345 -0
- package/src/migrations/services/ValidationService.ts +349 -0
- package/src/migrations/transfer.ts +1068 -0
- package/src/migrations/yaml/YamlImportConfigLoader.ts +439 -0
- package/src/migrations/yaml/YamlImportIntegration.ts +446 -0
- package/src/migrations/yaml/generateImportSchemas.ts +1354 -0
- package/src/schemas/authUser.ts +23 -0
- package/src/setup.ts +8 -0
- package/src/setupCommands.ts +603 -0
- package/src/setupController.ts +43 -0
- package/src/shared/attributeMapper.ts +229 -0
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +327 -0
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +525 -0
- package/src/shared/indexManager.ts +254 -0
- package/src/shared/jsonSchemaGenerator.ts +383 -0
- package/src/shared/logging.ts +149 -0
- package/src/shared/messageFormatter.ts +208 -0
- package/src/shared/migrationHelpers.ts +232 -0
- package/src/shared/operationLogger.ts +20 -0
- package/src/shared/operationQueue.ts +377 -0
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/progressManager.ts +278 -0
- package/src/shared/pydanticModelGenerator.ts +618 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +644 -0
- package/src/shared/selectionDialogs.ts +749 -0
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +698 -0
- package/src/storage/schemas.ts +205 -0
- package/src/types/node-appwrite-tablesdb.d.ts +44 -0
- package/src/types.ts +9 -0
- package/src/users/methods.ts +359 -0
- package/src/utils/ClientFactory.ts +240 -0
- package/src/utils/configDiscovery.ts +557 -0
- package/src/utils/configMigration.ts +348 -0
- package/src/utils/constantsGenerator.ts +369 -0
- package/src/utils/dataConverters.ts +159 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +257 -0
- package/src/utils/helperFunctions.ts +228 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/loadConfigs.ts +449 -0
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +29 -0
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +1238 -0
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/validationRules.ts +88 -0
- package/src/utils/versionDetection.ts +292 -0
- package/src/utils/yamlConverter.ts +542 -0
- package/src/utils/yamlLoader.ts +371 -0
- package/src/utilsController.ts +1203 -0
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/tsconfig.json +44 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Client } from "node-appwrite";
|
|
2
|
+
import { type AppwriteFunction, type Specification } from "@njdamstra/appwrite-utils";
|
|
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
|
+
}>;
|
|
10
|
+
export declare const deleteFunction: (client: Client, functionId: string) => Promise<{}>;
|
|
11
|
+
export declare const createFunction: (client: Client, functionConfig: AppwriteFunction) => 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 listFunctionDeployments: (client: Client, functionId: string, queries?: string[]) => Promise<import("node-appwrite").Models.DeploymentList>;
|
|
15
|
+
export declare const updateFunction: (client: Client, functionConfig: AppwriteFunction) => Promise<import("node-appwrite").Models.Function>;
|
|
16
|
+
export declare const createFunctionTemplate: (templateType: "typescript-node" | "uv" | "count-docs-in-collection" | "hono-typescript", functionName: string, basePath?: string) => Promise<string>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { AppwriteException, Client, Functions, Query, Runtime, } from "node-appwrite";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import {} from "@njdamstra/appwrite-utils";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { extract as extractTar } from "tar";
|
|
8
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
9
|
+
import { expandTildePath, normalizeFunctionName } from "./pathResolution.js";
|
|
10
|
+
/**
|
|
11
|
+
* Validates and filters events array for Appwrite functions
|
|
12
|
+
* - Filters out empty/invalid strings
|
|
13
|
+
* - Limits to 100 items maximum (Appwrite limit)
|
|
14
|
+
* - Returns empty array if input is invalid
|
|
15
|
+
*/
|
|
16
|
+
const validateEvents = (events) => {
|
|
17
|
+
if (!events || !Array.isArray(events))
|
|
18
|
+
return [];
|
|
19
|
+
return events
|
|
20
|
+
.filter(event => event && typeof event === 'string' && event.trim().length > 0)
|
|
21
|
+
.slice(0, 100);
|
|
22
|
+
};
|
|
23
|
+
export const listFunctions = async (client, queries, search) => {
|
|
24
|
+
const functions = new Functions(client);
|
|
25
|
+
const functionsList = await functions.list(queries, search);
|
|
26
|
+
return functionsList;
|
|
27
|
+
};
|
|
28
|
+
export const getFunction = async (client, functionId) => {
|
|
29
|
+
const functions = new Functions(client);
|
|
30
|
+
const functionResponse = await functions.get(functionId);
|
|
31
|
+
return functionResponse;
|
|
32
|
+
};
|
|
33
|
+
export const downloadLatestFunctionDeployment = async (client, functionId, basePath = process.cwd()) => {
|
|
34
|
+
const functions = new Functions(client);
|
|
35
|
+
const functionInfo = await getFunction(client, functionId);
|
|
36
|
+
const functionDeployments = await functions.listDeployments(functionId, [
|
|
37
|
+
Query.orderDesc("$createdAt"),
|
|
38
|
+
]);
|
|
39
|
+
if (functionDeployments.deployments.length === 0) {
|
|
40
|
+
throw new Error("No deployments found for function");
|
|
41
|
+
}
|
|
42
|
+
const latestDeployment = functionDeployments.deployments[0];
|
|
43
|
+
const deploymentData = await functions.getDeploymentDownload(functionId, latestDeployment.$id);
|
|
44
|
+
// Create function directory using provided basePath
|
|
45
|
+
const functionDir = join(basePath, normalizeFunctionName(functionInfo.name));
|
|
46
|
+
await fs.promises.mkdir(functionDir, { recursive: true });
|
|
47
|
+
// Create temporary file for tar extraction
|
|
48
|
+
const tarPath = join(functionDir, "temp.tar.gz");
|
|
49
|
+
const uint8Array = new Uint8Array(deploymentData);
|
|
50
|
+
await fs.promises.writeFile(tarPath, uint8Array);
|
|
51
|
+
try {
|
|
52
|
+
// Extract tar file
|
|
53
|
+
extractTar({
|
|
54
|
+
C: functionDir,
|
|
55
|
+
file: tarPath,
|
|
56
|
+
sync: true,
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
path: functionDir,
|
|
60
|
+
function: functionInfo,
|
|
61
|
+
deployment: latestDeployment,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
// Clean up tar file
|
|
66
|
+
await fs.promises.unlink(tarPath).catch(() => { });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
export const deleteFunction = async (client, functionId) => {
|
|
70
|
+
const functions = new Functions(client);
|
|
71
|
+
const functionResponse = await functions.delete(functionId);
|
|
72
|
+
return functionResponse;
|
|
73
|
+
};
|
|
74
|
+
export const createFunction = async (client, functionConfig) => {
|
|
75
|
+
const functions = new Functions(client);
|
|
76
|
+
const functionResponse = await functions.create(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute, validateEvents(functionConfig.events), functionConfig.schedule, functionConfig.timeout, functionConfig.enabled, functionConfig.logging, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes, functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory);
|
|
77
|
+
return functionResponse;
|
|
78
|
+
};
|
|
79
|
+
export const updateFunctionSpecifications = async (client, functionId, specification) => {
|
|
80
|
+
const curFunction = await listFunctions(client, [
|
|
81
|
+
Query.equal("$id", functionId),
|
|
82
|
+
]);
|
|
83
|
+
if (curFunction.functions.length === 0) {
|
|
84
|
+
throw new Error("Function not found");
|
|
85
|
+
}
|
|
86
|
+
const functionFound = curFunction.functions[0];
|
|
87
|
+
try {
|
|
88
|
+
const functionResponse = await updateFunction(client, {
|
|
89
|
+
...functionFound,
|
|
90
|
+
runtime: functionFound.runtime,
|
|
91
|
+
scopes: functionFound.scopes,
|
|
92
|
+
specification: specification,
|
|
93
|
+
});
|
|
94
|
+
return functionResponse;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error instanceof AppwriteException &&
|
|
98
|
+
error.message.includes("Invalid `specification`")) {
|
|
99
|
+
MessageFormatter.error("Error updating function specifications, please try setting the env variable `_FUNCTIONS_CPUS` and `_FUNCTIONS_RAM` to non-zero values", undefined, { prefix: "Functions" });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
MessageFormatter.error("Error updating function specifications", error instanceof Error ? error : undefined, { prefix: "Functions" });
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
export const listSpecifications = async (client) => {
|
|
108
|
+
const functions = new Functions(client);
|
|
109
|
+
const specifications = await functions.listSpecifications();
|
|
110
|
+
return specifications;
|
|
111
|
+
};
|
|
112
|
+
export const listFunctionDeployments = async (client, functionId, queries) => {
|
|
113
|
+
const functions = new Functions(client);
|
|
114
|
+
const deployments = await functions.listDeployments(functionId, queries);
|
|
115
|
+
return deployments;
|
|
116
|
+
};
|
|
117
|
+
export const updateFunction = async (client, functionConfig) => {
|
|
118
|
+
const functions = new Functions(client);
|
|
119
|
+
const functionResponse = await functions.update(functionConfig.$id, functionConfig.name, functionConfig.runtime, functionConfig.execute, validateEvents(functionConfig.events), functionConfig.schedule, functionConfig.timeout, functionConfig.enabled, functionConfig.logging, functionConfig.entrypoint, functionConfig.commands, functionConfig.scopes, functionConfig.installationId, functionConfig.providerRepositoryId, functionConfig.providerBranch, functionConfig.providerSilentMode, functionConfig.providerRootDirectory, functionConfig.specification);
|
|
120
|
+
return functionResponse;
|
|
121
|
+
};
|
|
122
|
+
export const createFunctionTemplate = async (templateType, functionName, basePath = "./functions") => {
|
|
123
|
+
const expandedBasePath = expandTildePath(basePath);
|
|
124
|
+
const functionPath = join(expandedBasePath, functionName);
|
|
125
|
+
const currentFileUrl = import.meta.url;
|
|
126
|
+
const currentDir = dirname(fileURLToPath(currentFileUrl));
|
|
127
|
+
const templatesPath = join(currentDir, "templates", templateType);
|
|
128
|
+
// Create function directory
|
|
129
|
+
await fs.promises.mkdir(functionPath, { recursive: true });
|
|
130
|
+
// Copy template files recursively
|
|
131
|
+
const copyTemplateFiles = async (sourcePath, targetPath) => {
|
|
132
|
+
const entries = await fs.promises.readdir(sourcePath, {
|
|
133
|
+
withFileTypes: true,
|
|
134
|
+
});
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
const srcPath = join(sourcePath, entry.name);
|
|
137
|
+
const destPath = join(targetPath, entry.name);
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
await fs.promises.mkdir(destPath, { recursive: true });
|
|
140
|
+
await copyTemplateFiles(srcPath, destPath);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
let content = await fs.promises.readFile(srcPath, "utf-8");
|
|
144
|
+
// Replace template variables
|
|
145
|
+
content = content
|
|
146
|
+
.replace(/\{\{functionName\}\}/g, functionName)
|
|
147
|
+
.replace(/\{\{databaseId\}\}/g, "{{databaseId}}")
|
|
148
|
+
.replace(/\{\{collectionId\}\}/g, "{{collectionId}}");
|
|
149
|
+
await fs.promises.writeFile(destPath, content);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
try {
|
|
154
|
+
await copyTemplateFiles(templatesPath, functionPath);
|
|
155
|
+
MessageFormatter.success(`Created ${templateType} function template at ${functionPath}`, { prefix: "Functions" });
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
MessageFormatter.error("Failed to create function template", error instanceof Error ? error : undefined, { prefix: "Functions" });
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
return functionPath;
|
|
162
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expands tilde (~) in paths to the user's home directory
|
|
3
|
+
* @param pathStr - Path string that may contain ~
|
|
4
|
+
* @returns Expanded path with home directory
|
|
5
|
+
*/
|
|
6
|
+
export declare function expandTildePath(pathStr: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes function name to standard format (lowercase, dashes instead of spaces)
|
|
9
|
+
* @param name - Function name to normalize
|
|
10
|
+
* @returns Normalized function name
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeFunctionName(name: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Validates that a directory exists and contains function markers
|
|
15
|
+
* @param dirPath - Directory path to validate
|
|
16
|
+
* @returns True if directory is a valid function directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateFunctionDirectory(dirPath: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Helper function to search for function in standard locations
|
|
21
|
+
* @param configDirPath - Directory where config file is located
|
|
22
|
+
* @param normalizedName - Normalized function name
|
|
23
|
+
* @returns First valid function directory path or undefined
|
|
24
|
+
*/
|
|
25
|
+
export declare function findFunctionInStandardLocations(configDirPath: string, normalizedName: string): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the absolute path to a function directory
|
|
28
|
+
* Handles multiple resolution strategies with proper priority
|
|
29
|
+
*
|
|
30
|
+
* @param functionName - Name of the function
|
|
31
|
+
* @param configDirPath - Directory where config file is located
|
|
32
|
+
* @param dirPath - Optional explicit dirPath from config
|
|
33
|
+
* @param explicitPath - Optional path passed as parameter (highest priority)
|
|
34
|
+
* @returns Absolute path to the function directory
|
|
35
|
+
* @throws Error if function directory cannot be found or is invalid
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveFunctionDirectory(functionName: string, configDirPath: string, dirPath?: string, explicitPath?: string): string;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { existsSync, statSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, isAbsolute } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { MessageFormatter } from '../shared/messageFormatter.js';
|
|
5
|
+
import { logger } from '../shared/logging.js';
|
|
6
|
+
/**
|
|
7
|
+
* Expands tilde (~) in paths to the user's home directory
|
|
8
|
+
* @param pathStr - Path string that may contain ~
|
|
9
|
+
* @returns Expanded path with home directory
|
|
10
|
+
*/
|
|
11
|
+
export function expandTildePath(pathStr) {
|
|
12
|
+
if (!pathStr)
|
|
13
|
+
return pathStr;
|
|
14
|
+
if (pathStr.startsWith('~/') || pathStr === '~') {
|
|
15
|
+
const expandedPath = pathStr.replace(/^~(?=$|\/|\\)/, homedir());
|
|
16
|
+
logger.debug('Expanded tilde path', { original: pathStr, expanded: expandedPath });
|
|
17
|
+
return expandedPath;
|
|
18
|
+
}
|
|
19
|
+
return pathStr;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Normalizes function name to standard format (lowercase, dashes instead of spaces)
|
|
23
|
+
* @param name - Function name to normalize
|
|
24
|
+
* @returns Normalized function name
|
|
25
|
+
*/
|
|
26
|
+
export function normalizeFunctionName(name) {
|
|
27
|
+
if (!name)
|
|
28
|
+
return name;
|
|
29
|
+
const normalized = name.toLowerCase().replace(/\s+/g, '-');
|
|
30
|
+
if (normalized !== name) {
|
|
31
|
+
logger.debug('Normalized function name', { original: name, normalized });
|
|
32
|
+
}
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validates that a directory exists and contains function markers
|
|
37
|
+
* @param dirPath - Directory path to validate
|
|
38
|
+
* @returns True if directory is a valid function directory
|
|
39
|
+
*/
|
|
40
|
+
export function validateFunctionDirectory(dirPath) {
|
|
41
|
+
try {
|
|
42
|
+
// Check if directory exists
|
|
43
|
+
if (!existsSync(dirPath)) {
|
|
44
|
+
logger.debug('Directory does not exist', { dirPath });
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
// Check if it's actually a directory
|
|
48
|
+
const stats = statSync(dirPath);
|
|
49
|
+
if (!stats.isDirectory()) {
|
|
50
|
+
logger.debug('Path is not a directory', { dirPath });
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Check for function markers
|
|
54
|
+
const contents = readdirSync(dirPath);
|
|
55
|
+
const hasPackageJson = contents.includes('package.json');
|
|
56
|
+
const hasPyprojectToml = contents.includes('pyproject.toml');
|
|
57
|
+
const hasSrcDir = contents.includes('src');
|
|
58
|
+
const isValid = hasPackageJson || hasPyprojectToml || hasSrcDir;
|
|
59
|
+
logger.debug('Function directory validation', {
|
|
60
|
+
dirPath,
|
|
61
|
+
isValid,
|
|
62
|
+
markers: {
|
|
63
|
+
hasPackageJson,
|
|
64
|
+
hasPyprojectToml,
|
|
65
|
+
hasSrcDir
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return isValid;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.debug('Error validating function directory', {
|
|
72
|
+
dirPath,
|
|
73
|
+
error: error instanceof Error ? error.message : String(error)
|
|
74
|
+
});
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Helper function to search for function in standard locations
|
|
80
|
+
* @param configDirPath - Directory where config file is located
|
|
81
|
+
* @param normalizedName - Normalized function name
|
|
82
|
+
* @returns First valid function directory path or undefined
|
|
83
|
+
*/
|
|
84
|
+
export function findFunctionInStandardLocations(configDirPath, normalizedName) {
|
|
85
|
+
const searchPaths = [
|
|
86
|
+
// Same directory as config
|
|
87
|
+
join(configDirPath, 'functions', normalizedName),
|
|
88
|
+
// Parent directory of config
|
|
89
|
+
join(configDirPath, '..', 'functions', normalizedName),
|
|
90
|
+
// Current working directory
|
|
91
|
+
join(process.cwd(), 'functions', normalizedName),
|
|
92
|
+
];
|
|
93
|
+
logger.debug('Searching for function in standard locations', {
|
|
94
|
+
normalizedName,
|
|
95
|
+
configDirPath,
|
|
96
|
+
searchPaths
|
|
97
|
+
});
|
|
98
|
+
for (const searchPath of searchPaths) {
|
|
99
|
+
const resolvedPath = resolve(searchPath);
|
|
100
|
+
logger.debug('Checking search path', { searchPath, resolvedPath });
|
|
101
|
+
if (validateFunctionDirectory(resolvedPath)) {
|
|
102
|
+
logger.debug('Found function in standard location', { resolvedPath });
|
|
103
|
+
return resolvedPath;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
logger.debug('Function not found in any standard location', { normalizedName });
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the absolute path to a function directory
|
|
111
|
+
* Handles multiple resolution strategies with proper priority
|
|
112
|
+
*
|
|
113
|
+
* @param functionName - Name of the function
|
|
114
|
+
* @param configDirPath - Directory where config file is located
|
|
115
|
+
* @param dirPath - Optional explicit dirPath from config
|
|
116
|
+
* @param explicitPath - Optional path passed as parameter (highest priority)
|
|
117
|
+
* @returns Absolute path to the function directory
|
|
118
|
+
* @throws Error if function directory cannot be found or is invalid
|
|
119
|
+
*/
|
|
120
|
+
export function resolveFunctionDirectory(functionName, configDirPath, dirPath, explicitPath) {
|
|
121
|
+
logger.debug('Resolving function directory', {
|
|
122
|
+
functionName,
|
|
123
|
+
configDirPath,
|
|
124
|
+
dirPath,
|
|
125
|
+
explicitPath
|
|
126
|
+
});
|
|
127
|
+
const normalizedName = normalizeFunctionName(functionName);
|
|
128
|
+
// Priority 1: Explicit path parameter (highest priority)
|
|
129
|
+
if (explicitPath) {
|
|
130
|
+
logger.debug('Using explicit path parameter');
|
|
131
|
+
const expandedPath = expandTildePath(explicitPath);
|
|
132
|
+
const resolvedPath = isAbsolute(expandedPath)
|
|
133
|
+
? expandedPath
|
|
134
|
+
: resolve(process.cwd(), expandedPath);
|
|
135
|
+
if (!validateFunctionDirectory(resolvedPath)) {
|
|
136
|
+
const errorMsg = `Explicit path is not a valid function directory: ${resolvedPath}`;
|
|
137
|
+
logger.error(errorMsg);
|
|
138
|
+
MessageFormatter.error('Invalid function directory', errorMsg, { prefix: 'Path Resolution' });
|
|
139
|
+
throw new Error(errorMsg);
|
|
140
|
+
}
|
|
141
|
+
logger.debug('Resolved using explicit path', { resolvedPath });
|
|
142
|
+
MessageFormatter.debug(`Resolved function directory using explicit path: ${resolvedPath}`, undefined, { prefix: 'Path Resolution' });
|
|
143
|
+
return resolvedPath;
|
|
144
|
+
}
|
|
145
|
+
// Priority 2: dirPath from config (relative to config location)
|
|
146
|
+
if (dirPath) {
|
|
147
|
+
logger.debug('Using dirPath from config');
|
|
148
|
+
const expandedPath = expandTildePath(dirPath);
|
|
149
|
+
const resolvedPath = isAbsolute(expandedPath)
|
|
150
|
+
? expandedPath
|
|
151
|
+
: resolve(configDirPath, expandedPath);
|
|
152
|
+
if (!validateFunctionDirectory(resolvedPath)) {
|
|
153
|
+
const errorMsg = `Config dirPath is not a valid function directory: ${resolvedPath}`;
|
|
154
|
+
logger.error(errorMsg);
|
|
155
|
+
MessageFormatter.error('Invalid function directory', errorMsg, { prefix: 'Path Resolution' });
|
|
156
|
+
throw new Error(errorMsg);
|
|
157
|
+
}
|
|
158
|
+
logger.debug('Resolved using config dirPath', { resolvedPath });
|
|
159
|
+
MessageFormatter.debug(`Resolved function directory using config dirPath: ${resolvedPath}`, undefined, { prefix: 'Path Resolution' });
|
|
160
|
+
return resolvedPath;
|
|
161
|
+
}
|
|
162
|
+
// Priority 3: Search standard locations
|
|
163
|
+
logger.debug('Searching standard locations for function');
|
|
164
|
+
const foundPath = findFunctionInStandardLocations(configDirPath, normalizedName);
|
|
165
|
+
if (foundPath) {
|
|
166
|
+
logger.debug('Resolved using standard location search', { foundPath });
|
|
167
|
+
MessageFormatter.debug(`Found function directory in standard location: ${foundPath}`, undefined, { prefix: 'Path Resolution' });
|
|
168
|
+
return foundPath;
|
|
169
|
+
}
|
|
170
|
+
// Priority 4: Not found - throw error
|
|
171
|
+
const searchedLocations = [
|
|
172
|
+
join(configDirPath, 'functions', normalizedName),
|
|
173
|
+
join(configDirPath, '..', 'functions', normalizedName),
|
|
174
|
+
join(process.cwd(), 'functions', normalizedName),
|
|
175
|
+
];
|
|
176
|
+
const errorMsg = `Function directory not found for '${functionName}' (normalized: '${normalizedName}'). ` +
|
|
177
|
+
`Searched locations:\n${searchedLocations.map(p => ` - ${p}`).join('\n')}`;
|
|
178
|
+
logger.error('Function directory not found', {
|
|
179
|
+
functionName,
|
|
180
|
+
normalizedName,
|
|
181
|
+
searchedLocations
|
|
182
|
+
});
|
|
183
|
+
MessageFormatter.error('Function directory not found', errorMsg, { prefix: 'Path Resolution' });
|
|
184
|
+
throw new Error(errorMsg);
|
|
185
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Count Documents in Collection Function
|
|
2
|
+
|
|
3
|
+
A utility function that accurately counts documents in an Appwrite collection, even when there are more than 5,000 documents.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- Handles collections with any number of documents
|
|
7
|
+
- Supports filtering using Appwrite queries
|
|
8
|
+
- Uses efficient binary search algorithm for large collections
|
|
9
|
+
- Provides detailed logging during the counting process
|
|
10
|
+
|
|
11
|
+
## Structure
|
|
12
|
+
- `src/main.ts`: Main function implementation with counting logic
|
|
13
|
+
- `src/request.ts`: Request validation schema using Zod
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
Send a POST request with:
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"databaseId": "your-database-id",
|
|
20
|
+
"collectionId": "your-collection-id",
|
|
21
|
+
"queries": [Query.orderDesc("$createdAt"), Query.contains("name", "John")] // Or put the string array from after this, they are the same
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Response
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"success": true,
|
|
29
|
+
"count": 12345
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Development
|
|
34
|
+
1. Install dependencies: `npm|yarn|bun install`
|
|
35
|
+
2. Build: `npm|yarn|bun run build`
|
|
36
|
+
3. Deploy: Function will be built automatically during deployment
|
|
37
|
+
|
|
38
|
+
## Deployment
|
|
39
|
+
Make sure it's inside `appwriteConfig.ts` functions array, and if you want to build it FIRST, before Appwrite (using your system), you can
|
|
40
|
+
add the `predeployCommands` to the function in `appwriteConfig.ts`.
|
|
41
|
+
|
|
42
|
+
## Example Config
|
|
43
|
+
```typescript
|
|
44
|
+
{
|
|
45
|
+
$id: 'count-docs',
|
|
46
|
+
name: 'Count Documents',
|
|
47
|
+
runtime: 'node-18.0',
|
|
48
|
+
path: 'functions/count-docs',
|
|
49
|
+
entrypoint: './main.js',
|
|
50
|
+
execute: ['any'],
|
|
51
|
+
predeployCommands: ['npm install', 'npm run build'],
|
|
52
|
+
deployDir: './dist'
|
|
53
|
+
}
|
|
54
|
+
```
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Client, Databases, Query } from "node-appwrite";
|
|
2
|
+
import { AppwriteRequest, type AppwriteResponse } from "@njdamstra/appwrite-utils";
|
|
3
|
+
import { requestSchema } from "./request.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Main function to handle document counting requests.
|
|
7
|
+
* @param {Object} params - The function parameters.
|
|
8
|
+
* @param {Object} params.req - The request object.
|
|
9
|
+
* @param {Object} params.res - The response object.
|
|
10
|
+
* @param {Function} params.log - Logging function.
|
|
11
|
+
* @param {Function} params.error - Error logging function.
|
|
12
|
+
* @returns {Promise<Object>} JSON response with count or error message.
|
|
13
|
+
*/
|
|
14
|
+
export default async ({
|
|
15
|
+
req,
|
|
16
|
+
res,
|
|
17
|
+
log,
|
|
18
|
+
error,
|
|
19
|
+
}: {
|
|
20
|
+
req: AppwriteRequest;
|
|
21
|
+
res: AppwriteResponse;
|
|
22
|
+
log: (message: string) => void;
|
|
23
|
+
error: (message: string) => void;
|
|
24
|
+
}) => {
|
|
25
|
+
// Initialize Appwrite client
|
|
26
|
+
const client = new Client()
|
|
27
|
+
.setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"]!)
|
|
28
|
+
.setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"]!)
|
|
29
|
+
.setKey(req.headers["x-appwrite-key"] || "");
|
|
30
|
+
|
|
31
|
+
const databases = new Databases(client);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
if (req.method === "POST") {
|
|
35
|
+
// Parse request body
|
|
36
|
+
const body = requestSchema.safeParse(
|
|
37
|
+
typeof req.body === "string" ? JSON.parse(req.body) : req.body
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!body.success) {
|
|
41
|
+
return res.json({ success: false, error: body.error }, 400);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { databaseId, collectionId, queries = [] } = body.data;
|
|
45
|
+
|
|
46
|
+
log(`Queries: ${JSON.stringify(queries)}`);
|
|
47
|
+
|
|
48
|
+
// Count documents in the specified collection
|
|
49
|
+
const count = await countAllDocuments(
|
|
50
|
+
log,
|
|
51
|
+
databases,
|
|
52
|
+
databaseId,
|
|
53
|
+
collectionId,
|
|
54
|
+
queries
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Return successful response with document count
|
|
58
|
+
return res.json({
|
|
59
|
+
success: true,
|
|
60
|
+
count: count,
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
// Return error for non-POST requests
|
|
64
|
+
return res.json({ success: false, error: "Method not allowed" }, 405);
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
// Log and return any errors
|
|
68
|
+
error(`Error processing request: ${err}`);
|
|
69
|
+
return res.json({ success: false, error: (err as Error).message }, 500);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Counts all documents in a collection, handling large collections efficiently.
|
|
75
|
+
* @param {Function} log - Logging function.
|
|
76
|
+
* @param {Databases} databases - Appwrite Databases instance.
|
|
77
|
+
* @param {string} databaseId - ID of the database.
|
|
78
|
+
* @param {string} collectionId - ID of the collection.
|
|
79
|
+
* @param {string[]} queries - Array of query strings to filter documents.
|
|
80
|
+
* @param {number} batchSize - Size of batches for processing (default: 1000).
|
|
81
|
+
* @returns {Promise<number>} Total count of documents.
|
|
82
|
+
*/
|
|
83
|
+
async function countAllDocuments(
|
|
84
|
+
log: any,
|
|
85
|
+
databases: Databases,
|
|
86
|
+
databaseId: string,
|
|
87
|
+
collectionId: string,
|
|
88
|
+
queries: string[] = [],
|
|
89
|
+
batchSize: number = 1000
|
|
90
|
+
): Promise<number> {
|
|
91
|
+
// Filter out limit and offset queries
|
|
92
|
+
const initialQueries = queries.filter(
|
|
93
|
+
(q) => !(q.includes("limit") || q.includes("offset"))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Initial query to check if total is less than 5000
|
|
97
|
+
const initialResponse = await databases.listDocuments(
|
|
98
|
+
databaseId,
|
|
99
|
+
collectionId,
|
|
100
|
+
[...initialQueries, Query.limit(1)]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (initialResponse.total < 5000) {
|
|
104
|
+
log(`Total documents (from initial response): ${initialResponse.total}`);
|
|
105
|
+
return initialResponse.total;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// If total is 5000 or more, we need to count manually
|
|
109
|
+
let bound = 5000;
|
|
110
|
+
|
|
111
|
+
// Exponential search to find an upper bound
|
|
112
|
+
while (true) {
|
|
113
|
+
log(`Querying for offset ${bound}`);
|
|
114
|
+
try {
|
|
115
|
+
const response = await databases.listDocuments(databaseId, collectionId, [
|
|
116
|
+
...initialQueries,
|
|
117
|
+
Query.limit(1),
|
|
118
|
+
Query.offset(bound),
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
if (response.documents.length === 0) {
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
bound *= 2;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Binary search to find the exact count
|
|
131
|
+
let low = Math.floor(bound / 2);
|
|
132
|
+
let high = bound;
|
|
133
|
+
let lastValidCount = low;
|
|
134
|
+
|
|
135
|
+
while (low <= high) {
|
|
136
|
+
const mid = Math.floor((low + high) / 2);
|
|
137
|
+
log(`Binary search: Querying for offset ${mid}`);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const response = await databases.listDocuments(databaseId, collectionId, [
|
|
141
|
+
...initialQueries,
|
|
142
|
+
Query.limit(1),
|
|
143
|
+
Query.offset(mid),
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
if (response.documents.length > 0) {
|
|
147
|
+
lastValidCount = mid + 1; // +1 because offset is 0-based
|
|
148
|
+
low = mid + 1;
|
|
149
|
+
} else {
|
|
150
|
+
high = mid - 1;
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
high = mid - 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
log(`Total documents: ${lastValidCount}`);
|
|
158
|
+
return lastValidCount;
|
|
159
|
+
}
|