alepha 0.20.2 → 0.20.4

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 (304) hide show
  1. package/README.md +0 -1
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui.css +1 -1
  4. package/dist/api/audits/index.browser.js +49 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.js +49 -0
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +2 -61
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js.map +1 -1
  12. package/dist/api/keys/index.d.ts +4 -4
  13. package/dist/api/keys/index.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +1 -10
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/parameters/index.browser.js +37 -0
  17. package/dist/api/parameters/index.browser.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +12 -68
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/parameters/index.js +57 -4
  21. package/dist/api/parameters/index.js.map +1 -1
  22. package/dist/api/payments/index.js.map +1 -1
  23. package/dist/api/users/index.browser.js +6 -0
  24. package/dist/api/users/index.browser.js.map +1 -1
  25. package/dist/api/users/index.d.ts +148 -227
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +60 -14
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts.map +1 -1
  30. package/dist/api/verifications/index.js +2 -1
  31. package/dist/api/verifications/index.js.map +1 -1
  32. package/dist/bucket/index.d.ts +77 -107
  33. package/dist/bucket/index.d.ts.map +1 -1
  34. package/dist/bucket/index.js +153 -5
  35. package/dist/bucket/index.js.map +1 -1
  36. package/dist/bucket/index.workerd.js +12 -2
  37. package/dist/bucket/index.workerd.js.map +1 -1
  38. package/dist/cache/core/index.d.ts +26 -0
  39. package/dist/cache/core/index.d.ts.map +1 -1
  40. package/dist/cache/core/index.js +11 -1
  41. package/dist/cache/core/index.js.map +1 -1
  42. package/dist/cache/core/index.workerd.js +11 -1
  43. package/dist/cache/core/index.workerd.js.map +1 -1
  44. package/dist/captcha/index.js.map +1 -1
  45. package/dist/cli/config/index.d.ts +7 -5
  46. package/dist/cli/config/index.d.ts.map +1 -1
  47. package/dist/cli/config/index.js +2 -3
  48. package/dist/cli/config/index.js.map +1 -1
  49. package/dist/cli/core/index.d.ts +637 -11660
  50. package/dist/cli/core/index.d.ts.map +1 -1
  51. package/dist/cli/core/index.js +707 -532
  52. package/dist/cli/core/index.js.map +1 -1
  53. package/dist/cli/devtools/index.d.ts +4 -8
  54. package/dist/cli/devtools/index.d.ts.map +1 -1
  55. package/dist/cli/devtools/index.js +20 -16
  56. package/dist/cli/devtools/index.js.map +1 -1
  57. package/dist/cli/platform/index.d.ts +51 -77
  58. package/dist/cli/platform/index.d.ts.map +1 -1
  59. package/dist/cli/platform/index.js +65 -15
  60. package/dist/cli/platform/index.js.map +1 -1
  61. package/dist/cli/vendor/index.d.ts +10 -13
  62. package/dist/cli/vendor/index.d.ts.map +1 -1
  63. package/dist/cli/vendor/index.js +30 -12
  64. package/dist/cli/vendor/index.js.map +1 -1
  65. package/dist/command/index.js +1 -1
  66. package/dist/command/index.js.map +1 -1
  67. package/dist/core/index.browser.js +27 -3
  68. package/dist/core/index.browser.js.map +1 -1
  69. package/dist/core/index.d.ts +8 -11
  70. package/dist/core/index.d.ts.map +1 -1
  71. package/dist/core/index.js +27 -3
  72. package/dist/core/index.js.map +1 -1
  73. package/dist/core/index.native.js +27 -3
  74. package/dist/core/index.native.js.map +1 -1
  75. package/dist/core/index.workerd.js +27 -3
  76. package/dist/core/index.workerd.js.map +1 -1
  77. package/dist/crypto/index.js.map +1 -1
  78. package/dist/datetime/index.d.ts +69 -10
  79. package/dist/datetime/index.d.ts.map +1 -1
  80. package/dist/datetime/index.js +135 -13
  81. package/dist/datetime/index.js.map +1 -1
  82. package/dist/email/core/index.js.map +1 -1
  83. package/dist/email/smtp/index.js +130 -16
  84. package/dist/email/smtp/index.js.map +1 -1
  85. package/dist/fake/index.js.map +1 -1
  86. package/dist/lock/core/index.d.ts +30 -2
  87. package/dist/lock/core/index.d.ts.map +1 -1
  88. package/dist/lock/core/index.js +35 -12
  89. package/dist/lock/core/index.js.map +1 -1
  90. package/dist/lock/redis/index.js.map +1 -1
  91. package/dist/logger/index.js +32 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/mcp/index.d.ts +238 -31
  94. package/dist/mcp/index.d.ts.map +1 -1
  95. package/dist/mcp/index.js +198 -67
  96. package/dist/mcp/index.js.map +1 -1
  97. package/dist/orm/core/index.browser.js +2 -362
  98. package/dist/orm/core/index.browser.js.map +1 -1
  99. package/dist/orm/core/index.bun.js +18 -409
  100. package/dist/orm/core/index.bun.js.map +1 -1
  101. package/dist/orm/core/index.d.ts +41 -194
  102. package/dist/orm/core/index.d.ts.map +1 -1
  103. package/dist/orm/core/index.js +27 -422
  104. package/dist/orm/core/index.js.map +1 -1
  105. package/dist/orm/postgres/index.bun.js +17 -20
  106. package/dist/orm/postgres/index.bun.js.map +1 -1
  107. package/dist/orm/postgres/index.d.ts +1 -5
  108. package/dist/orm/postgres/index.d.ts.map +1 -1
  109. package/dist/orm/postgres/index.js +17 -20
  110. package/dist/orm/postgres/index.js.map +1 -1
  111. package/dist/react/core/index.d.ts +102 -1
  112. package/dist/react/core/index.d.ts.map +1 -1
  113. package/dist/react/core/index.js +65 -1
  114. package/dist/react/core/index.js.map +1 -1
  115. package/dist/react/form/index.d.ts +6 -0
  116. package/dist/react/form/index.d.ts.map +1 -1
  117. package/dist/react/form/index.js +7 -7
  118. package/dist/react/form/index.js.map +1 -1
  119. package/dist/react/i18n/index.d.ts +7 -1
  120. package/dist/react/i18n/index.d.ts.map +1 -1
  121. package/dist/react/i18n/index.js +6 -0
  122. package/dist/react/i18n/index.js.map +1 -1
  123. package/dist/react/intro/index.js +22 -17
  124. package/dist/react/intro/index.js.map +1 -1
  125. package/dist/react/router/index.browser.js +98 -4
  126. package/dist/react/router/index.browser.js.map +1 -1
  127. package/dist/react/router/index.d.ts +58 -5
  128. package/dist/react/router/index.d.ts.map +1 -1
  129. package/dist/react/router/index.js +122 -6
  130. package/dist/react/router/index.js.map +1 -1
  131. package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
  132. package/dist/react/testing/index.js +1 -1
  133. package/dist/react/testing/index.js.map +1 -1
  134. package/dist/react/ui/index.d.ts +195 -1
  135. package/dist/react/ui/index.d.ts.map +1 -1
  136. package/dist/react/ui/index.js +64 -1
  137. package/dist/react/ui/index.js.map +1 -1
  138. package/dist/react/websocket/index.js.map +1 -1
  139. package/dist/redis/index.js.map +1 -1
  140. package/dist/scheduler/index.d.ts +1 -2
  141. package/dist/scheduler/index.d.ts.map +1 -1
  142. package/dist/scheduler/index.js +1 -1
  143. package/dist/scheduler/index.js.map +1 -1
  144. package/dist/scheduler/index.workerd.js +1 -1
  145. package/dist/scheduler/index.workerd.js.map +1 -1
  146. package/dist/security/index.browser.js.map +1 -1
  147. package/dist/security/index.d.ts.map +1 -1
  148. package/dist/security/index.js +2 -2
  149. package/dist/security/index.js.map +1 -1
  150. package/dist/server/auth/index.d.ts.map +1 -1
  151. package/dist/server/auth/index.js +24 -10
  152. package/dist/server/auth/index.js.map +1 -1
  153. package/dist/server/cookies/index.js.map +1 -1
  154. package/dist/server/core/index.browser.js +10 -3
  155. package/dist/server/core/index.browser.js.map +1 -1
  156. package/dist/server/core/index.d.ts +1 -4
  157. package/dist/server/core/index.d.ts.map +1 -1
  158. package/dist/server/core/index.js +47 -9
  159. package/dist/server/core/index.js.map +1 -1
  160. package/dist/server/links/index.browser.js.map +1 -1
  161. package/dist/server/links/index.js.map +1 -1
  162. package/dist/server/metrics/index.js +19 -1
  163. package/dist/server/metrics/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.js.map +1 -1
  165. package/dist/server/static/index.js.map +1 -1
  166. package/dist/server/swagger/index.d.ts.map +1 -1
  167. package/dist/server/swagger/index.js +4 -5
  168. package/dist/server/swagger/index.js.map +1 -1
  169. package/dist/sms/index.js.map +1 -1
  170. package/dist/system/index.browser.js.map +1 -1
  171. package/dist/system/index.js.map +1 -1
  172. package/dist/system/index.workerd.js.map +1 -1
  173. package/dist/topic/core/index.js.map +1 -1
  174. package/dist/websocket/index.browser.js +32 -5
  175. package/dist/websocket/index.browser.js.map +1 -1
  176. package/dist/websocket/index.d.ts +3 -1
  177. package/dist/websocket/index.d.ts.map +1 -1
  178. package/dist/websocket/index.js +42 -6
  179. package/dist/websocket/index.js.map +1 -1
  180. package/package.json +685 -274
  181. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  182. package/src/api/jobs/__tests__/$job.spec.ts +5 -1
  183. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  184. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  185. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  186. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  187. package/src/api/users/entities/sessions.ts +6 -0
  188. package/src/api/users/jobs/UserJobs.ts +44 -17
  189. package/src/api/users/providers/RealmProvider.ts +4 -0
  190. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  191. package/src/api/users/services/SessionService.ts +27 -0
  192. package/src/api/users/services/UserService.ts +1 -5
  193. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  194. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  195. package/src/api/verifications/services/VerificationService.ts +1 -0
  196. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  197. package/src/bucket/index.ts +19 -2
  198. package/src/bucket/primitives/$bucket.ts +9 -1
  199. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  200. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  201. package/src/cache/core/index.ts +29 -0
  202. package/src/cache/core/primitives/$cache.ts +14 -1
  203. package/src/cli/config/defineConfig.ts +13 -15
  204. package/src/cli/core/__tests__/init.spec.ts +214 -7
  205. package/src/cli/core/commands/init.ts +12 -0
  206. package/src/cli/core/services/PackageManagerUtils.ts +23 -6
  207. package/src/cli/core/services/ProjectScaffolder.ts +315 -33
  208. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  209. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  210. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  211. package/src/cli/core/templates/agentMd.ts +2 -10
  212. package/src/cli/core/templates/apiIndexTs.ts +23 -1
  213. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  214. package/src/cli/core/templates/mainCss.ts +1 -0
  215. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  216. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  217. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  218. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  219. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  220. package/src/cli/core/templates/webAppRouterTs.ts +104 -1
  221. package/src/cli/core/templates/webIndexTs.ts +23 -1
  222. package/src/cli/devtools/index.ts +12 -26
  223. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  224. package/src/cli/platform/index.ts +15 -24
  225. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  226. package/src/cli/vendor/index.ts +14 -23
  227. package/src/command/providers/CliProvider.ts +1 -1
  228. package/src/core/Alepha.ts +11 -1
  229. package/src/core/helpers/ref.ts +18 -0
  230. package/src/core/index.shared.ts +1 -0
  231. package/src/core/interfaces/Service.ts +3 -1
  232. package/src/core/providers/SchemaValidator.ts +9 -1
  233. package/src/core/providers/TypeProvider.ts +2 -3
  234. package/src/datetime/REFACTORING.md +118 -0
  235. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  236. package/src/lock/core/index.ts +31 -0
  237. package/src/lock/core/primitives/$lock.ts +14 -1
  238. package/src/logger/services/Logger.ts +1 -1
  239. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  240. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  241. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  242. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  243. package/src/mcp/helpers/jsonrpc.ts +26 -1
  244. package/src/mcp/index.ts +10 -5
  245. package/src/mcp/interfaces/McpTypes.ts +83 -6
  246. package/src/mcp/primitives/$prompt.ts +18 -1
  247. package/src/mcp/primitives/$resource.ts +18 -1
  248. package/src/mcp/primitives/$tool.ts +83 -7
  249. package/src/mcp/providers/McpServerProvider.ts +74 -16
  250. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  251. package/src/orm/REFACTORING.md +330 -0
  252. package/src/orm/__tests__/$repository-tests.ts +1 -0
  253. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  254. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  255. package/src/orm/core/index.shared.ts +0 -2
  256. package/src/orm/core/index.ts +1 -2
  257. package/src/orm/core/primitives/$repository.ts +3 -6
  258. package/src/orm/core/primitives/$transactional.ts +11 -0
  259. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  260. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  261. package/src/orm/core/schemas/updateSchema.ts +1 -1
  262. package/src/orm/core/services/ModelBuilder.ts +1 -13
  263. package/src/orm/core/services/PgRelationManager.ts +4 -2
  264. package/src/orm/core/services/Repository.ts +1 -42
  265. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  266. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  267. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  268. package/src/react/core/hooks/useQuery.ts +153 -0
  269. package/src/react/core/index.ts +1 -0
  270. package/src/react/form/services/FormModel.ts +15 -6
  271. package/src/react/form/services/parseField.ts +8 -0
  272. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  273. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  274. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  275. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  276. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  277. package/src/react/router/primitives/$page.ts +28 -4
  278. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  279. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  280. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  281. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  282. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  283. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  284. package/src/react/ui/index.ts +6 -0
  285. package/src/react/ui/services/SchemaControl.ts +209 -0
  286. package/src/scheduler/providers/CronProvider.ts +1 -1
  287. package/src/security/primitives/$basicAuth.ts +1 -1
  288. package/src/security/primitives/$issuer.ts +6 -3
  289. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  290. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  291. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  292. package/src/server/core/errors/ValidationError.ts +13 -1
  293. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  294. package/src/server/core/primitives/$action.ts +16 -5
  295. package/src/server/core/providers/ServerProvider.ts +1 -1
  296. package/src/server/core/providers/ServerRouterProvider.ts +28 -6
  297. package/src/server/core/services/HttpClient.ts +1 -1
  298. package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
  299. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  300. package/src/websocket/services/WebSocketClient.ts +11 -5
  301. package/src/mcp/transports/SseMcpTransport.ts +0 -182
  302. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  303. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  304. package/src/orm/core/primitives/$view.ts +0 -88
@@ -1 +1 @@
1
- {"version":3,"file":"index.workerd.js","names":["nodeJoin","nodeBasename"],"sources":["../../src/system/providers/FileSystemProvider.ts","../../src/system/providers/MemoryFileSystemProvider.ts","../../src/system/providers/MemoryShellProvider.ts","../../src/system/providers/ShellProvider.ts","../../src/system/services/FileDetector.ts","../../src/system/providers/WorkerdFileSystemProvider.ts","../../src/system/errors/FileError.ts","../../src/system/index.workerd.ts"],"sourcesContent":["import type { FileLike, StreamLike } from \"alepha\";\n\n/**\n * Options for creating a file from a URL\n */\nexport interface CreateFileFromUrlOptions {\n /**\n * The URL to load the file from (file://, http://, or https://)\n */\n url: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a path (URL with file:// scheme)\n */\nexport interface CreateFileFromPathOptions {\n /**\n * The path to the file on the local filesystem\n */\n path: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a Buffer\n */\nexport interface CreateFileFromBufferOptions {\n /**\n * The Buffer containing the file data\n */\n buffer: Buffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a stream\n */\nexport interface CreateFileFromStreamOptions {\n /**\n * The readable stream containing the file data\n */\n stream: StreamLike;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n /**\n * The size of the file in bytes (optional)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from text content\n */\nexport interface CreateFileFromTextOptions {\n /**\n * The text content to create the file from\n */\n text: string;\n /**\n * The MIME type of the file (default: text/plain)\n */\n type?: string;\n /**\n * The name of the file (default: \"file.txt\")\n */\n name?: string;\n}\n\nexport interface CreateFileFromResponseOptions {\n /**\n * The Response object containing the file data\n */\n response: Response;\n /**\n * Override the name (optional, uses filename from Content-Disposition header if not provided)\n */\n name?: string;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n}\n\n/**\n * Options for creating a file from a Web File object\n */\nexport interface CreateFileFromWebFileOptions {\n /**\n * The Web File object\n */\n file: File;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n /**\n * Override the name (optional, uses file.name if not provided)\n */\n name?: string;\n /**\n * Override the size (optional, uses file.size if not provided)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from an ArrayBuffer\n */\nexport interface CreateFileFromArrayBufferOptions {\n /**\n * The ArrayBuffer containing the file data\n */\n arrayBuffer: ArrayBuffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Union type for all createFile options\n */\nexport type CreateFileOptions =\n | CreateFileFromUrlOptions\n | CreateFileFromPathOptions\n | CreateFileFromBufferOptions\n | CreateFileFromStreamOptions\n | CreateFileFromTextOptions\n | CreateFileFromWebFileOptions\n | CreateFileFromResponseOptions\n | CreateFileFromArrayBufferOptions;\n\n/**\n * Options for rm (remove) operation\n */\nexport interface RmOptions {\n /**\n * If true, removes directories and their contents recursively\n */\n recursive?: boolean;\n /**\n * If true, no error will be thrown if the path does not exist\n */\n force?: boolean;\n}\n\n/**\n * Options for cp (copy) operation\n */\nexport interface CpOptions {\n /**\n * Copy directories recursively.\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, overwrite existing destination\n */\n force?: boolean;\n}\n\n/**\n * Options for mkdir operation\n */\nexport interface MkdirOptions {\n /**\n * If true, creates parent directories as needed\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, does not throw an error if the directory already exists\n *\n * @default true\n */\n force?: boolean;\n /**\n * File mode (permission and sticky bits)\n */\n mode?: number;\n}\n\n/**\n * Options for ls (list) operation\n */\nexport interface LsOptions {\n /**\n * If true, list contents of directories recursively\n */\n recursive?: boolean;\n /**\n * If true, include hidden files (starting with .)\n */\n hidden?: boolean;\n}\n\n/**\n * FileSystem interface providing utilities for working with files.\n */\nexport abstract class FileSystemProvider {\n /**\n * Joins multiple path segments into a single path.\n *\n * @param paths - The path segments to join\n * @returns The joined path\n */\n abstract join(...paths: string[]): string;\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n */\n abstract createFile(options: CreateFileOptions): FileLike;\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n */\n abstract rm(path: string, options?: RmOptions): Promise<void>;\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n */\n abstract cp(src: string, dest: string, options?: CpOptions): Promise<void>;\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n */\n abstract mv(src: string, dest: string): Promise<void>;\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n */\n abstract mkdir(path: string, options?: MkdirOptions): Promise<void>;\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n */\n abstract ls(path: string, options?: LsOptions): Promise<string[]>;\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n */\n abstract exists(path: string): Promise<boolean>;\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n */\n abstract readFile(path: string): Promise<Buffer>;\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n */\n abstract writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void>;\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n */\n abstract readTextFile(path: string): Promise<string>;\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n */\n abstract readJsonFile<T = unknown>(path: string): Promise<T>;\n}\n","import { basename as nodeBasename, join as nodeJoin } from \"node:path\";\nimport { $inject, AlephaError, type FileLike, Json } from \"alepha\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryFileSystemProviderOptions {\n /**\n * Error to throw on mkdir operations (for testing error handling)\n */\n mkdirError?: Error | null;\n /**\n * Error to throw on writeFile operations (for testing error handling)\n */\n writeFileError?: Error | null;\n /**\n * Error to throw on readFile operations (for testing error handling)\n */\n readFileError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of FileSystemProvider for testing.\n *\n * This provider stores all files and directories in memory, making it ideal for\n * unit tests that need to verify file operations without touching the real file system.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real FileSystemProvider with MemoryFileSystemProvider\n * const alepha = Alepha.create().with({\n * provide: FileSystemProvider,\n * use: MemoryFileSystemProvider,\n * });\n *\n * // Run code that uses FileSystemProvider\n * const service = alepha.inject(MyService);\n * await service.saveFile(\"test.txt\", \"Hello World\");\n *\n * // Verify the file was written\n * const memoryFs = alepha.inject(MemoryFileSystemProvider);\n * expect(memoryFs.files.get(\"test.txt\")?.toString()).toBe(\"Hello World\");\n * ```\n */\nexport class MemoryFileSystemProvider implements FileSystemProvider {\n protected json = $inject(Json);\n\n /**\n * In-memory storage for files (path -> content)\n */\n public files = new Map<string, Buffer>();\n\n /**\n * In-memory storage for directories\n */\n public directories = new Set<string>();\n\n /**\n * Track mkdir calls for test assertions\n */\n public mkdirCalls: Array<{ path: string; options?: MkdirOptions }> = [];\n\n /**\n * Track writeFile calls for test assertions\n */\n public writeFileCalls: Array<{ path: string; data: string }> = [];\n\n /**\n * Track readFile calls for test assertions\n */\n public readFileCalls: Array<string> = [];\n\n /**\n * Track rm calls for test assertions\n */\n public rmCalls: Array<{ path: string; options?: RmOptions }> = [];\n\n /**\n * Track join calls for test assertions\n */\n public joinCalls: Array<string[]> = [];\n\n /**\n * Error to throw on mkdir (for testing error handling)\n */\n public mkdirError: Error | null = null;\n\n /**\n * Error to throw on writeFile (for testing error handling)\n */\n public writeFileError: Error | null = null;\n\n /**\n * Error to throw on readFile (for testing error handling)\n */\n public readFileError: Error | null = null;\n\n constructor(options: MemoryFileSystemProviderOptions = {}) {\n this.mkdirError = options.mkdirError ?? null;\n this.writeFileError = options.writeFileError ?? null;\n this.readFileError = options.readFileError ?? null;\n }\n\n /**\n * Join path segments using forward slashes.\n * Uses Node's path.join for proper normalization (handles .. and .)\n */\n public join(...paths: string[]): string {\n this.joinCalls.push(paths);\n return nodeJoin(...paths);\n }\n\n /**\n * Normalize path separators to forward slashes for consistent internal storage.\n * This ensures Windows paths work correctly in the in-memory file system.\n */\n protected normalizePath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n }\n\n /**\n * Create a FileLike object from various sources.\n */\n public createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const filePath = options.path;\n const buffer = this.files.get(filePath);\n if (buffer === undefined) {\n throw new AlephaError(\n `ENOENT: no such file or directory, open '${filePath}'`,\n );\n }\n return {\n name: options.name ?? nodeBasename(filePath),\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"buffer\" in options) {\n const buffer = options.buffer;\n return {\n name: options.name ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"text\" in options) {\n const buffer = Buffer.from(options.text, \"utf-8\");\n return {\n name: options.name ?? \"file.txt\",\n type: options.type ?? \"text/plain\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => options.text,\n };\n }\n\n throw new AlephaError(\n \"MemoryFileSystemProvider.createFile: unsupported options. Only buffer and text are supported.\",\n );\n }\n\n /**\n * Remove a file or directory from memory.\n */\n public async rm(path: string, options?: RmOptions): Promise<void> {\n this.rmCalls.push({ path, options });\n\n const exists = this.files.has(path) || this.directories.has(path);\n\n if (!exists && !options?.force) {\n throw new AlephaError(`ENOENT: no such file or directory, rm '${path}'`);\n }\n\n if (this.directories.has(path)) {\n if (options?.recursive) {\n // Remove directory and all contents\n this.directories.delete(path);\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${path}/`)) {\n this.files.delete(filePath);\n }\n }\n for (const dirPath of this.directories) {\n if (dirPath.startsWith(`${path}/`)) {\n this.directories.delete(dirPath);\n }\n }\n } else {\n throw new AlephaError(\n `EISDIR: illegal operation on a directory, rm '${path}'`,\n );\n }\n } else {\n this.files.delete(path);\n }\n }\n\n /**\n * Copy a file or directory in memory.\n */\n public async cp(\n src: string,\n dest: string,\n options?: CpOptions,\n ): Promise<void> {\n if (this.directories.has(src)) {\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.set(newPath, Buffer.from(content));\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.set(dest, Buffer.from(content));\n } else {\n throw new AlephaError(`ENOENT: no such file or directory, cp '${src}'`);\n }\n }\n\n /**\n * Move/rename a file or directory in memory.\n */\n public async mv(src: string, dest: string): Promise<void> {\n if (this.directories.has(src)) {\n // Move directory and contents\n this.directories.delete(src);\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.delete(filePath);\n this.files.set(newPath, content);\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.delete(src);\n this.files.set(dest, content);\n } else {\n throw new AlephaError(`ENOENT: no such file or directory, mv '${src}'`);\n }\n }\n\n /**\n * Create a directory in memory.\n */\n public async mkdir(path: string, options?: MkdirOptions): Promise<void> {\n this.mkdirCalls.push({ path, options });\n\n if (this.mkdirError) {\n throw this.mkdirError;\n }\n\n const normalizedPath = this.normalizePath(path);\n\n if (this.directories.has(normalizedPath) && !options?.recursive) {\n throw new AlephaError(`EEXIST: file already exists, mkdir '${path}'`);\n }\n\n this.directories.add(normalizedPath);\n\n // If recursive, create parent directories\n if (options?.recursive) {\n const parts = normalizedPath.split(\"/\").filter(Boolean);\n let current = \"\";\n for (const part of parts) {\n current = current ? `${current}/${part}` : part;\n this.directories.add(current);\n }\n }\n }\n\n /**\n * List files in a directory.\n */\n public async ls(path: string, options?: LsOptions): Promise<string[]> {\n const normalizedPath = this.normalizePath(path).replace(/\\/$/, \"\");\n const entries = new Set<string>();\n\n // Find files in the directory\n for (const filePath of this.files.keys()) {\n const normalizedFilePath = this.normalizePath(filePath);\n if (normalizedFilePath.startsWith(`${normalizedPath}/`)) {\n const relativePath = normalizedFilePath.slice(\n normalizedPath.length + 1,\n );\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else {\n entries.add(parts[0]);\n }\n }\n }\n\n // Find subdirectories\n for (const dirPath of this.directories) {\n const normalizedDirPath = this.normalizePath(dirPath);\n if (\n normalizedDirPath.startsWith(`${normalizedPath}/`) &&\n normalizedDirPath !== normalizedPath\n ) {\n const relativePath = normalizedDirPath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else if (parts.length === 1) {\n entries.add(parts[0]);\n }\n }\n }\n\n let result = Array.from(entries);\n\n // Filter hidden files unless requested\n if (!options?.hidden) {\n result = result.filter((entry) => !entry.startsWith(\".\"));\n }\n\n return result.sort();\n }\n\n /**\n * Check if a file or directory exists in memory.\n */\n public async exists(path: string): Promise<boolean> {\n return this.files.has(path) || this.directories.has(path);\n }\n\n /**\n * Read a file from memory.\n */\n public async readFile(path: string): Promise<Buffer> {\n this.readFileCalls.push(path);\n\n if (this.readFileError) {\n throw this.readFileError;\n }\n\n const content = this.files.get(path);\n if (!content) {\n throw new AlephaError(\n `ENOENT: no such file or directory, open '${path}'`,\n );\n }\n return content;\n }\n\n /**\n * Read a file from memory as text.\n */\n public async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Read a file from memory as JSON.\n */\n public async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Write a file to memory.\n */\n public async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n const dataStr =\n typeof data === \"string\"\n ? data\n : data instanceof Buffer || data instanceof Uint8Array\n ? data.toString(\"utf-8\")\n : await data.text();\n\n this.writeFileCalls.push({ path, data: dataStr });\n\n if (this.writeFileError) {\n throw this.writeFileError;\n }\n\n const buffer =\n typeof data === \"string\"\n ? Buffer.from(data, \"utf-8\")\n : data instanceof Buffer\n ? data\n : data instanceof Uint8Array\n ? Buffer.from(data)\n : Buffer.from(await data.text(), \"utf-8\");\n\n this.files.set(path, buffer);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n */\n public reset(): void {\n this.files.clear();\n this.directories.clear();\n this.mkdirCalls = [];\n this.writeFileCalls = [];\n this.readFileCalls = [];\n this.rmCalls = [];\n this.joinCalls = [];\n this.mkdirError = null;\n this.writeFileError = null;\n this.readFileError = null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test assertion helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Check if a file was written during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasWritten(\"/project/tsconfig.json\")).toBe(true);\n * ```\n */\n public wasWritten(path: string): boolean {\n return this.writeFileCalls.some((call) => call.path === path);\n }\n\n /**\n * Check if a file was written with content matching a pattern.\n *\n * @example\n * ```typescript\n * expect(fs.wasWrittenMatching(\"/project/tsconfig.json\", /extends/)).toBe(true);\n * ```\n */\n public wasWrittenMatching(path: string, pattern: RegExp): boolean {\n const call = this.writeFileCalls.find((c) => c.path === path);\n return call ? pattern.test(call.data) : false;\n }\n\n /**\n * Check if a file was read during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasRead(\"/project/package.json\")).toBe(true);\n * ```\n */\n public wasRead(path: string): boolean {\n return this.readFileCalls.includes(path);\n }\n\n /**\n * Check if a file was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasDeleted(\"/project/old-file.txt\")).toBe(true);\n * ```\n */\n public wasDeleted(path: string): boolean {\n return this.rmCalls.some((call) => call.path === path);\n }\n\n /**\n * Get the content of a file as a string (convenience method for testing).\n */\n public getFileContent(path: string): string | undefined {\n return this.files.get(path)?.toString(\"utf-8\");\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryShellCall {\n command: string;\n options: ShellRunOptions;\n}\n\nexport interface MemoryShellProviderOptions {\n /**\n * Simulated outputs for specific commands.\n * Key is the command string, value is the stdout to return.\n */\n outputs?: Record<string, string>;\n\n /**\n * Commands that should throw an error.\n * Key is the command string, value is the error message.\n */\n errors?: Record<string, string>;\n\n /**\n * Commands that are considered \"installed\" in the system PATH.\n */\n installedCommands?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of ShellProvider for testing.\n *\n * Records all commands that would be executed without actually running them.\n * Can be configured to return specific outputs or throw errors for testing.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real ShellProvider with MemoryShellProvider\n * const alepha = Alepha.create().with({\n * provide: ShellProvider,\n * use: MemoryShellProvider,\n * });\n *\n * // Configure mock behavior\n * const shell = alepha.inject(MemoryShellProvider);\n * shell.configure({\n * outputs: { \"echo hello\": \"hello\\n\" },\n * errors: { \"failing-cmd\": \"Command failed\" },\n * });\n *\n * // Or use the fluent API\n * shell.outputs.set(\"another-cmd\", \"output\");\n * shell.errors.set(\"another-error\", \"Error message\");\n *\n * // Run code that uses ShellProvider\n * const service = alepha.inject(MyService);\n * await service.doSomething();\n *\n * // Verify commands were called\n * expect(shell.calls).toHaveLength(2);\n * expect(shell.calls[0].command).toBe(\"yarn install\");\n * ```\n */\nexport class MemoryShellProvider implements ShellProvider {\n /**\n * All recorded shell calls.\n */\n public calls: MemoryShellCall[] = [];\n\n /**\n * Simulated outputs for specific commands.\n */\n public outputs = new Map<string, string>();\n\n /**\n * Commands that should throw an error.\n */\n public errors = new Map<string, string>();\n\n /**\n * Commands considered installed in the system PATH.\n */\n public installedCommands = new Set<string>();\n\n /**\n * Configure the mock with predefined outputs, errors, and installed commands.\n */\n public configure(options: MemoryShellProviderOptions): this {\n if (options.outputs) {\n for (const [cmd, output] of Object.entries(options.outputs)) {\n this.outputs.set(cmd, output);\n }\n }\n if (options.errors) {\n for (const [cmd, error] of Object.entries(options.errors)) {\n this.errors.set(cmd, error);\n }\n }\n if (options.installedCommands) {\n for (const cmd of options.installedCommands) {\n this.installedCommands.add(cmd);\n }\n }\n return this;\n }\n\n /**\n * Record command and return simulated output.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n this.calls.push({ command, options });\n\n // Check for configured error\n const errorMsg = this.errors.get(command);\n if (errorMsg) {\n throw new AlephaError(errorMsg);\n }\n\n // Return configured output or empty string\n return this.outputs.get(command) ?? \"\";\n }\n\n /**\n * Check if a specific command was called.\n */\n public wasCalled(command: string): boolean {\n return this.calls.some((call) => call.command === command);\n }\n\n /**\n * Check if a command matching a pattern was called.\n */\n public wasCalledMatching(pattern: RegExp): boolean {\n return this.calls.some((call) => pattern.test(call.command));\n }\n\n /**\n * Get all calls matching a pattern.\n */\n public getCallsMatching(pattern: RegExp): MemoryShellCall[] {\n return this.calls.filter((call) => pattern.test(call.command));\n }\n\n /**\n * Check if a command is installed.\n */\n public async isInstalled(command: string): Promise<boolean> {\n return this.installedCommands.has(command);\n }\n\n /**\n * Reset all recorded state.\n */\n public reset(): void {\n this.calls = [];\n this.outputs.clear();\n this.errors.clear();\n this.installedCommands.clear();\n }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ShellRunOptions {\n /**\n * Working directory for the command.\n */\n root?: string;\n\n /**\n * Additional environment variables.\n */\n env?: Record<string, string>;\n\n /**\n * Resolve the executable from node_modules/.bin.\n * Supports local project, pnpm nested, and monorepo structures.\n * @default false\n */\n resolve?: boolean;\n\n /**\n * Capture stdout instead of inheriting stdio.\n * When true, returns stdout as string.\n * When false, streams output to terminal.\n * @default false\n */\n capture?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract provider for executing shell commands and binaries.\n *\n * Implementations:\n * - `NodeShellProvider` - Real shell execution using Node.js child_process\n * - `MemoryShellProvider` - In-memory mock for testing\n *\n * @example\n * ```typescript\n * class MyService {\n * protected readonly shell = $inject(ShellProvider);\n *\n * async build() {\n * // Run shell command directly\n * await this.shell.run(\"yarn install\");\n *\n * // Run local binary with resolution\n * await this.shell.run(\"vite build\", { resolve: true });\n *\n * // Capture output\n * const output = await this.shell.run(\"echo hello\", { capture: true });\n * }\n * }\n * ```\n */\nexport abstract class ShellProvider {\n /**\n * Run a shell command or binary.\n *\n * @param command - The command to run\n * @param options - Execution options\n * @returns stdout if capture is true, empty string otherwise\n */\n abstract run(command: string, options?: ShellRunOptions): Promise<string>;\n\n /**\n * Check if a command is installed and available in the system PATH.\n *\n * @param command - The command name to check\n * @returns true if the command is available\n */\n abstract isInstalled(command: string): Promise<boolean>;\n}\n","import { Readable } from \"node:stream\";\n\nexport interface FileTypeResult {\n /**\n * The detected MIME type\n */\n mimeType: string;\n /**\n * The detected file extension\n */\n extension: string;\n /**\n * Whether the file type was verified by magic bytes\n */\n verified: boolean;\n /**\n * The stream (potentially wrapped to allow re-reading)\n */\n stream: Readable;\n}\n\n/**\n * Service for detecting file types and getting content types.\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n *\n * // Get content type from filename\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n *\n * // Detect file type by magic bytes\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\nexport class FileDetector {\n /**\n * Magic byte signatures for common file formats.\n * Each signature is represented as an array of bytes or null (wildcard).\n */\n protected static readonly MAGIC_BYTES: Record<\n string,\n { signature: (number | null)[]; mimeType: string }[]\n > = {\n // Images\n png: [\n {\n signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],\n mimeType: \"image/png\",\n },\n ],\n jpg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n jpeg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n gif: [\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],\n mimeType: \"image/gif\",\n }, // GIF87a\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],\n mimeType: \"image/gif\",\n }, // GIF89a\n ],\n webp: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x45,\n 0x42,\n 0x50,\n ],\n mimeType: \"image/webp\",\n },\n ],\n bmp: [{ signature: [0x42, 0x4d], mimeType: \"image/bmp\" }],\n ico: [{ signature: [0x00, 0x00, 0x01, 0x00], mimeType: \"image/x-icon\" }],\n tiff: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" }, // Little-endian\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" }, // Big-endian\n ],\n tif: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" },\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" },\n ],\n\n // Documents\n pdf: [\n {\n signature: [0x25, 0x50, 0x44, 0x46, 0x2d],\n mimeType: \"application/pdf\",\n },\n ], // %PDF-\n zip: [\n { signature: [0x50, 0x4b, 0x03, 0x04], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x05, 0x06], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x07, 0x08], mimeType: \"application/zip\" },\n ],\n\n // Archives\n rar: [\n {\n signature: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07],\n mimeType: \"application/vnd.rar\",\n },\n ],\n \"7z\": [\n {\n signature: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c],\n mimeType: \"application/x-7z-compressed\",\n },\n ],\n tar: [\n {\n signature: [0x75, 0x73, 0x74, 0x61, 0x72],\n mimeType: \"application/x-tar\",\n },\n ],\n gz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n tgz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n\n // Audio\n mp3: [\n { signature: [0xff, 0xfb], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf3], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf2], mimeType: \"audio/mpeg\" },\n { signature: [0x49, 0x44, 0x33], mimeType: \"audio/mpeg\" }, // ID3\n ],\n wav: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x41,\n 0x56,\n 0x45,\n ],\n mimeType: \"audio/wav\",\n },\n ],\n ogg: [{ signature: [0x4f, 0x67, 0x67, 0x53], mimeType: \"audio/ogg\" }],\n flac: [{ signature: [0x66, 0x4c, 0x61, 0x43], mimeType: \"audio/flac\" }], // fLaC\n\n // Video\n mp4: [\n {\n signature: [null, null, null, null, 0x66, 0x74, 0x79, 0x70],\n mimeType: \"video/mp4\",\n }, // ftyp\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x69,\n 0x73,\n 0x6f,\n 0x6d,\n ],\n mimeType: \"video/mp4\",\n }, // ftypisom\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x6d,\n 0x70,\n 0x34,\n 0x32,\n ],\n mimeType: \"video/mp4\",\n }, // ftypmp42\n ],\n webm: [{ signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/webm\" }],\n avi: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x41,\n 0x56,\n 0x49,\n 0x20,\n ],\n mimeType: \"video/x-msvideo\",\n },\n ],\n mov: [\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x71,\n 0x74,\n 0x20,\n 0x20,\n ],\n mimeType: \"video/quicktime\",\n },\n ],\n mkv: [\n { signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/x-matroska\" },\n ],\n\n // Office (DOCX, XLSX, PPTX are all ZIP-based)\n docx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n },\n ],\n xlsx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n },\n ],\n pptx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n },\n ],\n doc: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/msword\",\n },\n ],\n xls: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-excel\",\n },\n ],\n ppt: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-powerpoint\",\n },\n ],\n };\n\n /**\n * All possible format signatures for checking against actual file content\n */\n protected static readonly ALL_SIGNATURES = Object.entries(\n FileDetector.MAGIC_BYTES,\n ).flatMap(([ext, signatures]) => signatures.map((sig) => ({ ext, ...sig })));\n\n /**\n * MIME type map for file extensions.\n *\n * Can be used to get the content type of file based on its extension.\n * Feel free to add more mime types in your project!\n */\n public static readonly mimeMap: Record<string, string> = {\n // Documents\n json: \"application/json\",\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n xml: \"application/xml\",\n csv: \"text/csv\",\n pdf: \"application/pdf\",\n md: \"text/markdown\",\n markdown: \"text/markdown\",\n rtf: \"application/rtf\",\n\n // Styles and scripts\n css: \"text/css\",\n js: \"application/javascript\",\n mjs: \"application/javascript\",\n ts: \"application/typescript\",\n jsx: \"text/jsx\",\n tsx: \"text/tsx\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n tgz: \"application/gzip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n\n // Video\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n mkv: \"video/x-matroska\",\n\n // Office\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n\n // Fonts\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n eot: \"application/vnd.ms-fontobject\",\n };\n\n /**\n * Reverse MIME type map for looking up extensions from MIME types.\n * Prefers shorter, more common extensions when multiple exist.\n */\n protected static readonly reverseMimeMap: Record<string, string> = (() => {\n const reverse: Record<string, string> = {};\n // Process in order so common extensions come first\n for (const [ext, mimeType] of Object.entries(FileDetector.mimeMap)) {\n // Only set if not already set (prefer first/shorter extension)\n if (!reverse[mimeType]) {\n reverse[mimeType] = ext;\n }\n }\n return reverse;\n })();\n\n /**\n * Returns the file extension for a given MIME type.\n *\n * @param mimeType - The MIME type to look up\n * @returns The file extension (without dot), or \"bin\" if not found\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const ext = detector.getExtensionFromMimeType(\"image/png\"); // \"png\"\n * const ext2 = detector.getExtensionFromMimeType(\"application/octet-stream\"); // \"bin\"\n * ```\n */\n getExtensionFromMimeType(mimeType: string): string {\n return FileDetector.reverseMimeMap[mimeType] || \"bin\";\n }\n /**\n * Returns the content type of file based on its filename.\n *\n * @param filename - The filename to check\n * @returns The MIME type\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n * ```\n */\n getContentType(filename: string): string {\n const ext = filename.toLowerCase().split(\".\").pop() || \"\";\n return FileDetector.mimeMap[ext] || \"application/octet-stream\";\n }\n\n /**\n * Detects the file type by checking magic bytes against the stream content.\n *\n * @param stream - The readable stream to check\n * @param filename - The filename (used to get the extension)\n * @returns File type information including MIME type, extension, and verification status\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\n async detectFileType(\n stream: Readable,\n filename: string,\n ): Promise<FileTypeResult> {\n // Get the expected MIME type from the filename extension\n const expectedMimeType = this.getContentType(filename);\n\n // Extract extension - only if filename contains a dot\n const lastDotIndex = filename.lastIndexOf(\".\");\n const ext =\n lastDotIndex > 0\n ? filename.substring(lastDotIndex + 1).toLowerCase()\n : \"\";\n\n // Read the first 16 bytes (enough for most magic byte checks)\n const { buffer, stream: newStream } = await this.peekBytes(stream, 16);\n\n // First, check if the extension's expected signature matches\n const expectedSignatures = FileDetector.MAGIC_BYTES[ext];\n if (expectedSignatures) {\n for (const { signature, mimeType } of expectedSignatures) {\n if (this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: ext,\n verified: true,\n stream: newStream,\n };\n }\n }\n }\n\n // If the expected signature didn't match, try all other signatures\n for (const {\n ext: detectedExt,\n signature,\n mimeType,\n } of FileDetector.ALL_SIGNATURES) {\n if (detectedExt !== ext && this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: detectedExt,\n verified: true,\n stream: newStream,\n };\n }\n }\n\n // If no magic bytes matched, fall back to extension-based detection\n // or return binary if extension is not recognized\n return {\n mimeType: expectedMimeType,\n extension: ext,\n verified: false,\n stream: newStream,\n };\n }\n\n /**\n * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.\n * This approach reads the entire stream upfront to avoid complex async handling issues.\n *\n * @protected\n */\n protected async peekBytes(\n stream: Readable,\n numBytes: number,\n ): Promise<{ buffer: Buffer; stream: Readable }> {\n const chunks: Buffer[] = [];\n\n // Read the entire stream\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const allData = Buffer.concat(chunks);\n const buffer = allData.subarray(0, numBytes);\n\n // Create a new stream with all the data\n const newStream = Readable.from(allData);\n\n return { buffer, stream: newStream };\n }\n\n /**\n * Checks if a buffer matches a magic byte signature.\n *\n * @protected\n */\n protected matchesSignature(\n buffer: Buffer,\n signature: (number | null)[],\n ): boolean {\n if (buffer.length < signature.length) {\n return false;\n }\n\n for (let i = 0; i < signature.length; i++) {\n if (signature[i] !== null && buffer[i] !== signature[i]) {\n return false;\n }\n }\n\n return true;\n }\n}\n","import { $inject, AlephaError, type FileLike, Json } from \"alepha\";\nimport { FileDetector } from \"../services/FileDetector.ts\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Web-standard implementation of FileSystemProvider for Cloudflare Workers and other edge runtimes.\n *\n * Uses only Web APIs (ReadableStream, TextEncoder, etc.) — no Node.js-specific APIs.\n * Provides working `createFile` with proper streaming support.\n * Filesystem operations (rm, cp, mv, etc.) are not available in edge runtimes and will throw.\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(WorkerdFileSystemProvider);\n *\n * // Create from text (returns FileLike with web ReadableStream)\n * const file = fs.createFile({ text: \"Hello!\", name: \"greeting.txt\" });\n * const stream = file.stream(); // ReadableStream (web standard)\n * ```\n */\nexport class WorkerdFileSystemProvider implements FileSystemProvider {\n protected detector = $inject(FileDetector);\n protected json = $inject(Json);\n\n protected encoder = new TextEncoder();\n protected decoder = new TextDecoder();\n\n public join(...paths: string[]): string {\n const joined = paths.join(\"/\").replace(/\\/+/g, \"/\");\n const parts = joined.split(\"/\");\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n resolved.pop();\n } else if (part !== \".\") {\n resolved.push(part);\n }\n }\n return resolved.join(\"/\") || \".\";\n }\n\n public createFile(options: CreateFileOptions): FileLike {\n if (\"text\" in options) {\n return this.createFileFromText(options.text, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"arrayBuffer\" in options) {\n return this.createFileFromArrayBuffer(options.arrayBuffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"buffer\" in options) {\n const ab =\n options.buffer instanceof ArrayBuffer\n ? options.buffer\n : options.buffer.buffer.slice(\n options.buffer.byteOffset,\n options.buffer.byteOffset + options.buffer.byteLength,\n );\n return this.createFileFromArrayBuffer(ab as ArrayBuffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"file\" in options) {\n return this.createFileFromWebFile(options.file, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n if (\"response\" in options) {\n return this.createFileFromResponse(options.response, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"stream\" in options) {\n return this.createFileFromStream(options.stream as ReadableStream, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n if (\"url\" in options) {\n return this.createFileFromUrl(options.url, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"path\" in options) {\n throw new AlephaError(\n \"WorkerdFileSystemProvider.createFile: 'path' source is not supported in edge runtimes.\",\n );\n }\n\n throw new AlephaError(\n \"WorkerdFileSystemProvider.createFile: unsupported options.\",\n );\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected createFileFromText(\n text: string,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const encoded = this.encoder.encode(text);\n const name = options.name ?? \"file.txt\";\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: encoded.byteLength,\n lastModified: Date.now(),\n stream: () =>\n new ReadableStream({\n start(controller) {\n controller.enqueue(encoded);\n controller.close();\n },\n }),\n arrayBuffer: async () =>\n encoded.buffer.slice(\n encoded.byteOffset,\n encoded.byteOffset + encoded.byteLength,\n ) as ArrayBuffer,\n text: async () => text,\n };\n }\n\n protected createFileFromArrayBuffer(\n source: ArrayBuffer,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const name = options.name ?? \"file\";\n const bytes = new Uint8Array(source);\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: source.byteLength,\n lastModified: Date.now(),\n stream: () =>\n new ReadableStream({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n }),\n arrayBuffer: async () => source,\n text: async () => this.decoder.decode(source),\n };\n }\n\n protected createFileFromWebFile(\n source: File,\n options: { type?: string; name?: string; size?: number } = {},\n ): FileLike {\n const name = options.name ?? source.name;\n return {\n name,\n type: options.type ?? (source.type || this.detector.getContentType(name)),\n size: options.size ?? source.size ?? 0,\n lastModified: source.lastModified || Date.now(),\n stream: () => source.stream(),\n arrayBuffer: async () => await source.arrayBuffer(),\n text: async () => await source.text(),\n };\n }\n\n protected createFileFromResponse(\n response: Response,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n if (!response.body) {\n throw new AlephaError(\"Response has no body stream\");\n }\n\n const sizeHeader = response.headers.get(\"content-length\");\n const size = sizeHeader ? parseInt(sizeHeader, 10) : 0;\n\n let name = options.name;\n if (!name) {\n const cd = response.headers.get(\"content-disposition\");\n if (cd) {\n const match = cd.match(/filename=\"?([^\"]+)\"?/);\n if (match) {\n name = match[1];\n }\n }\n }\n name ??= \"file\";\n\n const type =\n options.type ?? response.headers.get(\"content-type\") ?? undefined;\n\n return {\n name,\n type: type ?? this.detector.getContentType(name),\n size,\n lastModified: Date.now(),\n stream: () => response.body!,\n arrayBuffer: async () => await response.arrayBuffer(),\n text: async () => await response.text(),\n };\n }\n\n protected createFileFromStream(\n source: ReadableStream,\n options: { type?: string; name?: string; size?: number } = {},\n ): FileLike {\n const name = options.name ?? \"file\";\n let buffer: ArrayBuffer | null = null;\n\n const consumeStream = async (): Promise<ArrayBuffer> => {\n if (buffer) return buffer;\n const reader = source.getReader();\n const chunks: Uint8Array[] = [];\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) {\n chunks.push(result.value);\n }\n }\n const total = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n buffer = merged.buffer as ArrayBuffer;\n return buffer;\n };\n\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: options.size ?? 0,\n lastModified: Date.now(),\n stream: () => source,\n arrayBuffer: consumeStream,\n text: async () => this.decoder.decode(await consumeStream()),\n };\n }\n\n protected createFileFromUrl(\n url: string,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const parsedUrl = new URL(url);\n const name = options.name ?? parsedUrl.pathname.split(\"/\").pop() ?? \"file\";\n\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: 0,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: streaming from URL is not supported. Use fetch() and createFile({ response }) instead.\",\n );\n },\n arrayBuffer: async () => {\n const res = await fetch(url);\n return await res.arrayBuffer();\n },\n text: async () => {\n const res = await fetch(url);\n return await res.text();\n },\n };\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Filesystem operations — not available in edge runtimes\n // -------------------------------------------------------------------------------------------------------------------\n\n public async rm(_path: string, _options?: RmOptions): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: rm() is not available in edge runtimes.\",\n );\n }\n\n public async cp(\n _src: string,\n _dest: string,\n _options?: CpOptions,\n ): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: cp() is not available in edge runtimes.\",\n );\n }\n\n public async mv(_src: string, _dest: string): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: mv() is not available in edge runtimes.\",\n );\n }\n\n public async mkdir(_path: string, _options?: MkdirOptions): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: mkdir() is not available in edge runtimes.\",\n );\n }\n\n public async ls(_path: string, _options?: LsOptions): Promise<string[]> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: ls() is not available in edge runtimes.\",\n );\n }\n\n public async exists(_path: string): Promise<boolean> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: exists() is not available in edge runtimes.\",\n );\n }\n\n public async readFile(_path: string): Promise<Buffer> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readFile() is not available in edge runtimes.\",\n );\n }\n\n public async writeFile(\n _path: string,\n _data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: writeFile() is not available in edge runtimes.\",\n );\n }\n\n public async readTextFile(_path: string): Promise<string> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readTextFile() is not available in edge runtimes.\",\n );\n }\n\n public async readJsonFile<T = unknown>(_path: string): Promise<T> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readJsonFile() is not available in edge runtimes.\",\n );\n }\n}\n","export class FileError extends Error {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"FileError\";\n this.cause = cause;\n }\n}\n","import { $module } from \"alepha\";\nimport { FileSystemProvider } from \"./providers/FileSystemProvider.ts\";\nimport { MemoryFileSystemProvider } from \"./providers/MemoryFileSystemProvider.ts\";\nimport { MemoryShellProvider } from \"./providers/MemoryShellProvider.ts\";\nimport { ShellProvider } from \"./providers/ShellProvider.ts\";\nimport { WorkerdFileSystemProvider } from \"./providers/WorkerdFileSystemProvider.ts\";\nimport { FileDetector } from \"./services/FileDetector.ts\";\n\nexport * from \"./errors/FileError.ts\";\nexport * from \"./providers/FileSystemProvider.ts\";\nexport * from \"./providers/MemoryFileSystemProvider.ts\";\nexport * from \"./providers/MemoryShellProvider.ts\";\nexport * from \"./providers/ShellProvider.ts\";\nexport * from \"./providers/WorkerdFileSystemProvider.ts\";\nexport * from \"./services/FileDetector.ts\";\n\nexport const AlephaSystem = $module({\n name: \"alepha.system\",\n services: [FileDetector, FileSystemProvider, ShellProvider],\n variants: [\n MemoryFileSystemProvider,\n WorkerdFileSystemProvider,\n MemoryShellProvider,\n ],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileSystemProvider,\n use: WorkerdFileSystemProvider,\n })\n .with({\n optional: true,\n provide: ShellProvider,\n use: MemoryShellProvider,\n }),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.workerd.js","names":["nodeJoin","nodeBasename"],"sources":["../../src/system/providers/FileSystemProvider.ts","../../src/system/providers/MemoryFileSystemProvider.ts","../../src/system/providers/MemoryShellProvider.ts","../../src/system/providers/ShellProvider.ts","../../src/system/services/FileDetector.ts","../../src/system/providers/WorkerdFileSystemProvider.ts","../../src/system/errors/FileError.ts","../../src/system/index.workerd.ts"],"sourcesContent":["import type { FileLike, StreamLike } from \"alepha\";\n\n/**\n * Options for creating a file from a URL\n */\nexport interface CreateFileFromUrlOptions {\n /**\n * The URL to load the file from (file://, http://, or https://)\n */\n url: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a path (URL with file:// scheme)\n */\nexport interface CreateFileFromPathOptions {\n /**\n * The path to the file on the local filesystem\n */\n path: string;\n /**\n * The MIME type of the file (optional, will be detected from filename if not provided)\n */\n type?: string;\n /**\n * The name of the file (optional, will be extracted from URL if not provided)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a Buffer\n */\nexport interface CreateFileFromBufferOptions {\n /**\n * The Buffer containing the file data\n */\n buffer: Buffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Options for creating a file from a stream\n */\nexport interface CreateFileFromStreamOptions {\n /**\n * The readable stream containing the file data\n */\n stream: StreamLike;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n /**\n * The size of the file in bytes (optional)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from text content\n */\nexport interface CreateFileFromTextOptions {\n /**\n * The text content to create the file from\n */\n text: string;\n /**\n * The MIME type of the file (default: text/plain)\n */\n type?: string;\n /**\n * The name of the file (default: \"file.txt\")\n */\n name?: string;\n}\n\nexport interface CreateFileFromResponseOptions {\n /**\n * The Response object containing the file data\n */\n response: Response;\n /**\n * Override the name (optional, uses filename from Content-Disposition header if not provided)\n */\n name?: string;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n}\n\n/**\n * Options for creating a file from a Web File object\n */\nexport interface CreateFileFromWebFileOptions {\n /**\n * The Web File object\n */\n file: File;\n /**\n * Override the MIME type (optional, uses file.type if not provided)\n */\n type?: string;\n /**\n * Override the name (optional, uses file.name if not provided)\n */\n name?: string;\n /**\n * Override the size (optional, uses file.size if not provided)\n */\n size?: number;\n}\n\n/**\n * Options for creating a file from an ArrayBuffer\n */\nexport interface CreateFileFromArrayBufferOptions {\n /**\n * The ArrayBuffer containing the file data\n */\n arrayBuffer: ArrayBuffer;\n /**\n * The MIME type of the file (optional, will be detected from name if not provided)\n */\n type?: string;\n /**\n * The name of the file (required for proper content type detection)\n */\n name?: string;\n}\n\n/**\n * Union type for all createFile options\n */\nexport type CreateFileOptions =\n | CreateFileFromUrlOptions\n | CreateFileFromPathOptions\n | CreateFileFromBufferOptions\n | CreateFileFromStreamOptions\n | CreateFileFromTextOptions\n | CreateFileFromWebFileOptions\n | CreateFileFromResponseOptions\n | CreateFileFromArrayBufferOptions;\n\n/**\n * Options for rm (remove) operation\n */\nexport interface RmOptions {\n /**\n * If true, removes directories and their contents recursively\n */\n recursive?: boolean;\n /**\n * If true, no error will be thrown if the path does not exist\n */\n force?: boolean;\n}\n\n/**\n * Options for cp (copy) operation\n */\nexport interface CpOptions {\n /**\n * Copy directories recursively.\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, overwrite existing destination\n */\n force?: boolean;\n}\n\n/**\n * Options for mkdir operation\n */\nexport interface MkdirOptions {\n /**\n * If true, creates parent directories as needed\n *\n * @default true\n */\n recursive?: boolean;\n /**\n * If true, does not throw an error if the directory already exists\n *\n * @default true\n */\n force?: boolean;\n /**\n * File mode (permission and sticky bits)\n */\n mode?: number;\n}\n\n/**\n * Options for ls (list) operation\n */\nexport interface LsOptions {\n /**\n * If true, list contents of directories recursively\n */\n recursive?: boolean;\n /**\n * If true, include hidden files (starting with .)\n */\n hidden?: boolean;\n}\n\n/**\n * FileSystem interface providing utilities for working with files.\n */\nexport abstract class FileSystemProvider {\n /**\n * Joins multiple path segments into a single path.\n *\n * @param paths - The path segments to join\n * @returns The joined path\n */\n abstract join(...paths: string[]): string;\n\n /**\n * Creates a FileLike object from various sources.\n *\n * @param options - Options for creating the file\n * @returns A FileLike object\n */\n abstract createFile(options: CreateFileOptions): FileLike;\n\n /**\n * Removes a file or directory.\n *\n * @param path - The path to remove\n * @param options - Remove options\n */\n abstract rm(path: string, options?: RmOptions): Promise<void>;\n\n /**\n * Copies a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n * @param options - Copy options\n */\n abstract cp(src: string, dest: string, options?: CpOptions): Promise<void>;\n\n /**\n * Moves/renames a file or directory.\n *\n * @param src - Source path\n * @param dest - Destination path\n */\n abstract mv(src: string, dest: string): Promise<void>;\n\n /**\n * Creates a directory.\n *\n * @param path - The directory path to create\n * @param options - Mkdir options\n */\n abstract mkdir(path: string, options?: MkdirOptions): Promise<void>;\n\n /**\n * Lists files in a directory.\n *\n * @param path - The directory path to list\n * @param options - List options\n * @returns Array of filenames\n */\n abstract ls(path: string, options?: LsOptions): Promise<string[]>;\n\n /**\n * Checks if a file or directory exists.\n *\n * @param path - The path to check\n * @returns True if the path exists, false otherwise\n */\n abstract exists(path: string): Promise<boolean>;\n\n /**\n * Reads the content of a file.\n *\n * @param path - The file path to read\n * @returns The file content as a Buffer\n */\n abstract readFile(path: string): Promise<Buffer>;\n\n /**\n * Writes data to a file.\n *\n * @param path - The file path to write to\n * @param data - The data to write (Buffer or string)\n */\n abstract writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void>;\n\n /**\n * Reads the content of a file as a string.\n *\n * @param path - The file path to read\n * @returns The file content as a string\n */\n abstract readTextFile(path: string): Promise<string>;\n\n /**\n * Reads the content of a file as JSON.\n *\n * @param path - The file path to read\n * @returns The parsed JSON content\n */\n abstract readJsonFile<T = unknown>(path: string): Promise<T>;\n}\n","import { basename as nodeBasename, join as nodeJoin } from \"node:path\";\nimport { $inject, AlephaError, type FileLike, Json } from \"alepha\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryFileSystemProviderOptions {\n /**\n * Error to throw on mkdir operations (for testing error handling)\n */\n mkdirError?: Error | null;\n /**\n * Error to throw on writeFile operations (for testing error handling)\n */\n writeFileError?: Error | null;\n /**\n * Error to throw on readFile operations (for testing error handling)\n */\n readFileError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of FileSystemProvider for testing.\n *\n * This provider stores all files and directories in memory, making it ideal for\n * unit tests that need to verify file operations without touching the real file system.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real FileSystemProvider with MemoryFileSystemProvider\n * const alepha = Alepha.create().with({\n * provide: FileSystemProvider,\n * use: MemoryFileSystemProvider,\n * });\n *\n * // Run code that uses FileSystemProvider\n * const service = alepha.inject(MyService);\n * await service.saveFile(\"test.txt\", \"Hello World\");\n *\n * // Verify the file was written\n * const memoryFs = alepha.inject(MemoryFileSystemProvider);\n * expect(memoryFs.files.get(\"test.txt\")?.toString()).toBe(\"Hello World\");\n * ```\n */\nexport class MemoryFileSystemProvider implements FileSystemProvider {\n protected json = $inject(Json);\n\n /**\n * In-memory storage for files (path -> content)\n */\n public files = new Map<string, Buffer>();\n\n /**\n * In-memory storage for directories\n */\n public directories = new Set<string>();\n\n /**\n * Track mkdir calls for test assertions\n */\n public mkdirCalls: Array<{ path: string; options?: MkdirOptions }> = [];\n\n /**\n * Track writeFile calls for test assertions\n */\n public writeFileCalls: Array<{ path: string; data: string }> = [];\n\n /**\n * Track readFile calls for test assertions\n */\n public readFileCalls: Array<string> = [];\n\n /**\n * Track rm calls for test assertions\n */\n public rmCalls: Array<{ path: string; options?: RmOptions }> = [];\n\n /**\n * Track join calls for test assertions\n */\n public joinCalls: Array<string[]> = [];\n\n /**\n * Error to throw on mkdir (for testing error handling)\n */\n public mkdirError: Error | null = null;\n\n /**\n * Error to throw on writeFile (for testing error handling)\n */\n public writeFileError: Error | null = null;\n\n /**\n * Error to throw on readFile (for testing error handling)\n */\n public readFileError: Error | null = null;\n\n constructor(options: MemoryFileSystemProviderOptions = {}) {\n this.mkdirError = options.mkdirError ?? null;\n this.writeFileError = options.writeFileError ?? null;\n this.readFileError = options.readFileError ?? null;\n }\n\n /**\n * Join path segments using forward slashes.\n * Uses Node's path.join for proper normalization (handles .. and .)\n */\n public join(...paths: string[]): string {\n this.joinCalls.push(paths);\n return nodeJoin(...paths);\n }\n\n /**\n * Normalize path separators to forward slashes for consistent internal storage.\n * This ensures Windows paths work correctly in the in-memory file system.\n */\n protected normalizePath(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n }\n\n /**\n * Create a FileLike object from various sources.\n */\n public createFile(options: CreateFileOptions): FileLike {\n if (\"path\" in options) {\n const filePath = options.path;\n const buffer = this.files.get(filePath);\n if (buffer === undefined) {\n throw new AlephaError(\n `ENOENT: no such file or directory, open '${filePath}'`,\n );\n }\n return {\n name: options.name ?? nodeBasename(filePath),\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"buffer\" in options) {\n const buffer = options.buffer;\n return {\n name: options.name ?? \"file\",\n type: options.type ?? \"application/octet-stream\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => buffer.toString(\"utf-8\"),\n };\n }\n\n if (\"text\" in options) {\n const buffer = Buffer.from(options.text, \"utf-8\");\n return {\n name: options.name ?? \"file.txt\",\n type: options.type ?? \"text/plain\",\n size: buffer.byteLength,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"Stream not implemented in MemoryFileSystemProvider\",\n );\n },\n arrayBuffer: async (): Promise<ArrayBuffer> =>\n buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer,\n text: async () => options.text,\n };\n }\n\n throw new AlephaError(\n \"MemoryFileSystemProvider.createFile: unsupported options. Only buffer and text are supported.\",\n );\n }\n\n /**\n * Remove a file or directory from memory.\n */\n public async rm(path: string, options?: RmOptions): Promise<void> {\n this.rmCalls.push({ path, options });\n\n const exists = this.files.has(path) || this.directories.has(path);\n\n if (!exists && !options?.force) {\n throw new AlephaError(`ENOENT: no such file or directory, rm '${path}'`);\n }\n\n if (this.directories.has(path)) {\n if (options?.recursive) {\n // Remove directory and all contents\n this.directories.delete(path);\n for (const filePath of this.files.keys()) {\n if (filePath.startsWith(`${path}/`)) {\n this.files.delete(filePath);\n }\n }\n for (const dirPath of this.directories) {\n if (dirPath.startsWith(`${path}/`)) {\n this.directories.delete(dirPath);\n }\n }\n } else {\n throw new AlephaError(\n `EISDIR: illegal operation on a directory, rm '${path}'`,\n );\n }\n } else {\n this.files.delete(path);\n }\n }\n\n /**\n * Copy a file or directory in memory.\n */\n public async cp(\n src: string,\n dest: string,\n options?: CpOptions,\n ): Promise<void> {\n if (this.directories.has(src)) {\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.set(newPath, Buffer.from(content));\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.set(dest, Buffer.from(content));\n } else {\n throw new AlephaError(`ENOENT: no such file or directory, cp '${src}'`);\n }\n }\n\n /**\n * Move/rename a file or directory in memory.\n */\n public async mv(src: string, dest: string): Promise<void> {\n if (this.directories.has(src)) {\n // Move directory and contents\n this.directories.delete(src);\n this.directories.add(dest);\n for (const [filePath, content] of this.files) {\n if (filePath.startsWith(`${src}/`)) {\n const newPath = filePath.replace(src, dest);\n this.files.delete(filePath);\n this.files.set(newPath, content);\n }\n }\n } else if (this.files.has(src)) {\n const content = this.files.get(src)!;\n this.files.delete(src);\n this.files.set(dest, content);\n } else {\n throw new AlephaError(`ENOENT: no such file or directory, mv '${src}'`);\n }\n }\n\n /**\n * Create a directory in memory.\n */\n public async mkdir(path: string, options?: MkdirOptions): Promise<void> {\n this.mkdirCalls.push({ path, options });\n\n if (this.mkdirError) {\n throw this.mkdirError;\n }\n\n const normalizedPath = this.normalizePath(path);\n\n if (this.directories.has(normalizedPath) && !options?.recursive) {\n throw new AlephaError(`EEXIST: file already exists, mkdir '${path}'`);\n }\n\n this.directories.add(normalizedPath);\n\n // If recursive, create parent directories\n if (options?.recursive) {\n const parts = normalizedPath.split(\"/\").filter(Boolean);\n let current = \"\";\n for (const part of parts) {\n current = current ? `${current}/${part}` : part;\n this.directories.add(current);\n }\n }\n }\n\n /**\n * List files in a directory.\n */\n public async ls(path: string, options?: LsOptions): Promise<string[]> {\n const normalizedPath = this.normalizePath(path).replace(/\\/$/, \"\");\n const entries = new Set<string>();\n\n // Find files in the directory\n for (const filePath of this.files.keys()) {\n const normalizedFilePath = this.normalizePath(filePath);\n if (normalizedFilePath.startsWith(`${normalizedPath}/`)) {\n const relativePath = normalizedFilePath.slice(\n normalizedPath.length + 1,\n );\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else {\n entries.add(parts[0]);\n }\n }\n }\n\n // Find subdirectories\n for (const dirPath of this.directories) {\n const normalizedDirPath = this.normalizePath(dirPath);\n if (\n normalizedDirPath.startsWith(`${normalizedPath}/`) &&\n normalizedDirPath !== normalizedPath\n ) {\n const relativePath = normalizedDirPath.slice(normalizedPath.length + 1);\n const parts = relativePath.split(\"/\");\n\n if (options?.recursive) {\n entries.add(relativePath);\n } else if (parts.length === 1) {\n entries.add(parts[0]);\n }\n }\n }\n\n let result = Array.from(entries);\n\n // Filter hidden files unless requested\n if (!options?.hidden) {\n result = result.filter((entry) => !entry.startsWith(\".\"));\n }\n\n return result.sort();\n }\n\n /**\n * Check if a file or directory exists in memory.\n */\n public async exists(path: string): Promise<boolean> {\n return this.files.has(path) || this.directories.has(path);\n }\n\n /**\n * Read a file from memory.\n */\n public async readFile(path: string): Promise<Buffer> {\n this.readFileCalls.push(path);\n\n if (this.readFileError) {\n throw this.readFileError;\n }\n\n const content = this.files.get(path);\n if (!content) {\n throw new AlephaError(\n `ENOENT: no such file or directory, open '${path}'`,\n );\n }\n return content;\n }\n\n /**\n * Read a file from memory as text.\n */\n public async readTextFile(path: string): Promise<string> {\n const buffer = await this.readFile(path);\n return buffer.toString(\"utf-8\");\n }\n\n /**\n * Read a file from memory as JSON.\n */\n public async readJsonFile<T = unknown>(path: string): Promise<T> {\n const text = await this.readTextFile(path);\n return this.json.parse(text) as T;\n }\n\n /**\n * Write a file to memory.\n */\n public async writeFile(\n path: string,\n data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n const dataStr =\n typeof data === \"string\"\n ? data\n : data instanceof Buffer || data instanceof Uint8Array\n ? data.toString(\"utf-8\")\n : await data.text();\n\n this.writeFileCalls.push({ path, data: dataStr });\n\n if (this.writeFileError) {\n throw this.writeFileError;\n }\n\n const buffer =\n typeof data === \"string\"\n ? Buffer.from(data, \"utf-8\")\n : data instanceof Buffer\n ? data\n : data instanceof Uint8Array\n ? Buffer.from(data)\n : Buffer.from(await data.text(), \"utf-8\");\n\n this.files.set(path, buffer);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n */\n public reset(): void {\n this.files.clear();\n this.directories.clear();\n this.mkdirCalls = [];\n this.writeFileCalls = [];\n this.readFileCalls = [];\n this.rmCalls = [];\n this.joinCalls = [];\n this.mkdirError = null;\n this.writeFileError = null;\n this.readFileError = null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test assertion helpers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Check if a file was written during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasWritten(\"/project/tsconfig.json\")).toBe(true);\n * ```\n */\n public wasWritten(path: string): boolean {\n return this.writeFileCalls.some((call) => call.path === path);\n }\n\n /**\n * Check if a file was written with content matching a pattern.\n *\n * @example\n * ```typescript\n * expect(fs.wasWrittenMatching(\"/project/tsconfig.json\", /extends/)).toBe(true);\n * ```\n */\n public wasWrittenMatching(path: string, pattern: RegExp): boolean {\n const call = this.writeFileCalls.find((c) => c.path === path);\n return call ? pattern.test(call.data) : false;\n }\n\n /**\n * Check if a file was read during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasRead(\"/project/package.json\")).toBe(true);\n * ```\n */\n public wasRead(path: string): boolean {\n return this.readFileCalls.includes(path);\n }\n\n /**\n * Check if a file was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(fs.wasDeleted(\"/project/old-file.txt\")).toBe(true);\n * ```\n */\n public wasDeleted(path: string): boolean {\n return this.rmCalls.some((call) => call.path === path);\n }\n\n /**\n * Get the content of a file as a string (convenience method for testing).\n */\n public getFileContent(path: string): string | undefined {\n return this.files.get(path)?.toString(\"utf-8\");\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ShellProvider, ShellRunOptions } from \"./ShellProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryShellCall {\n command: string;\n options: ShellRunOptions;\n}\n\nexport interface MemoryShellProviderOptions {\n /**\n * Simulated outputs for specific commands.\n * Key is the command string, value is the stdout to return.\n */\n outputs?: Record<string, string>;\n\n /**\n * Commands that should throw an error.\n * Key is the command string, value is the error message.\n */\n errors?: Record<string, string>;\n\n /**\n * Commands that are considered \"installed\" in the system PATH.\n */\n installedCommands?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of ShellProvider for testing.\n *\n * Records all commands that would be executed without actually running them.\n * Can be configured to return specific outputs or throw errors for testing.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real ShellProvider with MemoryShellProvider\n * const alepha = Alepha.create().with({\n * provide: ShellProvider,\n * use: MemoryShellProvider,\n * });\n *\n * // Configure mock behavior\n * const shell = alepha.inject(MemoryShellProvider);\n * shell.configure({\n * outputs: { \"echo hello\": \"hello\\n\" },\n * errors: { \"failing-cmd\": \"Command failed\" },\n * });\n *\n * // Or use the fluent API\n * shell.outputs.set(\"another-cmd\", \"output\");\n * shell.errors.set(\"another-error\", \"Error message\");\n *\n * // Run code that uses ShellProvider\n * const service = alepha.inject(MyService);\n * await service.doSomething();\n *\n * // Verify commands were called\n * expect(shell.calls).toHaveLength(2);\n * expect(shell.calls[0].command).toBe(\"yarn install\");\n * ```\n */\nexport class MemoryShellProvider implements ShellProvider {\n /**\n * All recorded shell calls.\n */\n public calls: MemoryShellCall[] = [];\n\n /**\n * Simulated outputs for specific commands.\n */\n public outputs = new Map<string, string>();\n\n /**\n * Commands that should throw an error.\n */\n public errors = new Map<string, string>();\n\n /**\n * Commands considered installed in the system PATH.\n */\n public installedCommands = new Set<string>();\n\n /**\n * Configure the mock with predefined outputs, errors, and installed commands.\n */\n public configure(options: MemoryShellProviderOptions): this {\n if (options.outputs) {\n for (const [cmd, output] of Object.entries(options.outputs)) {\n this.outputs.set(cmd, output);\n }\n }\n if (options.errors) {\n for (const [cmd, error] of Object.entries(options.errors)) {\n this.errors.set(cmd, error);\n }\n }\n if (options.installedCommands) {\n for (const cmd of options.installedCommands) {\n this.installedCommands.add(cmd);\n }\n }\n return this;\n }\n\n /**\n * Record command and return simulated output.\n */\n public async run(\n command: string,\n options: ShellRunOptions = {},\n ): Promise<string> {\n this.calls.push({ command, options });\n\n // Check for configured error\n const errorMsg = this.errors.get(command);\n if (errorMsg) {\n throw new AlephaError(errorMsg);\n }\n\n // Return configured output or empty string\n return this.outputs.get(command) ?? \"\";\n }\n\n /**\n * Check if a specific command was called.\n */\n public wasCalled(command: string): boolean {\n return this.calls.some((call) => call.command === command);\n }\n\n /**\n * Check if a command matching a pattern was called.\n */\n public wasCalledMatching(pattern: RegExp): boolean {\n return this.calls.some((call) => pattern.test(call.command));\n }\n\n /**\n * Get all calls matching a pattern.\n */\n public getCallsMatching(pattern: RegExp): MemoryShellCall[] {\n return this.calls.filter((call) => pattern.test(call.command));\n }\n\n /**\n * Check if a command is installed.\n */\n public async isInstalled(command: string): Promise<boolean> {\n return this.installedCommands.has(command);\n }\n\n /**\n * Reset all recorded state.\n */\n public reset(): void {\n this.calls = [];\n this.outputs.clear();\n this.errors.clear();\n this.installedCommands.clear();\n }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ShellRunOptions {\n /**\n * Working directory for the command.\n */\n root?: string;\n\n /**\n * Additional environment variables.\n */\n env?: Record<string, string>;\n\n /**\n * Resolve the executable from node_modules/.bin.\n * Supports local project, pnpm nested, and monorepo structures.\n * @default false\n */\n resolve?: boolean;\n\n /**\n * Capture stdout instead of inheriting stdio.\n * When true, returns stdout as string.\n * When false, streams output to terminal.\n * @default false\n */\n capture?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract provider for executing shell commands and binaries.\n *\n * Implementations:\n * - `NodeShellProvider` - Real shell execution using Node.js child_process\n * - `MemoryShellProvider` - In-memory mock for testing\n *\n * @example\n * ```typescript\n * class MyService {\n * protected readonly shell = $inject(ShellProvider);\n *\n * async build() {\n * // Run shell command directly\n * await this.shell.run(\"yarn install\");\n *\n * // Run local binary with resolution\n * await this.shell.run(\"vite build\", { resolve: true });\n *\n * // Capture output\n * const output = await this.shell.run(\"echo hello\", { capture: true });\n * }\n * }\n * ```\n */\nexport abstract class ShellProvider {\n /**\n * Run a shell command or binary.\n *\n * @param command - The command to run\n * @param options - Execution options\n * @returns stdout if capture is true, empty string otherwise\n */\n abstract run(command: string, options?: ShellRunOptions): Promise<string>;\n\n /**\n * Check if a command is installed and available in the system PATH.\n *\n * @param command - The command name to check\n * @returns true if the command is available\n */\n abstract isInstalled(command: string): Promise<boolean>;\n}\n","import { Readable } from \"node:stream\";\n\nexport interface FileTypeResult {\n /**\n * The detected MIME type\n */\n mimeType: string;\n /**\n * The detected file extension\n */\n extension: string;\n /**\n * Whether the file type was verified by magic bytes\n */\n verified: boolean;\n /**\n * The stream (potentially wrapped to allow re-reading)\n */\n stream: Readable;\n}\n\n/**\n * Service for detecting file types and getting content types.\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n *\n * // Get content type from filename\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n *\n * // Detect file type by magic bytes\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\nexport class FileDetector {\n /**\n * Magic byte signatures for common file formats.\n * Each signature is represented as an array of bytes or null (wildcard).\n */\n protected static readonly MAGIC_BYTES: Record<\n string,\n { signature: (number | null)[]; mimeType: string }[]\n > = {\n // Images\n png: [\n {\n signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],\n mimeType: \"image/png\",\n },\n ],\n jpg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n jpeg: [\n { signature: [0xff, 0xd8, 0xff, 0xe0], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe1], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe2], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe3], mimeType: \"image/jpeg\" },\n { signature: [0xff, 0xd8, 0xff, 0xe8], mimeType: \"image/jpeg\" },\n ],\n gif: [\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],\n mimeType: \"image/gif\",\n }, // GIF87a\n {\n signature: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],\n mimeType: \"image/gif\",\n }, // GIF89a\n ],\n webp: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x45,\n 0x42,\n 0x50,\n ],\n mimeType: \"image/webp\",\n },\n ],\n bmp: [{ signature: [0x42, 0x4d], mimeType: \"image/bmp\" }],\n ico: [{ signature: [0x00, 0x00, 0x01, 0x00], mimeType: \"image/x-icon\" }],\n tiff: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" }, // Little-endian\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" }, // Big-endian\n ],\n tif: [\n { signature: [0x49, 0x49, 0x2a, 0x00], mimeType: \"image/tiff\" },\n { signature: [0x4d, 0x4d, 0x00, 0x2a], mimeType: \"image/tiff\" },\n ],\n\n // Documents\n pdf: [\n {\n signature: [0x25, 0x50, 0x44, 0x46, 0x2d],\n mimeType: \"application/pdf\",\n },\n ], // %PDF-\n zip: [\n { signature: [0x50, 0x4b, 0x03, 0x04], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x05, 0x06], mimeType: \"application/zip\" },\n { signature: [0x50, 0x4b, 0x07, 0x08], mimeType: \"application/zip\" },\n ],\n\n // Archives\n rar: [\n {\n signature: [0x52, 0x61, 0x72, 0x21, 0x1a, 0x07],\n mimeType: \"application/vnd.rar\",\n },\n ],\n \"7z\": [\n {\n signature: [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c],\n mimeType: \"application/x-7z-compressed\",\n },\n ],\n tar: [\n {\n signature: [0x75, 0x73, 0x74, 0x61, 0x72],\n mimeType: \"application/x-tar\",\n },\n ],\n gz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n tgz: [{ signature: [0x1f, 0x8b], mimeType: \"application/gzip\" }],\n\n // Audio\n mp3: [\n { signature: [0xff, 0xfb], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf3], mimeType: \"audio/mpeg\" },\n { signature: [0xff, 0xf2], mimeType: \"audio/mpeg\" },\n { signature: [0x49, 0x44, 0x33], mimeType: \"audio/mpeg\" }, // ID3\n ],\n wav: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x57,\n 0x41,\n 0x56,\n 0x45,\n ],\n mimeType: \"audio/wav\",\n },\n ],\n ogg: [{ signature: [0x4f, 0x67, 0x67, 0x53], mimeType: \"audio/ogg\" }],\n flac: [{ signature: [0x66, 0x4c, 0x61, 0x43], mimeType: \"audio/flac\" }], // fLaC\n\n // Video\n mp4: [\n {\n signature: [null, null, null, null, 0x66, 0x74, 0x79, 0x70],\n mimeType: \"video/mp4\",\n }, // ftyp\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x69,\n 0x73,\n 0x6f,\n 0x6d,\n ],\n mimeType: \"video/mp4\",\n }, // ftypisom\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x6d,\n 0x70,\n 0x34,\n 0x32,\n ],\n mimeType: \"video/mp4\",\n }, // ftypmp42\n ],\n webm: [{ signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/webm\" }],\n avi: [\n {\n signature: [\n 0x52,\n 0x49,\n 0x46,\n 0x46,\n null,\n null,\n null,\n null,\n 0x41,\n 0x56,\n 0x49,\n 0x20,\n ],\n mimeType: \"video/x-msvideo\",\n },\n ],\n mov: [\n {\n signature: [\n null,\n null,\n null,\n null,\n 0x66,\n 0x74,\n 0x79,\n 0x70,\n 0x71,\n 0x74,\n 0x20,\n 0x20,\n ],\n mimeType: \"video/quicktime\",\n },\n ],\n mkv: [\n { signature: [0x1a, 0x45, 0xdf, 0xa3], mimeType: \"video/x-matroska\" },\n ],\n\n // Office (DOCX, XLSX, PPTX are all ZIP-based)\n docx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n },\n ],\n xlsx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n },\n ],\n pptx: [\n {\n signature: [0x50, 0x4b, 0x03, 0x04],\n mimeType:\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n },\n ],\n doc: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/msword\",\n },\n ],\n xls: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-excel\",\n },\n ],\n ppt: [\n {\n signature: [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1],\n mimeType: \"application/vnd.ms-powerpoint\",\n },\n ],\n };\n\n /**\n * All possible format signatures for checking against actual file content\n */\n protected static readonly ALL_SIGNATURES = Object.entries(\n FileDetector.MAGIC_BYTES,\n ).flatMap(([ext, signatures]) => signatures.map((sig) => ({ ext, ...sig })));\n\n /**\n * MIME type map for file extensions.\n *\n * Can be used to get the content type of file based on its extension.\n * Feel free to add more mime types in your project!\n */\n public static readonly mimeMap: Record<string, string> = {\n // Documents\n json: \"application/json\",\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n xml: \"application/xml\",\n csv: \"text/csv\",\n pdf: \"application/pdf\",\n md: \"text/markdown\",\n markdown: \"text/markdown\",\n rtf: \"application/rtf\",\n\n // Styles and scripts\n css: \"text/css\",\n js: \"application/javascript\",\n mjs: \"application/javascript\",\n ts: \"application/typescript\",\n jsx: \"text/jsx\",\n tsx: \"text/tsx\",\n\n // Archives\n zip: \"application/zip\",\n rar: \"application/vnd.rar\",\n \"7z\": \"application/x-7z-compressed\",\n tar: \"application/x-tar\",\n gz: \"application/gzip\",\n tgz: \"application/gzip\",\n\n // Images\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n bmp: \"image/bmp\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n\n // Audio\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\",\n m4a: \"audio/mp4\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n\n // Video\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mov: \"video/quicktime\",\n wmv: \"video/x-ms-wmv\",\n flv: \"video/x-flv\",\n mkv: \"video/x-matroska\",\n\n // Office\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n\n // Fonts\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n eot: \"application/vnd.ms-fontobject\",\n };\n\n /**\n * Reverse MIME type map for looking up extensions from MIME types.\n * Prefers shorter, more common extensions when multiple exist.\n */\n protected static readonly reverseMimeMap: Record<string, string> = (() => {\n const reverse: Record<string, string> = {};\n // Process in order so common extensions come first\n for (const [ext, mimeType] of Object.entries(FileDetector.mimeMap)) {\n // Only set if not already set (prefer first/shorter extension)\n if (!reverse[mimeType]) {\n reverse[mimeType] = ext;\n }\n }\n return reverse;\n })();\n\n /**\n * Returns the file extension for a given MIME type.\n *\n * @param mimeType - The MIME type to look up\n * @returns The file extension (without dot), or \"bin\" if not found\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const ext = detector.getExtensionFromMimeType(\"image/png\"); // \"png\"\n * const ext2 = detector.getExtensionFromMimeType(\"application/octet-stream\"); // \"bin\"\n * ```\n */\n getExtensionFromMimeType(mimeType: string): string {\n return FileDetector.reverseMimeMap[mimeType] || \"bin\";\n }\n /**\n * Returns the content type of file based on its filename.\n *\n * @param filename - The filename to check\n * @returns The MIME type\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const mimeType = detector.getContentType(\"image.png\"); // \"image/png\"\n * ```\n */\n getContentType(filename: string): string {\n const ext = filename.toLowerCase().split(\".\").pop() || \"\";\n return FileDetector.mimeMap[ext] || \"application/octet-stream\";\n }\n\n /**\n * Detects the file type by checking magic bytes against the stream content.\n *\n * @param stream - The readable stream to check\n * @param filename - The filename (used to get the extension)\n * @returns File type information including MIME type, extension, and verification status\n *\n * @example\n * ```typescript\n * const detector = alepha.inject(FileDetector);\n * const stream = createReadStream('image.png');\n * const result = await detector.detectFileType(stream, 'image.png');\n * console.log(result.mimeType); // 'image/png'\n * console.log(result.verified); // true if magic bytes match\n * ```\n */\n async detectFileType(\n stream: Readable,\n filename: string,\n ): Promise<FileTypeResult> {\n // Get the expected MIME type from the filename extension\n const expectedMimeType = this.getContentType(filename);\n\n // Extract extension - only if filename contains a dot\n const lastDotIndex = filename.lastIndexOf(\".\");\n const ext =\n lastDotIndex > 0\n ? filename.substring(lastDotIndex + 1).toLowerCase()\n : \"\";\n\n // Read the first 16 bytes (enough for most magic byte checks)\n const { buffer, stream: newStream } = await this.peekBytes(stream, 16);\n\n // First, check if the extension's expected signature matches\n const expectedSignatures = FileDetector.MAGIC_BYTES[ext];\n if (expectedSignatures) {\n for (const { signature, mimeType } of expectedSignatures) {\n if (this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: ext,\n verified: true,\n stream: newStream,\n };\n }\n }\n }\n\n // If the expected signature didn't match, try all other signatures\n for (const {\n ext: detectedExt,\n signature,\n mimeType,\n } of FileDetector.ALL_SIGNATURES) {\n if (detectedExt !== ext && this.matchesSignature(buffer, signature)) {\n return {\n mimeType,\n extension: detectedExt,\n verified: true,\n stream: newStream,\n };\n }\n }\n\n // If no magic bytes matched, fall back to extension-based detection\n // or return binary if extension is not recognized\n return {\n mimeType: expectedMimeType,\n extension: ext,\n verified: false,\n stream: newStream,\n };\n }\n\n /**\n * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.\n * This approach reads the entire stream upfront to avoid complex async handling issues.\n *\n * @protected\n */\n protected async peekBytes(\n stream: Readable,\n numBytes: number,\n ): Promise<{ buffer: Buffer; stream: Readable }> {\n const chunks: Buffer[] = [];\n\n // Read the entire stream\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const allData = Buffer.concat(chunks);\n const buffer = allData.subarray(0, numBytes);\n\n // Create a new stream with all the data\n const newStream = Readable.from(allData);\n\n return { buffer, stream: newStream };\n }\n\n /**\n * Checks if a buffer matches a magic byte signature.\n *\n * @protected\n */\n protected matchesSignature(\n buffer: Buffer,\n signature: (number | null)[],\n ): boolean {\n if (buffer.length < signature.length) {\n return false;\n }\n\n for (let i = 0; i < signature.length; i++) {\n if (signature[i] !== null && buffer[i] !== signature[i]) {\n return false;\n }\n }\n\n return true;\n }\n}\n","import { $inject, AlephaError, type FileLike, Json } from \"alepha\";\nimport { FileDetector } from \"../services/FileDetector.ts\";\nimport type {\n CpOptions,\n CreateFileOptions,\n FileSystemProvider,\n LsOptions,\n MkdirOptions,\n RmOptions,\n} from \"./FileSystemProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Web-standard implementation of FileSystemProvider for Cloudflare Workers and other edge runtimes.\n *\n * Uses only Web APIs (ReadableStream, TextEncoder, etc.) — no Node.js-specific APIs.\n * Provides working `createFile` with proper streaming support.\n * Filesystem operations (rm, cp, mv, etc.) are not available in edge runtimes and will throw.\n *\n * @example\n * ```typescript\n * const fs = alepha.inject(WorkerdFileSystemProvider);\n *\n * // Create from text (returns FileLike with web ReadableStream)\n * const file = fs.createFile({ text: \"Hello!\", name: \"greeting.txt\" });\n * const stream = file.stream(); // ReadableStream (web standard)\n * ```\n */\nexport class WorkerdFileSystemProvider implements FileSystemProvider {\n protected detector = $inject(FileDetector);\n protected json = $inject(Json);\n\n protected encoder = new TextEncoder();\n protected decoder = new TextDecoder();\n\n public join(...paths: string[]): string {\n const joined = paths.join(\"/\").replace(/\\/+/g, \"/\");\n const parts = joined.split(\"/\");\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n resolved.pop();\n } else if (part !== \".\") {\n resolved.push(part);\n }\n }\n return resolved.join(\"/\") || \".\";\n }\n\n public createFile(options: CreateFileOptions): FileLike {\n if (\"text\" in options) {\n return this.createFileFromText(options.text, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"arrayBuffer\" in options) {\n return this.createFileFromArrayBuffer(options.arrayBuffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"buffer\" in options) {\n const ab =\n options.buffer instanceof ArrayBuffer\n ? options.buffer\n : options.buffer.buffer.slice(\n options.buffer.byteOffset,\n options.buffer.byteOffset + options.buffer.byteLength,\n );\n return this.createFileFromArrayBuffer(ab as ArrayBuffer, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"file\" in options) {\n return this.createFileFromWebFile(options.file, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n if (\"response\" in options) {\n return this.createFileFromResponse(options.response, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"stream\" in options) {\n return this.createFileFromStream(options.stream as ReadableStream, {\n type: options.type,\n name: options.name,\n size: options.size,\n });\n }\n\n if (\"url\" in options) {\n return this.createFileFromUrl(options.url, {\n type: options.type,\n name: options.name,\n });\n }\n\n if (\"path\" in options) {\n throw new AlephaError(\n \"WorkerdFileSystemProvider.createFile: 'path' source is not supported in edge runtimes.\",\n );\n }\n\n throw new AlephaError(\n \"WorkerdFileSystemProvider.createFile: unsupported options.\",\n );\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected createFileFromText(\n text: string,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const encoded = this.encoder.encode(text);\n const name = options.name ?? \"file.txt\";\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: encoded.byteLength,\n lastModified: Date.now(),\n stream: () =>\n new ReadableStream({\n start(controller) {\n controller.enqueue(encoded);\n controller.close();\n },\n }),\n arrayBuffer: async () =>\n encoded.buffer.slice(\n encoded.byteOffset,\n encoded.byteOffset + encoded.byteLength,\n ) as ArrayBuffer,\n text: async () => text,\n };\n }\n\n protected createFileFromArrayBuffer(\n source: ArrayBuffer,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const name = options.name ?? \"file\";\n const bytes = new Uint8Array(source);\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: source.byteLength,\n lastModified: Date.now(),\n stream: () =>\n new ReadableStream({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n }),\n arrayBuffer: async () => source,\n text: async () => this.decoder.decode(source),\n };\n }\n\n protected createFileFromWebFile(\n source: File,\n options: { type?: string; name?: string; size?: number } = {},\n ): FileLike {\n const name = options.name ?? source.name;\n return {\n name,\n type: options.type ?? (source.type || this.detector.getContentType(name)),\n size: options.size ?? source.size ?? 0,\n lastModified: source.lastModified || Date.now(),\n stream: () => source.stream(),\n arrayBuffer: async () => await source.arrayBuffer(),\n text: async () => await source.text(),\n };\n }\n\n protected createFileFromResponse(\n response: Response,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n if (!response.body) {\n throw new AlephaError(\"Response has no body stream\");\n }\n\n const sizeHeader = response.headers.get(\"content-length\");\n const size = sizeHeader ? parseInt(sizeHeader, 10) : 0;\n\n let name = options.name;\n if (!name) {\n const cd = response.headers.get(\"content-disposition\");\n if (cd) {\n const match = cd.match(/filename=\"?([^\"]+)\"?/);\n if (match) {\n name = match[1];\n }\n }\n }\n name ??= \"file\";\n\n const type =\n options.type ?? response.headers.get(\"content-type\") ?? undefined;\n\n return {\n name,\n type: type ?? this.detector.getContentType(name),\n size,\n lastModified: Date.now(),\n stream: () => response.body!,\n arrayBuffer: async () => await response.arrayBuffer(),\n text: async () => await response.text(),\n };\n }\n\n protected createFileFromStream(\n source: ReadableStream,\n options: { type?: string; name?: string; size?: number } = {},\n ): FileLike {\n const name = options.name ?? \"file\";\n let buffer: ArrayBuffer | null = null;\n\n const consumeStream = async (): Promise<ArrayBuffer> => {\n if (buffer) return buffer;\n const reader = source.getReader();\n const chunks: Uint8Array[] = [];\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) {\n chunks.push(result.value);\n }\n }\n const total = chunks.reduce((sum, c) => sum + c.byteLength, 0);\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n buffer = merged.buffer as ArrayBuffer;\n return buffer;\n };\n\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: options.size ?? 0,\n lastModified: Date.now(),\n stream: () => source,\n arrayBuffer: consumeStream,\n text: async () => this.decoder.decode(await consumeStream()),\n };\n }\n\n protected createFileFromUrl(\n url: string,\n options: { type?: string; name?: string } = {},\n ): FileLike {\n const parsedUrl = new URL(url);\n const name = options.name ?? parsedUrl.pathname.split(\"/\").pop() ?? \"file\";\n\n return {\n name,\n type: options.type ?? this.detector.getContentType(name),\n size: 0,\n lastModified: Date.now(),\n stream: () => {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: streaming from URL is not supported. Use fetch() and createFile({ response }) instead.\",\n );\n },\n arrayBuffer: async () => {\n const res = await fetch(url);\n return await res.arrayBuffer();\n },\n text: async () => {\n const res = await fetch(url);\n return await res.text();\n },\n };\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Filesystem operations — not available in edge runtimes\n // -------------------------------------------------------------------------------------------------------------------\n\n public async rm(_path: string, _options?: RmOptions): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: rm() is not available in edge runtimes.\",\n );\n }\n\n public async cp(\n _src: string,\n _dest: string,\n _options?: CpOptions,\n ): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: cp() is not available in edge runtimes.\",\n );\n }\n\n public async mv(_src: string, _dest: string): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: mv() is not available in edge runtimes.\",\n );\n }\n\n public async mkdir(_path: string, _options?: MkdirOptions): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: mkdir() is not available in edge runtimes.\",\n );\n }\n\n public async ls(_path: string, _options?: LsOptions): Promise<string[]> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: ls() is not available in edge runtimes.\",\n );\n }\n\n public async exists(_path: string): Promise<boolean> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: exists() is not available in edge runtimes.\",\n );\n }\n\n public async readFile(_path: string): Promise<Buffer> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readFile() is not available in edge runtimes.\",\n );\n }\n\n public async writeFile(\n _path: string,\n _data: Uint8Array | Buffer | string | FileLike,\n ): Promise<void> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: writeFile() is not available in edge runtimes.\",\n );\n }\n\n public async readTextFile(_path: string): Promise<string> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readTextFile() is not available in edge runtimes.\",\n );\n }\n\n public async readJsonFile<T = unknown>(_path: string): Promise<T> {\n throw new AlephaError(\n \"WorkerdFileSystemProvider: readJsonFile() is not available in edge runtimes.\",\n );\n }\n}\n","export class FileError extends Error {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"FileError\";\n this.cause = cause;\n }\n}\n","import { $module } from \"alepha\";\nimport { FileSystemProvider } from \"./providers/FileSystemProvider.ts\";\nimport { MemoryFileSystemProvider } from \"./providers/MemoryFileSystemProvider.ts\";\nimport { MemoryShellProvider } from \"./providers/MemoryShellProvider.ts\";\nimport { ShellProvider } from \"./providers/ShellProvider.ts\";\nimport { WorkerdFileSystemProvider } from \"./providers/WorkerdFileSystemProvider.ts\";\nimport { FileDetector } from \"./services/FileDetector.ts\";\n\nexport * from \"./errors/FileError.ts\";\nexport * from \"./providers/FileSystemProvider.ts\";\nexport * from \"./providers/MemoryFileSystemProvider.ts\";\nexport * from \"./providers/MemoryShellProvider.ts\";\nexport * from \"./providers/ShellProvider.ts\";\nexport * from \"./providers/WorkerdFileSystemProvider.ts\";\nexport * from \"./services/FileDetector.ts\";\n\nexport const AlephaSystem = $module({\n name: \"alepha.system\",\n services: [FileDetector, FileSystemProvider, ShellProvider],\n variants: [\n MemoryFileSystemProvider,\n WorkerdFileSystemProvider,\n MemoryShellProvider,\n ],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileSystemProvider,\n use: WorkerdFileSystemProvider,\n })\n .with({\n optional: true,\n provide: ShellProvider,\n use: MemoryShellProvider,\n }),\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/core/primitives/$subscriber.ts","../../../src/topic/core/errors/TopicTimeoutError.ts","../../../src/topic/core/providers/TopicProvider.ts","../../../src/topic/core/providers/MemoryTopicProvider.ts","../../../src/topic/core/primitives/$topic.ts","../../../src/topic/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n TopicHandler,\n TopicMessageSchema,\n TopicPrimitive,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber primitive to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic primitive\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberPrimitiveOptions<T>,\n): SubscriberPrimitive<T> => {\n return createPrimitive(SubscriberPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberPrimitiveOptions<T extends TopicMessageSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The topic primitive that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic primitive\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicPrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberPrimitive<\n T extends TopicMessageSchema,\n> extends PipelinePrimitive<SubscriberPrimitiveOptions<T>> {}\n\n$subscriber[KIND] = SubscriberPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class TopicTimeoutError extends AlephaError {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TemplatedPathParser } from \"alepha/router\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { $subscriber } from \"../primitives/$subscriber.ts\";\nimport {\n $topic,\n type TopicHandler,\n type TopicMessage,\n type TopicMessageSchema,\n type TopicWaitOptions,\n} from \"../primitives/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n /**\n * Publish a raw message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void>;\n\n /**\n * Subscribe to a topic with a raw callback.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Encode and publish a typed message to a topic.\n */\n public async publishMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n payload: TopicMessage<T>[\"payload\"],\n options?: TopicPublishOptions,\n ): Promise<void> {\n let topicName = name;\n if (options?.params) {\n const parser = new TemplatedPathParser(name);\n topicName = parser.interpolate(options.params);\n }\n\n await this.publish(\n topicName,\n JSON.stringify({\n payload: this.alepha.codec.encode(schema, payload),\n }),\n options,\n );\n }\n\n /**\n * Parse a raw message string into a typed topic message.\n */\n public parseMessage<T extends TopicMessageSchema>(\n schema: T[\"payload\"],\n message: string,\n ): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n schema,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n } as TopicMessage<T>;\n }\n\n /**\n * Subscribe a typed handler to a topic, with error wrapping and message parsing.\n */\n public async subscribeHandler<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n handler: TopicHandler<T>,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const parser = new TemplatedPathParser(name);\n const subscribeTopic = parser.hasParams\n ? parser.wildcardize(this.wildcardChar())\n : name;\n\n return this.subscribe(\n subscribeTopic,\n async (message, receivedTopic?: string) => {\n try {\n const parsed = this.parseMessage<T>(schema, message);\n\n if (parser.hasParams && receivedTopic) {\n const params = parser.extract(receivedTopic) ?? {};\n await handler({ ...parsed, params } as TopicMessage<T>);\n } else {\n await handler(parsed);\n }\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n },\n options,\n );\n }\n\n /**\n * Wait for a single message matching an optional filter, with timeout.\n */\n public async waitForMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout; clear?: () => void } = {};\n\n (async () => {\n const clear = await this.subscribe(name, (raw) => {\n const message = this.parseMessage<T>(schema, raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n if (ref.clear) {\n ref.clear();\n }\n resolve(message);\n });\n\n ref.clear = clear;\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n /**\n * The wildcard character used for pattern subscriptions.\n * Override in subclasses for provider-specific wildcards.\n */\n protected wildcardChar(): string {\n return \"+\";\n }\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.primitives($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.primitives($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n const handler = subscriber.handler.run.bind(subscriber.handler);\n handlers.push(() => subscriber.options.topic.subscribe(handler));\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (\n message: string,\n topic?: string,\n) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n\nexport interface TopicPublishOptions {\n retain?: boolean;\n params?: Record<string, string>;\n}\n\n// biome-ignore lint/suspicious/noEmptyInterface: augmented by provider-specific modules (e.g. MqttTopicProvider)\nexport interface TopicSubscribeOptions {}\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n protected readonly retained: Record<string, string> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n * @param options\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n this.retained[topic] = message;\n }\n\n for (const [pattern, callbacks] of Object.entries(this.subscriptions)) {\n if (this.topicMatches(pattern, topic)) {\n for (const callback of callbacks) {\n await callback(message, topic);\n }\n }\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n // Deliver retained messages matching the pattern\n for (const [retainedTopic, retainedMessage] of Object.entries(\n this.retained,\n )) {\n if (this.topicMatches(topic, retainedTopic)) {\n await callback(retainedMessage, retainedTopic);\n }\n }\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n\n /**\n * Check if a topic matches a subscription pattern.\n * Supports `+` single-level wildcard.\n */\n protected topicMatches(pattern: string, topic: string): boolean {\n if (pattern === topic) {\n return true;\n }\n\n const patternParts = pattern.split(\"/\");\n const topicParts = topic.split(\"/\");\n\n if (patternParts.length !== topicParts.length) {\n return false;\n }\n\n return patternParts.every(\n (part, i) => part === \"+\" || part === topicParts[i],\n );\n }\n}\n","import {\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type TopicPublishOptions,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic primitive for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicPrimitiveOptions<T>,\n): TopicPrimitive<T> => {\n return createPrimitive(TopicPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n\n /**\n * Whether the last published message should be retained and delivered to new subscribers.\n *\n * When enabled, the provider stores the last message for this topic.\n * New subscribers immediately receive the retained message upon subscribing.\n *\n * Supported by Memory, Redis, and MQTT providers.\n *\n * @default false\n */\n retain?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<\n TopicPrimitiveOptions<T>\n> {\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(\n message: T extends { params: TSchema }\n ? { params: Static<T[\"params\"]>; payload: TopicMessage<T>[\"payload\"] }\n : TopicMessage<T>[\"payload\"],\n ): Promise<void> {\n const hasParams = this.options.schema.params;\n const payload = hasParams ? (message as any).payload : message;\n const params = hasParams ? (message as any).params : undefined;\n\n await this.provider.publishMessage<T>(\n this.name,\n this.options.schema.payload,\n payload,\n {\n retain: this.options.retain,\n mqtt: (this.options as any).mqtt,\n params,\n } as TopicPublishOptions,\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribeHandler<T>(\n this.name,\n this.options.schema.payload,\n handler,\n { mqtt: (this.options as any).mqtt },\n );\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n return this.provider.waitForMessage<T>(\n this.name,\n this.options.schema.payload,\n options,\n );\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$topic[KIND] = TopicPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n params: T extends { params: TSchema } ? Static<T[\"params\"]> : never;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n params?: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./primitives/$subscriber.ts\";\nimport { $topic } from \"./primitives/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./primitives/$subscriber.ts\";\nexport * from \"./primitives/$topic.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Publish/subscribe messaging for event-driven architectures.\n *\n * **Features:**\n * - Pub/sub topics with type-safe messages\n * - Topic subscription handlers\n * - Multiple subscriber support\n * - Message filtering and routing\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n primitives: [$topic, $subscriber],\n services: [TopicProvider],\n variants: [MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/core/primitives/$subscriber.ts","../../../src/topic/core/errors/TopicTimeoutError.ts","../../../src/topic/core/providers/TopicProvider.ts","../../../src/topic/core/providers/MemoryTopicProvider.ts","../../../src/topic/core/primitives/$topic.ts","../../../src/topic/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n TopicHandler,\n TopicMessageSchema,\n TopicPrimitive,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber primitive to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic primitive\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberPrimitiveOptions<T>,\n): SubscriberPrimitive<T> => {\n return createPrimitive(SubscriberPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberPrimitiveOptions<T extends TopicMessageSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The topic primitive that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic primitive\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicPrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberPrimitive<\n T extends TopicMessageSchema,\n> extends PipelinePrimitive<SubscriberPrimitiveOptions<T>> {}\n\n$subscriber[KIND] = SubscriberPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class TopicTimeoutError extends AlephaError {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TemplatedPathParser } from \"alepha/router\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { $subscriber } from \"../primitives/$subscriber.ts\";\nimport {\n $topic,\n type TopicHandler,\n type TopicMessage,\n type TopicMessageSchema,\n type TopicWaitOptions,\n} from \"../primitives/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n /**\n * Publish a raw message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void>;\n\n /**\n * Subscribe to a topic with a raw callback.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Encode and publish a typed message to a topic.\n */\n public async publishMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n payload: TopicMessage<T>[\"payload\"],\n options?: TopicPublishOptions,\n ): Promise<void> {\n let topicName = name;\n if (options?.params) {\n const parser = new TemplatedPathParser(name);\n topicName = parser.interpolate(options.params);\n }\n\n await this.publish(\n topicName,\n JSON.stringify({\n payload: this.alepha.codec.encode(schema, payload),\n }),\n options,\n );\n }\n\n /**\n * Parse a raw message string into a typed topic message.\n */\n public parseMessage<T extends TopicMessageSchema>(\n schema: T[\"payload\"],\n message: string,\n ): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n schema,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n } as TopicMessage<T>;\n }\n\n /**\n * Subscribe a typed handler to a topic, with error wrapping and message parsing.\n */\n public async subscribeHandler<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n handler: TopicHandler<T>,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const parser = new TemplatedPathParser(name);\n const subscribeTopic = parser.hasParams\n ? parser.wildcardize(this.wildcardChar())\n : name;\n\n return this.subscribe(\n subscribeTopic,\n async (message, receivedTopic?: string) => {\n try {\n const parsed = this.parseMessage<T>(schema, message);\n\n if (parser.hasParams && receivedTopic) {\n const params = parser.extract(receivedTopic) ?? {};\n await handler({ ...parsed, params } as TopicMessage<T>);\n } else {\n await handler(parsed);\n }\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n },\n options,\n );\n }\n\n /**\n * Wait for a single message matching an optional filter, with timeout.\n */\n public async waitForMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout; clear?: () => void } = {};\n\n (async () => {\n const clear = await this.subscribe(name, (raw) => {\n const message = this.parseMessage<T>(schema, raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n if (ref.clear) {\n ref.clear();\n }\n resolve(message);\n });\n\n ref.clear = clear;\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n /**\n * The wildcard character used for pattern subscriptions.\n * Override in subclasses for provider-specific wildcards.\n */\n protected wildcardChar(): string {\n return \"+\";\n }\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.primitives($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.primitives($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n const handler = subscriber.handler.run.bind(subscriber.handler);\n handlers.push(() => subscriber.options.topic.subscribe(handler));\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (\n message: string,\n topic?: string,\n) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n\nexport interface TopicPublishOptions {\n retain?: boolean;\n params?: Record<string, string>;\n}\n\n// biome-ignore lint/suspicious/noEmptyInterface: augmented by provider-specific modules (e.g. MqttTopicProvider)\nexport interface TopicSubscribeOptions {}\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n protected readonly retained: Record<string, string> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n * @param options\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n this.retained[topic] = message;\n }\n\n for (const [pattern, callbacks] of Object.entries(this.subscriptions)) {\n if (this.topicMatches(pattern, topic)) {\n for (const callback of callbacks) {\n await callback(message, topic);\n }\n }\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n // Deliver retained messages matching the pattern\n for (const [retainedTopic, retainedMessage] of Object.entries(\n this.retained,\n )) {\n if (this.topicMatches(topic, retainedTopic)) {\n await callback(retainedMessage, retainedTopic);\n }\n }\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n\n /**\n * Check if a topic matches a subscription pattern.\n * Supports `+` single-level wildcard.\n */\n protected topicMatches(pattern: string, topic: string): boolean {\n if (pattern === topic) {\n return true;\n }\n\n const patternParts = pattern.split(\"/\");\n const topicParts = topic.split(\"/\");\n\n if (patternParts.length !== topicParts.length) {\n return false;\n }\n\n return patternParts.every(\n (part, i) => part === \"+\" || part === topicParts[i],\n );\n }\n}\n","import {\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type TopicPublishOptions,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic primitive for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicPrimitiveOptions<T>,\n): TopicPrimitive<T> => {\n return createPrimitive(TopicPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n\n /**\n * Whether the last published message should be retained and delivered to new subscribers.\n *\n * When enabled, the provider stores the last message for this topic.\n * New subscribers immediately receive the retained message upon subscribing.\n *\n * Supported by Memory, Redis, and MQTT providers.\n *\n * @default false\n */\n retain?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<\n TopicPrimitiveOptions<T>\n> {\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(\n message: T extends { params: TSchema }\n ? { params: Static<T[\"params\"]>; payload: TopicMessage<T>[\"payload\"] }\n : TopicMessage<T>[\"payload\"],\n ): Promise<void> {\n const hasParams = this.options.schema.params;\n const payload = hasParams ? (message as any).payload : message;\n const params = hasParams ? (message as any).params : undefined;\n\n await this.provider.publishMessage<T>(\n this.name,\n this.options.schema.payload,\n payload,\n {\n retain: this.options.retain,\n mqtt: (this.options as any).mqtt,\n params,\n } as TopicPublishOptions,\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribeHandler<T>(\n this.name,\n this.options.schema.payload,\n handler,\n { mqtt: (this.options as any).mqtt },\n );\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n return this.provider.waitForMessage<T>(\n this.name,\n this.options.schema.payload,\n options,\n );\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$topic[KIND] = TopicPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n params: T extends { params: TSchema } ? Static<T[\"params\"]> : never;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n params?: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./primitives/$subscriber.ts\";\nimport { $topic } from \"./primitives/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./primitives/$subscriber.ts\";\nexport * from \"./primitives/$topic.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Publish/subscribe messaging for event-driven architectures.\n *\n * **Features:**\n * - Pub/sub topics with type-safe messages\n * - Topic subscription handlers\n * - Multiple subscriber support\n * - Message filtering and routing\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n primitives: [$topic, $subscriber],\n services: [TopicProvider],\n variants: [MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}