@beignet/core 0.0.1 → 0.0.2

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 (274) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +149 -4
  3. package/dist/application/index.d.ts +93 -9
  4. package/dist/application/index.d.ts.map +1 -1
  5. package/dist/application/index.js +11 -11
  6. package/dist/application/index.js.map +1 -1
  7. package/dist/client/client.d.ts +73 -12
  8. package/dist/client/client.d.ts.map +1 -1
  9. package/dist/client/client.js +37 -12
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/index.d.ts +12 -0
  12. package/dist/client/index.d.ts.map +1 -1
  13. package/dist/client/index.js +6 -0
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/client/types.d.ts +69 -8
  16. package/dist/client/types.d.ts.map +1 -1
  17. package/dist/config/index.d.ts +84 -0
  18. package/dist/config/index.d.ts.map +1 -1
  19. package/dist/config/index.js +36 -0
  20. package/dist/config/index.js.map +1 -1
  21. package/dist/contracts/contract-builder.d.ts +49 -22
  22. package/dist/contracts/contract-builder.d.ts.map +1 -1
  23. package/dist/contracts/contract-builder.js +48 -21
  24. package/dist/contracts/contract-builder.js.map +1 -1
  25. package/dist/contracts/contract-group.d.ts +35 -19
  26. package/dist/contracts/contract-group.d.ts.map +1 -1
  27. package/dist/contracts/contract-group.js +35 -19
  28. package/dist/contracts/contract-group.js.map +1 -1
  29. package/dist/contracts/contract-like.d.ts +4 -4
  30. package/dist/contracts/contract-like.d.ts.map +1 -1
  31. package/dist/contracts/contract-like.js +2 -1
  32. package/dist/contracts/contract-like.js.map +1 -1
  33. package/dist/contracts/index.d.ts +28 -0
  34. package/dist/contracts/index.d.ts.map +1 -1
  35. package/dist/contracts/index.js +12 -0
  36. package/dist/contracts/index.js.map +1 -1
  37. package/dist/contracts/openapi-meta.d.ts +8 -8
  38. package/dist/contracts/openapi-meta.d.ts.map +1 -1
  39. package/dist/contracts/path-template.d.ts +27 -0
  40. package/dist/contracts/path-template.d.ts.map +1 -1
  41. package/dist/contracts/path-template.js +6 -0
  42. package/dist/contracts/path-template.js.map +1 -1
  43. package/dist/contracts/types.d.ts +104 -10
  44. package/dist/contracts/types.d.ts.map +1 -1
  45. package/dist/contracts/types.js +15 -0
  46. package/dist/contracts/types.js.map +1 -1
  47. package/dist/contracts/utils.d.ts +6 -0
  48. package/dist/contracts/utils.d.ts.map +1 -1
  49. package/dist/contracts/utils.js +6 -0
  50. package/dist/contracts/utils.js.map +1 -1
  51. package/dist/domain/entity.d.ts +22 -11
  52. package/dist/domain/entity.d.ts.map +1 -1
  53. package/dist/domain/entity.js +5 -1
  54. package/dist/domain/entity.js.map +1 -1
  55. package/dist/domain/events.d.ts +5 -2
  56. package/dist/domain/events.d.ts.map +1 -1
  57. package/dist/domain/events.js +4 -1
  58. package/dist/domain/events.js.map +1 -1
  59. package/dist/domain/value-object.d.ts +19 -9
  60. package/dist/domain/value-object.d.ts.map +1 -1
  61. package/dist/domain/value-object.js +5 -1
  62. package/dist/domain/value-object.js.map +1 -1
  63. package/dist/errors/catalog.d.ts +40 -16
  64. package/dist/errors/catalog.d.ts.map +1 -1
  65. package/dist/errors/catalog.js +18 -7
  66. package/dist/errors/catalog.js.map +1 -1
  67. package/dist/errors/response.d.ts +16 -4
  68. package/dist/errors/response.d.ts.map +1 -1
  69. package/dist/errors/response.js +3 -3
  70. package/dist/errors/response.js.map +1 -1
  71. package/dist/errors/validation.d.ts +10 -1
  72. package/dist/errors/validation.d.ts.map +1 -1
  73. package/dist/errors/validation.js +3 -0
  74. package/dist/errors/validation.js.map +1 -1
  75. package/dist/events/index.d.ts +133 -0
  76. package/dist/events/index.d.ts.map +1 -1
  77. package/dist/events/index.js +30 -0
  78. package/dist/events/index.js.map +1 -1
  79. package/dist/idempotency/index.d.ts +355 -0
  80. package/dist/idempotency/index.d.ts.map +1 -0
  81. package/dist/idempotency/index.js +360 -0
  82. package/dist/idempotency/index.js.map +1 -0
  83. package/dist/jobs/index.d.ts +110 -0
  84. package/dist/jobs/index.d.ts.map +1 -1
  85. package/dist/jobs/index.js +22 -0
  86. package/dist/jobs/index.js.map +1 -1
  87. package/dist/mail/index.d.ts +149 -0
  88. package/dist/mail/index.d.ts.map +1 -1
  89. package/dist/mail/index.js +30 -0
  90. package/dist/mail/index.js.map +1 -1
  91. package/dist/notifications/index.d.ts +369 -0
  92. package/dist/notifications/index.d.ts.map +1 -0
  93. package/dist/notifications/index.js +310 -0
  94. package/dist/notifications/index.js.map +1 -0
  95. package/dist/openapi/index.d.ts +132 -16
  96. package/dist/openapi/index.d.ts.map +1 -1
  97. package/dist/openapi/index.js +1 -1
  98. package/dist/openapi/index.js.map +1 -1
  99. package/dist/outbox/index.d.ts +469 -0
  100. package/dist/outbox/index.d.ts.map +1 -0
  101. package/dist/outbox/index.js +482 -0
  102. package/dist/outbox/index.js.map +1 -0
  103. package/dist/pagination/index.d.ts +166 -0
  104. package/dist/pagination/index.d.ts.map +1 -0
  105. package/dist/pagination/index.js +96 -0
  106. package/dist/pagination/index.js.map +1 -0
  107. package/dist/ports/audit.d.ts +271 -0
  108. package/dist/ports/audit.d.ts.map +1 -1
  109. package/dist/ports/audit.js +128 -0
  110. package/dist/ports/audit.js.map +1 -1
  111. package/dist/ports/auth.d.ts +70 -0
  112. package/dist/ports/auth.d.ts.map +1 -1
  113. package/dist/ports/auth.js +30 -0
  114. package/dist/ports/auth.js.map +1 -1
  115. package/dist/ports/cache.d.ts +41 -0
  116. package/dist/ports/cache.d.ts.map +1 -1
  117. package/dist/ports/cache.js +10 -0
  118. package/dist/ports/cache.js.map +1 -1
  119. package/dist/ports/clock.d.ts +38 -0
  120. package/dist/ports/clock.d.ts.map +1 -1
  121. package/dist/ports/clock.js +20 -0
  122. package/dist/ports/clock.js.map +1 -1
  123. package/dist/ports/id-generator.d.ts +37 -0
  124. package/dist/ports/id-generator.d.ts.map +1 -1
  125. package/dist/ports/id-generator.js +22 -0
  126. package/dist/ports/id-generator.js.map +1 -1
  127. package/dist/ports/index.d.ts +83 -0
  128. package/dist/ports/index.d.ts.map +1 -1
  129. package/dist/ports/index.js +41 -5
  130. package/dist/ports/index.js.map +1 -1
  131. package/dist/ports/logger.d.ts +56 -0
  132. package/dist/ports/logger.d.ts.map +1 -1
  133. package/dist/ports/logger.js +17 -0
  134. package/dist/ports/logger.js.map +1 -1
  135. package/dist/ports/policy.d.ts +132 -0
  136. package/dist/ports/policy.d.ts.map +1 -1
  137. package/dist/ports/policy.js +45 -0
  138. package/dist/ports/policy.js.map +1 -1
  139. package/dist/ports/rate-limit.d.ts +25 -0
  140. package/dist/ports/rate-limit.d.ts.map +1 -1
  141. package/dist/ports/rate-limit.js +10 -0
  142. package/dist/ports/rate-limit.js.map +1 -1
  143. package/dist/ports/redaction.d.ts +101 -0
  144. package/dist/ports/redaction.d.ts.map +1 -1
  145. package/dist/ports/redaction.js +59 -0
  146. package/dist/ports/redaction.js.map +1 -1
  147. package/dist/ports/storage.d.ts +100 -0
  148. package/dist/ports/storage.d.ts.map +1 -1
  149. package/dist/ports/storage.js +10 -0
  150. package/dist/ports/storage.js.map +1 -1
  151. package/dist/ports/testing.d.ts +47 -0
  152. package/dist/ports/testing.d.ts.map +1 -1
  153. package/dist/ports/testing.js +23 -0
  154. package/dist/ports/testing.js.map +1 -1
  155. package/dist/ports/unit-of-work.d.ts +60 -3
  156. package/dist/ports/unit-of-work.d.ts.map +1 -1
  157. package/dist/ports/unit-of-work.js +11 -2
  158. package/dist/ports/unit-of-work.js.map +1 -1
  159. package/dist/providers/instrumentation.d.ts +204 -0
  160. package/dist/providers/instrumentation.d.ts.map +1 -1
  161. package/dist/providers/instrumentation.js +14 -0
  162. package/dist/providers/instrumentation.js.map +1 -1
  163. package/dist/providers/provider.d.ts +14 -1
  164. package/dist/providers/provider.d.ts.map +1 -1
  165. package/dist/providers/provider.js.map +1 -1
  166. package/dist/schedules/index.d.ts +246 -0
  167. package/dist/schedules/index.d.ts.map +1 -1
  168. package/dist/schedules/index.js +27 -0
  169. package/dist/schedules/index.js.map +1 -1
  170. package/dist/server/health.d.ts +14 -5
  171. package/dist/server/health.d.ts.map +1 -1
  172. package/dist/server/health.js +5 -2
  173. package/dist/server/health.js.map +1 -1
  174. package/dist/server/hooks/auth.d.ts +57 -0
  175. package/dist/server/hooks/auth.d.ts.map +1 -1
  176. package/dist/server/hooks/auth.js.map +1 -1
  177. package/dist/server/hooks/cors.d.ts +27 -0
  178. package/dist/server/hooks/cors.d.ts.map +1 -1
  179. package/dist/server/hooks/cors.js +12 -0
  180. package/dist/server/hooks/cors.js.map +1 -1
  181. package/dist/server/hooks/errors.d.ts +15 -6
  182. package/dist/server/hooks/errors.d.ts.map +1 -1
  183. package/dist/server/hooks/errors.js.map +1 -1
  184. package/dist/server/hooks/index.d.ts +3 -0
  185. package/dist/server/hooks/index.d.ts.map +1 -1
  186. package/dist/server/hooks/index.js +3 -0
  187. package/dist/server/hooks/index.js.map +1 -1
  188. package/dist/server/hooks/logging.d.ts +36 -0
  189. package/dist/server/hooks/logging.d.ts.map +1 -1
  190. package/dist/server/hooks/logging.js +6 -0
  191. package/dist/server/hooks/logging.js.map +1 -1
  192. package/dist/server/hooks/rate-limit.d.ts +33 -0
  193. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  194. package/dist/server/hooks/rate-limit.js +11 -0
  195. package/dist/server/hooks/rate-limit.js.map +1 -1
  196. package/dist/server/http.d.ts +170 -0
  197. package/dist/server/http.d.ts.map +1 -1
  198. package/dist/server/index.d.ts +18 -0
  199. package/dist/server/index.d.ts.map +1 -1
  200. package/dist/server/index.js +6 -0
  201. package/dist/server/index.js.map +1 -1
  202. package/dist/server/openapi.d.ts +5 -3
  203. package/dist/server/openapi.d.ts.map +1 -1
  204. package/dist/server/openapi.js +4 -2
  205. package/dist/server/openapi.js.map +1 -1
  206. package/dist/server/providers/loadProviderConfig.d.ts +9 -0
  207. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
  208. package/dist/server/providers/loadProviderConfig.js +9 -0
  209. package/dist/server/providers/loadProviderConfig.js.map +1 -1
  210. package/dist/server/server.d.ts +107 -8
  211. package/dist/server/server.d.ts.map +1 -1
  212. package/dist/server/server.js +27 -7
  213. package/dist/server/server.js.map +1 -1
  214. package/dist/testing/index.d.ts +167 -0
  215. package/dist/testing/index.d.ts.map +1 -0
  216. package/dist/testing/index.js +119 -0
  217. package/dist/testing/index.js.map +1 -0
  218. package/package.json +21 -1
  219. package/src/application/index.ts +85 -22
  220. package/src/client/client.ts +73 -12
  221. package/src/client/index.ts +12 -0
  222. package/src/client/types.ts +70 -9
  223. package/src/config/index.ts +86 -0
  224. package/src/contracts/contract-builder.ts +49 -22
  225. package/src/contracts/contract-group.ts +35 -19
  226. package/src/contracts/contract-like.ts +4 -4
  227. package/src/contracts/index.ts +28 -1
  228. package/src/contracts/openapi-meta.ts +8 -8
  229. package/src/contracts/path-template.ts +27 -0
  230. package/src/contracts/types.ts +111 -10
  231. package/src/contracts/utils.ts +6 -0
  232. package/src/domain/entity.ts +22 -11
  233. package/src/domain/events.ts +5 -2
  234. package/src/domain/value-object.ts +19 -9
  235. package/src/errors/catalog.ts +40 -16
  236. package/src/errors/response.ts +16 -4
  237. package/src/errors/validation.ts +10 -1
  238. package/src/events/index.ts +134 -0
  239. package/src/idempotency/index.ts +767 -0
  240. package/src/jobs/index.ts +111 -0
  241. package/src/mail/index.ts +149 -0
  242. package/src/notifications/index.ts +771 -0
  243. package/src/openapi/index.ts +133 -16
  244. package/src/outbox/index.ts +1024 -0
  245. package/src/pagination/index.ts +278 -0
  246. package/src/ports/audit.ts +271 -0
  247. package/src/ports/auth.ts +70 -0
  248. package/src/ports/cache.ts +41 -0
  249. package/src/ports/clock.ts +38 -0
  250. package/src/ports/id-generator.ts +37 -0
  251. package/src/ports/index.ts +106 -11
  252. package/src/ports/logger.ts +56 -0
  253. package/src/ports/policy.ts +133 -0
  254. package/src/ports/rate-limit.ts +25 -0
  255. package/src/ports/redaction.ts +101 -0
  256. package/src/ports/storage.ts +100 -0
  257. package/src/ports/testing.ts +47 -0
  258. package/src/ports/unit-of-work.ts +60 -3
  259. package/src/providers/instrumentation.ts +204 -0
  260. package/src/providers/provider.ts +14 -1
  261. package/src/schedules/index.ts +247 -0
  262. package/src/server/health.ts +14 -5
  263. package/src/server/hooks/auth.ts +58 -0
  264. package/src/server/hooks/cors.ts +27 -0
  265. package/src/server/hooks/errors.ts +15 -6
  266. package/src/server/hooks/index.ts +3 -0
  267. package/src/server/hooks/logging.ts +36 -0
  268. package/src/server/hooks/rate-limit.ts +33 -0
  269. package/src/server/http.ts +170 -1
  270. package/src/server/index.ts +18 -1
  271. package/src/server/openapi.ts +5 -3
  272. package/src/server/providers/loadProviderConfig.ts +9 -0
  273. package/src/server/server.ts +107 -9
  274. package/src/testing/index.ts +337 -0
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Sort direction for list queries.
3
+ */
4
+ export type SortDirection = "asc" | "desc";
5
+
6
+ /**
7
+ * Sort option for a whitelisted field.
8
+ */
9
+ export type SortOption<Field extends string = string> = {
10
+ /**
11
+ * Field to sort by.
12
+ */
13
+ field: Field;
14
+ /**
15
+ * Sort direction.
16
+ */
17
+ direction: SortDirection;
18
+ };
19
+
20
+ /**
21
+ * Normalized offset pagination request.
22
+ */
23
+ export interface OffsetPage {
24
+ /**
25
+ * Pagination mode.
26
+ */
27
+ readonly kind: "offset";
28
+ /**
29
+ * Page size after defaulting and max-limit clamping.
30
+ */
31
+ readonly limit: number;
32
+ /**
33
+ * Zero-based item offset.
34
+ */
35
+ readonly offset: number;
36
+ }
37
+
38
+ /**
39
+ * Normalized cursor pagination request.
40
+ */
41
+ export interface CursorPage {
42
+ /**
43
+ * Pagination mode.
44
+ */
45
+ readonly kind: "cursor";
46
+ /**
47
+ * Page size after defaulting and max-limit clamping.
48
+ */
49
+ readonly limit: number;
50
+ /**
51
+ * Cursor for the current page, or null for the first page.
52
+ */
53
+ readonly cursor: string | null;
54
+ }
55
+
56
+ /**
57
+ * Normalized pagination request.
58
+ */
59
+ export type Page = OffsetPage | CursorPage;
60
+
61
+ /**
62
+ * Offset pagination response metadata.
63
+ */
64
+ export type OffsetPageInfo = OffsetPage & {
65
+ /**
66
+ * Total item count.
67
+ */
68
+ readonly total: number;
69
+ /**
70
+ * Whether another page exists.
71
+ */
72
+ readonly hasMore: boolean;
73
+ };
74
+
75
+ /**
76
+ * Cursor pagination response metadata.
77
+ */
78
+ export type CursorPageInfo = CursorPage & {
79
+ /**
80
+ * Cursor for the next page, or null when there is no next page.
81
+ */
82
+ readonly nextCursor: string | null;
83
+ /**
84
+ * Whether another page exists.
85
+ */
86
+ readonly hasMore: boolean;
87
+ };
88
+
89
+ /**
90
+ * Pagination response metadata.
91
+ */
92
+ export type PageInfo = OffsetPageInfo | CursorPageInfo;
93
+
94
+ /**
95
+ * Paginated list result.
96
+ */
97
+ export interface PageResult<TItem, TPage extends PageInfo = PageInfo> {
98
+ /**
99
+ * Page items. Result helpers clone the input array.
100
+ */
101
+ readonly items: TItem[];
102
+ /**
103
+ * Page metadata.
104
+ */
105
+ readonly page: TPage;
106
+ }
107
+
108
+ /**
109
+ * Options for normalizing pagination input.
110
+ */
111
+ export interface NormalizePageOptions {
112
+ /**
113
+ * Limit used when the caller does not provide one.
114
+ */
115
+ readonly defaultLimit: number;
116
+ /**
117
+ * Maximum allowed limit. Larger caller values are clamped.
118
+ */
119
+ readonly maxLimit: number;
120
+ }
121
+
122
+ /**
123
+ * Raw offset pagination input.
124
+ */
125
+ export interface OffsetPageInput {
126
+ /**
127
+ * Requested page size.
128
+ */
129
+ readonly limit?: number | null;
130
+ /**
131
+ * Requested zero-based offset.
132
+ */
133
+ readonly offset?: number | null;
134
+ }
135
+
136
+ /**
137
+ * Raw cursor pagination input.
138
+ */
139
+ export interface CursorPageInput {
140
+ /**
141
+ * Requested page size.
142
+ */
143
+ readonly limit?: number | null;
144
+ /**
145
+ * Requested cursor.
146
+ */
147
+ readonly cursor?: string | null;
148
+ }
149
+
150
+ /**
151
+ * Error thrown when pagination input is invalid.
152
+ */
153
+ export class PaginationError extends Error {
154
+ constructor(message: string) {
155
+ super(message);
156
+ this.name = "PaginationError";
157
+ }
158
+ }
159
+
160
+ function assertPositiveInteger(name: string, value: number): void {
161
+ if (!Number.isInteger(value) || value < 1) {
162
+ throw new PaginationError(`${name} must be a positive integer.`);
163
+ }
164
+ }
165
+
166
+ function normalizeLimit(
167
+ input: number | null | undefined,
168
+ options: NormalizePageOptions,
169
+ ): number {
170
+ assertPositiveInteger("defaultLimit", options.defaultLimit);
171
+ assertPositiveInteger("maxLimit", options.maxLimit);
172
+
173
+ if (options.defaultLimit > options.maxLimit) {
174
+ throw new PaginationError(
175
+ "defaultLimit must be less than or equal to maxLimit.",
176
+ );
177
+ }
178
+
179
+ if (input == null) return options.defaultLimit;
180
+
181
+ if (!Number.isInteger(input) || input < 1) {
182
+ throw new PaginationError("limit must be a positive integer.");
183
+ }
184
+
185
+ return Math.min(input, options.maxLimit);
186
+ }
187
+
188
+ /**
189
+ * Normalize offset pagination input.
190
+ *
191
+ * The limit is defaulted and clamped to `maxLimit`; offset must be a
192
+ * non-negative integer.
193
+ */
194
+ export function normalizeOffsetPage(
195
+ input: OffsetPageInput,
196
+ options: NormalizePageOptions,
197
+ ): OffsetPage {
198
+ const limit = normalizeLimit(input.limit, options);
199
+ const offset = input.offset ?? 0;
200
+
201
+ if (!Number.isInteger(offset) || offset < 0) {
202
+ throw new PaginationError("offset must be a non-negative integer.");
203
+ }
204
+
205
+ return {
206
+ kind: "offset",
207
+ limit,
208
+ offset,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Normalize cursor pagination input.
214
+ *
215
+ * The limit is defaulted and clamped to `maxLimit`; cursor must be a string or
216
+ * null.
217
+ */
218
+ export function normalizeCursorPage(
219
+ input: CursorPageInput,
220
+ options: NormalizePageOptions,
221
+ ): CursorPage {
222
+ const limit = normalizeLimit(input.limit, options);
223
+ const cursor = input.cursor ?? null;
224
+
225
+ if (cursor !== null && typeof cursor !== "string") {
226
+ throw new PaginationError("cursor must be a string or null.");
227
+ }
228
+
229
+ return {
230
+ kind: "cursor",
231
+ limit,
232
+ cursor,
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Create an offset page result and derive `hasMore`.
238
+ */
239
+ export function offsetPageResult<TItem>(
240
+ items: readonly TItem[],
241
+ page: OffsetPage,
242
+ total: number,
243
+ ): PageResult<TItem, OffsetPageInfo> {
244
+ if (!Number.isInteger(total) || total < 0) {
245
+ throw new PaginationError("total must be a non-negative integer.");
246
+ }
247
+
248
+ return {
249
+ items: [...items],
250
+ page: {
251
+ ...page,
252
+ total,
253
+ hasMore: page.offset + items.length < total,
254
+ },
255
+ };
256
+ }
257
+
258
+ /**
259
+ * Create a cursor page result and derive `hasMore` from `nextCursor`.
260
+ */
261
+ export function cursorPageResult<TItem>(
262
+ items: readonly TItem[],
263
+ page: CursorPage,
264
+ nextCursor: string | null,
265
+ ): PageResult<TItem, CursorPageInfo> {
266
+ if (nextCursor !== null && typeof nextCursor !== "string") {
267
+ throw new PaginationError("nextCursor must be a string or null.");
268
+ }
269
+
270
+ return {
271
+ items: [...items],
272
+ page: {
273
+ ...page,
274
+ nextCursor,
275
+ hasMore: nextCursor !== null,
276
+ },
277
+ };
278
+ }
@@ -1,9 +1,26 @@
1
1
  import { redactValue } from "./redaction";
2
2
 
3
+ /**
4
+ * The normalized category of actor that caused application activity.
5
+ *
6
+ * Actors describe who or what performed work for request context,
7
+ * authorization, audit logs, and diagnostics. They do not authenticate the
8
+ * request by themselves.
9
+ */
3
10
  export type ActivityActorType = "anonymous" | "service" | "system" | "user";
4
11
 
12
+ /**
13
+ * Whether an audited activity completed successfully or intentionally records
14
+ * a failed attempt.
15
+ */
5
16
  export type AuditOutcome = "success" | "failure";
6
17
 
18
+ /**
19
+ * JSON-like metadata values accepted by activity and audit descriptors.
20
+ *
21
+ * Metadata should stay intentionally small. Prefer stable IDs and short labels
22
+ * over full request bodies, secrets, PHI, or PII.
23
+ */
7
24
  export type ActivityMetadataValue =
8
25
  | ActivityMetadataValue[]
9
26
  | boolean
@@ -12,44 +29,146 @@ export type ActivityMetadataValue =
12
29
  | string
13
30
  | { [key: string]: ActivityMetadataValue | undefined };
14
31
 
32
+ /**
33
+ * Additional structured metadata attached to actors, tenants, resources, or
34
+ * audit entries.
35
+ */
15
36
  export type ActivityMetadata = Record<
16
37
  string,
17
38
  ActivityMetadataValue | undefined
18
39
  >;
19
40
 
41
+ /**
42
+ * A normalized descriptor for the person, service, or system process that
43
+ * caused application activity.
44
+ *
45
+ * Store this on application context as `ctx.actor` so routes, use cases, jobs,
46
+ * policies, audit logs, and devtools share one identity shape.
47
+ */
20
48
  export interface ActivityActor {
49
+ /**
50
+ * The actor category.
51
+ */
21
52
  type: ActivityActorType;
53
+ /**
54
+ * Stable application ID for this actor, when known.
55
+ */
22
56
  id?: string;
57
+ /**
58
+ * Human-readable label for diagnostics and audit views.
59
+ */
23
60
  displayName?: string;
61
+ /**
62
+ * Small, redaction-safe metadata about the actor.
63
+ */
24
64
  metadata?: ActivityMetadata;
25
65
  }
26
66
 
67
+ /**
68
+ * A normalized tenant/account/workspace scope for activity.
69
+ *
70
+ * This is a context value used by audit logs, authorization, and diagnostics.
71
+ * It does not create, load, or persist a tenant record.
72
+ */
27
73
  export interface ActivityTenant {
74
+ /**
75
+ * Stable tenant/account/workspace ID.
76
+ */
28
77
  id: string;
78
+ /**
79
+ * Optional human-readable tenant slug.
80
+ */
29
81
  slug?: string;
82
+ /**
83
+ * Small, redaction-safe metadata about the tenant.
84
+ */
30
85
  metadata?: ActivityMetadata;
31
86
  }
32
87
 
88
+ /**
89
+ * A normalized descriptor for the business object affected by an audit entry.
90
+ */
33
91
  export interface ActivityResource {
92
+ /**
93
+ * Resource type, usually a singular domain noun such as "post", "invoice",
94
+ * or "appointment".
95
+ */
34
96
  type: string;
97
+ /**
98
+ * Stable resource ID, when known.
99
+ */
35
100
  id?: string;
101
+ /**
102
+ * Human-readable resource label for audit views.
103
+ */
36
104
  name?: string;
105
+ /**
106
+ * Small, redaction-safe metadata about the resource.
107
+ */
37
108
  metadata?: ActivityMetadata;
38
109
  }
39
110
 
111
+ /**
112
+ * A normalized audit/activity log entry.
113
+ *
114
+ * Application code usually creates these through an app-owned helper such as
115
+ * `auditEntry(ctx, { action, resource })` so actor, tenant, request ID, and
116
+ * trace ID are copied from context consistently. Durability depends on the
117
+ * `AuditLogPort` implementation.
118
+ */
40
119
  export interface AuditLogEntry {
120
+ /**
121
+ * Stable action name, usually namespaced by feature and workflow.
122
+ *
123
+ * @example "posts.publish"
124
+ */
41
125
  action: string;
126
+ /**
127
+ * Actor that caused the activity.
128
+ */
42
129
  actor: ActivityActor;
130
+ /**
131
+ * Timestamp assigned when the activity occurred.
132
+ */
43
133
  occurredAt: Date;
134
+ /**
135
+ * Whether the activity succeeded or records a failed attempt.
136
+ */
44
137
  outcome: AuditOutcome;
138
+ /**
139
+ * Small, redaction-safe metadata about the activity.
140
+ */
45
141
  metadata?: ActivityMetadata;
142
+ /**
143
+ * Optional human-readable audit message.
144
+ */
46
145
  message?: string;
146
+ /**
147
+ * Request correlation ID, when the activity originated from a request or
148
+ * background context.
149
+ */
47
150
  requestId?: string;
151
+ /**
152
+ * Business resource affected by the activity.
153
+ */
48
154
  resource?: ActivityResource;
155
+ /**
156
+ * Tenant/account/workspace scope for the activity.
157
+ */
49
158
  tenant?: ActivityTenant;
159
+ /**
160
+ * Trace correlation ID, when tracing is enabled.
161
+ */
50
162
  traceId?: string;
51
163
  }
52
164
 
165
+ /**
166
+ * Input accepted by `AuditLogPort.record(...)`.
167
+ *
168
+ * `occurredAt` and `outcome` are optional at call sites. Adapters that store
169
+ * audit entries should call `normalizeAuditLogEntry(...)` before persistence or
170
+ * otherwise apply equivalent defaults.
171
+ */
53
172
  export type AuditLogEntryInput = Omit<
54
173
  AuditLogEntry,
55
174
  "occurredAt" | "outcome"
@@ -58,24 +177,78 @@ export type AuditLogEntryInput = Omit<
58
177
  outcome?: AuditOutcome;
59
178
  };
60
179
 
180
+ /**
181
+ * App-facing port for audit/activity logging.
182
+ *
183
+ * Production implementations should usually write to a durable database table,
184
+ * append-only log, or external audit service. Tests can use an in-memory
185
+ * adapter. Application code should depend on this interface, not on a concrete
186
+ * audit provider.
187
+ */
61
188
  export interface AuditLogPort {
189
+ /**
190
+ * Persist or capture an audit entry.
191
+ */
62
192
  record(entry: AuditLogEntryInput): Promise<void> | void;
63
193
  }
64
194
 
195
+ /**
196
+ * In-memory audit log port used by tests and local examples.
197
+ */
65
198
  export interface MemoryAuditLogPort extends AuditLogPort {
199
+ /**
200
+ * Captured, normalized, redacted audit entries.
201
+ */
66
202
  entries: AuditLogEntry[];
67
203
  }
68
204
 
205
+ /**
206
+ * Options shared by audit log wrappers and in-memory audit adapters.
207
+ */
69
208
  export interface AuditLogOptions {
209
+ /**
210
+ * Optional final redaction/customization step applied after Beignet's default
211
+ * metadata redaction.
212
+ */
70
213
  redact?: (entry: AuditLogEntry) => AuditLogEntry;
71
214
  }
72
215
 
216
+ /**
217
+ * Create an anonymous actor descriptor for unauthenticated activity.
218
+ *
219
+ * This helper only creates a normalized context value. It does not perform
220
+ * authentication.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * const actor = createAnonymousActor();
225
+ * ```
226
+ *
227
+ * @param options - Optional display name or metadata to include.
228
+ * @returns An activity actor with `type: "anonymous"`.
229
+ */
73
230
  export function createAnonymousActor(
74
231
  options: Omit<ActivityActor, "type"> = {},
75
232
  ): ActivityActor {
76
233
  return { type: "anonymous", ...options };
77
234
  }
78
235
 
236
+ /**
237
+ * Create a service actor descriptor for work initiated by another service or
238
+ * integration.
239
+ *
240
+ * This is useful for webhooks, internal service calls, or integration-driven
241
+ * background jobs.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * const actor = createServiceActor("stripe-webhook");
246
+ * ```
247
+ *
248
+ * @param id - Stable service or integration ID.
249
+ * @param options - Optional display name or metadata to include.
250
+ * @returns An activity actor with `type: "service"`.
251
+ */
79
252
  export function createServiceActor(
80
253
  id: string,
81
254
  options: Omit<ActivityActor, "type" | "id"> = {},
@@ -83,6 +256,21 @@ export function createServiceActor(
83
256
  return { type: "service", id, ...options };
84
257
  }
85
258
 
259
+ /**
260
+ * Create a system actor descriptor for framework or app-owned background work.
261
+ *
262
+ * Use this for scheduled tasks, scripts, maintenance jobs, and other work that
263
+ * is not directly caused by a user or external service.
264
+ *
265
+ * @example
266
+ * ```ts
267
+ * const actor = createSystemActor("nightly-maintenance");
268
+ * ```
269
+ *
270
+ * @param id - Stable system actor ID. Defaults to `"system"`.
271
+ * @param options - Optional display name or metadata to include.
272
+ * @returns An activity actor with `type: "system"`.
273
+ */
86
274
  export function createSystemActor(
87
275
  id = "system",
88
276
  options: Omit<ActivityActor, "type" | "id"> = {},
@@ -90,6 +278,25 @@ export function createSystemActor(
90
278
  return { type: "system", id, ...options };
91
279
  }
92
280
 
281
+ /**
282
+ * Create a user actor descriptor for authenticated user activity.
283
+ *
284
+ * This helper only normalizes a known user ID for context, authorization,
285
+ * audit, and diagnostics. It does not verify a session or load a user record.
286
+ * Resolve authentication first, then call this helper with the authenticated
287
+ * user ID.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * const actor = createUserActor(session.user.id, {
292
+ * displayName: session.user.name,
293
+ * });
294
+ * ```
295
+ *
296
+ * @param id - Stable application user ID.
297
+ * @param options - Optional display name or metadata to include.
298
+ * @returns An activity actor with `type: "user"`.
299
+ */
93
300
  export function createUserActor(
94
301
  id: string,
95
302
  options: Omit<ActivityActor, "type" | "id"> = {},
@@ -97,6 +304,25 @@ export function createUserActor(
97
304
  return { type: "user", id, ...options };
98
305
  }
99
306
 
307
+ /**
308
+ * Create a tenant/account/workspace descriptor for request or background
309
+ * context.
310
+ *
311
+ * This helper only creates a normalized context value used by audit,
312
+ * authorization, logs, and diagnostics. It does not create, load, or persist a
313
+ * tenant record.
314
+ *
315
+ * @example
316
+ * ```ts
317
+ * const tenant = createTenant(session.organizationId, {
318
+ * slug: session.organizationSlug,
319
+ * });
320
+ * ```
321
+ *
322
+ * @param id - Stable tenant/account/workspace ID.
323
+ * @param options - Optional slug or metadata to include.
324
+ * @returns A normalized activity tenant descriptor.
325
+ */
100
326
  export function createTenant(
101
327
  id: string,
102
328
  options: Omit<ActivityTenant, "id"> = {},
@@ -104,6 +330,12 @@ export function createTenant(
104
330
  return { id, ...options };
105
331
  }
106
332
 
333
+ /**
334
+ * Fill default audit fields for an input entry.
335
+ *
336
+ * @param entry - Partial audit entry accepted by `AuditLogPort.record(...)`.
337
+ * @returns A complete audit entry with `occurredAt` and `outcome` populated.
338
+ */
107
339
  export function normalizeAuditLogEntry(
108
340
  entry: AuditLogEntryInput,
109
341
  ): AuditLogEntry {
@@ -114,6 +346,15 @@ export function normalizeAuditLogEntry(
114
346
  };
115
347
  }
116
348
 
349
+ /**
350
+ * Redact metadata on an already-normalized audit entry.
351
+ *
352
+ * This redacts metadata values on the entry, actor, tenant, and resource using
353
+ * the default redaction rules from `redactValue(...)`.
354
+ *
355
+ * @param entry - Audit entry to redact.
356
+ * @returns A shallow copy with redacted metadata fields.
357
+ */
117
358
  export function redactAuditLogEntry(entry: AuditLogEntry): AuditLogEntry {
118
359
  return {
119
360
  ...entry,
@@ -143,6 +384,16 @@ export function redactAuditLogEntry(entry: AuditLogEntry): AuditLogEntry {
143
384
  };
144
385
  }
145
386
 
387
+ /**
388
+ * Wrap an audit log port with default audit metadata redaction.
389
+ *
390
+ * Use this around durable adapters so application code can record entries
391
+ * without each call site remembering to redact metadata.
392
+ *
393
+ * @param audit - Underlying audit log port to write to after redaction.
394
+ * @param options - Optional final redaction/customization hook.
395
+ * @returns An audit log port that normalizes and redacts before writing.
396
+ */
146
397
  export function createRedactedAuditLog(
147
398
  audit: AuditLogPort,
148
399
  options: AuditLogOptions = {},
@@ -158,6 +409,26 @@ export function createRedactedAuditLog(
158
409
  };
159
410
  }
160
411
 
412
+ /**
413
+ * Create an in-memory audit log for tests and local examples.
414
+ *
415
+ * Entries are normalized and redacted before being pushed into the shared
416
+ * `entries` array.
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * const audit = createMemoryAuditLog();
421
+ * await audit.record({
422
+ * action: "posts.publish",
423
+ * actor: createUserActor("user_1"),
424
+ * });
425
+ * expect(audit.entries).toHaveLength(1);
426
+ * ```
427
+ *
428
+ * @param entries - Optional backing array, useful when tests need shared state.
429
+ * @param options - Optional final redaction/customization hook.
430
+ * @returns An in-memory audit log port with captured `entries`.
431
+ */
161
432
  export function createMemoryAuditLog(
162
433
  entries: AuditLogEntry[] = [],
163
434
  options: AuditLogOptions = {},