@kiyasov/platform-hono 1.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.claude/settings.local.json +7 -1
  2. package/dist/cjs/src/adapters/hono-adapter.d.ts +1 -0
  3. package/dist/cjs/src/adapters/hono-adapter.js +14 -3
  4. package/dist/cjs/src/adapters/hono-adapter.js.map +1 -1
  5. package/dist/cjs/src/drivers/graphQLUpload/Upload.d.ts +5 -7
  6. package/dist/cjs/src/drivers/graphQLUpload/Upload.js.map +1 -1
  7. package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.d.ts +20 -8
  8. package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.js +111 -58
  9. package/dist/cjs/src/drivers/graphQLUpload/fs-capacitor.js.map +1 -1
  10. package/dist/cjs/src/drivers/graphQLUpload/index.d.ts +9 -3
  11. package/dist/cjs/src/drivers/graphQLUpload/index.js +21 -3
  12. package/dist/cjs/src/drivers/graphQLUpload/index.js.map +1 -1
  13. package/dist/cjs/src/drivers/graphQLUpload/processRequest.d.ts +8 -1
  14. package/dist/cjs/src/drivers/graphQLUpload/processRequest.js +43 -37
  15. package/dist/cjs/src/drivers/graphQLUpload/processRequest.js.map +1 -1
  16. package/dist/cjs/src/drivers/graphQLUpload/storage/capacitor-storage.d.ts +15 -0
  17. package/dist/cjs/src/drivers/graphQLUpload/storage/capacitor-storage.js +47 -0
  18. package/dist/cjs/src/drivers/graphQLUpload/storage/capacitor-storage.js.map +1 -0
  19. package/dist/cjs/src/drivers/graphQLUpload/storage/index.d.ts +3 -0
  20. package/dist/cjs/src/drivers/graphQLUpload/storage/index.js +20 -0
  21. package/dist/cjs/src/drivers/graphQLUpload/storage/index.js.map +1 -0
  22. package/dist/cjs/src/drivers/graphQLUpload/storage/memory-storage.d.ts +13 -0
  23. package/dist/cjs/src/drivers/graphQLUpload/storage/memory-storage.js +31 -0
  24. package/dist/cjs/src/drivers/graphQLUpload/storage/memory-storage.js.map +1 -0
  25. package/dist/cjs/src/drivers/graphQLUpload/storage/storage.d.ts +17 -0
  26. package/dist/cjs/src/drivers/graphQLUpload/storage/storage.js +3 -0
  27. package/dist/cjs/src/drivers/graphQLUpload/storage/storage.js.map +1 -0
  28. package/dist/cjs/src/drivers/graphQLUpload/utils/file.d.ts +6 -0
  29. package/dist/cjs/src/drivers/graphQLUpload/utils/file.js +62 -0
  30. package/dist/cjs/src/drivers/graphQLUpload/utils/file.js.map +1 -0
  31. package/dist/cjs/src/drivers/graphQLUpload/utils/index.d.ts +2 -0
  32. package/dist/cjs/src/drivers/graphQLUpload/utils/index.js +19 -0
  33. package/dist/cjs/src/drivers/graphQLUpload/utils/index.js.map +1 -0
  34. package/dist/cjs/src/drivers/graphQLUpload/utils/validators.d.ts +18 -0
  35. package/dist/cjs/src/drivers/graphQLUpload/utils/validators.js +171 -0
  36. package/dist/cjs/src/drivers/graphQLUpload/utils/validators.js.map +1 -0
  37. package/dist/cjs/src/multer/index.d.ts +1 -0
  38. package/dist/cjs/src/multer/index.js +1 -0
  39. package/dist/cjs/src/multer/index.js.map +1 -1
  40. package/dist/cjs/src/multer/interceptors/any-files-interceptor.d.ts +2 -2
  41. package/dist/cjs/src/multer/interceptors/any-files-interceptor.js +6 -23
  42. package/dist/cjs/src/multer/interceptors/any-files-interceptor.js.map +1 -1
  43. package/dist/cjs/src/multer/interceptors/base-interceptor.d.ts +6 -0
  44. package/dist/cjs/src/multer/interceptors/base-interceptor.js +26 -0
  45. package/dist/cjs/src/multer/interceptors/base-interceptor.js.map +1 -0
  46. package/dist/cjs/src/multer/interceptors/file-fields-interceptor.d.ts +2 -2
  47. package/dist/cjs/src/multer/interceptors/file-fields-interceptor.js +7 -24
  48. package/dist/cjs/src/multer/interceptors/file-fields-interceptor.js.map +1 -1
  49. package/dist/cjs/src/multer/interceptors/file-interceptor.d.ts +2 -2
  50. package/dist/cjs/src/multer/interceptors/file-interceptor.js +6 -23
  51. package/dist/cjs/src/multer/interceptors/file-interceptor.js.map +1 -1
  52. package/dist/cjs/src/multer/interceptors/files-interceptor.d.ts +2 -2
  53. package/dist/cjs/src/multer/interceptors/files-interceptor.js +6 -23
  54. package/dist/cjs/src/multer/interceptors/files-interceptor.js.map +1 -1
  55. package/dist/cjs/src/multer/interceptors/index.d.ts +1 -0
  56. package/dist/cjs/src/multer/interceptors/index.js +1 -0
  57. package/dist/cjs/src/multer/interceptors/index.js.map +1 -1
  58. package/dist/cjs/src/multer/multipart/handlers/any-files.d.ts +2 -8
  59. package/dist/cjs/src/multer/multipart/handlers/any-files.js +12 -25
  60. package/dist/cjs/src/multer/multipart/handlers/any-files.js.map +1 -1
  61. package/dist/cjs/src/multer/multipart/handlers/base-handler.d.ts +42 -0
  62. package/dist/cjs/src/multer/multipart/handlers/base-handler.js +106 -0
  63. package/dist/cjs/src/multer/multipart/handlers/base-handler.js.map +1 -0
  64. package/dist/cjs/src/multer/multipart/handlers/file-fields.d.ts +3 -10
  65. package/dist/cjs/src/multer/multipart/handlers/file-fields.js +19 -33
  66. package/dist/cjs/src/multer/multipart/handlers/file-fields.js.map +1 -1
  67. package/dist/cjs/src/multer/multipart/handlers/index.d.ts +6 -1
  68. package/dist/cjs/src/multer/multipart/handlers/index.js +13 -0
  69. package/dist/cjs/src/multer/multipart/handlers/index.js.map +1 -1
  70. package/dist/cjs/src/multer/multipart/handlers/multiple-files.d.ts +2 -8
  71. package/dist/cjs/src/multer/multipart/handlers/multiple-files.js +18 -36
  72. package/dist/cjs/src/multer/multipart/handlers/multiple-files.js.map +1 -1
  73. package/dist/cjs/src/multer/multipart/handlers/single-file.d.ts +2 -8
  74. package/dist/cjs/src/multer/multipart/handlers/single-file.js +11 -33
  75. package/dist/cjs/src/multer/multipart/handlers/single-file.js.map +1 -1
  76. package/dist/cjs/src/multer/multipart/index.d.ts +1 -1
  77. package/dist/cjs/src/multer/multipart/options.d.ts +10 -16
  78. package/dist/cjs/src/multer/multipart/options.js.map +1 -1
  79. package/dist/cjs/src/multer/multipart/request.js +14 -3
  80. package/dist/cjs/src/multer/multipart/request.js.map +1 -1
  81. package/dist/cjs/src/multer/storage/disk-storage.d.ts +2 -1
  82. package/dist/cjs/src/multer/storage/disk-storage.js +2 -1
  83. package/dist/cjs/src/multer/storage/disk-storage.js.map +1 -1
  84. package/dist/cjs/src/multer/storage/memory-storage.d.ts +2 -11
  85. package/dist/cjs/src/multer/storage/memory-storage.js +6 -4
  86. package/dist/cjs/src/multer/storage/memory-storage.js.map +1 -1
  87. package/dist/cjs/src/multer/storage/storage.d.ts +6 -5
  88. package/dist/cjs/src/multer/utils/file.d.ts +6 -0
  89. package/dist/cjs/src/multer/utils/file.js +62 -0
  90. package/dist/cjs/src/multer/utils/file.js.map +1 -0
  91. package/dist/cjs/src/multer/utils/index.d.ts +2 -0
  92. package/dist/cjs/src/multer/utils/index.js +19 -0
  93. package/dist/cjs/src/multer/utils/index.js.map +1 -0
  94. package/dist/cjs/src/multer/utils/validators.d.ts +18 -0
  95. package/dist/cjs/src/multer/utils/validators.js +171 -0
  96. package/dist/cjs/src/multer/utils/validators.js.map +1 -0
  97. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  98. package/dist/esm/src/adapters/hono-adapter.d.ts +1 -0
  99. package/dist/esm/src/adapters/hono-adapter.js +14 -3
  100. package/dist/esm/src/adapters/hono-adapter.js.map +1 -1
  101. package/dist/esm/src/drivers/graphQLUpload/Upload.d.ts +5 -7
  102. package/dist/esm/src/drivers/graphQLUpload/Upload.js.map +1 -1
  103. package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.d.ts +20 -8
  104. package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.js +112 -59
  105. package/dist/esm/src/drivers/graphQLUpload/fs-capacitor.js.map +1 -1
  106. package/dist/esm/src/drivers/graphQLUpload/index.d.ts +9 -3
  107. package/dist/esm/src/drivers/graphQLUpload/index.js +7 -3
  108. package/dist/esm/src/drivers/graphQLUpload/index.js.map +1 -1
  109. package/dist/esm/src/drivers/graphQLUpload/processRequest.d.ts +8 -1
  110. package/dist/esm/src/drivers/graphQLUpload/processRequest.js +42 -36
  111. package/dist/esm/src/drivers/graphQLUpload/processRequest.js.map +1 -1
  112. package/dist/esm/src/drivers/graphQLUpload/storage/capacitor-storage.d.ts +15 -0
  113. package/dist/esm/src/drivers/graphQLUpload/storage/capacitor-storage.js +43 -0
  114. package/dist/esm/src/drivers/graphQLUpload/storage/capacitor-storage.js.map +1 -0
  115. package/dist/esm/src/drivers/graphQLUpload/storage/index.d.ts +3 -0
  116. package/dist/esm/src/drivers/graphQLUpload/storage/index.js +4 -0
  117. package/dist/esm/src/drivers/graphQLUpload/storage/index.js.map +1 -0
  118. package/dist/esm/src/drivers/graphQLUpload/storage/memory-storage.d.ts +13 -0
  119. package/dist/esm/src/drivers/graphQLUpload/storage/memory-storage.js +27 -0
  120. package/dist/esm/src/drivers/graphQLUpload/storage/memory-storage.js.map +1 -0
  121. package/dist/esm/src/drivers/graphQLUpload/storage/storage.d.ts +17 -0
  122. package/dist/esm/src/drivers/graphQLUpload/storage/storage.js +2 -0
  123. package/dist/esm/src/drivers/graphQLUpload/storage/storage.js.map +1 -0
  124. package/dist/esm/src/drivers/graphQLUpload/utils/file.d.ts +6 -0
  125. package/dist/esm/src/drivers/graphQLUpload/utils/file.js +54 -0
  126. package/dist/esm/src/drivers/graphQLUpload/utils/file.js.map +1 -0
  127. package/dist/esm/src/drivers/graphQLUpload/utils/index.d.ts +2 -0
  128. package/dist/esm/src/drivers/graphQLUpload/utils/index.js +3 -0
  129. package/dist/esm/src/drivers/graphQLUpload/utils/index.js.map +1 -0
  130. package/dist/esm/src/drivers/graphQLUpload/utils/validators.d.ts +18 -0
  131. package/dist/esm/src/drivers/graphQLUpload/utils/validators.js +167 -0
  132. package/dist/esm/src/drivers/graphQLUpload/utils/validators.js.map +1 -0
  133. package/dist/esm/src/multer/index.d.ts +1 -0
  134. package/dist/esm/src/multer/index.js +1 -0
  135. package/dist/esm/src/multer/index.js.map +1 -1
  136. package/dist/esm/src/multer/interceptors/any-files-interceptor.d.ts +2 -2
  137. package/dist/esm/src/multer/interceptors/any-files-interceptor.js +6 -23
  138. package/dist/esm/src/multer/interceptors/any-files-interceptor.js.map +1 -1
  139. package/dist/esm/src/multer/interceptors/base-interceptor.d.ts +6 -0
  140. package/dist/esm/src/multer/interceptors/base-interceptor.js +23 -0
  141. package/dist/esm/src/multer/interceptors/base-interceptor.js.map +1 -0
  142. package/dist/esm/src/multer/interceptors/file-fields-interceptor.d.ts +2 -2
  143. package/dist/esm/src/multer/interceptors/file-fields-interceptor.js +7 -24
  144. package/dist/esm/src/multer/interceptors/file-fields-interceptor.js.map +1 -1
  145. package/dist/esm/src/multer/interceptors/file-interceptor.d.ts +2 -2
  146. package/dist/esm/src/multer/interceptors/file-interceptor.js +6 -23
  147. package/dist/esm/src/multer/interceptors/file-interceptor.js.map +1 -1
  148. package/dist/esm/src/multer/interceptors/files-interceptor.d.ts +2 -2
  149. package/dist/esm/src/multer/interceptors/files-interceptor.js +6 -23
  150. package/dist/esm/src/multer/interceptors/files-interceptor.js.map +1 -1
  151. package/dist/esm/src/multer/interceptors/index.d.ts +1 -0
  152. package/dist/esm/src/multer/interceptors/index.js +1 -0
  153. package/dist/esm/src/multer/interceptors/index.js.map +1 -1
  154. package/dist/esm/src/multer/multipart/handlers/any-files.d.ts +2 -8
  155. package/dist/esm/src/multer/multipart/handlers/any-files.js +12 -25
  156. package/dist/esm/src/multer/multipart/handlers/any-files.js.map +1 -1
  157. package/dist/esm/src/multer/multipart/handlers/base-handler.d.ts +42 -0
  158. package/dist/esm/src/multer/multipart/handlers/base-handler.js +102 -0
  159. package/dist/esm/src/multer/multipart/handlers/base-handler.js.map +1 -0
  160. package/dist/esm/src/multer/multipart/handlers/file-fields.d.ts +3 -10
  161. package/dist/esm/src/multer/multipart/handlers/file-fields.js +19 -33
  162. package/dist/esm/src/multer/multipart/handlers/file-fields.js.map +1 -1
  163. package/dist/esm/src/multer/multipart/handlers/index.d.ts +6 -1
  164. package/dist/esm/src/multer/multipart/handlers/index.js +6 -1
  165. package/dist/esm/src/multer/multipart/handlers/index.js.map +1 -1
  166. package/dist/esm/src/multer/multipart/handlers/multiple-files.d.ts +2 -8
  167. package/dist/esm/src/multer/multipart/handlers/multiple-files.js +18 -36
  168. package/dist/esm/src/multer/multipart/handlers/multiple-files.js.map +1 -1
  169. package/dist/esm/src/multer/multipart/handlers/single-file.d.ts +2 -8
  170. package/dist/esm/src/multer/multipart/handlers/single-file.js +11 -33
  171. package/dist/esm/src/multer/multipart/handlers/single-file.js.map +1 -1
  172. package/dist/esm/src/multer/multipart/index.d.ts +1 -1
  173. package/dist/esm/src/multer/multipart/options.d.ts +10 -16
  174. package/dist/esm/src/multer/multipart/options.js.map +1 -1
  175. package/dist/esm/src/multer/multipart/request.js +14 -3
  176. package/dist/esm/src/multer/multipart/request.js.map +1 -1
  177. package/dist/esm/src/multer/storage/disk-storage.d.ts +2 -1
  178. package/dist/esm/src/multer/storage/disk-storage.js +2 -1
  179. package/dist/esm/src/multer/storage/disk-storage.js.map +1 -1
  180. package/dist/esm/src/multer/storage/memory-storage.d.ts +2 -11
  181. package/dist/esm/src/multer/storage/memory-storage.js +6 -4
  182. package/dist/esm/src/multer/storage/memory-storage.js.map +1 -1
  183. package/dist/esm/src/multer/storage/storage.d.ts +6 -5
  184. package/dist/esm/src/multer/utils/file.d.ts +6 -0
  185. package/dist/esm/src/multer/utils/file.js +54 -0
  186. package/dist/esm/src/multer/utils/file.js.map +1 -0
  187. package/dist/esm/src/multer/utils/index.d.ts +2 -0
  188. package/dist/esm/src/multer/utils/index.js +3 -0
  189. package/dist/esm/src/multer/utils/index.js.map +1 -0
  190. package/dist/esm/src/multer/utils/validators.d.ts +18 -0
  191. package/dist/esm/src/multer/utils/validators.js +167 -0
  192. package/dist/esm/src/multer/utils/validators.js.map +1 -0
  193. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  194. package/package.json +8 -6
  195. package/src/adapters/hono-adapter.ts +18 -3
  196. package/src/drivers/graphQLUpload/Upload.ts +21 -14
  197. package/src/drivers/graphQLUpload/fs-capacitor.ts +240 -116
  198. package/src/drivers/graphQLUpload/index.ts +37 -3
  199. package/src/drivers/graphQLUpload/processRequest.ts +92 -38
  200. package/src/drivers/graphQLUpload/storage/capacitor-storage.ts +86 -0
  201. package/src/drivers/graphQLUpload/storage/index.ts +3 -0
  202. package/src/drivers/graphQLUpload/storage/memory-storage.ts +62 -0
  203. package/src/drivers/graphQLUpload/storage/storage.ts +52 -0
  204. package/src/drivers/graphQLUpload/utils/file.ts +109 -0
  205. package/src/drivers/graphQLUpload/utils/index.ts +2 -0
  206. package/src/drivers/graphQLUpload/utils/validators.ts +219 -0
  207. package/src/multer/index.ts +1 -0
  208. package/src/multer/interceptors/any-files-interceptor.ts +12 -43
  209. package/src/multer/interceptors/base-interceptor.ts +54 -0
  210. package/src/multer/interceptors/file-fields-interceptor.ts +14 -48
  211. package/src/multer/interceptors/file-interceptor.ts +12 -44
  212. package/src/multer/interceptors/files-interceptor.ts +13 -45
  213. package/src/multer/interceptors/index.ts +1 -0
  214. package/src/multer/multipart/handlers/any-files.ts +14 -32
  215. package/src/multer/multipart/handlers/base-handler.ts +204 -0
  216. package/src/multer/multipart/handlers/file-fields.ts +29 -57
  217. package/src/multer/multipart/handlers/index.ts +11 -1
  218. package/src/multer/multipart/handlers/multiple-files.ts +23 -54
  219. package/src/multer/multipart/handlers/single-file.ts +14 -47
  220. package/src/multer/multipart/index.ts +1 -1
  221. package/src/multer/multipart/options.ts +26 -8
  222. package/src/multer/multipart/request.ts +19 -3
  223. package/src/multer/storage/disk-storage.ts +2 -1
  224. package/src/multer/storage/memory-storage.ts +13 -6
  225. package/src/multer/storage/storage.ts +12 -5
  226. package/src/multer/utils/file.ts +109 -0
  227. package/src/multer/utils/index.ts +2 -0
  228. package/src/multer/utils/validators.ts +219 -0
  229. package/test/README.md +247 -0
  230. package/test/graphql-upload.test.ts +509 -0
  231. package/test/helpers.ts +70 -0
  232. package/test/integration.test.ts +197 -0
  233. package/test/interceptors-e2e.test.ts +362 -0
  234. package/test/multipart-upload.test.ts +354 -0
  235. package/test/smoke.test.ts +227 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiyasov/platform-hono",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "Nest adapter for Hono",
5
5
  "author": "Islam Kiiasov",
6
6
  "repository": {
@@ -20,7 +20,8 @@
20
20
  "server",
21
21
  "rest",
22
22
  "api",
23
- "typescript"
23
+ "typescript",
24
+ "graphql"
24
25
  ],
25
26
  "main": "./dist/cjs/index.js",
26
27
  "module": "./dist/esm/index.js",
@@ -43,13 +44,13 @@
43
44
  },
44
45
  "dependencies": {
45
46
  "@apollo/server": "^5.2.0",
46
- "@hono/node-server": "^1.19.7",
47
+ "@hono/node-server": "^1.19.8",
47
48
  "@nestjs/graphql": "^13.2.3",
49
+ "@nestjs/apollo": "^13.2.3",
48
50
  "hono": "^4.11.3"
49
51
  },
50
52
  "devDependencies": {
51
53
  "@eslint/js": "^9.39.2",
52
- "@nestjs/apollo": "^13.2.3",
53
54
  "@nestjs/cli": "^11.0.14",
54
55
  "@nestjs/common": "^11.1.11",
55
56
  "@nestjs/core": "^11.1.11",
@@ -58,7 +59,6 @@
58
59
  "@swc/core": "^1.15.8",
59
60
  "@types/autocannon": "^7.12.7",
60
61
  "@types/bun": "^1.3.5",
61
- "@types/busboy": "^1.5.4",
62
62
  "autocannon": "^8.0.0",
63
63
  "bun": "^1.3.5",
64
64
  "class-transformer": "^0.5.1",
@@ -86,7 +86,9 @@
86
86
  "build": "bun run build:esm && bun run build:cjs",
87
87
  "dev": "cd example && nest start -w --copy-files --exec \"bun run --inspect\"",
88
88
  "benchmark": "bun dev:bun & ( sleep 5 && autocannon -c 200 -d 5 -p 10 http://localhost:3000 )",
89
- "benchmark:file": "bun run dev:bun & ( sleep 5 && bun run ./benchmarks/benchmark.mjs)"
89
+ "benchmark:file": "bun run dev:bun & ( sleep 5 && bun run ./benchmarks/benchmark.mjs)",
90
+ "test": "bun test",
91
+ "test:watch": "bun test --watch"
90
92
  },
91
93
  "trustedDependencies": [
92
94
  "@apollo/protobufjs",
@@ -221,7 +221,8 @@ export class HonoAdapter extends AbstractHttpAdapter<
221
221
 
222
222
  public setErrorHandler(handler: ErrorHandler) {
223
223
  this.instance.onError(async (err: Error, ctx: Context) => {
224
- await handler(err, ctx.req, ctx);
224
+ const argumentsHost = this.createArgumentsHost(ctx);
225
+ await handler(err, ctx.req, argumentsHost);
225
226
 
226
227
  return this.getBody(ctx);
227
228
  });
@@ -229,7 +230,8 @@ export class HonoAdapter extends AbstractHttpAdapter<
229
230
 
230
231
  public setNotFoundHandler(handler: RequestHandler) {
231
232
  this.instance.notFound(async (ctx: Context) => {
232
- await handler(ctx.req, ctx);
233
+ const argumentsHost = this.createArgumentsHost(ctx);
234
+ await handler(ctx.req, argumentsHost);
233
235
  await this.status(ctx, HttpStatus.NOT_FOUND);
234
236
 
235
237
  return this.getBody(ctx, 'Not Found');
@@ -411,7 +413,8 @@ export class HonoAdapter extends AbstractHttpAdapter<
411
413
  ).bind(this.instance);
412
414
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
413
415
  routeMethod(path, async (ctx: Context, next: Function) => {
414
- await callback(ctx.req, ctx, next);
416
+ const argumentsHost = this.createArgumentsHost(ctx);
417
+ await callback(ctx.req, argumentsHost, next);
415
418
  });
416
419
  };
417
420
  }
@@ -434,4 +437,16 @@ export class HonoAdapter extends AbstractHttpAdapter<
434
437
  },
435
438
  });
436
439
  }
440
+
441
+ /**
442
+ * Creates a NestJS-compatible ArgumentsHost wrapper around Hono Context
443
+ */
444
+ private createArgumentsHost(ctx: Context) {
445
+ const argumentsHost = {
446
+ getRequest: () => ctx.req,
447
+ getResponse: () => ctx,
448
+ switchToHttp: () => argumentsHost,
449
+ };
450
+ return argumentsHost;
451
+ }
437
452
  }
@@ -1,16 +1,25 @@
1
1
  import { ReadStream, ReadStreamOptions, WriteStream } from './fs-capacitor';
2
-
3
- export interface FileUpload {
4
- filename: string;
5
- fieldName: string;
6
- mimetype: string;
7
- encoding: string;
8
-
9
- createReadStream(options?: ReadStreamOptions): ReadStream;
10
-
11
- capacitor: WriteStream;
2
+ import { StorageFile } from './storage';
3
+
4
+ /**
5
+ * Represents a file upload with metadata and stream access.
6
+ * Compatible with capacitor storage for streaming file content.
7
+ *
8
+ * Extends StorageFile with optional stream support for capacitor storage.
9
+ */
10
+ export interface FileUpload extends StorageFile {
11
+ /** Creates a readable stream for the file content (capacitor storage only) */
12
+ createReadStream?(options?: ReadStreamOptions): ReadStream;
13
+ /** The write stream capacitor (capacitor storage only) */
14
+ capacitor?: WriteStream;
15
+ /** Original File object if available */
16
+ file?: File;
12
17
  }
13
18
 
19
+ /**
20
+ * Upload promise wrapper for GraphQL file uploads.
21
+ * Manages the asynchronous file upload process with proper error handling.
22
+ */
14
23
  export class Upload {
15
24
  promise: Promise<FileUpload>;
16
25
  resolve: (file?: FileUpload) => void;
@@ -21,15 +30,13 @@ export class Upload {
21
30
  this.promise = new Promise((resolve, reject) => {
22
31
  this.resolve = (file) => {
23
32
  this.file = file;
24
-
25
33
  resolve(file);
26
34
  };
27
-
28
35
  this.reject = reject;
29
36
  });
30
37
 
31
- // Prevent errors crashing Node.js, see:
32
- // https://github.com/nodejs/node/issues/20392
38
+ // Prevent unhandled promise rejection errors
39
+ // See: https://github.com/nodejs/node/issues/20392
33
40
  this.promise.catch(() => {});
34
41
  }
35
42
  }
@@ -1,30 +1,65 @@
1
1
  import { randomBytes } from 'crypto';
2
2
  import { EventEmitter } from 'events';
3
- import { read, open, closeSync, unlinkSync, write, close, unlink } from 'fs';
3
+ import { close, closeSync, open, read, unlink, unlinkSync, write } from 'fs';
4
4
  import { tmpdir } from 'os';
5
5
  import { join } from 'path';
6
6
  import { Readable, ReadableOptions, Writable, WritableOptions } from 'stream';
7
7
 
8
- export class ReadAfterDestroyedError extends Error {}
9
- export class ReadAfterReleasedError extends Error {}
8
+ /**
9
+ * Error thrown when attempting to create a ReadStream from a destroyed WriteStream.
10
+ */
11
+ export class ReadAfterDestroyedError extends Error {
12
+ constructor() {
13
+ super('A ReadStream cannot be created from a destroyed WriteStream.');
14
+ this.name = 'ReadAfterDestroyedError';
15
+ }
16
+ }
10
17
 
18
+ /**
19
+ * Error thrown when attempting to create a ReadStream from a released WriteStream.
20
+ */
21
+ export class ReadAfterReleasedError extends Error {
22
+ constructor() {
23
+ super('A ReadStream cannot be created from a released WriteStream.');
24
+ this.name = 'ReadAfterReleasedError';
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Options for creating a ReadStream.
30
+ */
11
31
  export interface ReadStreamOptions {
32
+ /** Maximum number of bytes to store in the internal buffer before ceasing to read from the underlying resource. */
12
33
  highWaterMark?: ReadableOptions['highWaterMark'];
34
+ /** Encoding to use for the readable stream. */
13
35
  encoding?: ReadableOptions['encoding'];
14
36
  }
15
37
 
16
- // Use a “proxy” event emitter configured to have an infinite maximum number of
17
- // listeners to prevent Node.js max listeners exceeded warnings if many
18
- // `fs-capacitor` `ReadStream` instances are created at the same time. See:
19
- // https://github.com/mike-marcacci/fs-capacitor/issues/30
20
- const processExitProxy = new EventEmitter();
21
- processExitProxy.setMaxListeners(Infinity);
22
- process.once('exit', () => processExitProxy.emit('exit'));
38
+ /**
39
+ * Proxy event emitter to handle process exit events without triggering max listeners warnings.
40
+ */
41
+ const PROCESS_EXIT_PROXY = new EventEmitter();
42
+ PROCESS_EXIT_PROXY.setMaxListeners(Infinity);
43
+ process.once('exit', () => PROCESS_EXIT_PROXY.emit('exit'));
23
44
 
24
- export class ReadStream extends Readable {
25
- private _pos: number = 0;
26
- private _writeStream: WriteStream;
45
+ interface WritableState {
46
+ finished: boolean;
47
+ }
27
48
 
49
+ /**
50
+ * A readable stream that reads from a WriteStream's temporary file.
51
+ * Allows multiple concurrent reads from the same WriteStream.
52
+ */
53
+ export class ReadStream extends Readable {
54
+ private _pos = 0;
55
+ private readonly _writeStream: WriteStream;
56
+ private _retryListenersAttached = false;
57
+
58
+ /**
59
+ * Creates a new ReadStream attached to a WriteStream.
60
+ * @param writeStream - The WriteStream to read from.
61
+ * @param options - Stream options.
62
+ */
28
63
  constructor(writeStream: WriteStream, options?: ReadStreamOptions) {
29
64
  super({
30
65
  highWaterMark: options?.highWaterMark,
@@ -34,79 +69,135 @@ export class ReadStream extends Readable {
34
69
  this._writeStream = writeStream;
35
70
  }
36
71
 
72
+ /**
73
+ * Retries reading from the write stream after data is available.
74
+ */
75
+ private _retry = (): void => {
76
+ this._detachRetryListeners();
77
+ this._read(this.readableHighWaterMark || 65536);
78
+ };
79
+
80
+ /**
81
+ * Attaches retry listeners to wait for more data or stream completion.
82
+ */
83
+ private _attachRetryListeners(): void {
84
+ if (this._retryListenersAttached) return;
85
+ this._writeStream.on('finish', this._retry);
86
+ this._writeStream.on('write', this._retry);
87
+ this._retryListenersAttached = true;
88
+ }
89
+
90
+ /**
91
+ * Detaches retry listeners when they are no longer needed.
92
+ */
93
+ private _detachRetryListeners(): void {
94
+ if (!this._retryListenersAttached) return;
95
+ this._writeStream.off('finish', this._retry);
96
+ this._writeStream.off('write', this._retry);
97
+ this._retryListenersAttached = false;
98
+ }
99
+
100
+ /**
101
+ * Internal read implementation that fetches data from the temporary file.
102
+ * @param n - Number of bytes to read.
103
+ */
37
104
  _read(n: number): void {
38
- if (this.destroyed) return;
105
+ if (this.destroyed) {
106
+ this._detachRetryListeners();
107
+ return;
108
+ }
39
109
 
40
- if (typeof this._writeStream['_fd'] !== 'number') {
110
+ const fd = this._writeStream.getFd();
111
+ if (fd === null) {
41
112
  this._writeStream.once('ready', () => this._read(n));
42
113
  return;
43
114
  }
44
115
 
45
- // Using `allocUnsafe` here is OK because we return a slice the length of
46
- // `bytesRead`, and discard the rest. This prevents node from having to zero
47
- // out the entire allocation first.
48
116
  const buf = new Uint8Array(Buffer.allocUnsafe(n).buffer);
49
- read(this._writeStream['_fd'], buf, 0, n, this._pos, (error, bytesRead) => {
50
- if (error) this.destroy(error);
117
+ read(fd, buf, 0, n, this._pos, (error, bytesRead) => {
118
+ if (error) {
119
+ this.destroy(error);
120
+ this._detachRetryListeners();
121
+ return;
122
+ }
51
123
 
52
- // Push any read bytes into the local stream buffer.
53
124
  if (bytesRead) {
54
125
  this._pos += bytesRead;
55
126
  this.push(buf.slice(0, bytesRead));
56
127
  return;
57
128
  }
58
129
 
59
- // If there were no more bytes to read and the write stream is finished,
60
- // then this stream has reached the end.
61
- if (
62
- (
63
- this._writeStream as unknown as {
64
- _writableState: { finished: boolean };
65
- }
66
- )._writableState.finished
67
- ) {
68
- // Check if we have consumed the whole file up to where
69
- // the write stream has written before ending the stream
70
- if (this._pos < (this._writeStream as unknown as { _pos: number })._pos)
130
+ if (this._writeStream.isWritableFinished()) {
131
+ const writePos = this._writeStream.getWritePosition();
132
+ if (this._pos < writePos) {
71
133
  this._read(n);
72
- else this.push(null);
134
+ } else {
135
+ this.push(null);
136
+ this._detachRetryListeners();
137
+ }
73
138
  return;
74
139
  }
75
140
 
76
- // Otherwise, wait for the write stream to add more data or finish.
77
- const retry = (): void => {
78
- this._writeStream.off('finish', retry);
79
- this._writeStream.off('write', retry);
80
- this._read(n);
81
- };
82
-
83
- this._writeStream.on('finish', retry);
84
- this._writeStream.on('write', retry);
141
+ this._attachRetryListeners();
85
142
  });
86
143
  }
144
+
145
+ /**
146
+ * Cleanup when the stream is destroyed.
147
+ */
148
+ _destroy(
149
+ error: Error | null,
150
+ callback: (error?: Error | null) => void,
151
+ ): void {
152
+ this._detachRetryListeners();
153
+ super._destroy(error, callback);
154
+ }
87
155
  }
88
156
 
157
+ /**
158
+ * Options for creating a WriteStream.
159
+ */
89
160
  export interface WriteStreamOptions {
161
+ /** Maximum number of bytes to store in the internal buffer before ceasing to write to the underlying resource. */
90
162
  highWaterMark?: WritableOptions['highWaterMark'];
163
+ /** Default encoding to use for the writable stream. */
91
164
  defaultEncoding?: WritableOptions['defaultEncoding'];
165
+ /** Function that returns the temporary directory path. */
92
166
  tmpdir?: () => string;
93
167
  }
94
168
 
169
+ /**
170
+ * A writable stream that stores data in a temporary file.
171
+ * Supports multiple concurrent ReadStream instances reading from the same file.
172
+ * Automatically cleans up the temporary file when all streams are closed.
173
+ */
95
174
  export class WriteStream extends Writable {
96
- private _fd: null | number = null;
97
- private _path: null | string = null;
98
- private _pos: number = 0;
99
- private _readStreams: Set<ReadStream> = new Set();
100
- private _released: boolean = false;
101
-
102
- constructor(options?: WriteStreamOptions) {
175
+ private _fd: number | null = null;
176
+ private _path: string | null = null;
177
+ private _pos = 0;
178
+ private readonly _readStreams = new Set<ReadStream>();
179
+ private _released = false;
180
+ private _tmpdir: () => string;
181
+
182
+ /**
183
+ * Creates a new WriteStream with a temporary file.
184
+ * @param options - Stream options.
185
+ */
186
+ constructor(options: WriteStreamOptions = {}) {
103
187
  super({
104
- highWaterMark: options?.highWaterMark,
105
- defaultEncoding: options?.defaultEncoding,
188
+ highWaterMark: options.highWaterMark,
189
+ defaultEncoding: options.defaultEncoding,
106
190
  autoDestroy: false,
107
191
  });
192
+ this._tmpdir = options.tmpdir ?? tmpdir;
193
+ this._initFile();
194
+ }
108
195
 
109
- // Generate a random filename.
196
+ /**
197
+ * Initializes the temporary file with a random name.
198
+ * Emits 'ready' when the file is ready for writing.
199
+ */
200
+ private _initFile(): void {
110
201
  randomBytes(16, (error, buffer) => {
111
202
  if (error) {
112
203
  this.destroy(error);
@@ -114,27 +205,53 @@ export class WriteStream extends Writable {
114
205
  }
115
206
 
116
207
  this._path = join(
117
- (options?.tmpdir ?? tmpdir)(),
208
+ this._tmpdir(),
118
209
  `capacitor-${buffer.toString('hex')}.tmp`,
119
210
  );
120
211
 
121
- // Create a file in the OS's temporary files directory.
122
212
  open(this._path, 'wx+', 0o600, (error, fd) => {
123
213
  if (error) {
124
214
  this.destroy(error);
125
215
  return;
126
216
  }
127
217
 
128
- // Cleanup when the process exits or is killed.
129
- processExitProxy.once('exit', this._cleanupSync);
130
-
218
+ PROCESS_EXIT_PROXY.once('exit', this._cleanupSync);
131
219
  this._fd = fd;
132
220
  this.emit('ready');
133
221
  });
134
222
  });
135
223
  }
136
224
 
137
- _cleanup = (callback: (error: null | Error) => void): void => {
225
+ /**
226
+ * Returns the file descriptor for the temporary file.
227
+ * @returns The file descriptor or null if not yet initialized.
228
+ */
229
+ getFd(): number | null {
230
+ return this._fd;
231
+ }
232
+
233
+ /**
234
+ * Returns the current write position in the file.
235
+ * @returns The number of bytes written so far.
236
+ */
237
+ getWritePosition(): number {
238
+ return this._pos;
239
+ }
240
+
241
+ /**
242
+ * Checks if the writable stream has finished.
243
+ * @returns True if the stream is finished, false otherwise.
244
+ */
245
+ isWritableFinished(): boolean {
246
+ return (this as unknown as { _writableState: WritableState })._writableState
247
+ .finished;
248
+ }
249
+
250
+ /**
251
+ * Asynchronously cleans up the temporary file.
252
+ * Closes the file descriptor and deletes the file.
253
+ */
254
+ private _cleanup(callback: (error: Error | null) => void): void {
138
255
  const fd = this._fd;
139
256
  const path = this._path;
140
257
 
@@ -143,59 +260,61 @@ export class WriteStream extends Writable {
143
260
  return;
144
261
  }
145
262
 
146
- // Close the file descriptor.
147
263
  close(fd, (closeError) => {
148
- // An error here probably means the fd was already closed, but we can
149
- // still try to unlink the file.
264
+ this._fd = null;
265
+ PROCESS_EXIT_PROXY.off('exit', this._cleanupSync);
266
+
150
267
  unlink(path, (unlinkError) => {
151
- // If we are unable to unlink the file, the operating system will
152
- // clean up on next restart, since we use store thes in `os.tmpdir()`
153
- this._fd = null;
154
-
155
- // We avoid removing this until now in case an exit occurs while
156
- // asyncronously cleaning up.
157
- processExitProxy.off('exit', this._cleanupSync);
158
- callback(unlinkError ?? closeError);
268
+ callback(unlinkError ?? closeError ?? null);
159
269
  });
160
270
  });
161
- };
271
+ }
162
272
 
163
- _cleanupSync = (): void => {
164
- processExitProxy.off('exit', this._cleanupSync);
273
+ /**
274
+ * Synchronously cleans up the temporary file.
275
+ * Called on process exit to ensure cleanup even if async operations are interrupted.
276
+ */
277
+ private _cleanupSync = (): void => {
278
+ PROCESS_EXIT_PROXY.off('exit', this._cleanupSync);
165
279
 
166
- if (typeof this._fd === 'number')
280
+ if (typeof this._fd === 'number') {
167
281
  try {
168
282
  closeSync(this._fd);
169
283
  } catch {
170
- // An error here probably means the fd was already closed, but we can
171
- // still try to unlink the file.
284
+ // File descriptor already closed
172
285
  }
286
+ }
173
287
 
174
- try {
175
- if (this._path !== null) {
288
+ if (this._path !== null) {
289
+ try {
176
290
  unlinkSync(this._path);
291
+ } catch {
292
+ // File already deleted
177
293
  }
178
- } catch {
179
- // If we are unable to unlink the file, the operating system will clean
180
- // up on next restart, since we use store thes in `os.tmpdir()`
181
294
  }
182
295
  };
183
296
 
184
- _final(callback: (error?: null | Error) => unknown): void {
185
- if (typeof this._fd !== 'number') {
297
+ /**
298
+ * Called when the stream is finished writing.
299
+ */
300
+ _final(callback: (error?: Error | null) => void): void {
301
+ if (this._fd === null) {
186
302
  this.once('ready', () => this._final(callback));
187
303
  return;
188
304
  }
189
305
  callback();
190
306
  }
191
307
 
308
+ /**
309
+ * Internal write implementation that writes data to the temporary file.
310
+ */
192
311
  _write(
193
312
  chunk: Buffer,
194
- encoding: string,
195
- callback: (error?: null | Error) => unknown,
313
+ _encoding: string,
314
+ callback: (error?: Error | null) => void,
196
315
  ): void {
197
- if (typeof this._fd !== 'number') {
198
- this.once('ready', () => this._write(chunk, encoding, callback));
316
+ if (this._fd === null) {
317
+ this.once('ready', () => this._write(chunk, _encoding, callback));
199
318
  return;
200
319
  }
201
320
 
@@ -211,41 +330,29 @@ export class WriteStream extends Writable {
211
330
  return;
212
331
  }
213
332
 
214
- // It's safe to increment `this._pos` after flushing to the filesystem
215
- // because node streams ensure that only one `_write()` is active at a
216
- // time. If this assumption is broken, the behavior of this library is
217
- // undefined, regardless of where this is incremented. Relocating this
218
- // to increment syncronously would result in correct file contents, but
219
- // the out-of-order writes would still open the potential for read streams
220
- // to scan positions that have not yet been written.
221
333
  this._pos += chunk.length;
222
334
  this.emit('write');
223
335
  callback();
224
336
  });
225
337
  }
226
338
 
227
- release(): void {
228
- this._released = true;
229
- if (this._readStreams.size === 0) this.destroy();
230
- }
231
-
339
+ /**
340
+ * Cleanup when the stream is destroyed.
341
+ * Destroys all associated ReadStreams and cleans up the temporary file.
342
+ */
232
343
  _destroy(
233
- error: undefined | null | Error,
234
- callback: (error?: null | Error) => unknown,
344
+ error: Error | null,
345
+ callback: (error?: Error | null) => void,
235
346
  ): void {
236
- // Destroy all attached read streams.
237
347
  for (const readStream of this._readStreams) {
238
- readStream.destroy(error || undefined);
348
+ readStream.destroy(error ?? undefined);
239
349
  }
240
350
 
241
- // This capacitor is fully initialized.
242
- if (typeof this._fd === 'number' && typeof this._path === 'string') {
351
+ if (this._fd !== null && this._path !== null) {
243
352
  this._cleanup((cleanupError) => callback(cleanupError ?? error));
244
353
  return;
245
354
  }
246
355
 
247
- // This capacitor has not yet finished initialization; if initialization
248
- // does complete, immediately clean up after.
249
356
  this.once('ready', () => {
250
357
  this._cleanup((cleanupError) => {
251
358
  if (cleanupError) {
@@ -257,21 +364,27 @@ export class WriteStream extends Writable {
257
364
  callback(error);
258
365
  }
259
366
 
367
+ /**
368
+ * Creates a new ReadStream that reads from this WriteStream.
369
+ * Multiple ReadStreams can be created and will read independently.
370
+ * @param options - Stream options for the ReadStream.
371
+ * @returns A new ReadStream instance.
372
+ * @throws {ReadAfterDestroyedError} If the WriteStream has been destroyed.
373
+ * @throws {ReadAfterReleasedError} If the WriteStream has been released.
374
+ */
260
375
  createReadStream(options?: ReadStreamOptions): ReadStream {
261
- if (this.destroyed)
262
- throw new ReadAfterDestroyedError(
263
- 'A ReadStream cannot be created from a destroyed WriteStream.',
264
- );
376
+ if (this.destroyed) {
377
+ throw new ReadAfterDestroyedError();
378
+ }
265
379
 
266
- if (this._released)
267
- throw new ReadAfterReleasedError(
268
- 'A ReadStream cannot be created from a released WriteStream.',
269
- );
380
+ if (this._released) {
381
+ throw new ReadAfterReleasedError();
382
+ }
270
383
 
271
384
  const readStream = new ReadStream(this, options);
272
385
  this._readStreams.add(readStream);
273
386
 
274
- readStream.once('close', (): void => {
387
+ readStream.once('close', () => {
275
388
  this._readStreams.delete(readStream);
276
389
 
277
390
  if (this._released && this._readStreams.size === 0) {
@@ -281,6 +394,17 @@ export class WriteStream extends Writable {
281
394
 
282
395
  return readStream;
283
396
  }
397
+
398
+ /**
399
+ * Releases the WriteStream, marking it for cleanup.
400
+ * The stream will be destroyed once all ReadStreams are closed.
401
+ */
402
+ release(): void {
403
+ this._released = true;
404
+ if (this._readStreams.size === 0) {
405
+ this.destroy();
406
+ }
407
+ }
284
408
  }
285
409
 
286
410
  export default {