appwrite-utils-cli 1.5.2 → 1.6.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 (233) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +251 -29
  3. package/dist/adapters/AdapterFactory.d.ts +10 -3
  4. package/dist/adapters/AdapterFactory.js +213 -17
  5. package/dist/adapters/TablesDBAdapter.js +60 -17
  6. package/dist/backups/operations/bucketBackup.d.ts +19 -0
  7. package/dist/backups/operations/bucketBackup.js +197 -0
  8. package/dist/backups/operations/collectionBackup.d.ts +30 -0
  9. package/dist/backups/operations/collectionBackup.js +201 -0
  10. package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
  11. package/dist/backups/operations/comprehensiveBackup.js +238 -0
  12. package/dist/backups/schemas/bucketManifest.d.ts +93 -0
  13. package/dist/backups/schemas/bucketManifest.js +33 -0
  14. package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
  15. package/dist/backups/schemas/comprehensiveManifest.js +32 -0
  16. package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
  17. package/dist/backups/tracking/centralizedTracking.js +274 -0
  18. package/dist/cli/commands/configCommands.d.ts +8 -0
  19. package/dist/cli/commands/configCommands.js +160 -0
  20. package/dist/cli/commands/databaseCommands.d.ts +13 -0
  21. package/dist/cli/commands/databaseCommands.js +478 -0
  22. package/dist/cli/commands/functionCommands.d.ts +7 -0
  23. package/dist/cli/commands/functionCommands.js +289 -0
  24. package/dist/cli/commands/schemaCommands.d.ts +7 -0
  25. package/dist/cli/commands/schemaCommands.js +134 -0
  26. package/dist/cli/commands/transferCommands.d.ts +5 -0
  27. package/dist/cli/commands/transferCommands.js +384 -0
  28. package/dist/collections/attributes.d.ts +5 -4
  29. package/dist/collections/attributes.js +539 -246
  30. package/dist/collections/indexes.js +39 -37
  31. package/dist/collections/methods.d.ts +2 -16
  32. package/dist/collections/methods.js +90 -538
  33. package/dist/collections/transferOperations.d.ts +7 -0
  34. package/dist/collections/transferOperations.js +331 -0
  35. package/dist/collections/wipeOperations.d.ts +16 -0
  36. package/dist/collections/wipeOperations.js +328 -0
  37. package/dist/config/configMigration.d.ts +87 -0
  38. package/dist/config/configMigration.js +390 -0
  39. package/dist/config/configValidation.d.ts +66 -0
  40. package/dist/config/configValidation.js +358 -0
  41. package/dist/config/yamlConfig.d.ts +455 -1
  42. package/dist/config/yamlConfig.js +145 -52
  43. package/dist/databases/methods.js +3 -2
  44. package/dist/databases/setup.d.ts +1 -2
  45. package/dist/databases/setup.js +9 -87
  46. package/dist/examples/yamlTerminologyExample.d.ts +42 -0
  47. package/dist/examples/yamlTerminologyExample.js +269 -0
  48. package/dist/functions/deployments.js +11 -10
  49. package/dist/functions/methods.d.ts +1 -1
  50. package/dist/functions/methods.js +5 -4
  51. package/dist/init.js +9 -9
  52. package/dist/interactiveCLI.d.ts +8 -17
  53. package/dist/interactiveCLI.js +181 -1172
  54. package/dist/main.js +364 -21
  55. package/dist/migrations/afterImportActions.js +22 -30
  56. package/dist/migrations/appwriteToX.js +71 -25
  57. package/dist/migrations/dataLoader.js +35 -26
  58. package/dist/migrations/importController.js +29 -30
  59. package/dist/migrations/relationships.js +13 -12
  60. package/dist/migrations/services/ImportOrchestrator.js +16 -19
  61. package/dist/migrations/transfer.js +46 -46
  62. package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
  63. package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
  64. package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
  65. package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
  66. package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
  67. package/dist/migrations/yaml/generateImportSchemas.js +736 -7
  68. package/dist/schemas/authUser.d.ts +1 -1
  69. package/dist/setupController.js +3 -2
  70. package/dist/shared/backupMetadataSchema.d.ts +94 -0
  71. package/dist/shared/backupMetadataSchema.js +38 -0
  72. package/dist/shared/backupTracking.d.ts +18 -0
  73. package/dist/shared/backupTracking.js +176 -0
  74. package/dist/shared/confirmationDialogs.js +15 -15
  75. package/dist/shared/errorUtils.d.ts +54 -0
  76. package/dist/shared/errorUtils.js +95 -0
  77. package/dist/shared/functionManager.js +20 -19
  78. package/dist/shared/indexManager.js +12 -11
  79. package/dist/shared/jsonSchemaGenerator.js +10 -26
  80. package/dist/shared/logging.d.ts +51 -0
  81. package/dist/shared/logging.js +70 -0
  82. package/dist/shared/messageFormatter.d.ts +2 -0
  83. package/dist/shared/messageFormatter.js +10 -0
  84. package/dist/shared/migrationHelpers.d.ts +6 -16
  85. package/dist/shared/migrationHelpers.js +24 -21
  86. package/dist/shared/operationLogger.d.ts +8 -1
  87. package/dist/shared/operationLogger.js +11 -24
  88. package/dist/shared/operationQueue.d.ts +28 -1
  89. package/dist/shared/operationQueue.js +268 -66
  90. package/dist/shared/operationsTable.d.ts +26 -0
  91. package/dist/shared/operationsTable.js +286 -0
  92. package/dist/shared/operationsTableSchema.d.ts +48 -0
  93. package/dist/shared/operationsTableSchema.js +35 -0
  94. package/dist/shared/relationshipExtractor.d.ts +56 -0
  95. package/dist/shared/relationshipExtractor.js +138 -0
  96. package/dist/shared/schemaGenerator.d.ts +19 -1
  97. package/dist/shared/schemaGenerator.js +56 -75
  98. package/dist/storage/backupCompression.d.ts +20 -0
  99. package/dist/storage/backupCompression.js +67 -0
  100. package/dist/storage/methods.d.ts +16 -2
  101. package/dist/storage/methods.js +98 -14
  102. package/dist/users/methods.js +9 -8
  103. package/dist/utils/configDiscovery.d.ts +78 -0
  104. package/dist/utils/configDiscovery.js +430 -0
  105. package/dist/utils/directoryUtils.d.ts +22 -0
  106. package/dist/utils/directoryUtils.js +59 -0
  107. package/dist/utils/getClientFromConfig.d.ts +17 -8
  108. package/dist/utils/getClientFromConfig.js +162 -17
  109. package/dist/utils/helperFunctions.d.ts +16 -2
  110. package/dist/utils/helperFunctions.js +19 -5
  111. package/dist/utils/loadConfigs.d.ts +34 -9
  112. package/dist/utils/loadConfigs.js +236 -316
  113. package/dist/utils/pathResolvers.d.ts +53 -0
  114. package/dist/utils/pathResolvers.js +72 -0
  115. package/dist/utils/projectConfig.d.ts +119 -0
  116. package/dist/utils/projectConfig.js +171 -0
  117. package/dist/utils/retryFailedPromises.js +4 -2
  118. package/dist/utils/sessionAuth.d.ts +48 -0
  119. package/dist/utils/sessionAuth.js +164 -0
  120. package/dist/utils/sessionPreservationExample.d.ts +1666 -0
  121. package/dist/utils/sessionPreservationExample.js +101 -0
  122. package/dist/utils/setupFiles.js +301 -41
  123. package/dist/utils/typeGuards.d.ts +35 -0
  124. package/dist/utils/typeGuards.js +57 -0
  125. package/dist/utils/versionDetection.js +145 -9
  126. package/dist/utils/yamlConverter.d.ts +53 -3
  127. package/dist/utils/yamlConverter.js +232 -13
  128. package/dist/utils/yamlLoader.d.ts +70 -0
  129. package/dist/utils/yamlLoader.js +263 -0
  130. package/dist/utilsController.d.ts +36 -3
  131. package/dist/utilsController.js +186 -56
  132. package/package.json +12 -2
  133. package/src/adapters/AdapterFactory.ts +263 -35
  134. package/src/adapters/TablesDBAdapter.ts +225 -36
  135. package/src/backups/operations/bucketBackup.ts +277 -0
  136. package/src/backups/operations/collectionBackup.ts +310 -0
  137. package/src/backups/operations/comprehensiveBackup.ts +342 -0
  138. package/src/backups/schemas/bucketManifest.ts +78 -0
  139. package/src/backups/schemas/comprehensiveManifest.ts +76 -0
  140. package/src/backups/tracking/centralizedTracking.ts +352 -0
  141. package/src/cli/commands/configCommands.ts +194 -0
  142. package/src/cli/commands/databaseCommands.ts +635 -0
  143. package/src/cli/commands/functionCommands.ts +379 -0
  144. package/src/cli/commands/schemaCommands.ts +163 -0
  145. package/src/cli/commands/transferCommands.ts +457 -0
  146. package/src/collections/attributes.ts +900 -621
  147. package/src/collections/attributes.ts.backup +1555 -0
  148. package/src/collections/indexes.ts +116 -114
  149. package/src/collections/methods.ts +295 -968
  150. package/src/collections/transferOperations.ts +516 -0
  151. package/src/collections/wipeOperations.ts +501 -0
  152. package/src/config/README.md +274 -0
  153. package/src/config/configMigration.ts +575 -0
  154. package/src/config/configValidation.ts +445 -0
  155. package/src/config/yamlConfig.ts +168 -55
  156. package/src/databases/methods.ts +3 -2
  157. package/src/databases/setup.ts +11 -138
  158. package/src/examples/yamlTerminologyExample.ts +341 -0
  159. package/src/functions/deployments.ts +14 -12
  160. package/src/functions/methods.ts +11 -11
  161. package/src/functions/templates/hono-typescript/README.md +286 -0
  162. package/src/functions/templates/hono-typescript/package.json +26 -0
  163. package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
  164. package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
  165. package/src/functions/templates/hono-typescript/src/app.ts +180 -0
  166. package/src/functions/templates/hono-typescript/src/context.ts +103 -0
  167. package/src/functions/templates/hono-typescript/src/index.ts +54 -0
  168. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
  169. package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
  170. package/src/functions/templates/typescript-node/package.json +2 -1
  171. package/src/functions/templates/typescript-node/src/context.ts +103 -0
  172. package/src/functions/templates/typescript-node/src/index.ts +18 -12
  173. package/src/functions/templates/uv/pyproject.toml +1 -0
  174. package/src/functions/templates/uv/src/context.py +125 -0
  175. package/src/functions/templates/uv/src/index.py +35 -5
  176. package/src/init.ts +9 -11
  177. package/src/interactiveCLI.ts +278 -1596
  178. package/src/main.ts +418 -24
  179. package/src/migrations/afterImportActions.ts +71 -44
  180. package/src/migrations/appwriteToX.ts +100 -34
  181. package/src/migrations/dataLoader.ts +48 -34
  182. package/src/migrations/importController.ts +44 -39
  183. package/src/migrations/relationships.ts +28 -18
  184. package/src/migrations/services/ImportOrchestrator.ts +24 -27
  185. package/src/migrations/transfer.ts +159 -121
  186. package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
  187. package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
  188. package/src/migrations/yaml/generateImportSchemas.ts +751 -12
  189. package/src/setupController.ts +3 -2
  190. package/src/shared/backupMetadataSchema.ts +93 -0
  191. package/src/shared/backupTracking.ts +211 -0
  192. package/src/shared/confirmationDialogs.ts +19 -19
  193. package/src/shared/errorUtils.ts +110 -0
  194. package/src/shared/functionManager.ts +21 -20
  195. package/src/shared/indexManager.ts +12 -11
  196. package/src/shared/jsonSchemaGenerator.ts +38 -52
  197. package/src/shared/logging.ts +75 -0
  198. package/src/shared/messageFormatter.ts +14 -1
  199. package/src/shared/migrationHelpers.ts +45 -38
  200. package/src/shared/operationLogger.ts +11 -36
  201. package/src/shared/operationQueue.ts +322 -93
  202. package/src/shared/operationsTable.ts +338 -0
  203. package/src/shared/operationsTableSchema.ts +60 -0
  204. package/src/shared/relationshipExtractor.ts +214 -0
  205. package/src/shared/schemaGenerator.ts +179 -219
  206. package/src/storage/backupCompression.ts +88 -0
  207. package/src/storage/methods.ts +131 -34
  208. package/src/users/methods.ts +11 -9
  209. package/src/utils/configDiscovery.ts +502 -0
  210. package/src/utils/directoryUtils.ts +61 -0
  211. package/src/utils/getClientFromConfig.ts +205 -22
  212. package/src/utils/helperFunctions.ts +23 -5
  213. package/src/utils/loadConfigs.ts +313 -345
  214. package/src/utils/pathResolvers.ts +81 -0
  215. package/src/utils/projectConfig.ts +299 -0
  216. package/src/utils/retryFailedPromises.ts +4 -2
  217. package/src/utils/sessionAuth.ts +230 -0
  218. package/src/utils/setupFiles.ts +322 -54
  219. package/src/utils/typeGuards.ts +65 -0
  220. package/src/utils/versionDetection.ts +218 -64
  221. package/src/utils/yamlConverter.ts +296 -13
  222. package/src/utils/yamlLoader.ts +364 -0
  223. package/src/utilsController.ts +314 -110
  224. package/tests/README.md +497 -0
  225. package/tests/adapters/AdapterFactory.test.ts +277 -0
  226. package/tests/integration/syncOperations.test.ts +463 -0
  227. package/tests/jest.config.js +25 -0
  228. package/tests/migration/configMigration.test.ts +546 -0
  229. package/tests/setup.ts +62 -0
  230. package/tests/testUtils.ts +340 -0
  231. package/tests/utils/loadConfigs.test.ts +350 -0
  232. package/tests/validation/configValidation.test.ts +412 -0
  233. package/src/utils/schemaStrings.ts +0 -517
@@ -0,0 +1,103 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Appwrite Request Headers Schema
5
+ */
6
+ export const AppwriteHeadersSchema = z.object({
7
+ "x-appwrite-trigger": z.enum(["http", "schedule", "event"]).optional(),
8
+ "x-appwrite-event": z.string().optional(),
9
+ "x-appwrite-key": z.string().optional(),
10
+ "x-appwrite-user-id": z.string().optional(),
11
+ "x-appwrite-user-jwt": z.string().optional(),
12
+ "x-appwrite-country-code": z.string().optional(),
13
+ "x-appwrite-continent-code": z.string().optional(),
14
+ "x-appwrite-continent-eu": z.string().optional(),
15
+ }).catchall(z.string()); // Allow additional headers
16
+
17
+ /**
18
+ * Appwrite Environment Variables Schema
19
+ */
20
+ export const AppwriteEnvSchema = z.object({
21
+ APPWRITE_FUNCTION_API_ENDPOINT: z.string(),
22
+ APPWRITE_VERSION: z.string(),
23
+ APPWRITE_REGION: z.string(),
24
+ APPWRITE_FUNCTION_API_KEY: z.string().optional(),
25
+ APPWRITE_FUNCTION_ID: z.string(),
26
+ APPWRITE_FUNCTION_NAME: z.string(),
27
+ APPWRITE_FUNCTION_DEPLOYMENT: z.string(),
28
+ APPWRITE_FUNCTION_PROJECT_ID: z.string(),
29
+ APPWRITE_FUNCTION_RUNTIME_NAME: z.string(),
30
+ APPWRITE_FUNCTION_RUNTIME_VERSION: z.string(),
31
+ });
32
+
33
+ /**
34
+ * Appwrite Request Schema (full version from docs)
35
+ */
36
+ export const AppwriteRequestSchema = z.object({
37
+ bodyText: z.string().optional(),
38
+ bodyJson: z.union([z.any(), z.string()]).transform((t) => {
39
+ if (typeof t === "string") {
40
+ try {
41
+ return JSON.parse(t);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ return t;
47
+ }).optional(),
48
+ body: z.any().optional(),
49
+ headers: AppwriteHeadersSchema,
50
+ scheme: z.enum(["http", "https"]).optional(),
51
+ method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]),
52
+ url: z.string().optional(),
53
+ host: z.string().optional(),
54
+ port: z.number().or(z.string()).optional(),
55
+ path: z.string().optional(),
56
+ queryString: z.string().optional(),
57
+ query: z.record(z.string(), z.any()).optional(),
58
+ variables: z.record(z.string(), z.any()).optional(),
59
+ text: z.any().optional(),
60
+ payload: z.string().optional(),
61
+ });
62
+
63
+ /**
64
+ * Appwrite Response Schema
65
+ */
66
+ export const AppwriteResponseSchema = z.object({
67
+ json: z.any(),
68
+ text: z.any(),
69
+ empty: z.any().optional(),
70
+ binary: z.any().optional(),
71
+ redirect: z.any().optional(),
72
+ });
73
+
74
+ /**
75
+ * Log Function Schema - Simple function type
76
+ */
77
+ export const AppwriteLogSchema = z.any();
78
+
79
+ /**
80
+ * Error Function Schema - Simple function type
81
+ */
82
+ export const AppwriteErrorSchema = z.any();
83
+
84
+ /**
85
+ * Complete Appwrite Context Schema
86
+ */
87
+ export const AppwriteContextSchema = z.object({
88
+ req: AppwriteRequestSchema,
89
+ res: AppwriteResponseSchema,
90
+ log: AppwriteLogSchema,
91
+ error: AppwriteErrorSchema,
92
+ });
93
+
94
+ /**
95
+ * Type inference helpers
96
+ */
97
+ export type AppwriteHeaders = z.infer<typeof AppwriteHeadersSchema>;
98
+ export type AppwriteEnv = z.infer<typeof AppwriteEnvSchema>;
99
+ export type AppwriteRequest = z.infer<typeof AppwriteRequestSchema>;
100
+ export type AppwriteResponse = z.infer<typeof AppwriteResponseSchema>;
101
+ export type AppwriteLog = z.infer<typeof AppwriteLogSchema>;
102
+ export type AppwriteError = z.infer<typeof AppwriteErrorSchema>;
103
+ export type AppwriteContext = z.infer<typeof AppwriteContextSchema>;
@@ -0,0 +1,54 @@
1
+ import { app } from "./app.js";
2
+ import { AppwriteContext, AppwriteContextSchema } from "./context.js";
3
+ import { convertAppwriteToWebRequest } from "./adapters/request.js";
4
+ import { convertWebResponseToAppwrite } from "./adapters/response.js";
5
+ import { appwriteMiddleware } from "./middleware/appwrite.js";
6
+
7
+ /**
8
+ * Main Appwrite function entry point
9
+ * This function receives the Appwrite context and routes it through Hono
10
+ */
11
+ export default async function (context: AppwriteContext) {
12
+ const { req, res, log, error } = context;
13
+
14
+ try {
15
+ // Optional: Validate the context using Zod schema
16
+ AppwriteContextSchema.parse(context);
17
+ log("Context validation successful");
18
+
19
+ // Add Appwrite context middleware to the Hono app
20
+ app.use("*", appwriteMiddleware(context));
21
+
22
+ // Convert Appwrite request to Web API Request for Hono
23
+ const webRequest = convertAppwriteToWebRequest(req);
24
+
25
+ log(`Processing ${req.method} request to ${req.path || "/"}`);
26
+
27
+ // Create execution environment for Hono
28
+ const env = {
29
+ // Add any environment variables or context needed by Hono
30
+ APPWRITE_FUNCTION_ENDPOINT: process.env["APPWRITE_FUNCTION_ENDPOINT"],
31
+ APPWRITE_FUNCTION_PROJECT_ID: process.env["APPWRITE_FUNCTION_PROJECT_ID"],
32
+ APPWRITE_FUNCTION_API_KEY: process.env["APPWRITE_FUNCTION_API_KEY"],
33
+ };
34
+
35
+ // Process request through Hono app
36
+ const honoResponse = await app.fetch(webRequest, env);
37
+
38
+ // Convert Hono response back to Appwrite response format
39
+ await convertWebResponseToAppwrite(honoResponse, res);
40
+
41
+ log(`Request completed with status ${honoResponse.status}`);
42
+
43
+ } catch (validationError) {
44
+ error(`Function execution failed: ${validationError}`);
45
+
46
+ // Return error response
47
+ return res.json({
48
+ error: "Function execution failed",
49
+ message: validationError instanceof Error ? validationError.message : String(validationError),
50
+ functionName: "{{functionName}}",
51
+ timestamp: new Date().toISOString(),
52
+ }, 500);
53
+ }
54
+ }
@@ -0,0 +1,119 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import type { AppwriteContext } from "../context.js";
3
+
4
+ // Extend Hono's context to include Appwrite context
5
+ declare module "hono" {
6
+ interface ContextVariableMap {
7
+ appwriteContext: AppwriteContext;
8
+ }
9
+ }
10
+
11
+ /**
12
+ * Middleware to inject Appwrite context into Hono
13
+ */
14
+ export const appwriteMiddleware = (appwriteContext: AppwriteContext): MiddlewareHandler => {
15
+ return async (c, next) => {
16
+ // Inject Appwrite context into Hono context
17
+ c.set("appwriteContext", appwriteContext);
18
+
19
+ // Add logging helper methods to Hono context
20
+ c.log = (message: string) => {
21
+ appwriteContext.log(`[${c.req.method} ${c.req.path}] ${message}`);
22
+ };
23
+
24
+ c.error = (message: string) => {
25
+ appwriteContext.error(`[${c.req.method} ${c.req.path}] ${message}`);
26
+ };
27
+
28
+ await next();
29
+ };
30
+ };
31
+
32
+ /**
33
+ * Middleware for request logging
34
+ */
35
+ export const requestLogger = (): MiddlewareHandler => {
36
+ return async (c, next) => {
37
+ const start = Date.now();
38
+ const appwriteContext = c.get("appwriteContext");
39
+
40
+ appwriteContext?.log(`→ ${c.req.method} ${c.req.path}`);
41
+
42
+ await next();
43
+
44
+ const duration = Date.now() - start;
45
+ appwriteContext?.log(`← ${c.req.method} ${c.req.path} ${c.res.status} (${duration}ms)`);
46
+ };
47
+ };
48
+
49
+ /**
50
+ * Middleware for error handling
51
+ */
52
+ export const errorHandler = (): MiddlewareHandler => {
53
+ return async (c, next) => {
54
+ try {
55
+ await next();
56
+ } catch (err) {
57
+ const appwriteContext = c.get("appwriteContext");
58
+ const error = err instanceof Error ? err : new Error(String(err));
59
+
60
+ appwriteContext?.error(`Error in ${c.req.method} ${c.req.path}: ${error.message}`);
61
+
62
+ return c.json(
63
+ {
64
+ error: "Internal Server Error",
65
+ message: error.message,
66
+ path: c.req.path,
67
+ method: c.req.method,
68
+ },
69
+ 500
70
+ );
71
+ }
72
+ };
73
+ };
74
+
75
+ /**
76
+ * Middleware to extract and validate Appwrite headers
77
+ */
78
+ export const appwriteHeaders = (): MiddlewareHandler => {
79
+ return async (c, next) => {
80
+ const appwriteContext = c.get("appwriteContext");
81
+ const headers = appwriteContext?.req.headers;
82
+
83
+ // Add helpful getters for common Appwrite headers
84
+ c.appwrite = {
85
+ trigger: headers?.["x-appwrite-trigger"] as "http" | "schedule" | "event" | undefined,
86
+ event: headers?.["x-appwrite-event"],
87
+ key: headers?.["x-appwrite-key"],
88
+ userId: headers?.["x-appwrite-user-id"],
89
+ userJwt: headers?.["x-appwrite-user-jwt"],
90
+ countryCode: headers?.["x-appwrite-country-code"],
91
+ continentCode: headers?.["x-appwrite-continent-code"],
92
+ continentEu: headers?.["x-appwrite-continent-eu"],
93
+ isUserAuthenticated: () => !!headers?.["x-appwrite-user-id"],
94
+ isApiKeyRequest: () => !!headers?.["x-appwrite-key"],
95
+ };
96
+
97
+ await next();
98
+ };
99
+ };
100
+
101
+ // Extend Hono context to include Appwrite helpers
102
+ declare module "hono" {
103
+ interface Context {
104
+ log: (message: string) => void;
105
+ error: (message: string) => void;
106
+ appwrite: {
107
+ trigger?: "http" | "schedule" | "event";
108
+ event?: string;
109
+ key?: string;
110
+ userId?: string;
111
+ userJwt?: string;
112
+ countryCode?: string;
113
+ continentCode?: string;
114
+ continentEu?: string;
115
+ isUserAuthenticated: () => boolean;
116
+ isApiKeyRequest: () => boolean;
117
+ };
118
+ }
119
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "node",
6
+ "allowSyntheticDefaultImports": true,
7
+ "esModuleInterop": true,
8
+ "allowJs": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "outDir": "./dist",
14
+ "rootDir": "./src",
15
+ "resolveJsonModule": true,
16
+ "types": ["node"]
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }
@@ -11,7 +11,8 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "node-appwrite": "^13.0.0",
14
- "appwrite-utils": "latest"
14
+ "appwrite-utils": "latest",
15
+ "zod": "^3.22.0"
15
16
  },
16
17
  "devDependencies": {
17
18
  "@types/node": "^20.0.0",
@@ -0,0 +1,103 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Appwrite Request Headers Schema
5
+ */
6
+ export const AppwriteHeadersSchema = z.object({
7
+ "x-appwrite-trigger": z.enum(["http", "schedule", "event"]).optional(),
8
+ "x-appwrite-event": z.string().optional(),
9
+ "x-appwrite-key": z.string().optional(),
10
+ "x-appwrite-user-id": z.string().optional(),
11
+ "x-appwrite-user-jwt": z.string().optional(),
12
+ "x-appwrite-country-code": z.string().optional(),
13
+ "x-appwrite-continent-code": z.string().optional(),
14
+ "x-appwrite-continent-eu": z.string().optional(),
15
+ }).catchall(z.string()); // Allow additional headers
16
+
17
+ /**
18
+ * Appwrite Environment Variables Schema
19
+ */
20
+ export const AppwriteEnvSchema = z.object({
21
+ APPWRITE_FUNCTION_API_ENDPOINT: z.string(),
22
+ APPWRITE_VERSION: z.string(),
23
+ APPWRITE_REGION: z.string(),
24
+ APPWRITE_FUNCTION_API_KEY: z.string().optional(),
25
+ APPWRITE_FUNCTION_ID: z.string(),
26
+ APPWRITE_FUNCTION_NAME: z.string(),
27
+ APPWRITE_FUNCTION_DEPLOYMENT: z.string(),
28
+ APPWRITE_FUNCTION_PROJECT_ID: z.string(),
29
+ APPWRITE_FUNCTION_RUNTIME_NAME: z.string(),
30
+ APPWRITE_FUNCTION_RUNTIME_VERSION: z.string(),
31
+ });
32
+
33
+ /**
34
+ * Appwrite Request Schema (full version from docs)
35
+ */
36
+ export const AppwriteRequestSchema = z.object({
37
+ bodyText: z.string().optional(),
38
+ bodyJson: z.union([z.any(), z.string()]).transform((t) => {
39
+ if (typeof t === "string") {
40
+ try {
41
+ return JSON.parse(t);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ return t;
47
+ }).optional(),
48
+ body: z.any().optional(),
49
+ headers: AppwriteHeadersSchema,
50
+ scheme: z.enum(["http", "https"]).optional(),
51
+ method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]),
52
+ url: z.string().optional(),
53
+ host: z.string().optional(),
54
+ port: z.number().or(z.string()).optional(),
55
+ path: z.string().optional(),
56
+ queryString: z.string().optional(),
57
+ query: z.record(z.string(), z.any()).optional(),
58
+ variables: z.record(z.string(), z.any()).optional(),
59
+ text: z.any().optional(),
60
+ payload: z.string().optional(),
61
+ });
62
+
63
+ /**
64
+ * Appwrite Response Schema
65
+ */
66
+ export const AppwriteResponseSchema = z.object({
67
+ json: z.any(),
68
+ text: z.any(),
69
+ empty: z.any().optional(),
70
+ binary: z.any().optional(),
71
+ redirect: z.any().optional(),
72
+ });
73
+
74
+ /**
75
+ * Log Function Schema - Simple function type
76
+ */
77
+ export const AppwriteLogSchema = z.any();
78
+
79
+ /**
80
+ * Error Function Schema - Simple function type
81
+ */
82
+ export const AppwriteErrorSchema = z.any();
83
+
84
+ /**
85
+ * Complete Appwrite Context Schema
86
+ */
87
+ export const AppwriteContextSchema = z.object({
88
+ req: AppwriteRequestSchema,
89
+ res: AppwriteResponseSchema,
90
+ log: AppwriteLogSchema,
91
+ error: AppwriteErrorSchema,
92
+ });
93
+
94
+ /**
95
+ * Type inference helpers
96
+ */
97
+ export type AppwriteHeaders = z.infer<typeof AppwriteHeadersSchema>;
98
+ export type AppwriteEnv = z.infer<typeof AppwriteEnvSchema>;
99
+ export type AppwriteRequest = z.infer<typeof AppwriteRequestSchema>;
100
+ export type AppwriteResponse = z.infer<typeof AppwriteResponseSchema>;
101
+ export type AppwriteLog = z.infer<typeof AppwriteLogSchema>;
102
+ export type AppwriteError = z.infer<typeof AppwriteErrorSchema>;
103
+ export type AppwriteContext = z.infer<typeof AppwriteContextSchema>;
@@ -1,23 +1,29 @@
1
1
  import { Client } from "node-appwrite";
2
- import { AppwriteRequest, type AppwriteResponse } from "appwrite-utils";
2
+ import { AppwriteContext, AppwriteContextSchema } from "./context.js";
3
+
4
+ export default async function (context: AppwriteContext) {
5
+ const { req, res, log, error } = context;
6
+
7
+ // Optional: Validate the context using Zod schema
8
+ try {
9
+ AppwriteContextSchema.parse(context);
10
+ log("Context validation successful");
11
+ } catch (validationError) {
12
+ error(`Context validation failed: ${validationError}`);
13
+ }
3
14
 
4
- export default async function ({
5
- req,
6
- res,
7
- log,
8
- error,
9
- }: {
10
- req: AppwriteRequest;
11
- res: AppwriteResponse;
12
- log: (message: string) => void;
13
- error: (message: string) => void;
14
- }) {
15
15
  const client = new Client()
16
16
  .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"]!)
17
17
  .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"]!)
18
18
  .setKey(req.headers["x-appwrite-key"] || "");
19
19
 
20
+ log(`Processing ${req.method} request to ${req.path}`);
21
+
20
22
  return res.json({
21
23
  message: "Hello from TypeScript function!",
24
+ functionName: "{{functionName}}",
25
+ method: req.method,
26
+ path: req.path,
27
+ headers: req.headers,
22
28
  });
23
29
  }
@@ -7,6 +7,7 @@ readme = "README.md"
7
7
  requires-python = ">=3.9"
8
8
  dependencies = [
9
9
  "appwrite>=7.0.1",
10
+ "pydantic>=2.0.0",
10
11
  ]
11
12
 
12
13
  [build-system]
@@ -0,0 +1,125 @@
1
+ from collections.abc import Callable
2
+ from typing import Any, Literal, Protocol
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class AppwriteHeaders(BaseModel):
8
+ """Appwrite request headers"""
9
+
10
+ x_appwrite_trigger: Literal["http", "schedule", "event"] | None = Field(
11
+ None, alias="x-appwrite-trigger"
12
+ )
13
+ x_appwrite_event: str | None = Field(None, alias="x-appwrite-event")
14
+ x_appwrite_key: str | None = Field(None, alias="x-appwrite-key")
15
+ x_appwrite_user_id: str | None = Field(None, alias="x-appwrite-user-id")
16
+ x_appwrite_user_jwt: str | None = Field(None, alias="x-appwrite-user-jwt")
17
+ x_appwrite_country_code: str | None = Field(None, alias="x-appwrite-country-code")
18
+ x_appwrite_continent_code: str | None = Field(
19
+ None, alias="x-appwrite-continent-code"
20
+ )
21
+ x_appwrite_continent_eu: str | None = Field(None, alias="x-appwrite-continent-eu")
22
+
23
+ # Allow additional headers
24
+ model_config = {"extra": "allow", "populate_by_name": True}
25
+
26
+
27
+ class AppwriteEnv(BaseModel):
28
+ """Appwrite environment variables"""
29
+
30
+ APPWRITE_FUNCTION_API_ENDPOINT: str
31
+ APPWRITE_VERSION: str
32
+ APPWRITE_REGION: str
33
+ APPWRITE_FUNCTION_API_KEY: str | None = None
34
+ APPWRITE_FUNCTION_ID: str
35
+ APPWRITE_FUNCTION_NAME: str
36
+ APPWRITE_FUNCTION_DEPLOYMENT: str
37
+ APPWRITE_FUNCTION_PROJECT_ID: str
38
+ APPWRITE_FUNCTION_RUNTIME_NAME: str
39
+ APPWRITE_FUNCTION_RUNTIME_VERSION: str
40
+
41
+
42
+ class AppwriteRequest(Protocol):
43
+ """Appwrite function request object with proper callable typing"""
44
+
45
+ bodyText: str | None
46
+ bodyJson: dict[str, Any] | str | None
47
+ body: Any
48
+ headers: dict[str, str]
49
+ scheme: Literal["http", "https"] | None
50
+ method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
51
+ url: str | None
52
+ host: str | None
53
+ port: int | str | None
54
+ path: str | None
55
+ queryString: str | None
56
+ query: dict[str, str | list[str]] | None
57
+ variables: dict[str, str] | None
58
+ payload: str | None
59
+
60
+ async def text(self) -> str: ...
61
+
62
+
63
+ class AppwriteResponse(Protocol):
64
+ """Appwrite function response object with proper callable typing"""
65
+
66
+ def json(
67
+ self,
68
+ data: Any,
69
+ status: int | None = None,
70
+ headers: dict[str, str] | None = None,
71
+ ) -> None: ...
72
+
73
+ def text(
74
+ self,
75
+ body: str,
76
+ status: int | None = None,
77
+ headers: dict[str, str] | None = None,
78
+ ) -> None: ...
79
+
80
+ def empty(self) -> None: ...
81
+
82
+ def binary(self, data: bytes) -> None: ...
83
+
84
+ def redirect(self, url: str, status: int | None = None) -> None: ...
85
+
86
+
87
+ class AppwriteContext(Protocol):
88
+ """Complete Appwrite function context with proper typing"""
89
+
90
+ req: AppwriteRequest
91
+ res: AppwriteResponse
92
+
93
+ def log(self, message: str) -> None: ...
94
+
95
+ def error(self, message: str) -> None: ...
96
+
97
+
98
+ # Alternative: BaseModel version for cases where you need Pydantic features
99
+ # but want proper typing hints
100
+
101
+
102
+ class AppwriteRequestModel(BaseModel):
103
+ """Pydantic model for request validation (if needed)"""
104
+
105
+ bodyText: str | None = None
106
+ bodyJson: dict[str, Any] | str | None = None
107
+ body: Any = None
108
+ headers: dict[str, str] = Field(default_factory=dict)
109
+ scheme: Literal["http", "https"] | None = None
110
+ method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
111
+ url: str | None = None
112
+ host: str | None = None
113
+ port: int | str | None = None
114
+ path: str | None = None
115
+ queryString: str | None = None
116
+ query: dict[str, str | list[str]] | None = None
117
+ variables: dict[str, str] | None = None
118
+ payload: str | None = None
119
+
120
+ model_config = {"arbitrary_types_allowed": True}
121
+
122
+
123
+ # Type alias for the log/error functions
124
+ LogFunction = Callable[[str], None]
125
+ ErrorFunction = Callable[[str], None]
@@ -1,16 +1,46 @@
1
+ import os
1
2
  from appwrite.client import Client
3
+ from context import AppwriteContext, AppwriteHeaders, AppwriteRequestModel
2
4
 
3
- def main(context):
5
+ def main(context: AppwriteContext):
4
6
  req = context.req
5
7
  res = context.res
6
8
  log = context.log
7
9
  error = context.error
8
10
 
11
+ # Optional: Validate request headers using Pydantic
12
+ try:
13
+ headers = AppwriteHeaders.model_validate(req.headers)
14
+ log("Headers validation successful")
15
+ except Exception as validation_error:
16
+ error(f"Headers validation failed: {validation_error}")
17
+
18
+ # Optional: Validate full request using Pydantic model
19
+ try:
20
+ request_model = AppwriteRequestModel.model_validate({
21
+ 'method': req.method,
22
+ 'headers': req.headers,
23
+ 'path': req.path,
24
+ 'url': req.url,
25
+ 'body': req.body,
26
+ 'bodyText': req.bodyText,
27
+ 'bodyJson': req.bodyJson
28
+ })
29
+ log("Request validation successful")
30
+ except Exception as validation_error:
31
+ error(f"Request validation failed: {validation_error}")
32
+
9
33
  client = Client()
10
- client.set_endpoint(req.env['APPWRITE_FUNCTION_ENDPOINT'])
11
- client.set_project(req.env['APPWRITE_FUNCTION_PROJECT_ID'])
12
- client.set_key(req.env['APPWRITE_FUNCTION_API_KEY'])
34
+ client.set_endpoint(os.getenv('APPWRITE_FUNCTION_ENDPOINT'))
35
+ client.set_project(os.getenv('APPWRITE_FUNCTION_PROJECT_ID'))
36
+ client.set_key(os.getenv('APPWRITE_FUNCTION_API_KEY'))
37
+
38
+ log(f"Processing {req.method} request to {req.path}")
13
39
 
14
40
  return res.json({
15
- 'message': 'Hello from Python function!'
41
+ 'message': 'Hello from Python function!',
42
+ 'functionName': '{{functionName}}',
43
+ 'method': req.method,
44
+ 'path': req.path,
45
+ 'headers': req.headers
16
46
  })