@beignet/core 0.0.1

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 (331) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +288 -0
  3. package/dist/application/index.d.ts +260 -0
  4. package/dist/application/index.d.ts.map +1 -0
  5. package/dist/application/index.js +324 -0
  6. package/dist/application/index.js.map +1 -0
  7. package/dist/client/client.d.ts +241 -0
  8. package/dist/client/client.d.ts.map +1 -0
  9. package/dist/client/client.js +531 -0
  10. package/dist/client/client.js.map +1 -0
  11. package/dist/client/index.d.ts +10 -0
  12. package/dist/client/index.d.ts.map +1 -0
  13. package/dist/client/index.js +8 -0
  14. package/dist/client/index.js.map +1 -0
  15. package/dist/client/types.d.ts +139 -0
  16. package/dist/client/types.d.ts.map +1 -0
  17. package/dist/client/types.js +2 -0
  18. package/dist/client/types.js.map +1 -0
  19. package/dist/config/index.d.ts +122 -0
  20. package/dist/config/index.d.ts.map +1 -0
  21. package/dist/config/index.js +216 -0
  22. package/dist/config/index.js.map +1 -0
  23. package/dist/contracts/contract-builder.d.ts +121 -0
  24. package/dist/contracts/contract-builder.d.ts.map +1 -0
  25. package/dist/contracts/contract-builder.js +346 -0
  26. package/dist/contracts/contract-builder.js.map +1 -0
  27. package/dist/contracts/contract-group.d.ts +106 -0
  28. package/dist/contracts/contract-group.d.ts.map +1 -0
  29. package/dist/contracts/contract-group.js +240 -0
  30. package/dist/contracts/contract-group.js.map +1 -0
  31. package/dist/contracts/contract-like.d.ts +21 -0
  32. package/dist/contracts/contract-like.d.ts.map +1 -0
  33. package/dist/contracts/contract-like.js +9 -0
  34. package/dist/contracts/contract-like.js.map +1 -0
  35. package/dist/contracts/index.d.ts +15 -0
  36. package/dist/contracts/index.d.ts.map +1 -0
  37. package/dist/contracts/index.js +11 -0
  38. package/dist/contracts/index.js.map +1 -0
  39. package/dist/contracts/openapi-meta.d.ts +23 -0
  40. package/dist/contracts/openapi-meta.d.ts.map +1 -0
  41. package/dist/contracts/openapi-meta.js +2 -0
  42. package/dist/contracts/openapi-meta.js.map +1 -0
  43. package/dist/contracts/path-template.d.ts +17 -0
  44. package/dist/contracts/path-template.d.ts.map +1 -0
  45. package/dist/contracts/path-template.js +50 -0
  46. package/dist/contracts/path-template.js.map +1 -0
  47. package/dist/contracts/rate-limit.d.ts +50 -0
  48. package/dist/contracts/rate-limit.d.ts.map +1 -0
  49. package/dist/contracts/rate-limit.js +2 -0
  50. package/dist/contracts/rate-limit.js.map +1 -0
  51. package/dist/contracts/types.d.ts +97 -0
  52. package/dist/contracts/types.d.ts.map +1 -0
  53. package/dist/contracts/types.js +54 -0
  54. package/dist/contracts/types.js.map +1 -0
  55. package/dist/contracts/utils.d.ts +3 -0
  56. package/dist/contracts/utils.d.ts.map +1 -0
  57. package/dist/contracts/utils.js +44 -0
  58. package/dist/contracts/utils.js.map +1 -0
  59. package/dist/domain/entity.d.ts +87 -0
  60. package/dist/domain/entity.d.ts.map +1 -0
  61. package/dist/domain/entity.js +155 -0
  62. package/dist/domain/entity.js.map +1 -0
  63. package/dist/domain/events.d.ts +41 -0
  64. package/dist/domain/events.d.ts.map +1 -0
  65. package/dist/domain/events.js +21 -0
  66. package/dist/domain/events.js.map +1 -0
  67. package/dist/domain/index.d.ts +14 -0
  68. package/dist/domain/index.d.ts.map +1 -0
  69. package/dist/domain/index.js +14 -0
  70. package/dist/domain/index.js.map +1 -0
  71. package/dist/domain/value-object.d.ts +60 -0
  72. package/dist/domain/value-object.d.ts.map +1 -0
  73. package/dist/domain/value-object.js +87 -0
  74. package/dist/domain/value-object.js.map +1 -0
  75. package/dist/errors/catalog.d.ts +71 -0
  76. package/dist/errors/catalog.d.ts.map +1 -0
  77. package/dist/errors/catalog.js +71 -0
  78. package/dist/errors/catalog.js.map +1 -0
  79. package/dist/errors/http.d.ts +77 -0
  80. package/dist/errors/http.d.ts.map +1 -0
  81. package/dist/errors/http.js +74 -0
  82. package/dist/errors/http.js.map +1 -0
  83. package/dist/errors/index.d.ts +10 -0
  84. package/dist/errors/index.d.ts.map +1 -0
  85. package/dist/errors/index.js +14 -0
  86. package/dist/errors/index.js.map +1 -0
  87. package/dist/errors/response.d.ts +26 -0
  88. package/dist/errors/response.d.ts.map +1 -0
  89. package/dist/errors/response.js +34 -0
  90. package/dist/errors/response.js.map +1 -0
  91. package/dist/errors/validation.d.ts +18 -0
  92. package/dist/errors/validation.d.ts.map +1 -0
  93. package/dist/errors/validation.js +21 -0
  94. package/dist/errors/validation.js.map +1 -0
  95. package/dist/events/index.d.ts +58 -0
  96. package/dist/events/index.d.ts.map +1 -0
  97. package/dist/events/index.js +102 -0
  98. package/dist/events/index.js.map +1 -0
  99. package/dist/jobs/index.d.ts +56 -0
  100. package/dist/jobs/index.d.ts.map +1 -0
  101. package/dist/jobs/index.js +89 -0
  102. package/dist/jobs/index.js.map +1 -0
  103. package/dist/mail/index.d.ts +75 -0
  104. package/dist/mail/index.d.ts.map +1 -0
  105. package/dist/mail/index.js +84 -0
  106. package/dist/mail/index.js.map +1 -0
  107. package/dist/openapi/index.d.ts +207 -0
  108. package/dist/openapi/index.d.ts.map +1 -0
  109. package/dist/openapi/index.js +449 -0
  110. package/dist/openapi/index.js.map +1 -0
  111. package/dist/openapi/schema-introspector.d.ts +38 -0
  112. package/dist/openapi/schema-introspector.d.ts.map +1 -0
  113. package/dist/openapi/schema-introspector.js +67 -0
  114. package/dist/openapi/schema-introspector.js.map +1 -0
  115. package/dist/ports/audit.d.ts +58 -0
  116. package/dist/ports/audit.d.ts.map +1 -0
  117. package/dist/ports/audit.js +74 -0
  118. package/dist/ports/audit.js.map +1 -0
  119. package/dist/ports/auth.d.ts +23 -0
  120. package/dist/ports/auth.d.ts.map +1 -0
  121. package/dist/ports/auth.js +31 -0
  122. package/dist/ports/auth.js.map +1 -0
  123. package/dist/ports/builder.d.ts +61 -0
  124. package/dist/ports/builder.d.ts.map +1 -0
  125. package/dist/ports/builder.js +48 -0
  126. package/dist/ports/builder.js.map +1 -0
  127. package/dist/ports/cache.d.ts +15 -0
  128. package/dist/ports/cache.d.ts.map +1 -0
  129. package/dist/ports/cache.js +57 -0
  130. package/dist/ports/cache.js.map +1 -0
  131. package/dist/ports/clock.d.ts +10 -0
  132. package/dist/ports/clock.d.ts.map +1 -0
  133. package/dist/ports/clock.js +21 -0
  134. package/dist/ports/clock.js.map +1 -0
  135. package/dist/ports/events.d.ts +71 -0
  136. package/dist/ports/events.d.ts.map +1 -0
  137. package/dist/ports/events.js +2 -0
  138. package/dist/ports/events.js.map +1 -0
  139. package/dist/ports/id-generator.d.ts +12 -0
  140. package/dist/ports/id-generator.d.ts.map +1 -0
  141. package/dist/ports/id-generator.js +22 -0
  142. package/dist/ports/id-generator.js.map +1 -0
  143. package/dist/ports/index.d.ts +98 -0
  144. package/dist/ports/index.d.ts.map +1 -0
  145. package/dist/ports/index.js +67 -0
  146. package/dist/ports/index.js.map +1 -0
  147. package/dist/ports/logger.d.ts +22 -0
  148. package/dist/ports/logger.d.ts.map +1 -0
  149. package/dist/ports/logger.js +34 -0
  150. package/dist/ports/logger.js.map +1 -0
  151. package/dist/ports/mailer.d.ts +6 -0
  152. package/dist/ports/mailer.d.ts.map +1 -0
  153. package/dist/ports/mailer.js +2 -0
  154. package/dist/ports/mailer.js.map +1 -0
  155. package/dist/ports/policy.d.ts +53 -0
  156. package/dist/ports/policy.d.ts.map +1 -0
  157. package/dist/ports/policy.js +81 -0
  158. package/dist/ports/policy.js.map +1 -0
  159. package/dist/ports/rate-limit.d.ts +41 -0
  160. package/dist/ports/rate-limit.d.ts.map +1 -0
  161. package/dist/ports/rate-limit.js +37 -0
  162. package/dist/ports/rate-limit.js.map +1 -0
  163. package/dist/ports/redaction.d.ts +26 -0
  164. package/dist/ports/redaction.d.ts.map +1 -0
  165. package/dist/ports/redaction.js +126 -0
  166. package/dist/ports/redaction.js.map +1 -0
  167. package/dist/ports/schedules.d.ts +9 -0
  168. package/dist/ports/schedules.d.ts.map +1 -0
  169. package/dist/ports/schedules.js +2 -0
  170. package/dist/ports/schedules.js.map +1 -0
  171. package/dist/ports/storage.d.ts +47 -0
  172. package/dist/ports/storage.d.ts.map +1 -0
  173. package/dist/ports/storage.js +185 -0
  174. package/dist/ports/storage.js.map +1 -0
  175. package/dist/ports/testing.d.ts +73 -0
  176. package/dist/ports/testing.d.ts.map +1 -0
  177. package/dist/ports/testing.js +105 -0
  178. package/dist/ports/testing.js.map +1 -0
  179. package/dist/ports/unit-of-work.d.ts +56 -0
  180. package/dist/ports/unit-of-work.d.ts.map +1 -0
  181. package/dist/ports/unit-of-work.js +64 -0
  182. package/dist/ports/unit-of-work.js.map +1 -0
  183. package/dist/providers/index.d.ts +8 -0
  184. package/dist/providers/index.d.ts.map +1 -0
  185. package/dist/providers/index.js +8 -0
  186. package/dist/providers/index.js.map +1 -0
  187. package/dist/providers/instrumentation.d.ts +91 -0
  188. package/dist/providers/instrumentation.d.ts.map +1 -0
  189. package/dist/providers/instrumentation.js +93 -0
  190. package/dist/providers/instrumentation.js.map +1 -0
  191. package/dist/providers/provider.d.ts +146 -0
  192. package/dist/providers/provider.d.ts.map +1 -0
  193. package/dist/providers/provider.js +31 -0
  194. package/dist/providers/provider.js.map +1 -0
  195. package/dist/schedules/index.d.ts +105 -0
  196. package/dist/schedules/index.d.ts.map +1 -0
  197. package/dist/schedules/index.js +178 -0
  198. package/dist/schedules/index.js.map +1 -0
  199. package/dist/server/contract-like.d.ts +5 -0
  200. package/dist/server/contract-like.d.ts.map +1 -0
  201. package/dist/server/contract-like.js +5 -0
  202. package/dist/server/contract-like.js.map +1 -0
  203. package/dist/server/health.d.ts +41 -0
  204. package/dist/server/health.d.ts.map +1 -0
  205. package/dist/server/health.js +46 -0
  206. package/dist/server/health.js.map +1 -0
  207. package/dist/server/hooks/auth.d.ts +42 -0
  208. package/dist/server/hooks/auth.d.ts.map +1 -0
  209. package/dist/server/hooks/auth.js +61 -0
  210. package/dist/server/hooks/auth.js.map +1 -0
  211. package/dist/server/hooks/cors.d.ts +13 -0
  212. package/dist/server/hooks/cors.d.ts.map +1 -0
  213. package/dist/server/hooks/cors.js +70 -0
  214. package/dist/server/hooks/cors.js.map +1 -0
  215. package/dist/server/hooks/errors.d.ts +66 -0
  216. package/dist/server/hooks/errors.d.ts.map +1 -0
  217. package/dist/server/hooks/errors.js +83 -0
  218. package/dist/server/hooks/errors.js.map +1 -0
  219. package/dist/server/hooks/index.d.ts +12 -0
  220. package/dist/server/hooks/index.d.ts.map +1 -0
  221. package/dist/server/hooks/index.js +12 -0
  222. package/dist/server/hooks/index.js.map +1 -0
  223. package/dist/server/hooks/logging.d.ts +33 -0
  224. package/dist/server/hooks/logging.d.ts.map +1 -0
  225. package/dist/server/hooks/logging.js +90 -0
  226. package/dist/server/hooks/logging.js.map +1 -0
  227. package/dist/server/hooks/rate-limit.d.ts +29 -0
  228. package/dist/server/hooks/rate-limit.d.ts.map +1 -0
  229. package/dist/server/hooks/rate-limit.js +93 -0
  230. package/dist/server/hooks/rate-limit.js.map +1 -0
  231. package/dist/server/hooks/utils.d.ts +9 -0
  232. package/dist/server/hooks/utils.d.ts.map +1 -0
  233. package/dist/server/hooks/utils.js +16 -0
  234. package/dist/server/hooks/utils.js.map +1 -0
  235. package/dist/server/hooks.d.ts +2 -0
  236. package/dist/server/hooks.d.ts.map +1 -0
  237. package/dist/server/hooks.js +2 -0
  238. package/dist/server/hooks.js.map +1 -0
  239. package/dist/server/http.d.ts +124 -0
  240. package/dist/server/http.d.ts.map +1 -0
  241. package/dist/server/http.js +2 -0
  242. package/dist/server/http.js.map +1 -0
  243. package/dist/server/index.d.ts +19 -0
  244. package/dist/server/index.d.ts.map +1 -0
  245. package/dist/server/index.js +15 -0
  246. package/dist/server/index.js.map +1 -0
  247. package/dist/server/openapi.d.ts +32 -0
  248. package/dist/server/openapi.d.ts.map +1 -0
  249. package/dist/server/openapi.js +43 -0
  250. package/dist/server/openapi.js.map +1 -0
  251. package/dist/server/providers/index.d.ts +4 -0
  252. package/dist/server/providers/index.d.ts.map +1 -0
  253. package/dist/server/providers/index.js +4 -0
  254. package/dist/server/providers/index.js.map +1 -0
  255. package/dist/server/providers/loadProviderConfig.d.ts +7 -0
  256. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -0
  257. package/dist/server/providers/loadProviderConfig.js +42 -0
  258. package/dist/server/providers/loadProviderConfig.js.map +1 -0
  259. package/dist/server/server.d.ts +86 -0
  260. package/dist/server/server.d.ts.map +1 -0
  261. package/dist/server/server.js +1031 -0
  262. package/dist/server/server.js.map +1 -0
  263. package/dist/server/types.d.ts +3 -0
  264. package/dist/server/types.d.ts.map +1 -0
  265. package/dist/server/types.js +3 -0
  266. package/dist/server/types.js.map +1 -0
  267. package/package.json +129 -0
  268. package/src/application/index.ts +747 -0
  269. package/src/client/client.ts +1105 -0
  270. package/src/client/index.ts +45 -0
  271. package/src/client/types.ts +305 -0
  272. package/src/config/index.ts +497 -0
  273. package/src/contracts/contract-builder.ts +583 -0
  274. package/src/contracts/contract-group.ts +502 -0
  275. package/src/contracts/contract-like.ts +29 -0
  276. package/src/contracts/index.ts +53 -0
  277. package/src/contracts/openapi-meta.ts +22 -0
  278. package/src/contracts/path-template.ts +91 -0
  279. package/src/contracts/rate-limit.ts +50 -0
  280. package/src/contracts/types.ts +207 -0
  281. package/src/contracts/utils.ts +56 -0
  282. package/src/domain/entity.ts +256 -0
  283. package/src/domain/events.ts +52 -0
  284. package/src/domain/index.ts +18 -0
  285. package/src/domain/value-object.ts +135 -0
  286. package/src/errors/catalog.ts +149 -0
  287. package/src/errors/http.ts +80 -0
  288. package/src/errors/index.ts +28 -0
  289. package/src/errors/response.ts +54 -0
  290. package/src/errors/validation.ts +35 -0
  291. package/src/events/index.ts +246 -0
  292. package/src/jobs/index.ts +211 -0
  293. package/src/mail/index.ts +177 -0
  294. package/src/openapi/index.ts +865 -0
  295. package/src/openapi/schema-introspector.ts +107 -0
  296. package/src/ports/audit.ts +176 -0
  297. package/src/ports/auth.ts +76 -0
  298. package/src/ports/builder.ts +97 -0
  299. package/src/ports/cache.ts +94 -0
  300. package/src/ports/clock.ts +34 -0
  301. package/src/ports/events.ts +100 -0
  302. package/src/ports/id-generator.ts +36 -0
  303. package/src/ports/index.ts +221 -0
  304. package/src/ports/logger.ts +67 -0
  305. package/src/ports/policy.ts +242 -0
  306. package/src/ports/rate-limit.ts +91 -0
  307. package/src/ports/redaction.ts +199 -0
  308. package/src/ports/storage.ts +282 -0
  309. package/src/ports/testing.ts +234 -0
  310. package/src/ports/unit-of-work.ts +134 -0
  311. package/src/providers/index.ts +40 -0
  312. package/src/providers/instrumentation.ts +248 -0
  313. package/src/providers/provider.ts +191 -0
  314. package/src/schedules/index.ts +442 -0
  315. package/src/server/contract-like.ts +8 -0
  316. package/src/server/health.ts +82 -0
  317. package/src/server/hooks/auth.ts +147 -0
  318. package/src/server/hooks/cors.ts +87 -0
  319. package/src/server/hooks/errors.ts +126 -0
  320. package/src/server/hooks/index.ts +43 -0
  321. package/src/server/hooks/logging.ts +121 -0
  322. package/src/server/hooks/rate-limit.ts +171 -0
  323. package/src/server/hooks/utils.ts +16 -0
  324. package/src/server/hooks.ts +1 -0
  325. package/src/server/http.ts +189 -0
  326. package/src/server/index.ts +35 -0
  327. package/src/server/openapi.ts +72 -0
  328. package/src/server/providers/index.ts +3 -0
  329. package/src/server/providers/loadProviderConfig.ts +72 -0
  330. package/src/server/server.ts +1521 -0
  331. package/src/server/types.ts +2 -0
@@ -0,0 +1,36 @@
1
+ export interface IdGeneratorPort {
2
+ nextId(): string;
3
+ }
4
+
5
+ export interface SequenceIdGeneratorPort extends IdGeneratorPort {
6
+ reset(next?: number): void;
7
+ }
8
+
9
+ export function createUuidIdGenerator(): IdGeneratorPort {
10
+ return {
11
+ nextId: () => {
12
+ if (typeof globalThis.crypto?.randomUUID !== "function") {
13
+ throw new Error(
14
+ "createUuidIdGenerator requires globalThis.crypto.randomUUID(). Provide a custom IdGeneratorPort in this runtime.",
15
+ );
16
+ }
17
+
18
+ return globalThis.crypto.randomUUID();
19
+ },
20
+ };
21
+ }
22
+
23
+ export function createSequenceIdGenerator(
24
+ options: { prefix?: string; start?: number } = {},
25
+ ): SequenceIdGeneratorPort {
26
+ const prefix = options.prefix ?? "id";
27
+ const start = options.start ?? 1;
28
+ let next = start;
29
+
30
+ return {
31
+ nextId: () => `${prefix}_${next++}`,
32
+ reset: (value = start) => {
33
+ next = value;
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * @beignet/core/ports
3
+ *
4
+ * A tiny, framework-agnostic subpath that standardizes how apps define and
5
+ * type their "ports" (outbound dependencies like db, cache, event bus).
6
+ *
7
+ * This subpath provides:
8
+ * - `definePorts` – helper to define a typed ports object
9
+ * - `PortsContext` – a generic type that describes ctx objects that carry ports
10
+ * - `EventBusPort` – interface for event bus implementations
11
+ * - `JobDispatcherPort` – interface for job dispatch implementations
12
+ * - `UnitOfWorkPort` – interface for app-owned transaction boundaries
13
+ * - `AuthPort` – interface for request authentication implementations
14
+ * - `AuditLogPort` – interface for audit/activity log implementations
15
+ * - `ClockPort` – interface for deterministic time
16
+ * - `IdGeneratorPort` – interface for deterministic id generation
17
+ * - `LoggerPort` – interface for structured application logging
18
+ * - `GatePort` – interface for application authorization policies
19
+ * - `RateLimitPort` – interface for rate limiting implementations
20
+ * - `CachePort` – interface for cache implementations
21
+ * - `StoragePort` – interface for object/file storage implementations
22
+ *
23
+ * Dedicated framework areas own their capability-specific APIs:
24
+ * - `@beignet/core/mail` owns `MailerPort` and mail test adapters
25
+ * - `@beignet/core/schedules` owns scheduled task definitions and runners
26
+ */
27
+
28
+ /**
29
+ * A generic map of named "ports" (outbound dependencies) that your
30
+ * application depends on: db, mailer, cache, event bus, etc.
31
+ *
32
+ * This is intentionally very loose: users define the exact shape via
33
+ * `definePorts(...)`.
34
+ */
35
+ export type AnyPorts = Record<string, unknown>;
36
+
37
+ /**
38
+ * Define the set of ports (outbound dependencies) for your application.
39
+ *
40
+ * This is essentially a typed identity function. It:
41
+ * - captures the exact shape of the provided `ports` object
42
+ * - allows you to export `type AppPorts = typeof appPorts` in your app
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const appPorts = definePorts({
47
+ * db: dbAdapter,
48
+ * mailer: mailerAdapter,
49
+ * });
50
+ *
51
+ * export type AppPorts = typeof appPorts;
52
+ * ```
53
+ *
54
+ * @param ports - The ports object to define
55
+ * @returns The same ports object, unchanged
56
+ */
57
+ export function definePorts<P extends AnyPorts>(ports: P): P {
58
+ return ports;
59
+ }
60
+
61
+ /**
62
+ * A small helper type describing a context object that carries `ports`.
63
+ *
64
+ * You typically use this to define your application context:
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import type { PortsContext } from "@beignet/core/ports";
69
+ * import type { AppPorts } from "./core/ports";
70
+ *
71
+ * export interface AppCtx extends PortsContext<AppPorts> {
72
+ * user: { id: string } | null;
73
+ * now: () => Date;
74
+ * appError: AppErrorCreator<typeof errors>;
75
+ * }
76
+ * ```
77
+ */
78
+ export interface PortsContext<P extends AnyPorts = AnyPorts> {
79
+ ports: P;
80
+ }
81
+
82
+ export type {
83
+ ActivityActor,
84
+ ActivityActorType,
85
+ ActivityMetadata,
86
+ ActivityMetadataValue,
87
+ ActivityResource,
88
+ ActivityTenant,
89
+ AuditLogEntry,
90
+ AuditLogEntryInput,
91
+ AuditLogOptions,
92
+ AuditLogPort,
93
+ AuditOutcome,
94
+ MemoryAuditLogPort,
95
+ } from "./audit";
96
+ export {
97
+ createAnonymousActor,
98
+ createMemoryAuditLog,
99
+ createRedactedAuditLog,
100
+ createServiceActor,
101
+ createSystemActor,
102
+ createTenant,
103
+ createUserActor,
104
+ normalizeAuditLogEntry,
105
+ redactAuditLogEntry,
106
+ } from "./audit";
107
+ export type {
108
+ AuthPort,
109
+ AuthRequestLike,
110
+ AuthSession,
111
+ StaticAuthSessionFactory,
112
+ } from "./auth";
113
+ export {
114
+ AuthUnauthorizedError,
115
+ createAnonymousAuth,
116
+ createStaticAuth,
117
+ } from "./auth";
118
+ // Ports builder exports
119
+ export {
120
+ createPortsBuilder,
121
+ type PortsBuilder,
122
+ type PortsOf,
123
+ } from "./builder";
124
+ export type { CachePort, CacheSetOptions } from "./cache";
125
+ // Cache port exports
126
+ export { createMemoryCache } from "./cache";
127
+ // Clock port exports
128
+ export type { ClockPort, FrozenClockPort } from "./clock";
129
+ export { createFrozenClock, createSystemClock } from "./clock";
130
+ // Event bus exports
131
+ export type {
132
+ DomainEventDef,
133
+ EventBusPort,
134
+ InferEventPayload,
135
+ InferJobPayload,
136
+ JobDef,
137
+ JobDispatcherPort,
138
+ } from "./events";
139
+ // Id generator port exports
140
+ export type { IdGeneratorPort, SequenceIdGeneratorPort } from "./id-generator";
141
+ export {
142
+ createSequenceIdGenerator,
143
+ createUuidIdGenerator,
144
+ } from "./id-generator";
145
+ // Logger port exports
146
+ export type {
147
+ LoggerPort,
148
+ LogLevel,
149
+ MemoryLogEntry,
150
+ MemoryLoggerPort,
151
+ } from "./logger";
152
+ export { createMemoryLogger, createNoopLogger } from "./logger";
153
+ // Policy and authorization gate exports
154
+ export type {
155
+ BoundGate,
156
+ CreateGateOptions,
157
+ GateAllowedDecision,
158
+ GateDecision,
159
+ GateDeniedDecision,
160
+ GateDenyHandler,
161
+ GatePolicyResult,
162
+ GatePort,
163
+ PolicyContextFromDefinitions,
164
+ PolicyDefinition,
165
+ PolicyMapFromDefinitions,
166
+ PolicyResolver,
167
+ PolicySubjectArgs,
168
+ } from "./policy";
169
+ export {
170
+ allow,
171
+ createGate,
172
+ definePolicy,
173
+ deny,
174
+ GateAuthorizationError,
175
+ } from "./policy";
176
+ export type {
177
+ RateLimitHitOptions,
178
+ RateLimitPort,
179
+ RateLimitResult,
180
+ } from "./rate-limit";
181
+ // Rate limit port exports
182
+ export { createMemoryRateLimiter } from "./rate-limit";
183
+ // Redaction exports
184
+ export {
185
+ createRedactor,
186
+ DEFAULT_CIRCULAR_VALUE,
187
+ DEFAULT_REDACTED_VALUE,
188
+ DEFAULT_SENSITIVE_KEY_TERMS,
189
+ DEFAULT_SENSITIVE_KEYS,
190
+ DEFAULT_TRUNCATED_VALUE,
191
+ isSensitiveKey,
192
+ type RedactableHeaders,
193
+ type RedactionDecisionContext,
194
+ type RedactionOptions,
195
+ type Redactor,
196
+ redactHeaders,
197
+ redactValue,
198
+ } from "./redaction";
199
+ // Storage port exports
200
+ export type {
201
+ MemoryStorageOptions,
202
+ StorageBody,
203
+ StorageMetadata,
204
+ StorageObject,
205
+ StorageObjectBody,
206
+ StoragePort,
207
+ StoragePutOptions,
208
+ StorageVisibility,
209
+ } from "./storage";
210
+ export { createMemoryStorage } from "./storage";
211
+ // Unit of Work exports
212
+ export {
213
+ type BufferedDomainEventRecorder,
214
+ createDomainEventRecorder,
215
+ createNoopUnitOfWork,
216
+ type DomainEventRecorderPort,
217
+ type NoopUnitOfWorkOptions,
218
+ type RecordedDomainEvent,
219
+ type UnitOfWorkCallback,
220
+ type UnitOfWorkPort,
221
+ } from "./unit-of-work";
@@ -0,0 +1,67 @@
1
+ export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
2
+
3
+ export interface LoggerPort {
4
+ trace(message: string, meta?: Record<string, unknown>): void;
5
+ debug(message: string, meta?: Record<string, unknown>): void;
6
+ info(message: string, meta?: Record<string, unknown>): void;
7
+ warn(message: string, meta?: Record<string, unknown>): void;
8
+ error(message: string, meta?: Record<string, unknown>): void;
9
+ fatal(message: string, meta?: Record<string, unknown>): void;
10
+ child(bindings: Record<string, unknown>): LoggerPort;
11
+ }
12
+
13
+ export interface MemoryLogEntry {
14
+ level: LogLevel;
15
+ message: string;
16
+ meta?: Record<string, unknown>;
17
+ bindings: Record<string, unknown>;
18
+ }
19
+
20
+ export interface MemoryLoggerPort extends LoggerPort {
21
+ entries: MemoryLogEntry[];
22
+ }
23
+
24
+ export function createNoopLogger(): LoggerPort {
25
+ const logger: LoggerPort = {
26
+ trace: () => {},
27
+ debug: () => {},
28
+ info: () => {},
29
+ warn: () => {},
30
+ error: () => {},
31
+ fatal: () => {},
32
+ child: () => logger,
33
+ };
34
+
35
+ return logger;
36
+ }
37
+
38
+ export function createMemoryLogger(
39
+ bindings: Record<string, unknown> = {},
40
+ entries: MemoryLogEntry[] = [],
41
+ ): MemoryLoggerPort {
42
+ const capturedBindings = { ...bindings };
43
+ const record = (
44
+ level: LogLevel,
45
+ message: string,
46
+ meta?: Record<string, unknown>,
47
+ ) => {
48
+ entries.push({
49
+ level,
50
+ message,
51
+ meta: meta ? { ...meta } : undefined,
52
+ bindings: { ...capturedBindings },
53
+ });
54
+ };
55
+
56
+ return {
57
+ entries,
58
+ trace: (message, meta) => record("trace", message, meta),
59
+ debug: (message, meta) => record("debug", message, meta),
60
+ info: (message, meta) => record("info", message, meta),
61
+ warn: (message, meta) => record("warn", message, meta),
62
+ error: (message, meta) => record("error", message, meta),
63
+ fatal: (message, meta) => record("fatal", message, meta),
64
+ child: (childBindings) =>
65
+ createMemoryLogger({ ...capturedBindings, ...childBindings }, entries),
66
+ };
67
+ }
@@ -0,0 +1,242 @@
1
+ type MaybePromise<T> = T | Promise<T>;
2
+
3
+ export type GateAllowedDecision = {
4
+ allowed: true;
5
+ };
6
+
7
+ export type GateDeniedDecision = {
8
+ allowed: false;
9
+ reason?: string;
10
+ code?: string;
11
+ details?: unknown;
12
+ };
13
+
14
+ export type GateDecision = GateAllowedDecision | GateDeniedDecision;
15
+ export type GatePolicyResult = boolean | GateDecision;
16
+
17
+ export type PolicyResolver = (
18
+ ...args: never[]
19
+ ) => MaybePromise<GatePolicyResult>;
20
+
21
+ export type PolicyDefinition<
22
+ TPolicies extends Record<string, PolicyResolver> = Record<
23
+ string,
24
+ PolicyResolver
25
+ >,
26
+ > = {
27
+ policies: TPolicies;
28
+ };
29
+
30
+ export type PolicyContext<TResolver> = TResolver extends (
31
+ ctx: infer Ctx,
32
+ ...args: never[]
33
+ ) => MaybePromise<GatePolicyResult>
34
+ ? Ctx
35
+ : never;
36
+
37
+ export type PolicySubjectArgs<TResolver> = TResolver extends (
38
+ ...args: infer TArgs
39
+ ) => MaybePromise<GatePolicyResult>
40
+ ? TArgs extends [unknown, infer Subject]
41
+ ? [subject: Subject]
42
+ : []
43
+ : [];
44
+
45
+ type UnionToIntersection<T> = (
46
+ T extends unknown
47
+ ? (value: T) => void
48
+ : never
49
+ ) extends (value: infer U) => void
50
+ ? U
51
+ : never;
52
+
53
+ export type PolicyMapFromDefinitions<
54
+ TPolicies extends readonly PolicyDefinition[],
55
+ > = UnionToIntersection<
56
+ TPolicies[number] extends PolicyDefinition<infer TPolicyMap>
57
+ ? TPolicyMap
58
+ : never
59
+ >;
60
+
61
+ export type PolicyContextFromDefinitions<
62
+ TPolicies extends readonly PolicyDefinition[],
63
+ > = PolicyContext<
64
+ PolicyMapFromDefinitions<TPolicies>[keyof PolicyMapFromDefinitions<TPolicies>]
65
+ >;
66
+
67
+ export type BoundGate<TPolicies extends readonly PolicyDefinition[]> = {
68
+ can<TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string>(
69
+ ability: TAbility,
70
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
71
+ ): Promise<boolean>;
72
+ inspect<TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string>(
73
+ ability: TAbility,
74
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
75
+ ): Promise<GateDecision>;
76
+ authorize<
77
+ TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string,
78
+ >(
79
+ ability: TAbility,
80
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
81
+ ): Promise<GateAllowedDecision>;
82
+ };
83
+
84
+ export type GatePort<
85
+ TContext,
86
+ TPolicies extends readonly PolicyDefinition[] = readonly PolicyDefinition[],
87
+ > = {
88
+ bind(ctx: TContext): BoundGate<TPolicies>;
89
+ can<TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string>(
90
+ ctx: TContext,
91
+ ability: TAbility,
92
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
93
+ ): Promise<boolean>;
94
+ inspect<TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string>(
95
+ ctx: TContext,
96
+ ability: TAbility,
97
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
98
+ ): Promise<GateDecision>;
99
+ authorize<
100
+ TAbility extends keyof PolicyMapFromDefinitions<TPolicies> & string,
101
+ >(
102
+ ctx: TContext,
103
+ ability: TAbility,
104
+ ...subject: PolicySubjectArgs<PolicyMapFromDefinitions<TPolicies>[TAbility]>
105
+ ): Promise<GateAllowedDecision>;
106
+ };
107
+
108
+ export type GateDenyHandler<TContext> = (
109
+ decision: GateDeniedDecision,
110
+ params: {
111
+ ctx: TContext;
112
+ ability: string;
113
+ subject?: unknown;
114
+ },
115
+ ) => MaybePromise<Error | undefined>;
116
+
117
+ export type CreateGateOptions<
118
+ TContext,
119
+ TPolicies extends readonly PolicyDefinition[],
120
+ > = {
121
+ policies: TPolicies;
122
+ onDeny?: GateDenyHandler<TContext>;
123
+ };
124
+
125
+ export class GateAuthorizationError extends Error {
126
+ readonly code: string;
127
+ readonly status = 403;
128
+ readonly details?: unknown;
129
+
130
+ constructor(decision: GateDeniedDecision = deny()) {
131
+ super(decision.reason ?? "Forbidden");
132
+ this.name = "GateAuthorizationError";
133
+ this.code = decision.code ?? "FORBIDDEN";
134
+ this.details = decision.details;
135
+ }
136
+ }
137
+
138
+ export function allow(): GateAllowedDecision {
139
+ return { allowed: true };
140
+ }
141
+
142
+ export function deny(
143
+ reasonOrDecision?: string | Omit<GateDeniedDecision, "allowed">,
144
+ ): GateDeniedDecision {
145
+ if (typeof reasonOrDecision === "string") {
146
+ return { allowed: false, reason: reasonOrDecision };
147
+ }
148
+
149
+ return {
150
+ allowed: false,
151
+ ...reasonOrDecision,
152
+ };
153
+ }
154
+
155
+ export function definePolicy<
156
+ const TPolicies extends Record<string, PolicyResolver>,
157
+ >(policies: TPolicies): PolicyDefinition<TPolicies> {
158
+ return { policies };
159
+ }
160
+
161
+ export function createGate<
162
+ TContext,
163
+ const TPolicies extends readonly PolicyDefinition[],
164
+ >(
165
+ options: CreateGateOptions<TContext, TPolicies>,
166
+ ): GatePort<TContext, TPolicies> {
167
+ const registry = new Map<string, PolicyResolver>();
168
+
169
+ for (const definition of options.policies) {
170
+ for (const [ability, resolver] of Object.entries(definition.policies)) {
171
+ if (registry.has(ability)) {
172
+ throw new Error(`Policy ability "${ability}" is already registered.`);
173
+ }
174
+
175
+ registry.set(ability, resolver);
176
+ }
177
+ }
178
+
179
+ async function inspect(
180
+ ctx: TContext,
181
+ ability: string,
182
+ subject?: unknown,
183
+ ): Promise<GateDecision> {
184
+ const resolver = registry.get(ability);
185
+ if (!resolver) {
186
+ return deny({
187
+ reason: `No policy registered for "${ability}".`,
188
+ code: "POLICY_NOT_FOUND",
189
+ });
190
+ }
191
+
192
+ const result = await resolver(ctx as never, subject as never);
193
+ return normalizeDecision(result);
194
+ }
195
+
196
+ async function authorize(
197
+ ctx: TContext,
198
+ ability: string,
199
+ subject?: unknown,
200
+ ): Promise<GateAllowedDecision> {
201
+ const decision = await inspect(ctx, ability, subject);
202
+ if (decision.allowed) return decision;
203
+
204
+ const thrown = await options.onDeny?.(decision, { ctx, ability, subject });
205
+ throw thrown ?? new GateAuthorizationError(decision);
206
+ }
207
+
208
+ function bind(ctx: TContext): BoundGate<TPolicies> {
209
+ return {
210
+ can: async (ability, ...subject) => gate.can(ctx, ability, ...subject),
211
+ inspect: async (ability, ...subject) =>
212
+ gate.inspect(ctx, ability, ...subject),
213
+ authorize: async (ability, ...subject) =>
214
+ gate.authorize(ctx, ability, ...subject),
215
+ };
216
+ }
217
+
218
+ const gate: GatePort<TContext, TPolicies> = {
219
+ bind,
220
+ async can(ctx, ability, ...subject) {
221
+ return (await inspect(ctx, ability, firstSubject(subject))).allowed;
222
+ },
223
+ inspect: async (ctx, ability, ...subject) =>
224
+ inspect(ctx, ability, firstSubject(subject)),
225
+ authorize: async (ctx, ability, ...subject) =>
226
+ authorize(ctx, ability, firstSubject(subject)),
227
+ };
228
+
229
+ return gate;
230
+ }
231
+
232
+ function normalizeDecision(result: GatePolicyResult): GateDecision {
233
+ if (typeof result === "boolean") {
234
+ return result ? allow() : deny();
235
+ }
236
+
237
+ return result;
238
+ }
239
+
240
+ function firstSubject(subject: readonly unknown[]): unknown {
241
+ return subject[0];
242
+ }
@@ -0,0 +1,91 @@
1
+ export interface RateLimitHitOptions {
2
+ /**
3
+ * Unique key for this rate limit window.
4
+ *
5
+ * Examples: "global", "ip:203.0.113.10", "user:123".
6
+ */
7
+ key: string;
8
+ /**
9
+ * Maximum allowed hits inside the window.
10
+ */
11
+ limit: number;
12
+ /**
13
+ * Length of the window in seconds.
14
+ */
15
+ windowSec: number;
16
+ }
17
+
18
+ export interface RateLimitResult {
19
+ /**
20
+ * True when the hit is within the configured limit.
21
+ */
22
+ allowed: boolean;
23
+ /**
24
+ * Remaining allowed hits in the window, if known. May be null if the
25
+ * implementation does not track it.
26
+ */
27
+ remaining: number | null;
28
+ /**
29
+ * Date when the window resets, if known. May be null.
30
+ */
31
+ resetAt: Date | null;
32
+ /**
33
+ * Seconds until the caller should retry, if the hit was rejected and the
34
+ * implementation can calculate it.
35
+ */
36
+ retryAfterSeconds: number | null;
37
+ }
38
+
39
+ export interface RateLimitPort {
40
+ hit(options: RateLimitHitOptions): Promise<RateLimitResult>;
41
+ }
42
+
43
+ type MemoryRateLimitWindow = {
44
+ count: number;
45
+ resetAt: number;
46
+ };
47
+
48
+ function assertPositiveInteger(name: string, value: number): void {
49
+ if (!Number.isInteger(value) || value <= 0) {
50
+ throw new Error(`${name} must be a positive integer`);
51
+ }
52
+ }
53
+
54
+ function toRetryAfterSeconds(resetAt: number): number {
55
+ return Math.max(0, Math.ceil((resetAt - Date.now()) / 1000));
56
+ }
57
+
58
+ export function createMemoryRateLimiter(): RateLimitPort {
59
+ const windows = new Map<string, MemoryRateLimitWindow>();
60
+
61
+ return {
62
+ async hit({ key, limit, windowSec }) {
63
+ assertPositiveInteger("limit", limit);
64
+ assertPositiveInteger("windowSec", windowSec);
65
+
66
+ const now = Date.now();
67
+ const current = windows.get(key);
68
+ const window =
69
+ current && current.resetAt > now
70
+ ? current
71
+ : {
72
+ count: 0,
73
+ resetAt: now + windowSec * 1000,
74
+ };
75
+
76
+ window.count += 1;
77
+ windows.set(key, window);
78
+
79
+ const allowed = window.count <= limit;
80
+ const remaining = Math.max(0, limit - window.count);
81
+ const resetAt = new Date(window.resetAt);
82
+
83
+ return {
84
+ allowed,
85
+ remaining,
86
+ resetAt,
87
+ retryAfterSeconds: allowed ? null : toRetryAfterSeconds(window.resetAt),
88
+ };
89
+ },
90
+ };
91
+ }