appwrite-utils-cli 1.5.1 → 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 +186 -1171
  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 +276 -1591
  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,286 @@
1
+ # {{functionName}} - Hono + Appwrite Function
2
+
3
+ This is an Appwrite TypeScript function built with the [Hono](https://hono.dev) web framework. Hono provides a fast, lightweight, and modern way to build web APIs with excellent TypeScript support.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Ultra-fast routing** with Hono framework
8
+ - 🔒 **Built-in Appwrite integration** with context injection
9
+ - 📝 **Full TypeScript support** with type safety
10
+ - 🛠️ **Comprehensive middleware** for logging, error handling, and Appwrite context
11
+ - 🔧 **Request/Response adapters** for seamless integration
12
+ - 📊 **Example API endpoints** with database operations
13
+
14
+ ## Project Structure
15
+
16
+ ```
17
+ src/
18
+ ├── index.ts # Main Appwrite function entry point
19
+ ├── app.ts # Hono application with routes
20
+ ├── context.ts # Appwrite context type definitions
21
+ ├── adapters/
22
+ │ ├── request.ts # Appwrite → Hono request conversion
23
+ │ └── response.ts # Hono → Appwrite response conversion
24
+ └── middleware/
25
+ └── appwrite.ts # Appwrite context middleware
26
+ ```
27
+
28
+ ## Getting Started
29
+
30
+ ### 1. Install Dependencies
31
+
32
+ ```bash
33
+ npm install
34
+ ```
35
+
36
+ ### 2. Available Routes
37
+
38
+ - `GET /` - Welcome message with function info
39
+ - `GET /health` - Health check endpoint
40
+ - `GET /api/user` - Get current authenticated user (requires user session)
41
+ - `POST /api/webhook` - Generic webhook handler
42
+ - `GET /api/databases` - List databases (requires API key)
43
+ - `POST /api/data/:databaseId/:collectionId` - Create document
44
+ - `GET /api/data/:databaseId/:collectionId` - List documents
45
+
46
+ ### 3. Development
47
+
48
+ ```bash
49
+ npm run dev
50
+ ```
51
+
52
+ ### 4. Build
53
+
54
+ ```bash
55
+ npm run build
56
+ ```
57
+
58
+ ## Adding New Routes
59
+
60
+ You can easily add new routes to your Hono app in `src/app.ts`:
61
+
62
+ ```typescript
63
+ // GET endpoint
64
+ app.get("/api/hello/:name", (c) => {
65
+ const name = c.req.param("name");
66
+ c.log(`Hello endpoint called for ${name}`);
67
+
68
+ return c.json({
69
+ message: `Hello, ${name}!`,
70
+ timestamp: new Date().toISOString(),
71
+ });
72
+ });
73
+
74
+ // POST endpoint with JSON body
75
+ app.post("/api/items", async (c) => {
76
+ const data = await c.req.json();
77
+
78
+ // Access Appwrite context
79
+ const { databases } = getAppwriteClient(c);
80
+
81
+ try {
82
+ const document = await databases.createDocument(
83
+ "database-id",
84
+ "collection-id",
85
+ "unique()",
86
+ data
87
+ );
88
+
89
+ c.log("Item created successfully");
90
+ return c.json({ item: document }, 201);
91
+ } catch (error) {
92
+ c.error(`Failed to create item: ${error}`);
93
+ return c.json({ error: "Failed to create item" }, 500);
94
+ }
95
+ });
96
+ ```
97
+
98
+ ## Middleware
99
+
100
+ The template includes several built-in middleware:
101
+
102
+ ### Appwrite Context Middleware
103
+ Automatically injects Appwrite context into every Hono request:
104
+
105
+ ```typescript
106
+ // Access Appwrite context in any route handler
107
+ app.get("/api/example", (c) => {
108
+ const appwriteContext = c.get("appwriteContext");
109
+
110
+ // Use Appwrite logging
111
+ c.log("This will appear in Appwrite function logs");
112
+ c.error("This will appear as an error in Appwrite logs");
113
+
114
+ // Access Appwrite headers
115
+ const userId = c.appwrite.userId;
116
+ const isAuthenticated = c.appwrite.isUserAuthenticated();
117
+
118
+ return c.json({ userId, isAuthenticated });
119
+ });
120
+ ```
121
+
122
+ ### Request Logging
123
+ Automatically logs incoming requests and responses with timing:
124
+
125
+ ```
126
+ → GET /api/user
127
+ ← GET /api/user 200 (45ms)
128
+ ```
129
+
130
+ ### Error Handling
131
+ Catches and properly formats errors:
132
+
133
+ ```typescript
134
+ app.get("/api/error-example", (c) => {
135
+ throw new Error("Something went wrong");
136
+ // This will be automatically caught and returned as:
137
+ // { error: "Internal Server Error", message: "Something went wrong", ... }
138
+ });
139
+ ```
140
+
141
+ ## Appwrite Integration
142
+
143
+ ### Authentication
144
+
145
+ Check if a user is authenticated:
146
+
147
+ ```typescript
148
+ app.get("/api/protected", (c) => {
149
+ if (!c.appwrite.isUserAuthenticated()) {
150
+ return c.json({ error: "Authentication required" }, 401);
151
+ }
152
+
153
+ const userId = c.appwrite.userId;
154
+ return c.json({ message: `Hello user ${userId}` });
155
+ });
156
+ ```
157
+
158
+ ### API Key Access
159
+
160
+ Check if request has valid API key:
161
+
162
+ ```typescript
163
+ app.get("/api/admin", (c) => {
164
+ if (!c.appwrite.isApiKeyRequest()) {
165
+ return c.json({ error: "API key required" }, 401);
166
+ }
167
+
168
+ // Admin operations here
169
+ return c.json({ message: "Admin access granted" });
170
+ });
171
+ ```
172
+
173
+ ### Database Operations
174
+
175
+ ```typescript
176
+ app.post("/api/posts", async (c) => {
177
+ const data = await c.req.json();
178
+ const { databases } = getAppwriteClient(c);
179
+
180
+ try {
181
+ const post = await databases.createDocument(
182
+ "blog-db",
183
+ "posts",
184
+ "unique()",
185
+ {
186
+ title: data.title,
187
+ content: data.content,
188
+ authorId: c.appwrite.userId,
189
+ createdAt: new Date().toISOString(),
190
+ }
191
+ );
192
+
193
+ return c.json({ post }, 201);
194
+ } catch (error) {
195
+ c.error(`Failed to create post: ${error}`);
196
+ return c.json({ error: "Failed to create post" }, 500);
197
+ }
198
+ });
199
+ ```
200
+
201
+ ## Environment Variables
202
+
203
+ The following environment variables are automatically available:
204
+
205
+ - `APPWRITE_FUNCTION_ENDPOINT` - Appwrite server endpoint
206
+ - `APPWRITE_FUNCTION_PROJECT_ID` - Current project ID
207
+ - `APPWRITE_FUNCTION_API_KEY` - Function API key
208
+ - `APPWRITE_FUNCTION_ID` - Current function ID
209
+ - `APPWRITE_FUNCTION_NAME` - Function name
210
+
211
+ ## Response Types
212
+
213
+ Hono provides several response helpers:
214
+
215
+ ```typescript
216
+ // JSON response
217
+ return c.json({ data: "value" });
218
+
219
+ // Text response
220
+ return c.text("Hello World");
221
+
222
+ // HTML response
223
+ return c.html("<h1>Hello</h1>");
224
+
225
+ // Redirect
226
+ return c.redirect("/new-url");
227
+
228
+ // Custom status
229
+ return c.json({ error: "Not found" }, 404);
230
+
231
+ // Custom headers
232
+ return c.json({ data: "value" }, 200, {
233
+ "X-Custom-Header": "value"
234
+ });
235
+ ```
236
+
237
+ ## Advanced Usage
238
+
239
+ ### Custom Middleware
240
+
241
+ ```typescript
242
+ const authMiddleware = () => {
243
+ return async (c, next) => {
244
+ const token = c.req.header("Authorization");
245
+
246
+ if (!token) {
247
+ return c.json({ error: "Missing token" }, 401);
248
+ }
249
+
250
+ // Validate token logic here
251
+ c.set("user", { id: "123", email: "user@example.com" });
252
+
253
+ await next();
254
+ };
255
+ };
256
+
257
+ // Apply to specific routes
258
+ app.use("/api/protected/*", authMiddleware());
259
+ ```
260
+
261
+ ### Path Parameters and Query Strings
262
+
263
+ ```typescript
264
+ // Path parameters
265
+ app.get("/api/users/:id/posts/:postId", (c) => {
266
+ const userId = c.req.param("id");
267
+ const postId = c.req.param("postId");
268
+
269
+ return c.json({ userId, postId });
270
+ });
271
+
272
+ // Query parameters
273
+ app.get("/api/search", (c) => {
274
+ const query = c.req.query("q");
275
+ const page = parseInt(c.req.query("page") || "1");
276
+ const limit = parseInt(c.req.query("limit") || "10");
277
+
278
+ return c.json({ query, page, limit });
279
+ });
280
+ ```
281
+
282
+ ## Learn More
283
+
284
+ - [Hono Documentation](https://hono.dev/docs)
285
+ - [Appwrite Functions Documentation](https://appwrite.io/docs/functions)
286
+ - [TypeScript Documentation](https://www.typescriptlang.org/docs/)
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{functionName}}",
3
+ "version": "1.0.0",
4
+ "description": "Appwrite TypeScript function with Hono web framework",
5
+ "main": "src/index.ts",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "start": "node dist/index.js",
10
+ "dev": "tsx src/index.ts"
11
+ },
12
+ "dependencies": {
13
+ "node-appwrite": "^13.0.0",
14
+ "appwrite-utils": "latest",
15
+ "hono": "^4.0.0",
16
+ "zod": "^3.22.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^20.0.0",
20
+ "typescript": "^5.0.0",
21
+ "tsx": "^4.0.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ }
26
+ }
@@ -0,0 +1,74 @@
1
+ import type { AppwriteRequest } from "../context.js";
2
+
3
+ /**
4
+ * Convert Appwrite request to Web API Request for Hono
5
+ */
6
+ export function convertAppwriteToWebRequest(appwriteReq: AppwriteRequest): Request {
7
+ // Construct the URL
8
+ const protocol = appwriteReq.scheme || "https";
9
+ const host = appwriteReq.host || "localhost";
10
+ const port = appwriteReq.port ? `:${appwriteReq.port}` : "";
11
+ const path = appwriteReq.path || "/";
12
+ const queryString = appwriteReq.queryString ? `?${appwriteReq.queryString}` : "";
13
+
14
+ const url = `${protocol}://${host}${port}${path}${queryString}`;
15
+
16
+ // Prepare headers
17
+ const headers = new Headers();
18
+ if (appwriteReq.headers) {
19
+ Object.entries(appwriteReq.headers).forEach(([key, value]) => {
20
+ if (value) {
21
+ headers.set(key, value);
22
+ }
23
+ });
24
+ }
25
+
26
+ // Prepare body for POST/PUT/PATCH requests
27
+ let body: string | undefined;
28
+ if (appwriteReq.method !== "GET" && appwriteReq.method !== "HEAD") {
29
+ if (appwriteReq.bodyText) {
30
+ body = appwriteReq.bodyText;
31
+ } else if (appwriteReq.bodyJson) {
32
+ body = typeof appwriteReq.bodyJson === "string"
33
+ ? appwriteReq.bodyJson
34
+ : JSON.stringify(appwriteReq.bodyJson);
35
+ if (!headers.has("content-type")) {
36
+ headers.set("content-type", "application/json");
37
+ }
38
+ } else if (appwriteReq.body) {
39
+ body = typeof appwriteReq.body === "string"
40
+ ? appwriteReq.body
41
+ : JSON.stringify(appwriteReq.body);
42
+ }
43
+ }
44
+
45
+ // Create Web API Request
46
+ const request = new Request(url, {
47
+ method: appwriteReq.method,
48
+ headers,
49
+ body,
50
+ });
51
+
52
+ return request;
53
+ }
54
+
55
+ /**
56
+ * Extract path parameters from Hono context for Appwrite compatibility
57
+ */
58
+ export function extractPathParams(honoParams: Record<string, string>): Record<string, string> {
59
+ return honoParams;
60
+ }
61
+
62
+ /**
63
+ * Extract query parameters from URL for Appwrite compatibility
64
+ */
65
+ export function extractQueryParams(url: string): Record<string, string> {
66
+ const urlObj = new URL(url);
67
+ const params: Record<string, string> = {};
68
+
69
+ urlObj.searchParams.forEach((value, key) => {
70
+ params[key] = value;
71
+ });
72
+
73
+ return params;
74
+ }
@@ -0,0 +1,106 @@
1
+ import type { AppwriteResponse } from "../context.js";
2
+
3
+ /**
4
+ * Convert Hono Response to Appwrite response format
5
+ */
6
+ export async function convertWebResponseToAppwrite(
7
+ honoResponse: Response,
8
+ appwriteRes: AppwriteResponse
9
+ ): Promise<void> {
10
+ const status = honoResponse.status;
11
+ const headers: Record<string, string> = {};
12
+
13
+ // Extract headers from Hono response
14
+ honoResponse.headers.forEach((value, key) => {
15
+ headers[key] = value;
16
+ });
17
+
18
+ // Get content type to determine response handling
19
+ const contentType = honoResponse.headers.get("content-type") || "";
20
+
21
+ try {
22
+ // Handle different response types based on content-type
23
+ if (contentType.includes("application/json")) {
24
+ const data = await honoResponse.json();
25
+ return appwriteRes.json(data, status, headers);
26
+ } else if (contentType.includes("text/")) {
27
+ const text = await honoResponse.text();
28
+ return appwriteRes.text(text, status, headers);
29
+ } else if (contentType.includes("application/octet-stream") || contentType.includes("image/") || contentType.includes("video/")) {
30
+ const arrayBuffer = await honoResponse.arrayBuffer();
31
+ const bytes = new Uint8Array(arrayBuffer);
32
+ return appwriteRes.binary(bytes);
33
+ } else {
34
+ // Default to text for unknown content types
35
+ const text = await honoResponse.text();
36
+ return appwriteRes.text(text, status, headers);
37
+ }
38
+ } catch (error) {
39
+ // If response body parsing fails, return empty response
40
+ console.error("Error converting Hono response:", error);
41
+ return appwriteRes.empty();
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Helper function to create common response types for Hono handlers
47
+ */
48
+ export class AppwriteResponseHelpers {
49
+ /**
50
+ * Create a JSON response helper
51
+ */
52
+ static json(data: any, status: number = 200, headers?: Record<string, string>) {
53
+ const responseHeaders = new Headers(headers);
54
+ responseHeaders.set("content-type", "application/json");
55
+
56
+ return new Response(JSON.stringify(data), {
57
+ status,
58
+ headers: responseHeaders,
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Create a text response helper
64
+ */
65
+ static text(text: string, status: number = 200, headers?: Record<string, string>) {
66
+ const responseHeaders = new Headers(headers);
67
+ responseHeaders.set("content-type", "text/plain");
68
+
69
+ return new Response(text, {
70
+ status,
71
+ headers: responseHeaders,
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Create an HTML response helper
77
+ */
78
+ static html(html: string, status: number = 200, headers?: Record<string, string>) {
79
+ const responseHeaders = new Headers(headers);
80
+ responseHeaders.set("content-type", "text/html");
81
+
82
+ return new Response(html, {
83
+ status,
84
+ headers: responseHeaders,
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Create a redirect response helper
90
+ */
91
+ static redirect(url: string, status: number = 302) {
92
+ return new Response(null, {
93
+ status,
94
+ headers: {
95
+ location: url,
96
+ },
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Create an empty response helper
102
+ */
103
+ static empty(status: number = 204) {
104
+ return new Response(null, { status });
105
+ }
106
+ }
@@ -0,0 +1,180 @@
1
+ import { Hono } from "hono";
2
+ import { Client, Databases, Storage, Users } from "node-appwrite";
3
+ import { appwriteMiddleware, requestLogger, errorHandler, appwriteHeaders } from "./middleware/appwrite.js";
4
+
5
+ // Create the Hono app
6
+ export const app = new Hono();
7
+
8
+ // Global middleware - Applied to all routes
9
+ app.use("*", errorHandler());
10
+ app.use("*", requestLogger());
11
+ app.use("*", appwriteHeaders());
12
+
13
+ // Helper function to get initialized Appwrite client
14
+ function getAppwriteClient(c: any) {
15
+ const appwriteContext = c.get("appwriteContext");
16
+
17
+ const client = new Client()
18
+ .setEndpoint(process.env["APPWRITE_FUNCTION_ENDPOINT"]!)
19
+ .setProject(process.env["APPWRITE_FUNCTION_PROJECT_ID"]!)
20
+ .setKey(appwriteContext.req.headers["x-appwrite-key"] || process.env["APPWRITE_FUNCTION_API_KEY"]!);
21
+
22
+ return {
23
+ client,
24
+ databases: new Databases(client),
25
+ storage: new Storage(client),
26
+ users: new Users(client),
27
+ };
28
+ }
29
+
30
+ // Routes
31
+
32
+ // Health check endpoint
33
+ app.get("/", (c) => {
34
+ c.log("Health check requested");
35
+ return c.json({
36
+ message: "Hello from {{functionName}} with Hono!",
37
+ functionName: "{{functionName}}",
38
+ timestamp: new Date().toISOString(),
39
+ method: c.req.method,
40
+ path: c.req.path,
41
+ appwriteTrigger: c.appwrite.trigger,
42
+ isAuthenticated: c.appwrite.isUserAuthenticated(),
43
+ });
44
+ });
45
+
46
+ // API health endpoint
47
+ app.get("/health", (c) => {
48
+ return c.json({
49
+ status: "healthy",
50
+ timestamp: new Date().toISOString(),
51
+ version: "1.0.0",
52
+ environment: {
53
+ nodeVersion: process.version,
54
+ appwriteEndpoint: process.env["APPWRITE_FUNCTION_ENDPOINT"],
55
+ appwriteProject: process.env["APPWRITE_FUNCTION_PROJECT_ID"],
56
+ appwriteRuntime: process.env["APPWRITE_FUNCTION_RUNTIME_NAME"],
57
+ },
58
+ });
59
+ });
60
+
61
+ // Example API endpoints
62
+
63
+ // GET /api/user - Get current user info
64
+ app.get("/api/user", async (c) => {
65
+ if (!c.appwrite.isUserAuthenticated()) {
66
+ return c.json({ error: "User not authenticated" }, 401);
67
+ }
68
+
69
+ try {
70
+ const { users } = getAppwriteClient(c);
71
+ const user = await users.get(c.appwrite.userId!);
72
+
73
+ c.log(`Retrieved user info for ${user.email}`);
74
+ return c.json({ user });
75
+ } catch (error) {
76
+ c.error(`Failed to get user: ${error}`);
77
+ return c.json({ error: "Failed to retrieve user" }, 500);
78
+ }
79
+ });
80
+
81
+ // POST /api/webhook - Generic webhook handler
82
+ app.post("/api/webhook", async (c) => {
83
+ const data = await c.req.json();
84
+
85
+ c.log(`Webhook received: ${JSON.stringify(data)}`);
86
+
87
+ // Process webhook data here
88
+ // Example: Save to database, send notifications, etc.
89
+
90
+ return c.json({
91
+ message: "Webhook processed successfully",
92
+ received: data,
93
+ timestamp: new Date().toISOString(),
94
+ event: c.appwrite.event,
95
+ });
96
+ });
97
+
98
+ // GET /api/databases - List databases (requires API key)
99
+ app.get("/api/databases", async (c) => {
100
+ if (!c.appwrite.isApiKeyRequest()) {
101
+ return c.json({ error: "API key required" }, 401);
102
+ }
103
+
104
+ try {
105
+ const { databases } = getAppwriteClient(c);
106
+ const databasesList = await databases.list();
107
+
108
+ c.log(`Listed ${databasesList.databases.length} databases`);
109
+ return c.json({ databases: databasesList.databases });
110
+ } catch (error) {
111
+ c.error(`Failed to list databases: ${error}`);
112
+ return c.json({ error: "Failed to list databases" }, 500);
113
+ }
114
+ });
115
+
116
+ // POST /api/data/:databaseId/:collectionId - Create document
117
+ app.post("/api/data/:databaseId/:collectionId", async (c) => {
118
+ const databaseId = c.req.param("databaseId");
119
+ const collectionId = c.req.param("collectionId");
120
+ const data = await c.req.json();
121
+
122
+ try {
123
+ const { databases } = getAppwriteClient(c);
124
+ const document = await databases.createDocument(
125
+ databaseId,
126
+ collectionId,
127
+ "unique()",
128
+ data
129
+ );
130
+
131
+ c.log(`Created document in ${databaseId}/${collectionId}`);
132
+ return c.json({ document }, 201);
133
+ } catch (error) {
134
+ c.error(`Failed to create document: ${error}`);
135
+ return c.json({ error: "Failed to create document" }, 500);
136
+ }
137
+ });
138
+
139
+ // GET /api/data/:databaseId/:collectionId - List documents
140
+ app.get("/api/data/:databaseId/:collectionId", async (c) => {
141
+ const databaseId = c.req.param("databaseId");
142
+ const collectionId = c.req.param("collectionId");
143
+ const limit = parseInt(c.req.query("limit") || "25");
144
+ const offset = parseInt(c.req.query("offset") || "0");
145
+
146
+ try {
147
+ const { databases } = getAppwriteClient(c);
148
+ const documents = await databases.listDocuments(
149
+ databaseId,
150
+ collectionId,
151
+ undefined,
152
+ limit,
153
+ offset
154
+ );
155
+
156
+ c.log(`Listed ${documents.documents.length} documents from ${databaseId}/${collectionId}`);
157
+ return c.json({ documents: documents.documents, total: documents.total });
158
+ } catch (error) {
159
+ c.error(`Failed to list documents: ${error}`);
160
+ return c.json({ error: "Failed to list documents" }, 500);
161
+ }
162
+ });
163
+
164
+ // Handle 404s
165
+ app.notFound((c) => {
166
+ c.log(`Route not found: ${c.req.method} ${c.req.path}`);
167
+ return c.json({
168
+ error: "Not Found",
169
+ message: `Route ${c.req.method} ${c.req.path} not found`,
170
+ availableRoutes: [
171
+ "GET /",
172
+ "GET /health",
173
+ "GET /api/user",
174
+ "POST /api/webhook",
175
+ "GET /api/databases",
176
+ "POST /api/data/:databaseId/:collectionId",
177
+ "GET /api/data/:databaseId/:collectionId",
178
+ ],
179
+ }, 404);
180
+ });