@daloyjs/core 0.17.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +3 -3
  2. package/dist/adapters/bun.d.ts.map +1 -1
  3. package/dist/adapters/bun.js +38 -7
  4. package/dist/adapters/bun.js.map +1 -1
  5. package/dist/adapters/node.d.ts.map +1 -1
  6. package/dist/adapters/node.js +29 -7
  7. package/dist/adapters/node.js.map +1 -1
  8. package/dist/app.d.ts +322 -3
  9. package/dist/app.d.ts.map +1 -1
  10. package/dist/app.js +715 -29
  11. package/dist/app.js.map +1 -1
  12. package/dist/cli.d.ts +4 -0
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +218 -1
  15. package/dist/cli.js.map +1 -1
  16. package/dist/combine.d.ts +97 -0
  17. package/dist/combine.d.ts.map +1 -0
  18. package/dist/combine.js +247 -0
  19. package/dist/combine.js.map +1 -0
  20. package/dist/compression.d.ts +127 -0
  21. package/dist/compression.d.ts.map +1 -0
  22. package/dist/compression.js +368 -0
  23. package/dist/compression.js.map +1 -0
  24. package/dist/config.d.ts +97 -0
  25. package/dist/config.d.ts.map +1 -0
  26. package/dist/config.js +132 -0
  27. package/dist/config.js.map +1 -0
  28. package/dist/conn-info.d.ts +121 -0
  29. package/dist/conn-info.d.ts.map +1 -0
  30. package/dist/conn-info.js +145 -0
  31. package/dist/conn-info.js.map +1 -0
  32. package/dist/cookie.d.ts +112 -0
  33. package/dist/cookie.d.ts.map +1 -0
  34. package/dist/cookie.js +185 -0
  35. package/dist/cookie.js.map +1 -0
  36. package/dist/dependency.d.ts +47 -0
  37. package/dist/dependency.d.ts.map +1 -0
  38. package/dist/dependency.js +68 -0
  39. package/dist/dependency.js.map +1 -0
  40. package/dist/etag.d.ts +48 -0
  41. package/dist/etag.d.ts.map +1 -0
  42. package/dist/etag.js +117 -0
  43. package/dist/etag.js.map +1 -0
  44. package/dist/index.d.ts +40 -11
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +19 -4
  47. package/dist/index.js.map +1 -1
  48. package/dist/ip-restriction.d.ts +75 -0
  49. package/dist/ip-restriction.d.ts.map +1 -0
  50. package/dist/ip-restriction.js +203 -0
  51. package/dist/ip-restriction.js.map +1 -0
  52. package/dist/jwk.d.ts +82 -0
  53. package/dist/jwk.d.ts.map +1 -0
  54. package/dist/jwk.js +269 -0
  55. package/dist/jwk.js.map +1 -0
  56. package/dist/jwt.d.ts +103 -0
  57. package/dist/jwt.d.ts.map +1 -0
  58. package/dist/jwt.js +437 -0
  59. package/dist/jwt.js.map +1 -0
  60. package/dist/load-shedding.d.ts +73 -0
  61. package/dist/load-shedding.d.ts.map +1 -0
  62. package/dist/load-shedding.js +171 -0
  63. package/dist/load-shedding.js.map +1 -0
  64. package/dist/middleware.d.ts +181 -2
  65. package/dist/middleware.d.ts.map +1 -1
  66. package/dist/middleware.js +367 -77
  67. package/dist/middleware.js.map +1 -1
  68. package/dist/multipart.d.ts +17 -0
  69. package/dist/multipart.d.ts.map +1 -1
  70. package/dist/multipart.js +117 -1
  71. package/dist/multipart.js.map +1 -1
  72. package/dist/openapi.d.ts +8 -0
  73. package/dist/openapi.d.ts.map +1 -1
  74. package/dist/openapi.js +18 -1
  75. package/dist/openapi.js.map +1 -1
  76. package/dist/security-schemes.d.ts +21 -5
  77. package/dist/security-schemes.d.ts.map +1 -1
  78. package/dist/security-schemes.js +32 -5
  79. package/dist/security-schemes.js.map +1 -1
  80. package/dist/session.d.ts +23 -1
  81. package/dist/session.d.ts.map +1 -1
  82. package/dist/session.js +106 -86
  83. package/dist/session.js.map +1 -1
  84. package/dist/subdomains.d.ts +97 -0
  85. package/dist/subdomains.d.ts.map +1 -0
  86. package/dist/subdomains.js +157 -0
  87. package/dist/subdomains.js.map +1 -0
  88. package/dist/time-claims.d.ts +72 -0
  89. package/dist/time-claims.d.ts.map +1 -0
  90. package/dist/time-claims.js +88 -0
  91. package/dist/time-claims.js.map +1 -0
  92. package/dist/types.d.ts +50 -0
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/websocket.d.ts +51 -34
  95. package/dist/websocket.d.ts.map +1 -1
  96. package/dist/websocket.js +162 -2
  97. package/dist/websocket.js.map +1 -1
  98. package/package.json +30 -3
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Wave 7 (first focused slice): first-party `compression()` middleware with
3
+ * BREACH-aware safe defaults.
4
+ *
5
+ * Built on the web-standard `CompressionStream` API so the same line works
6
+ * on Node, Bun, Deno, Cloudflare Workers, Vercel Edge, and Fastly Compute.
7
+ * The middleware deliberately refuses to expose a configurable compression
8
+ * level — `CompressionStream` uses the runtime's documented default (gzip
9
+ * level 6 on Node / V8); a `level: 9` opt-in is rejected at construction
10
+ * because the bytes-saved-per-CPU-cycle tradeoff turns into a small-vCPU
11
+ * DoS amplifier under sustained load with single-digit-percent bytes-saved
12
+ * gains on typical JSON payloads.
13
+ *
14
+ * BREACH guards (CRIME-style cross-domain compression-oracle defenses) are
15
+ * always-on and not opt-out: the middleware refuses to compress responses
16
+ * that carry `Set-Cookie`, responses on requests that carry an
17
+ * `Authorization` header, responses on requests that carry a
18
+ * recognized session / CSRF cookie, and responses whose content type is
19
+ * already entropy-coded (images, video, audio, archives, fonts, wasm, pdf).
20
+ *
21
+ * @since 0.25.0
22
+ */
23
+ /**
24
+ * Internal marker stamped on the hook so a future audit gate can confirm
25
+ * the BREACH-aware middleware is installed (Wave 9 audit-item parity).
26
+ *
27
+ * @since 0.25.0
28
+ */
29
+ export const COMPRESSION_HOOK_MARKER = Symbol.for("daloy.compression.hook");
30
+ // Lowercased MIME-prefix deny-list. Each entry is matched as a leading
31
+ // prefix of the response's resolved content-type token (no parameters).
32
+ const ALWAYS_DENY_CONTENT_TYPES = Object.freeze([
33
+ "image/",
34
+ "video/",
35
+ "audio/",
36
+ "font/",
37
+ "application/zip",
38
+ "application/gzip",
39
+ "application/x-7z",
40
+ "application/x-gzip",
41
+ "application/x-bzip",
42
+ "application/x-xz",
43
+ "application/zstd",
44
+ "application/x-zstd",
45
+ "application/wasm",
46
+ "application/pdf",
47
+ "application/epub+zip",
48
+ ]);
49
+ // Allow lossless override: `image/svg+xml` IS compressible (XML text).
50
+ const ALLOW_SUBTYPES = Object.freeze(["image/svg+xml"]);
51
+ // Cookie-name substring tokens that flag a request as security-state-bound.
52
+ const ALWAYS_AUTH_COOKIE_TOKENS = Object.freeze([
53
+ "session",
54
+ "csrf",
55
+ "xsrf",
56
+ "__host-",
57
+ "__secure-",
58
+ ]);
59
+ function parseAcceptEncoding(header) {
60
+ if (!header)
61
+ return [];
62
+ const out = [];
63
+ for (const raw of header.split(",")) {
64
+ const part = raw.trim();
65
+ if (!part)
66
+ continue;
67
+ const [tokenRaw, ...paramsRaw] = part.split(";");
68
+ const token = (tokenRaw ?? "").trim().toLowerCase();
69
+ if (!token)
70
+ continue;
71
+ let q = 1;
72
+ for (const p of paramsRaw) {
73
+ const m = p.trim().match(/^q=([0-9.]+)$/i);
74
+ if (m) {
75
+ const parsed = Number.parseFloat(m[1]);
76
+ if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 1)
77
+ q = parsed;
78
+ }
79
+ }
80
+ if (token === "br" || token === "gzip" || token === "deflate") {
81
+ out.push({ encoding: token, q });
82
+ }
83
+ else if (token === "identity") {
84
+ out.push({ encoding: "identity", q });
85
+ }
86
+ else if (token === "*") {
87
+ out.push({ encoding: "*", q });
88
+ }
89
+ }
90
+ return out;
91
+ }
92
+ function pickEncoding(accept, serverPreferred, runtimeSupported) {
93
+ if (accept.length === 0)
94
+ return null;
95
+ // Reject if `identity;q=0` AND no positive coded entries OR `*;q=0`.
96
+ // Per RFC 9110 §12.5.3, if the server cannot satisfy, it should send 406
97
+ // — we instead choose to skip compression (return null) which serves
98
+ // `identity` and lets upstream content-negotiation logic make any
99
+ // 406-vs-200 call.
100
+ // Build a map of encoding → highest q value the client expressed.
101
+ const explicit = new Map();
102
+ let starQ;
103
+ for (const a of accept) {
104
+ if (a.encoding === "*") {
105
+ starQ = a.q;
106
+ }
107
+ else {
108
+ const prev = explicit.get(a.encoding);
109
+ if (prev === undefined || a.q > prev)
110
+ explicit.set(a.encoding, a.q);
111
+ }
112
+ }
113
+ for (const enc of serverPreferred) {
114
+ if (!runtimeSupported.has(enc))
115
+ continue;
116
+ const q = explicit.get(enc) ?? starQ;
117
+ if (q !== undefined && q > 0)
118
+ return enc;
119
+ }
120
+ return null;
121
+ }
122
+ let cachedRuntimeSupport = null;
123
+ function detectRuntimeSupport() {
124
+ if (cachedRuntimeSupport)
125
+ return cachedRuntimeSupport;
126
+ const Stream = globalThis.CompressionStream;
127
+ const supported = new Set();
128
+ if (!Stream) {
129
+ cachedRuntimeSupport = supported;
130
+ return supported;
131
+ }
132
+ for (const enc of ["br", "gzip", "deflate"]) {
133
+ try {
134
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
135
+ const _ = new Stream(enc);
136
+ supported.add(enc);
137
+ }
138
+ catch {
139
+ // not supported on this runtime
140
+ }
141
+ }
142
+ cachedRuntimeSupport = supported;
143
+ return supported;
144
+ }
145
+ /**
146
+ * @internal Reset cached runtime probe. Test-only.
147
+ * @since 0.25.0
148
+ */
149
+ export function _resetCompressionRuntimeProbeForTests() {
150
+ cachedRuntimeSupport = null;
151
+ }
152
+ function isExcludedContentType(contentType, extraDeny) {
153
+ if (!contentType)
154
+ return false;
155
+ const token = contentType.split(";")[0].trim().toLowerCase();
156
+ if (!token)
157
+ return false;
158
+ for (const allow of ALLOW_SUBTYPES) {
159
+ if (token === allow)
160
+ return false;
161
+ }
162
+ for (const deny of ALWAYS_DENY_CONTENT_TYPES) {
163
+ if (token.startsWith(deny))
164
+ return true;
165
+ }
166
+ for (const deny of extraDeny) {
167
+ if (token.startsWith(deny))
168
+ return true;
169
+ }
170
+ return false;
171
+ }
172
+ function requestCarriesAuthCookie(cookieHeader, extra) {
173
+ if (!cookieHeader)
174
+ return false;
175
+ const lower = cookieHeader.toLowerCase();
176
+ for (const tok of ALWAYS_AUTH_COOKIE_TOKENS) {
177
+ if (lower.includes(tok))
178
+ return true;
179
+ }
180
+ for (const tok of extra) {
181
+ if (lower.includes(tok.toLowerCase()))
182
+ return true;
183
+ }
184
+ return false;
185
+ }
186
+ function appendVaryAcceptEncoding(headers) {
187
+ const existing = headers.get("vary");
188
+ if (!existing) {
189
+ headers.set("vary", "Accept-Encoding");
190
+ return;
191
+ }
192
+ const parts = existing
193
+ .split(",")
194
+ .map((s) => s.trim())
195
+ .filter((s) => s.length > 0);
196
+ for (const p of parts) {
197
+ if (p === "*")
198
+ return;
199
+ if (p.toLowerCase() === "accept-encoding")
200
+ return;
201
+ }
202
+ headers.set("vary", `${existing}, Accept-Encoding`);
203
+ }
204
+ function normalizeOptionTokens(values, optionName) {
205
+ if (!values)
206
+ return Object.freeze([]);
207
+ const normalized = [];
208
+ for (const value of values) {
209
+ if (typeof value !== "string") {
210
+ throw new TypeError(`compression(): \`${optionName}\` entries must be non-empty strings.`);
211
+ }
212
+ const token = value.trim().toLowerCase();
213
+ if (!token) {
214
+ throw new TypeError(`compression(): \`${optionName}\` entries must be non-empty strings.`);
215
+ }
216
+ normalized.push(token);
217
+ }
218
+ return Object.freeze(normalized);
219
+ }
220
+ async function compressBytes(bytes, encoding) {
221
+ const Stream = globalThis.CompressionStream;
222
+ const cs = new Stream(encoding);
223
+ const writer = cs.writable.getWriter();
224
+ // Intentionally do not `await writer.write(...)` separately from
225
+ // close — the stream queues the chunk synchronously and we want both
226
+ // operations to settle together for predictable backpressure on the
227
+ // tiny in-memory body we hand it.
228
+ void writer.write(bytes);
229
+ const closePromise = writer.close();
230
+ const reader = cs.readable.getReader();
231
+ const chunks = [];
232
+ let total = 0;
233
+ // eslint-disable-next-line no-constant-condition
234
+ while (true) {
235
+ const { done, value } = await reader.read();
236
+ if (done)
237
+ break;
238
+ if (value) {
239
+ chunks.push(value);
240
+ total += value.byteLength;
241
+ }
242
+ }
243
+ await closePromise;
244
+ const out = new Uint8Array(total);
245
+ let offset = 0;
246
+ for (const c of chunks) {
247
+ out.set(c, offset);
248
+ offset += c.byteLength;
249
+ }
250
+ return out;
251
+ }
252
+ /**
253
+ * BREACH-aware response compression middleware.
254
+ *
255
+ * Picks `br` > `gzip` > `deflate` based on the client's `Accept-Encoding`
256
+ * header AND the runtime's `CompressionStream` support (probed once,
257
+ * cached). Skips compression when ANY of the following are true:
258
+ *
259
+ * - request method is not `GET` / `HEAD` (compressing responses on
260
+ * state-changing requests offers no caching win and trips BREACH
261
+ * oracles more easily);
262
+ * - request carried an `Authorization` header (auth-bound bodies);
263
+ * - request carried a recognized session / CSRF / `__Host-` /
264
+ * `__Secure-` cookie (auth-bound bodies);
265
+ * - response status is not `2xx`;
266
+ * - response already declares a `Content-Encoding`;
267
+ * - response declares a `Set-Cookie` (response is mutating auth state);
268
+ * - response body byte length is below `minimumSize` (default `1024`);
269
+ * - response `Content-Type` is in the always-on already-compressed
270
+ * deny-list (image/video/audio/archives/fonts/wasm/pdf, with
271
+ * `image/svg+xml` carved back in as compressible XML).
272
+ *
273
+ * When skipping, the middleware still appends `Vary: Accept-Encoding`
274
+ * to the response so cache keys remain content-negotiation-correct.
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * import { App, compression } from "@daloyjs/core";
279
+ *
280
+ * const app = new App();
281
+ * app.use(compression());
282
+ * ```
283
+ *
284
+ * @since 0.25.0
285
+ */
286
+ export function compression(opts = {}) {
287
+ if (Object.prototype.hasOwnProperty.call(opts, "compressLevel")) {
288
+ throw new TypeError("compression(): `compressLevel` is not configurable. CompressionStream uses the runtime default (typically 6); higher levels are a CPU-DoS amplifier with single-digit-percent bytes-saved gains.");
289
+ }
290
+ const minimumSize = opts.minimumSize === undefined ? 1024 : opts.minimumSize;
291
+ if (!Number.isFinite(minimumSize) ||
292
+ !Number.isInteger(minimumSize) ||
293
+ minimumSize < 0 ||
294
+ minimumSize > 2 ** 31 - 1) {
295
+ throw new TypeError("compression(): `minimumSize` must be a finite non-negative integer.");
296
+ }
297
+ const serverPreferred = opts.encodings && opts.encodings.length > 0
298
+ ? Object.freeze([...opts.encodings])
299
+ : Object.freeze(["br", "gzip", "deflate"]);
300
+ // Validate the encodings list.
301
+ for (const enc of serverPreferred) {
302
+ if (enc !== "br" && enc !== "gzip" && enc !== "deflate") {
303
+ throw new TypeError(`compression(): unknown encoding ${JSON.stringify(enc)} — supported: br, gzip, deflate.`);
304
+ }
305
+ }
306
+ const extraDeny = normalizeOptionTokens(opts.excludeContentTypes, "excludeContentTypes");
307
+ const extraAuthCookies = normalizeOptionTokens(opts.authCookieNames, "authCookieNames");
308
+ const hooks = {
309
+ async onSend(res, ctx) {
310
+ const req = ctx?.request;
311
+ if (!req)
312
+ return undefined;
313
+ appendVaryAcceptEncoding(res.headers);
314
+ const method = req.method;
315
+ if (method !== "GET" && method !== "HEAD")
316
+ return undefined;
317
+ if (res.status < 200 || res.status >= 300)
318
+ return undefined;
319
+ if (res.headers.has("content-encoding"))
320
+ return undefined;
321
+ if (res.headers.has("set-cookie"))
322
+ return undefined;
323
+ if (req.headers.has("authorization"))
324
+ return undefined;
325
+ if (requestCarriesAuthCookie(req.headers.get("cookie"), extraAuthCookies)) {
326
+ return undefined;
327
+ }
328
+ if (isExcludedContentType(res.headers.get("content-type"), extraDeny)) {
329
+ return undefined;
330
+ }
331
+ const runtimeSupported = detectRuntimeSupport();
332
+ if (runtimeSupported.size === 0)
333
+ return undefined;
334
+ const accept = parseAcceptEncoding(req.headers.get("accept-encoding"));
335
+ const chosen = pickEncoding(accept, serverPreferred, runtimeSupported);
336
+ if (!chosen)
337
+ return undefined;
338
+ const original = new Uint8Array(await res.clone().arrayBuffer());
339
+ if (original.byteLength < minimumSize)
340
+ return undefined;
341
+ const compressed = await compressBytes(original, chosen);
342
+ if (compressed.byteLength >= original.byteLength) {
343
+ // Negative-compression-ratio guard: if the compressor produced a
344
+ // larger payload (very small / high-entropy bodies that slipped past
345
+ // the minimumSize check) skip rather than ship more bytes.
346
+ return undefined;
347
+ }
348
+ const headers = new Headers(res.headers);
349
+ headers.set("content-encoding", chosen);
350
+ headers.set("content-length", String(compressed.byteLength));
351
+ // Strong ETag computed upstream over the uncompressed body is now
352
+ // inconsistent with the wire bytes — downgrade to weak per RFC 9110.
353
+ const existingEtag = headers.get("etag");
354
+ if (existingEtag && !existingEtag.startsWith("W/")) {
355
+ headers.set("etag", `W/${existingEtag}`);
356
+ }
357
+ const responseBody = method === "HEAD" ? null : compressed;
358
+ return new Response(responseBody, {
359
+ status: res.status,
360
+ statusText: res.statusText,
361
+ headers,
362
+ });
363
+ },
364
+ };
365
+ hooks[COMPRESSION_HOOK_MARKER] = true;
366
+ return hooks;
367
+ }
368
+ //# sourceMappingURL=compression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.js","sourceRoot":"","sources":["../src/compression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA8DH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAkB,MAAM,CAAC,GAAG,CAC9D,wBAAwB,CACzB,CAAC;AAEF,uEAAuE;AACvE,wEAAwE;AACxE,MAAM,yBAAyB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACjE,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,kBAAkB;IAClB,iBAAiB;IACjB,sBAAsB;CACvB,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,cAAc,GAAsB,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AAE3E,4EAA4E;AAC5E,MAAM,yBAAyB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACjE,SAAS;IACT,MAAM;IACN,MAAM;IACN,SAAS;IACT,WAAW;CACZ,CAAC,CAAC;AAOH,SAAS,mBAAmB,CAAC,MAAqB;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACpD,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAC3C,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;gBACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC;oBAAE,CAAC,GAAG,MAAM,CAAC;YACxE,CAAC;QACH,CAAC;QACD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9D,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CACnB,MAAsB,EACtB,eAA+C,EAC/C,gBAAkD;IAElD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,qEAAqE;IACrE,yEAAyE;IACzE,qEAAqE;IACrE,kEAAkE;IAClE,mBAAmB;IACnB,kEAAkE;IAClE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,IAAI,KAAyB,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YACvB,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI;gBAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;QACrC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,IAAI,oBAAoB,GAAoC,IAAI,CAAC;AAEjE,SAAS,oBAAoB;IAC3B,IAAI,oBAAoB;QAAE,OAAO,oBAAoB,CAAC;IACtD,MAAM,MAAM,GAAI,UAEd,CAAC,iBAAiB,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IACjD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,oBAAoB,GAAG,SAAS,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAU,EAAE,CAAC;QACrD,IAAI,CAAC;YACH,6DAA6D;YAC7D,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,gCAAgC;QAClC,CAAC;IACH,CAAC;IACD,oBAAoB,GAAG,SAAS,CAAC;IACjC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qCAAqC;IACnD,oBAAoB,GAAG,IAAI,CAAC;AAC9B,CAAC;AAED,SAAS,qBAAqB,CAC5B,WAA0B,EAC1B,SAA4B;IAE5B,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;IACpC,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,yBAAyB,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1C,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,wBAAwB,CAC/B,YAA2B,EAC3B,KAAwB;IAExB,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,yBAAyB,EAAE,CAAC;QAC5C,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACvC,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;IACrD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAgB;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ;SACnB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO;QACtB,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,iBAAiB;YAAE,OAAO;IACpD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,QAAQ,mBAAmB,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAAqC,EACrC,UAAkB;IAElB,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,SAAS,CACjB,oBAAoB,UAAU,uCAAuC,CACtE,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,SAAS,CACjB,oBAAoB,UAAU,uCAAuC,CACtE,CAAC;QACJ,CAAC;QACD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,KAAiB,EACjB,QAA6B;IAE7B,MAAM,MAAM,GAAI,UAKd,CAAC,iBAAiB,CAAC;IACrB,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IACvC,iEAAiE;IACjE,qEAAqE;IACrE,oEAAoE;IACpE,kCAAkC;IAClC,KAAK,MAAM,CAAC,KAAK,CAAC,KAAmB,CAAC,CAAC;IACvC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IACvC,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,iDAAiD;IACjD,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,MAAM,YAAY,CAAC;IACnB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACnB,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE;IACvD,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CACjB,kMAAkM,CACnM,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GACf,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;IAC3D,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;QAC9B,WAAW,GAAG,CAAC;QACf,WAAW,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EACzB,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,qEAAqE,CACtE,CAAC;IACJ,CAAC;IACD,MAAM,eAAe,GACnB,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QACzC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC,CAAC;IACxD,+BAA+B;IAC/B,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACxD,MAAM,IAAI,SAAS,CACjB,mCAAmC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,kCAAkC,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,qBAAqB,CACrC,IAAI,CAAC,mBAAmB,EACxB,qBAAqB,CACtB,CAAC;IACF,MAAM,gBAAgB,GAAG,qBAAqB,CAC5C,IAAI,CAAC,eAAe,EACpB,iBAAiB,CAClB,CAAC;IAEF,MAAM,KAAK,GAAiD;QAC1D,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG;YACnB,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO,CAAC;YACzB,IAAI,CAAC,GAAG;gBAAE,OAAO,SAAS,CAAC;YAC3B,wBAAwB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;YAC1B,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM;gBAAE,OAAO,SAAS,CAAC;YAC5D,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;gBAAE,OAAO,SAAS,CAAC;YAC5D,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC1D,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;gBAAE,OAAO,SAAS,CAAC;YACpD,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;gBAAE,OAAO,SAAS,CAAC;YACvD,IACE,wBAAwB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,EACrE,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,IACE,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,SAAS,CAAC,EACjE,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,MAAM,gBAAgB,GAAG,oBAAoB,EAAE,CAAC;YAChD,IAAI,gBAAgB,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YAClD,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACvE,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YAE9B,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YACjE,IAAI,QAAQ,CAAC,UAAU,GAAG,WAAW;gBAAE,OAAO,SAAS,CAAC;YACxD,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACzD,IAAI,UAAU,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACjD,iEAAiE;gBACjE,qEAAqE;gBACrE,2DAA2D;gBAC3D,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;YAC7D,kEAAkE;YAClE,qEAAqE;YACrE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,YAAY,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,CAAC,CAAC;YAC3C,CAAC;YACD,MAAM,YAAY,GAChB,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,UAAuB,CAAC;YACtD,OAAO,IAAI,QAAQ,CAAC,YAAY,EAAE;gBAChC,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,OAAO;aACR,CAAC,CAAC;QACL,CAAC;KACF,CAAC;IACF,KAAK,CAAC,uBAAuB,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Wave 4 leftover: `defineConfig({ schema, source })`.
3
+ *
4
+ * Single boot-time helper that loads application configuration from a
5
+ * caller-chosen source (`process.env`, a file on disk, or an async secrets
6
+ * resolver), validates the merged object against a Standard-Schema
7
+ * validator, and aggregates **every** validation issue into a single
8
+ * structured error printed to stderr before the process exits.
9
+ *
10
+ * The point is to fail fast and loud: a misconfigured deployment should
11
+ * surface every missing/invalid key in one shot so operators do not have
12
+ * to redeploy four times to discover four different typos.
13
+ *
14
+ * @since 0.20.0
15
+ */
16
+ import { type StandardSchemaV1 } from "./schema.js";
17
+ /**
18
+ * Aggregated validation failure thrown by {@link defineConfig}. Holds the
19
+ * full list of `{ key, message }` issues so callers can surface them in a
20
+ * dashboard, a startup probe, or a custom error renderer.
21
+ */
22
+ export declare class ConfigValidationError extends Error {
23
+ readonly issues: ReadonlyArray<{
24
+ key: string;
25
+ message: string;
26
+ }>;
27
+ constructor(issues: ReadonlyArray<{
28
+ key: string;
29
+ message: string;
30
+ }>);
31
+ }
32
+ /**
33
+ * Source the raw object that gets handed to the schema validator. The
34
+ * built-in sources are intentionally narrow — anything more elaborate
35
+ * (Vault, Doppler, AWS Secrets Manager, ...) should arrive via the
36
+ * `"custom"` source with an async resolver.
37
+ */
38
+ export type ConfigSource = "env" | {
39
+ kind: "env";
40
+ env: Record<string, string | undefined>;
41
+ } | {
42
+ kind: "file";
43
+ path: string;
44
+ parse?: (text: string) => unknown;
45
+ } | {
46
+ kind: "object";
47
+ data: Record<string, unknown>;
48
+ } | {
49
+ kind: "custom";
50
+ resolve: () => Promise<Record<string, unknown>>;
51
+ };
52
+ /** Options accepted by {@link defineConfig}. */
53
+ export interface DefineConfigOptions<S extends StandardSchemaV1> {
54
+ /** Standard-Schema validator (Zod, Valibot, ArkType, TypeBox, ...). */
55
+ schema: S;
56
+ /** Source of the raw input. Default `"env"` (reads from `process.env`). */
57
+ source?: ConfigSource;
58
+ /**
59
+ * Optional pre-validation transform. Useful for coercing string env
60
+ * values (`"true"` / `"42"`) to typed primitives before they hit the
61
+ * schema, or for renaming `FOO_BAR` → `fooBar`. Receives the raw
62
+ * source object and must return the object handed to the validator.
63
+ */
64
+ transform?: (raw: Record<string, unknown>) => Record<string, unknown>;
65
+ /**
66
+ * Stream errors are written to. Defaults to `process.stderr`. Set to
67
+ * `false` to suppress the printed summary; the thrown
68
+ * {@link ConfigValidationError} still carries `issues`.
69
+ */
70
+ stderr?: {
71
+ write: (chunk: string) => void;
72
+ } | false;
73
+ }
74
+ /**
75
+ * Load and validate configuration at boot. Aggregates every Standard-Schema
76
+ * issue into a single {@link ConfigValidationError}, prints a human-readable
77
+ * summary to stderr, and only resolves with the typed config object when
78
+ * validation succeeded.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * import { z } from "zod";
83
+ * import { defineConfig } from "@daloyjs/core";
84
+ *
85
+ * const Config = z.object({
86
+ * PORT: z.coerce.number().int().min(1).max(65535),
87
+ * DATABASE_URL: z.string().url(),
88
+ * NODE_ENV: z.enum(["development", "production", "test"]),
89
+ * });
90
+ *
91
+ * export const config = await defineConfig({ schema: Config });
92
+ * ```
93
+ *
94
+ * @since 0.20.0
95
+ */
96
+ export declare function defineConfig<S extends StandardSchemaV1>(opts: DefineConfigOptions<S>): Promise<StandardSchemaV1.InferOutput<S>>;
97
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAY,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;gBACrD,MAAM,EAAE,aAAa,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAUpE;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,KAAK,GACL;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,CAAC;AAExE,gDAAgD;AAChD,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,gBAAgB;IAC7D,uEAAuE;IACvE,MAAM,EAAE,CAAC,CAAC;IACV,2EAA2E;IAC3E,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GAAG,KAAK,CAAC;CACrD;AA8CD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,gBAAgB,EAC3D,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAkC1C"}
package/dist/config.js ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Wave 4 leftover: `defineConfig({ schema, source })`.
3
+ *
4
+ * Single boot-time helper that loads application configuration from a
5
+ * caller-chosen source (`process.env`, a file on disk, or an async secrets
6
+ * resolver), validates the merged object against a Standard-Schema
7
+ * validator, and aggregates **every** validation issue into a single
8
+ * structured error printed to stderr before the process exits.
9
+ *
10
+ * The point is to fail fast and loud: a misconfigured deployment should
11
+ * surface every missing/invalid key in one shot so operators do not have
12
+ * to redeploy four times to discover four different typos.
13
+ *
14
+ * @since 0.20.0
15
+ */
16
+ import { validate } from "./schema.js";
17
+ /**
18
+ * Aggregated validation failure thrown by {@link defineConfig}. Holds the
19
+ * full list of `{ key, message }` issues so callers can surface them in a
20
+ * dashboard, a startup probe, or a custom error renderer.
21
+ */
22
+ export class ConfigValidationError extends Error {
23
+ issues;
24
+ constructor(issues) {
25
+ const summary = issues
26
+ .map((i) => ` - ${i.key || "<root>"}: ${i.message}`)
27
+ .join("\n");
28
+ super(`defineConfig(): configuration is invalid (${issues.length} issue${issues.length === 1 ? "" : "s"})\n${summary}`);
29
+ this.name = "ConfigValidationError";
30
+ this.issues = issues;
31
+ }
32
+ }
33
+ async function readSource(source) {
34
+ if (source === "env") {
35
+ return readEnvObject((typeof process !== "undefined" ? process.env : {}));
36
+ }
37
+ if (source.kind === "env")
38
+ return readEnvObject(source.env);
39
+ if (source.kind === "object")
40
+ return { ...source.data };
41
+ if (source.kind === "file") {
42
+ const fs = await import("node:fs/promises");
43
+ const text = await fs.readFile(source.path, "utf8");
44
+ const parsed = (source.parse ?? JSON.parse)(text);
45
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
46
+ throw new ConfigValidationError([
47
+ { key: source.path, message: "file did not parse to an object" },
48
+ ]);
49
+ }
50
+ return { ...parsed };
51
+ }
52
+ return { ...(await source.resolve()) };
53
+ }
54
+ function readEnvObject(env) {
55
+ const out = {};
56
+ for (const [key, value] of Object.entries(env)) {
57
+ if (value !== undefined)
58
+ out[key] = value;
59
+ }
60
+ return out;
61
+ }
62
+ function pathToKey(path) {
63
+ return path
64
+ .map((segment) => {
65
+ if (typeof segment === "object" && segment !== null && "key" in segment) {
66
+ return String(segment.key);
67
+ }
68
+ return String(segment);
69
+ })
70
+ .join(".");
71
+ }
72
+ /**
73
+ * Load and validate configuration at boot. Aggregates every Standard-Schema
74
+ * issue into a single {@link ConfigValidationError}, prints a human-readable
75
+ * summary to stderr, and only resolves with the typed config object when
76
+ * validation succeeded.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { z } from "zod";
81
+ * import { defineConfig } from "@daloyjs/core";
82
+ *
83
+ * const Config = z.object({
84
+ * PORT: z.coerce.number().int().min(1).max(65535),
85
+ * DATABASE_URL: z.string().url(),
86
+ * NODE_ENV: z.enum(["development", "production", "test"]),
87
+ * });
88
+ *
89
+ * export const config = await defineConfig({ schema: Config });
90
+ * ```
91
+ *
92
+ * @since 0.20.0
93
+ */
94
+ export async function defineConfig(opts) {
95
+ const stderr = opts.stderr === false
96
+ ? null
97
+ : (opts.stderr ?? (typeof process !== "undefined" ? process.stderr : null));
98
+ let raw;
99
+ try {
100
+ raw = await readSource(opts.source ?? "env");
101
+ }
102
+ catch (err) {
103
+ if (err instanceof ConfigValidationError) {
104
+ if (stderr)
105
+ stderr.write(`${err.message}\n`);
106
+ throw err;
107
+ }
108
+ const wrapped = new ConfigValidationError([
109
+ {
110
+ key: "<source>",
111
+ message: err instanceof Error ? err.message : "failed to read source",
112
+ },
113
+ ]);
114
+ if (stderr)
115
+ stderr.write(`${wrapped.message}\n`);
116
+ throw wrapped;
117
+ }
118
+ const input = opts.transform ? opts.transform(raw) : raw;
119
+ const result = await validate(opts.schema, input);
120
+ if (result.issues) {
121
+ const issues = result.issues.map((issue) => ({
122
+ key: pathToKey(issue.path ?? []),
123
+ message: issue.message,
124
+ }));
125
+ const err = new ConfigValidationError(issues);
126
+ if (stderr)
127
+ stderr.write(`${err.message}\n`);
128
+ throw err;
129
+ }
130
+ return result.value;
131
+ }
132
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}