@sleepy-hollow/framework 0.3.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.
- package/CHANGELOG.md +28 -0
- package/LICENSE +373 -0
- package/README.md +95 -0
- package/dist/chunk-53TZY5YP.js +470 -0
- package/dist/chunk-53TZY5YP.js.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-BAKXP7IR.js +85 -0
- package/dist/chunk-BAKXP7IR.js.map +1 -0
- package/dist/chunk-BJONRVDG.js +429 -0
- package/dist/chunk-BJONRVDG.js.map +1 -0
- package/dist/chunk-CAPFDC25.js +598 -0
- package/dist/chunk-CAPFDC25.js.map +1 -0
- package/dist/chunk-D4U3ZY4O.js +4585 -0
- package/dist/chunk-D4U3ZY4O.js.map +1 -0
- package/dist/chunk-DGTHFZPZ.js +830 -0
- package/dist/chunk-DGTHFZPZ.js.map +1 -0
- package/dist/chunk-LNJDFJGT.js +47 -0
- package/dist/chunk-LNJDFJGT.js.map +1 -0
- package/dist/cli.d.ts +427 -0
- package/dist/cli.js +5910 -0
- package/dist/cli.js.map +1 -0
- package/dist/database.d.ts +25 -0
- package/dist/database.js +16 -0
- package/dist/database.js.map +1 -0
- package/dist/dist-DUSC2237.js +546 -0
- package/dist/dist-DUSC2237.js.map +1 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-string.es-GTFBNHZR.js +1309 -0
- package/dist/magic-string.es-GTFBNHZR.js.map +1 -0
- package/dist/routing.d.ts +89 -0
- package/dist/routing.js +17 -0
- package/dist/routing.js.map +1 -0
- package/dist/security.d.ts +319 -0
- package/dist/security.js +21 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +8 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.d.ts +157 -0
- package/dist/testing.js +29 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-BC7LJJ6G.d.ts +131 -0
- package/dist/types-BUXw3UwN.d.ts +54 -0
- package/dist/types-Bet36nZS.d.ts +390 -0
- package/dist/types-DmzdxsaA.d.ts +113 -0
- package/dist/validation.d.ts +57 -0
- package/dist/validation.js +20 -0
- package/dist/validation.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../core/database/errors.ts","../core/database/postgres.ts","../core/database/resource.ts","../core/database/sqlite.ts","../core/database/mod.ts"],"sourcesContent":["export class DatabaseConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DatabaseConfigurationError\";\n }\n}\n","import { drizzle } from \"drizzle-orm/node-postgres\";\nimport { Pool } from \"pg\";\nimport { DatabaseConfigurationError } from \"./errors.ts\";\nimport type { PostgresDatabase, PostgresOptions } from \"./types.ts\";\n\nconst DEFAULT_MAX_CONNECTIONS = 10;\n\n/** Opens the optional externally managed PostgreSQL profile. */\nexport function openPostgres(options: PostgresOptions): PostgresDatabase {\n const databaseUrl = options.databaseUrl.trim();\n if (!/^postgres(?:ql)?:\\/\\//i.test(databaseUrl)) {\n throw new DatabaseConfigurationError(\"PostgreSQL requires a postgresql:// DATABASE_URL.\");\n }\n const pool = new Pool({\n connectionString: databaseUrl,\n max: options.maxConnections ?? DEFAULT_MAX_CONNECTIONS,\n ssl: options.tls === false ? undefined : { rejectUnauthorized: true }\n });\n return {\n profile: \"postgres\",\n pool,\n orm: drizzle(pool),\n close: () => pool.end()\n };\n}\n","import { DatabaseConfigurationError } from \"./errors.ts\";\nimport type { ResourceDefinition } from \"./types.ts\";\n\nconst identifier = /^[a-z][a-z0-9_]*$/;\n\n/** Defines the portable relational subset accepted by framework repositories. */\nexport function defineResource(definition: ResourceDefinition): ResourceDefinition {\n if (!identifier.test(definition.name)) {\n throw new DatabaseConfigurationError(\"Resource names must be lowercase SQL identifiers.\");\n }\n if (!Object.hasOwn(definition.fields, definition.primaryKey)) {\n throw new DatabaseConfigurationError(\"The resource primary key must name a declared field.\");\n }\n for (const field of Object.keys(definition.fields)) {\n if (!identifier.test(field)) {\n throw new DatabaseConfigurationError(\"Resource field names must be lowercase SQL identifiers.\");\n }\n }\n return Object.freeze({\n ...definition,\n fields: Object.freeze({ ...definition.fields })\n });\n}\n","import BetterSqlite3 from \"better-sqlite3\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { DatabaseConfigurationError } from \"./errors.ts\";\nimport type { EmbeddedSqliteDatabase, EmbeddedSqliteOptions } from \"./types.ts\";\n\nconst DEFAULT_BUSY_TIMEOUT_MS = 5_000;\n\n/**\n * Opens the self-contained database profile. The caller chooses the path so\n * production storage can be mounted explicitly by the selected host.\n */\nexport function openEmbeddedSqlite(options: EmbeddedSqliteOptions): EmbeddedSqliteDatabase {\n const filename = options.filename.trim();\n if (filename.length === 0) {\n throw new DatabaseConfigurationError(\"Embedded SQLite requires an explicit database filename.\");\n }\n if (options.production && filename === \":memory:\") {\n throw new DatabaseConfigurationError(\"Production SQLite requires a durable database path, not :memory:.\");\n }\n\n const client = new BetterSqlite3(filename);\n client.pragma(\"foreign_keys = ON\");\n client.pragma(`busy_timeout = ${options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS}`);\n if (filename !== \":memory:\") client.pragma(\"journal_mode = WAL\");\n\n return {\n profile: \"sqlite\",\n client,\n orm: drizzle(client),\n close: () => client.close()\n };\n}\n","/**\n * Relational storage for Sleepy Hollow applications. SQLite is embedded by\n * default; PostgreSQL is an explicit external profile for scaled deployments.\n */\nexport { DatabaseConfigurationError } from \"./errors.ts\";\nexport { openPostgres } from \"./postgres.ts\";\nexport { defineResource } from \"./resource.ts\";\nexport { openEmbeddedSqlite } from \"./sqlite.ts\";\nexport { sql } from \"drizzle-orm\";\nexport type {\n DatabaseProfile,\n EmbeddedSqliteDatabase,\n EmbeddedSqliteOptions,\n PostgresDatabase,\n PostgresOptions,\n ResourceDefinition,\n ResourceField,\n ResourceRepository\n} from \"./types.ts\";\n"],"mappings":";AAAO,IAAM,6BAAN,cAAyC,MAAM;AAAA,EACpD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACLA,SAAS,eAAe;AACxB,SAAS,YAAY;AAIrB,IAAM,0BAA0B;AAGzB,SAAS,aAAa,SAA4C;AACvE,QAAM,cAAc,QAAQ,YAAY,KAAK;AAC7C,MAAI,CAAC,yBAAyB,KAAK,WAAW,GAAG;AAC/C,UAAM,IAAI,2BAA2B,mDAAmD;AAAA,EAC1F;AACA,QAAM,OAAO,IAAI,KAAK;AAAA,IACpB,kBAAkB;AAAA,IAClB,KAAK,QAAQ,kBAAkB;AAAA,IAC/B,KAAK,QAAQ,QAAQ,QAAQ,SAAY,EAAE,oBAAoB,KAAK;AAAA,EACtE,CAAC;AACD,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,KAAK,QAAQ,IAAI;AAAA,IACjB,OAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AACF;;;ACrBA,IAAM,aAAa;AAGZ,SAAS,eAAe,YAAoD;AACjF,MAAI,CAAC,WAAW,KAAK,WAAW,IAAI,GAAG;AACrC,UAAM,IAAI,2BAA2B,mDAAmD;AAAA,EAC1F;AACA,MAAI,CAAC,OAAO,OAAO,WAAW,QAAQ,WAAW,UAAU,GAAG;AAC5D,UAAM,IAAI,2BAA2B,sDAAsD;AAAA,EAC7F;AACA,aAAW,SAAS,OAAO,KAAK,WAAW,MAAM,GAAG;AAClD,QAAI,CAAC,WAAW,KAAK,KAAK,GAAG;AAC3B,YAAM,IAAI,2BAA2B,yDAAyD;AAAA,IAChG;AAAA,EACF;AACA,SAAO,OAAO,OAAO;AAAA,IACnB,GAAG;AAAA,IACH,QAAQ,OAAO,OAAO,EAAE,GAAG,WAAW,OAAO,CAAC;AAAA,EAChD,CAAC;AACH;;;ACtBA,OAAO,mBAAmB;AAC1B,SAAS,WAAAA,gBAAe;AAIxB,IAAM,0BAA0B;AAMzB,SAAS,mBAAmB,SAAwD;AACzF,QAAM,WAAW,QAAQ,SAAS,KAAK;AACvC,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI,2BAA2B,yDAAyD;AAAA,EAChG;AACA,MAAI,QAAQ,cAAc,aAAa,YAAY;AACjD,UAAM,IAAI,2BAA2B,mEAAmE;AAAA,EAC1G;AAEA,QAAM,SAAS,IAAI,cAAc,QAAQ;AACzC,SAAO,OAAO,mBAAmB;AACjC,SAAO,OAAO,kBAAkB,QAAQ,iBAAiB,uBAAuB,EAAE;AAClF,MAAI,aAAa,WAAY,QAAO,OAAO,oBAAoB;AAE/D,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,KAAKC,SAAQ,MAAM;AAAA,IACnB,OAAO,MAAM,OAAO,MAAM;AAAA,EAC5B;AACF;;;ACvBA,SAAS,WAAW;","names":["drizzle","drizzle"]}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import {
|
|
2
|
+
redactSecurityData
|
|
3
|
+
} from "./chunk-DGTHFZPZ.js";
|
|
4
|
+
import {
|
|
5
|
+
z
|
|
6
|
+
} from "./chunk-CAPFDC25.js";
|
|
7
|
+
import {
|
|
8
|
+
platform
|
|
9
|
+
} from "./chunk-53TZY5YP.js";
|
|
10
|
+
|
|
11
|
+
// core/config/types.ts
|
|
12
|
+
var RUNTIME_MODES = [
|
|
13
|
+
"development",
|
|
14
|
+
"test",
|
|
15
|
+
"preview",
|
|
16
|
+
"production"
|
|
17
|
+
];
|
|
18
|
+
var ConfigurationError = class extends Error {
|
|
19
|
+
/**
|
|
20
|
+
* Builds an error whose message lists every diagnostic, one per line.
|
|
21
|
+
*
|
|
22
|
+
* @param diagnostics Every fault found, in the order detected.
|
|
23
|
+
*/
|
|
24
|
+
constructor(diagnostics) {
|
|
25
|
+
super(
|
|
26
|
+
diagnostics.map(
|
|
27
|
+
(diagnostic3) => `${diagnostic3.code}: ${diagnostic3.key ?? diagnostic3.expected}`
|
|
28
|
+
).join("\n")
|
|
29
|
+
);
|
|
30
|
+
this.diagnostics = diagnostics;
|
|
31
|
+
this.name = "ConfigurationError";
|
|
32
|
+
}
|
|
33
|
+
diagnostics;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// core/config/configuration.ts
|
|
37
|
+
import { z as z2 } from "zod";
|
|
38
|
+
function diagnostic(code, expected, correction, mode, key) {
|
|
39
|
+
return {
|
|
40
|
+
code,
|
|
41
|
+
severity: "error",
|
|
42
|
+
...mode ? { mode } : {},
|
|
43
|
+
...key ? { key } : {},
|
|
44
|
+
expected,
|
|
45
|
+
correction
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function schemaKeys(schema) {
|
|
49
|
+
return Object.keys(schema.shape).sort();
|
|
50
|
+
}
|
|
51
|
+
function strictObject(schema) {
|
|
52
|
+
if (!(schema instanceof z2.ZodObject)) return false;
|
|
53
|
+
try {
|
|
54
|
+
const contract = z2.toJSONSchema(schema, { io: "input" });
|
|
55
|
+
return contract.type === "object" && contract.additionalProperties === false;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function defineConfiguration(definition) {
|
|
61
|
+
const diagnostics = [];
|
|
62
|
+
const modes = definition?.modes;
|
|
63
|
+
for (const mode of RUNTIME_MODES) {
|
|
64
|
+
if (!modes || !strictObject(modes[mode])) {
|
|
65
|
+
diagnostics.push(diagnostic(
|
|
66
|
+
"SH_CONFIG_SCHEMA_INVALID",
|
|
67
|
+
"a strict Zod 4 object schema",
|
|
68
|
+
`Declare a strict configuration schema for ${mode}.`,
|
|
69
|
+
mode
|
|
70
|
+
));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
74
|
+
if (modes) {
|
|
75
|
+
for (const mode of RUNTIME_MODES) {
|
|
76
|
+
const schema = modes[mode];
|
|
77
|
+
if (schema instanceof z2.ZodObject) {
|
|
78
|
+
for (const key of schemaKeys(schema)) allKeys.add(key);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const key of definition?.sensitiveKeys ?? []) {
|
|
83
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key) || !allKeys.has(key)) {
|
|
84
|
+
diagnostics.push(diagnostic(
|
|
85
|
+
"SH_CONFIG_SENSITIVE_KEY_INVALID",
|
|
86
|
+
"a declared configuration key",
|
|
87
|
+
"List only declared configuration keys in sensitiveKeys.",
|
|
88
|
+
void 0,
|
|
89
|
+
key
|
|
90
|
+
));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const files = definition?.localEnvFiles;
|
|
94
|
+
for (const [mode, path] of Object.entries(files ?? {})) {
|
|
95
|
+
if (!["development", "test"].includes(mode)) {
|
|
96
|
+
diagnostics.push(diagnostic(
|
|
97
|
+
"SH_CONFIG_ENV_FILE_MODE_INVALID",
|
|
98
|
+
"a development or test local environment file",
|
|
99
|
+
"Remove local environment files from preview and production.",
|
|
100
|
+
RUNTIME_MODES.includes(mode) ? mode : void 0
|
|
101
|
+
));
|
|
102
|
+
} else if (typeof path !== "string" || !path.trim()) {
|
|
103
|
+
diagnostics.push(diagnostic(
|
|
104
|
+
"SH_CONFIG_ENV_FILE_INVALID",
|
|
105
|
+
"a non-empty explicit local file path",
|
|
106
|
+
"Declare a concrete local environment-file path.",
|
|
107
|
+
mode
|
|
108
|
+
));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (diagnostics.length > 0) throw new ConfigurationError(diagnostics);
|
|
112
|
+
return Object.freeze({
|
|
113
|
+
...definition,
|
|
114
|
+
sensitiveKeys: Object.freeze([...definition.sensitiveKeys ?? []]),
|
|
115
|
+
localEnvFiles: Object.freeze({ ...definition.localEnvFiles ?? {} })
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function envFileError(code, mode, key) {
|
|
119
|
+
return new ConfigurationError([diagnostic(
|
|
120
|
+
code,
|
|
121
|
+
key ? "one unique KEY=VALUE declaration" : "a readable valid environment file",
|
|
122
|
+
key ? "Remove duplicate or malformed declarations from the local environment file." : "Provide the declared local environment file or remove its configuration.",
|
|
123
|
+
mode,
|
|
124
|
+
key
|
|
125
|
+
)]);
|
|
126
|
+
}
|
|
127
|
+
function parseEnvFile(text, mode) {
|
|
128
|
+
const values = {};
|
|
129
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
130
|
+
const line = rawLine.trim();
|
|
131
|
+
if (!line || line.startsWith("#")) continue;
|
|
132
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
133
|
+
if (!match) throw envFileError("SH_ENV_FILE_INVALID", mode);
|
|
134
|
+
const key = match[1];
|
|
135
|
+
if (Object.hasOwn(values, key)) {
|
|
136
|
+
throw envFileError("SH_ENV_FILE_DUPLICATE", mode, key);
|
|
137
|
+
}
|
|
138
|
+
let value = match[2].trim();
|
|
139
|
+
if (value.startsWith("'") || value.startsWith('"')) {
|
|
140
|
+
const quote = value[0];
|
|
141
|
+
if (value.length < 2 || value.at(-1) !== quote) {
|
|
142
|
+
throw envFileError("SH_ENV_FILE_INVALID", mode, key);
|
|
143
|
+
}
|
|
144
|
+
value = value.slice(1, -1);
|
|
145
|
+
}
|
|
146
|
+
values[key] = value;
|
|
147
|
+
}
|
|
148
|
+
return values;
|
|
149
|
+
}
|
|
150
|
+
async function resolveConfiguration(definition, options) {
|
|
151
|
+
if (!RUNTIME_MODES.includes(options.mode)) {
|
|
152
|
+
throw new ConfigurationError([diagnostic(
|
|
153
|
+
"SH_CONFIG_MODE_INVALID",
|
|
154
|
+
"development, test, preview, or production",
|
|
155
|
+
"Pass one explicit supported runtime mode."
|
|
156
|
+
)]);
|
|
157
|
+
}
|
|
158
|
+
const schema = definition.modes[options.mode];
|
|
159
|
+
const keys = schemaKeys(schema);
|
|
160
|
+
let fileValues = {};
|
|
161
|
+
const filePath = definition.localEnvFiles?.[options.mode];
|
|
162
|
+
if (filePath) {
|
|
163
|
+
const reader = options.readTextFile ?? ((path) => platform.readTextFile(path));
|
|
164
|
+
try {
|
|
165
|
+
fileValues = parseEnvFile(await reader(filePath), options.mode);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof ConfigurationError) throw error;
|
|
168
|
+
throw envFileError("SH_ENV_FILE_READ_FAILED", options.mode);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const environment = options.environment ?? platform.env.toObject();
|
|
172
|
+
const input = {};
|
|
173
|
+
for (const key of keys) {
|
|
174
|
+
if (fileValues[key] !== void 0) input[key] = fileValues[key];
|
|
175
|
+
if (environment[key] !== void 0) input[key] = environment[key];
|
|
176
|
+
}
|
|
177
|
+
const parsed = await schema.safeParseAsync(input);
|
|
178
|
+
if (!parsed.success) {
|
|
179
|
+
const diagnostics = parsed.error.issues.map((issue) => {
|
|
180
|
+
const key = typeof issue.path[0] === "string" ? issue.path[0] : void 0;
|
|
181
|
+
return diagnostic(
|
|
182
|
+
"SH_CONFIG_VALUE_INVALID",
|
|
183
|
+
`a value accepted by the ${key ?? "selected mode"} schema`,
|
|
184
|
+
"Provide the required configuration key in its declared safe form.",
|
|
185
|
+
options.mode,
|
|
186
|
+
key
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
throw new ConfigurationError(diagnostics);
|
|
190
|
+
}
|
|
191
|
+
const output = parsed.data;
|
|
192
|
+
const sensitive = new Set(definition.sensitiveKeys ?? []);
|
|
193
|
+
const metadata = Object.freeze({
|
|
194
|
+
mode: options.mode,
|
|
195
|
+
keys: Object.freeze(keys.map((name) => {
|
|
196
|
+
const source = environment[name] !== void 0 ? "environment" : fileValues[name] !== void 0 ? "env-file" : Object.hasOwn(output, name) ? "default" : "absent";
|
|
197
|
+
return Object.freeze({
|
|
198
|
+
name,
|
|
199
|
+
source,
|
|
200
|
+
present: Object.hasOwn(output, name),
|
|
201
|
+
sensitive: sensitive.has(name)
|
|
202
|
+
});
|
|
203
|
+
}))
|
|
204
|
+
});
|
|
205
|
+
return Object.freeze({
|
|
206
|
+
mode: options.mode,
|
|
207
|
+
values: Object.freeze(output),
|
|
208
|
+
metadata
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// core/config/logger.ts
|
|
213
|
+
var EVENT = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
214
|
+
var REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
215
|
+
var RESERVED = /* @__PURE__ */ new Set(["level", "timestamp", "event", "mode", "requestId"]);
|
|
216
|
+
function customRedaction(value, sensitive) {
|
|
217
|
+
const active = /* @__PURE__ */ new WeakSet();
|
|
218
|
+
function visit(current) {
|
|
219
|
+
if (current === null || typeof current !== "object") return current;
|
|
220
|
+
if (current instanceof Request || current instanceof Headers || current instanceof Response || current instanceof Error || current instanceof Date) return current;
|
|
221
|
+
if (active.has(current)) return "[Circular]";
|
|
222
|
+
active.add(current);
|
|
223
|
+
const result = Array.isArray(current) ? current.map(visit) : Object.fromEntries(
|
|
224
|
+
Object.entries(current).map(([key, item]) => [
|
|
225
|
+
key,
|
|
226
|
+
sensitive.has(key) ? "[REDACTED]" : visit(item)
|
|
227
|
+
])
|
|
228
|
+
);
|
|
229
|
+
active.delete(current);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
return redactSecurityData(visit(value));
|
|
233
|
+
}
|
|
234
|
+
function safeContext(value, sensitive) {
|
|
235
|
+
const redacted = customRedaction(value, sensitive);
|
|
236
|
+
if (!redacted || typeof redacted !== "object" || Array.isArray(redacted)) {
|
|
237
|
+
return value === void 0 ? {} : { context: redacted };
|
|
238
|
+
}
|
|
239
|
+
return Object.fromEntries(
|
|
240
|
+
Object.entries(redacted).filter(([key]) => !RESERVED.has(key))
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
function createJsonLogger(options) {
|
|
244
|
+
if (!RUNTIME_MODES.includes(options.mode)) {
|
|
245
|
+
throw new TypeError("Logger mode must be explicit");
|
|
246
|
+
}
|
|
247
|
+
if (typeof options.sink !== "function") {
|
|
248
|
+
throw new TypeError("Logger sink is required");
|
|
249
|
+
}
|
|
250
|
+
const sensitive = new Set(options.sensitiveFields ?? []);
|
|
251
|
+
if ([...sensitive].some((field) => !field)) {
|
|
252
|
+
throw new TypeError("Sensitive field names must be non-empty");
|
|
253
|
+
}
|
|
254
|
+
const clock = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
255
|
+
function build(requestId) {
|
|
256
|
+
function write(level, event, context) {
|
|
257
|
+
if (!EVENT.test(event)) throw new TypeError("Log event name is invalid");
|
|
258
|
+
const instant = clock();
|
|
259
|
+
if (!(instant instanceof Date) || Number.isNaN(instant.getTime())) {
|
|
260
|
+
throw new TypeError("Logger clock returned an invalid date");
|
|
261
|
+
}
|
|
262
|
+
options.sink(JSON.stringify({
|
|
263
|
+
...safeContext(context, sensitive),
|
|
264
|
+
level,
|
|
265
|
+
timestamp: instant.toISOString(),
|
|
266
|
+
event,
|
|
267
|
+
mode: options.mode,
|
|
268
|
+
...requestId ? { requestId } : {}
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
return Object.freeze({
|
|
272
|
+
debug: (event, context) => write("debug", event, context),
|
|
273
|
+
info: (event, context) => write("info", event, context),
|
|
274
|
+
warn: (event, context) => write("warn", event, context),
|
|
275
|
+
error: (event, context) => write("error", event, context),
|
|
276
|
+
withRequest(nextRequestId) {
|
|
277
|
+
if (!REQUEST_ID.test(nextRequestId)) {
|
|
278
|
+
throw new TypeError("Request ID is invalid");
|
|
279
|
+
}
|
|
280
|
+
return build(nextRequestId);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return build();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// core/config/operational.ts
|
|
288
|
+
var SAFE_NAME = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
289
|
+
function diagnostic2(code, expected, correction, key) {
|
|
290
|
+
return {
|
|
291
|
+
code,
|
|
292
|
+
severity: "error",
|
|
293
|
+
...key ? { key } : {},
|
|
294
|
+
expected,
|
|
295
|
+
correction
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function validPath(path) {
|
|
299
|
+
return typeof path === "string" && path.startsWith("/") && path !== "/" && !path.endsWith("/") && path.split("/").slice(1).every(
|
|
300
|
+
(segment) => /^[A-Za-z0-9._~-]+$/.test(segment)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function operationRoute(path, source, responses, handler) {
|
|
304
|
+
return {
|
|
305
|
+
method: "GET",
|
|
306
|
+
path,
|
|
307
|
+
source,
|
|
308
|
+
parameterNames: [],
|
|
309
|
+
operation: {
|
|
310
|
+
schemas: { responses },
|
|
311
|
+
security: { authentication: { mode: "none" } },
|
|
312
|
+
contract: { summary: source },
|
|
313
|
+
handler
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
async function runCheck(check) {
|
|
318
|
+
const controller = new AbortController();
|
|
319
|
+
let timer;
|
|
320
|
+
const timeout = new Promise((resolve) => {
|
|
321
|
+
timer = setTimeout(() => {
|
|
322
|
+
controller.abort("Readiness check timed out");
|
|
323
|
+
resolve(false);
|
|
324
|
+
}, check.timeoutMs);
|
|
325
|
+
});
|
|
326
|
+
const execution = Promise.resolve().then(() => check.check(controller.signal)).then((ready) => ready === true).catch(() => false);
|
|
327
|
+
try {
|
|
328
|
+
return await Promise.race([execution, timeout]);
|
|
329
|
+
} finally {
|
|
330
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function createOperationalRoutes(options) {
|
|
334
|
+
const diagnostics = [];
|
|
335
|
+
if (!validPath(options.healthPath)) {
|
|
336
|
+
diagnostics.push(diagnostic2(
|
|
337
|
+
"SH_OPERATIONAL_PATH_INVALID",
|
|
338
|
+
"an explicit absolute static health path",
|
|
339
|
+
"Use a path such as /_health.",
|
|
340
|
+
"healthPath"
|
|
341
|
+
));
|
|
342
|
+
}
|
|
343
|
+
const checks = [...options.readiness ?? []];
|
|
344
|
+
if (checks.length > 0 && !validPath(options.readinessPath)) {
|
|
345
|
+
diagnostics.push(diagnostic2(
|
|
346
|
+
"SH_OPERATIONAL_PATH_INVALID",
|
|
347
|
+
"an explicit absolute static readiness path",
|
|
348
|
+
"Declare readinessPath when readiness checks exist.",
|
|
349
|
+
"readinessPath"
|
|
350
|
+
));
|
|
351
|
+
}
|
|
352
|
+
if (checks.length > 0 && options.readinessPath === options.healthPath) {
|
|
353
|
+
diagnostics.push(diagnostic2(
|
|
354
|
+
"SH_OPERATIONAL_PATH_DUPLICATE",
|
|
355
|
+
"distinct health and readiness paths",
|
|
356
|
+
"Choose a different readiness path.",
|
|
357
|
+
"readinessPath"
|
|
358
|
+
));
|
|
359
|
+
}
|
|
360
|
+
const names = /* @__PURE__ */ new Set();
|
|
361
|
+
for (const check of checks) {
|
|
362
|
+
if (!SAFE_NAME.test(check.name) || names.has(check.name) || !Number.isSafeInteger(check.timeoutMs) || check.timeoutMs <= 0 || typeof check.check !== "function") {
|
|
363
|
+
diagnostics.push(diagnostic2(
|
|
364
|
+
names.has(check.name) ? "SH_READINESS_CHECK_DUPLICATE" : "SH_READINESS_CHECK_INVALID",
|
|
365
|
+
"a unique safe name, positive timeout, and async check",
|
|
366
|
+
"Repair the readiness declaration before startup.",
|
|
367
|
+
check.name
|
|
368
|
+
));
|
|
369
|
+
}
|
|
370
|
+
names.add(check.name);
|
|
371
|
+
}
|
|
372
|
+
if (diagnostics.length > 0) throw new ConfigurationError(diagnostics);
|
|
373
|
+
const healthSchema = z.strictObject({
|
|
374
|
+
status: z.enum(["healthy", "unhealthy"])
|
|
375
|
+
});
|
|
376
|
+
const routes = [operationRoute(
|
|
377
|
+
options.healthPath,
|
|
378
|
+
"sleepyhollow:operational/health",
|
|
379
|
+
{ 200: healthSchema, 503: healthSchema },
|
|
380
|
+
() => {
|
|
381
|
+
let healthy = false;
|
|
382
|
+
try {
|
|
383
|
+
healthy = options.isHealthy?.() ?? true;
|
|
384
|
+
} catch {
|
|
385
|
+
healthy = false;
|
|
386
|
+
}
|
|
387
|
+
return Response.json(
|
|
388
|
+
{ status: healthy ? "healthy" : "unhealthy" },
|
|
389
|
+
{ status: healthy ? 200 : 503 }
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
)];
|
|
393
|
+
if (checks.length > 0) {
|
|
394
|
+
const readinessSchema = z.strictObject({
|
|
395
|
+
status: z.enum(["ready", "not-ready"]),
|
|
396
|
+
checks: z.array(z.strictObject({ name: z.string(), ready: z.boolean() }))
|
|
397
|
+
});
|
|
398
|
+
const sorted = checks.sort(
|
|
399
|
+
(left, right) => left.name.localeCompare(right.name)
|
|
400
|
+
);
|
|
401
|
+
routes.push(operationRoute(
|
|
402
|
+
options.readinessPath,
|
|
403
|
+
"sleepyhollow:operational/readiness",
|
|
404
|
+
{ 200: readinessSchema, 503: readinessSchema },
|
|
405
|
+
async () => {
|
|
406
|
+
const states = await Promise.all(sorted.map(async (check) => ({
|
|
407
|
+
name: check.name,
|
|
408
|
+
ready: await runCheck(check)
|
|
409
|
+
})));
|
|
410
|
+
const ready = states.every((state) => state.ready);
|
|
411
|
+
return Response.json({
|
|
412
|
+
status: ready ? "ready" : "not-ready",
|
|
413
|
+
checks: states
|
|
414
|
+
}, { status: ready ? 200 : 503 });
|
|
415
|
+
}
|
|
416
|
+
));
|
|
417
|
+
}
|
|
418
|
+
return Object.freeze(routes);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export {
|
|
422
|
+
RUNTIME_MODES,
|
|
423
|
+
ConfigurationError,
|
|
424
|
+
defineConfiguration,
|
|
425
|
+
resolveConfiguration,
|
|
426
|
+
createJsonLogger,
|
|
427
|
+
createOperationalRoutes
|
|
428
|
+
};
|
|
429
|
+
//# sourceMappingURL=chunk-BJONRVDG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../core/config/types.ts","../core/config/configuration.ts","../core/config/logger.ts","../core/config/operational.ts"],"sourcesContent":["import type { z } from \"zod\";\n\n/** The runtime modes a project may be configured for. */\nexport const RUNTIME_MODES = [\n \"development\",\n \"test\",\n \"preview\",\n \"production\",\n] as const;\n\n/** One of the {@linkcode RUNTIME_MODES} a process may run in. */\nexport type RuntimeMode = (typeof RUNTIME_MODES)[number];\n\n/** One schema per runtime mode: what configuration that mode requires. */\nexport type ModeSchemas = Readonly<Record<RuntimeMode, z.ZodObject>>;\n\n/**\n * A project's configuration contract.\n *\n * Each mode carries its own schema, so production can demand values that\n * development supplies a default for, and the difference is stated rather than\n * discovered on deployment.\n */\nexport interface ConfigurationDefinition<Schemas extends ModeSchemas> {\n /** The schema each runtime mode must satisfy. */\n readonly modes: Schemas;\n /** Keys whose values are redacted from metadata and logs. */\n readonly sensitiveKeys?: readonly string[];\n /** Env files read in the named modes; never consulted in production. */\n readonly localEnvFiles?: Readonly<\n Partial<Record<\"development\" | \"test\", string>>\n >;\n}\n\n/** One reason configuration could not be resolved. */\nexport interface ConfigurationDiagnostic {\n /** Stable machine-readable identifier for this kind of fault. */\n readonly code: string;\n /** Configuration faults are always fatal; there are no warnings. */\n readonly severity: \"error\";\n /** The mode being resolved, when the fault is specific to one. */\n readonly mode?: RuntimeMode;\n /** The configuration key concerned; omitted for whole-shape faults. */\n readonly key?: string;\n /** What was required of the value. */\n readonly expected: string;\n /** What to change to resolve it. */\n readonly correction: string;\n}\n\n/**\n * Thrown when configuration cannot be resolved.\n *\n * Raised at startup rather than at first use, so a missing value stops the\n * process instead of failing the first request that happens to need it. The\n * message names keys but never their values, which may be secrets.\n */\nexport class ConfigurationError extends Error {\n /**\n * Builds an error whose message lists every diagnostic, one per line.\n *\n * @param diagnostics Every fault found, in the order detected.\n */\n constructor(readonly diagnostics: readonly ConfigurationDiagnostic[]) {\n super(\n diagnostics.map((diagnostic) =>\n `${diagnostic.code}: ${diagnostic.key ?? diagnostic.expected}`\n ).join(\"\\n\"),\n );\n this.name = \"ConfigurationError\";\n }\n}\n\n/** Where one configuration key's value came from, without the value. */\nexport interface ConfigurationKeyMetadata {\n /** The key's name. */\n readonly name: string;\n /** Which source supplied it, or `absent` when nothing did. */\n readonly source: \"absent\" | \"default\" | \"env-file\" | \"environment\";\n /** Whether a value was supplied at all. */\n readonly present: boolean;\n /** Whether the key was declared sensitive, and so never reported. */\n readonly sensitive: boolean;\n}\n\n/**\n * Provenance for a resolved configuration: which keys were set and from where,\n * safe to log because it carries no values.\n */\nexport interface ConfigurationMetadata<M extends RuntimeMode = RuntimeMode> {\n /** The mode this configuration was resolved for. */\n readonly mode: M;\n /** One entry per declared key. */\n readonly keys: readonly ConfigurationKeyMetadata[];\n}\n\n/** How to resolve configuration, and where to read it from. */\nexport interface ResolveConfigurationOptions<M extends RuntimeMode> {\n /** The mode to resolve for. */\n readonly mode: M;\n /** Environment to read; defaults to the process environment. */\n readonly environment?: Readonly<Record<string, string | undefined>>;\n /** Reads env files; supply your own to resolve without disk access. */\n readonly readTextFile?: (path: string) => Promise<string>;\n}\n\n/** Configuration after resolution: the values, and where they came from. */\nexport interface ResolvedConfiguration<M extends RuntimeMode, Values> {\n /** The mode these values were resolved for. */\n readonly mode: M;\n /** The parsed values, typed by that mode's schema. */\n readonly values: Readonly<Values>;\n /** Provenance for each key, without values. */\n readonly metadata: ConfigurationMetadata<M>;\n}\n\n/** The value type a definition yields in a given mode. */\nexport type ConfigurationValues<\n Definition extends ConfigurationDefinition<ModeSchemas>,\n M extends RuntimeMode,\n> = z.output<Definition[\"modes\"][M]>;\n\n/** Severity of a log line. */\nexport type LogLevel = \"debug\" | \"error\" | \"info\" | \"warn\";\n\n/** How to build a JSON logger. */\nexport interface JsonLoggerOptions {\n /** The mode being run in; recorded on every line. */\n readonly mode: RuntimeMode;\n /** Receives each serialized line. */\n readonly sink: (line: string) => void;\n /** Supplies timestamps; override to make log output deterministic. */\n readonly clock?: () => Date;\n /** Field names redacted from every line's context. */\n readonly sensitiveFields?: readonly string[];\n}\n\n/**\n * A logger that emits one JSON object per line.\n *\n * Declared sensitive fields are redacted from context before serialization, so\n * a secret passed to a log call does not reach the sink.\n */\nexport interface JsonLogger {\n /**\n * Logs at debug severity.\n *\n * @param event Stable event name, not a sentence.\n * @param context Structured detail; sensitive fields are redacted.\n */\n debug(event: string, context?: unknown): void;\n /**\n * Logs at info severity.\n *\n * @param event Stable event name, not a sentence.\n * @param context Structured detail; sensitive fields are redacted.\n */\n info(event: string, context?: unknown): void;\n /**\n * Logs at warning severity.\n *\n * @param event Stable event name, not a sentence.\n * @param context Structured detail; sensitive fields are redacted.\n */\n warn(event: string, context?: unknown): void;\n /**\n * Logs at error severity.\n *\n * @param event Stable event name, not a sentence.\n * @param context Structured detail; sensitive fields are redacted.\n */\n error(event: string, context?: unknown): void;\n /**\n * Derives a logger that stamps every line with a request identifier.\n *\n * @param requestId Correlates lines belonging to one request.\n * @returns A logger writing to the same sink.\n */\n withRequest(requestId: string): JsonLogger;\n}\n\n/** One dependency readiness probes before reporting the process ready. */\nexport interface ReadinessCheck {\n /** Names the dependency in the readiness response. */\n readonly name: string;\n /** How long this check may take before it counts as failed. */\n readonly timeoutMs: number;\n /**\n * Probes the dependency.\n *\n * @param signal Aborts when the check exceeds its timeout.\n * @returns Whether the dependency is usable.\n */\n check(signal: AbortSignal): Promise<boolean>;\n}\n\n/** Which operational endpoints to expose, and what they report. */\nexport interface OperationalRouteOptions {\n /** Path of the liveness endpoint. */\n readonly healthPath: string;\n /** Reports whether the process itself is healthy; defaults to always. */\n readonly isHealthy?: () => boolean;\n /** Path of the readiness endpoint; omit to expose liveness only. */\n readonly readinessPath?: string;\n /** Dependencies probed before reporting ready. */\n readonly readiness?: readonly ReadinessCheck[];\n}\n","import { platform } from \"#platform\";\nimport { z } from \"zod\";\n\nimport {\n type ConfigurationDefinition,\n type ConfigurationDiagnostic,\n ConfigurationError,\n type ConfigurationValues,\n type ModeSchemas,\n type ResolveConfigurationOptions,\n type ResolvedConfiguration,\n RUNTIME_MODES,\n type RuntimeMode,\n} from \"./types.ts\";\n\nfunction diagnostic(\n code: string,\n expected: string,\n correction: string,\n mode?: RuntimeMode,\n key?: string,\n): ConfigurationDiagnostic {\n return {\n code,\n severity: \"error\",\n ...(mode ? { mode } : {}),\n ...(key ? { key } : {}),\n expected,\n correction,\n };\n}\n\nfunction schemaKeys(schema: z.ZodObject): readonly string[] {\n return Object.keys(schema.shape).sort();\n}\n\nfunction strictObject(schema: unknown): schema is z.ZodObject {\n if (!(schema instanceof z.ZodObject)) return false;\n try {\n const contract = z.toJSONSchema(schema, { io: \"input\" });\n return contract.type === \"object\" &&\n contract.additionalProperties === false;\n } catch {\n return false;\n }\n}\n\n/**\n * Declares what configuration each runtime mode requires.\n *\n * The definition itself is validated here, so a mode missing a schema or a\n * sensitive key naming nothing is caught before any value is read.\n *\n * @param definition The per-mode schemas, sensitive keys, and env files.\n * @returns The validated definition, for {@linkcode resolveConfiguration}.\n * @throws {ConfigurationError} When the definition is malformed.\n */\nexport function defineConfiguration<const Schemas extends ModeSchemas>(\n definition: ConfigurationDefinition<Schemas>,\n): ConfigurationDefinition<Schemas> {\n const diagnostics: ConfigurationDiagnostic[] = [];\n const modes = definition?.modes as\n | Readonly<Record<string, unknown>>\n | undefined;\n for (const mode of RUNTIME_MODES) {\n if (!modes || !strictObject(modes[mode])) {\n diagnostics.push(diagnostic(\n \"SH_CONFIG_SCHEMA_INVALID\",\n \"a strict Zod 4 object schema\",\n `Declare a strict configuration schema for ${mode}.`,\n mode,\n ));\n }\n }\n\n const allKeys = new Set<string>();\n if (modes) {\n for (const mode of RUNTIME_MODES) {\n const schema = modes[mode];\n if (schema instanceof z.ZodObject) {\n for (const key of schemaKeys(schema)) allKeys.add(key);\n }\n }\n }\n for (const key of definition?.sensitiveKeys ?? []) {\n if (!/^[A-Z][A-Z0-9_]*$/.test(key) || !allKeys.has(key)) {\n diagnostics.push(diagnostic(\n \"SH_CONFIG_SENSITIVE_KEY_INVALID\",\n \"a declared configuration key\",\n \"List only declared configuration keys in sensitiveKeys.\",\n undefined,\n key,\n ));\n }\n }\n\n const files = definition?.localEnvFiles as\n | Readonly<Record<string, unknown>>\n | undefined;\n for (const [mode, path] of Object.entries(files ?? {})) {\n if (!([\"development\", \"test\"] as string[]).includes(mode)) {\n diagnostics.push(diagnostic(\n \"SH_CONFIG_ENV_FILE_MODE_INVALID\",\n \"a development or test local environment file\",\n \"Remove local environment files from preview and production.\",\n RUNTIME_MODES.includes(mode as RuntimeMode)\n ? mode as RuntimeMode\n : undefined,\n ));\n } else if (typeof path !== \"string\" || !path.trim()) {\n diagnostics.push(diagnostic(\n \"SH_CONFIG_ENV_FILE_INVALID\",\n \"a non-empty explicit local file path\",\n \"Declare a concrete local environment-file path.\",\n mode as RuntimeMode,\n ));\n }\n }\n\n if (diagnostics.length > 0) throw new ConfigurationError(diagnostics);\n return Object.freeze({\n ...definition,\n sensitiveKeys: Object.freeze([...(definition.sensitiveKeys ?? [])]),\n localEnvFiles: Object.freeze({ ...(definition.localEnvFiles ?? {}) }),\n });\n}\n\nfunction envFileError(\n code: string,\n mode: RuntimeMode,\n key?: string,\n): ConfigurationError {\n return new ConfigurationError([diagnostic(\n code,\n key\n ? \"one unique KEY=VALUE declaration\"\n : \"a readable valid environment file\",\n key\n ? \"Remove duplicate or malformed declarations from the local environment file.\"\n : \"Provide the declared local environment file or remove its configuration.\",\n mode,\n key,\n )]);\n}\n\nfunction parseEnvFile(text: string, mode: RuntimeMode): Record<string, string> {\n const values: Record<string, string> = {};\n for (const rawLine of text.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/);\n if (!match) throw envFileError(\"SH_ENV_FILE_INVALID\", mode);\n const key = match[1];\n if (Object.hasOwn(values, key)) {\n throw envFileError(\"SH_ENV_FILE_DUPLICATE\", mode, key);\n }\n let value = match[2].trim();\n if (value.startsWith(\"'\") || value.startsWith('\"')) {\n const quote = value[0];\n if (value.length < 2 || value.at(-1) !== quote) {\n throw envFileError(\"SH_ENV_FILE_INVALID\", mode, key);\n }\n value = value.slice(1, -1);\n }\n values[key] = value;\n }\n return values;\n}\n\n/**\n * Resolves configuration for one mode, from env files and the environment.\n *\n * Every fault is collected and thrown together, so one startup reports the\n * whole set of missing or malformed values rather than the first. Env files\n * are consulted only in the modes the definition names, never in production.\n *\n * @param definition The contract, from {@linkcode defineConfiguration}.\n * @param options The mode to resolve, and where to read values from.\n * @returns The parsed values, and provenance for each key.\n * @throws {ConfigurationError} When any required value is missing or invalid.\n */\nexport async function resolveConfiguration<\n const Schemas extends ModeSchemas,\n const M extends RuntimeMode,\n>(\n definition: ConfigurationDefinition<Schemas>,\n options: ResolveConfigurationOptions<M>,\n): Promise<\n ResolvedConfiguration<\n M,\n ConfigurationValues<ConfigurationDefinition<Schemas>, M>\n >\n> {\n if (!RUNTIME_MODES.includes(options.mode)) {\n throw new ConfigurationError([diagnostic(\n \"SH_CONFIG_MODE_INVALID\",\n \"development, test, preview, or production\",\n \"Pass one explicit supported runtime mode.\",\n )]);\n }\n\n const schema = definition.modes[options.mode];\n const keys = schemaKeys(schema);\n let fileValues: Record<string, string> = {};\n const filePath = definition.localEnvFiles\n ?.[options.mode as \"development\" | \"test\"];\n if (filePath) {\n const reader = options.readTextFile ??\n ((path: string) => platform.readTextFile(path));\n try {\n fileValues = parseEnvFile(await reader(filePath), options.mode);\n } catch (error) {\n if (error instanceof ConfigurationError) throw error;\n throw envFileError(\"SH_ENV_FILE_READ_FAILED\", options.mode);\n }\n }\n\n const environment = options.environment ?? platform.env.toObject();\n const input: Record<string, string> = {};\n for (const key of keys) {\n if (fileValues[key] !== undefined) input[key] = fileValues[key];\n if (environment[key] !== undefined) input[key] = environment[key]!;\n }\n\n const parsed = await schema.safeParseAsync(input);\n if (!parsed.success) {\n const diagnostics = parsed.error.issues.map((issue) => {\n const key = typeof issue.path[0] === \"string\" ? issue.path[0] : undefined;\n return diagnostic(\n \"SH_CONFIG_VALUE_INVALID\",\n `a value accepted by the ${key ?? \"selected mode\"} schema`,\n \"Provide the required configuration key in its declared safe form.\",\n options.mode,\n key,\n );\n });\n throw new ConfigurationError(diagnostics);\n }\n\n const output = parsed.data as ConfigurationValues<\n ConfigurationDefinition<Schemas>,\n M\n >;\n const sensitive = new Set(definition.sensitiveKeys ?? []);\n const metadata = Object.freeze({\n mode: options.mode,\n keys: Object.freeze(keys.map((name) => {\n const source = environment[name] !== undefined\n ? \"environment\" as const\n : fileValues[name] !== undefined\n ? \"env-file\" as const\n : Object.hasOwn(output as object, name)\n ? \"default\" as const\n : \"absent\" as const;\n return Object.freeze({\n name,\n source,\n present: Object.hasOwn(output as object, name),\n sensitive: sensitive.has(name),\n });\n })),\n });\n\n return Object.freeze({\n mode: options.mode,\n values: Object.freeze(output),\n metadata,\n });\n}\n","import { redactSecurityData } from \"../security/mod.ts\";\nimport {\n type JsonLogger,\n type JsonLoggerOptions,\n type LogLevel,\n RUNTIME_MODES,\n} from \"./types.ts\";\n\nconst EVENT = /^[a-z0-9][a-z0-9._-]{0,127}$/;\nconst REQUEST_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nconst RESERVED = new Set([\"level\", \"timestamp\", \"event\", \"mode\", \"requestId\"]);\n\nfunction customRedaction(\n value: unknown,\n sensitive: ReadonlySet<string>,\n): unknown {\n const active = new WeakSet<object>();\n function visit(current: unknown): unknown {\n if (current === null || typeof current !== \"object\") return current;\n if (\n current instanceof Request || current instanceof Headers ||\n current instanceof Response || current instanceof Error ||\n current instanceof Date\n ) return current;\n if (active.has(current)) return \"[Circular]\";\n active.add(current);\n const result = Array.isArray(current)\n ? current.map(visit)\n : Object.fromEntries(\n Object.entries(current).map(([key, item]) => [\n key,\n sensitive.has(key) ? \"[REDACTED]\" : visit(item),\n ]),\n );\n active.delete(current);\n return result;\n }\n return redactSecurityData(visit(value));\n}\n\nfunction safeContext(\n value: unknown,\n sensitive: ReadonlySet<string>,\n): Record<string, unknown> {\n const redacted = customRedaction(value, sensitive);\n if (!redacted || typeof redacted !== \"object\" || Array.isArray(redacted)) {\n return value === undefined ? {} : { context: redacted };\n }\n return Object.fromEntries(\n Object.entries(redacted).filter(([key]) => !RESERVED.has(key)),\n );\n}\n\n/**\n * Builds a logger that writes one JSON object per line.\n *\n * Fields named as sensitive are redacted before serialization, so a secret\n * passed in context never reaches the sink.\n *\n * @param options The mode, the sink, and which fields to redact.\n * @returns A logger, derivable per request with `withRequest`.\n */\nexport function createJsonLogger(options: JsonLoggerOptions): JsonLogger {\n if (!RUNTIME_MODES.includes(options.mode)) {\n throw new TypeError(\"Logger mode must be explicit\");\n }\n if (typeof options.sink !== \"function\") {\n throw new TypeError(\"Logger sink is required\");\n }\n const sensitive = new Set(options.sensitiveFields ?? []);\n if ([...sensitive].some((field) => !field)) {\n throw new TypeError(\"Sensitive field names must be non-empty\");\n }\n const clock = options.clock ?? (() => new Date());\n\n function build(requestId?: string): JsonLogger {\n function write(level: LogLevel, event: string, context?: unknown): void {\n if (!EVENT.test(event)) throw new TypeError(\"Log event name is invalid\");\n const instant = clock();\n if (!(instant instanceof Date) || Number.isNaN(instant.getTime())) {\n throw new TypeError(\"Logger clock returned an invalid date\");\n }\n options.sink(JSON.stringify({\n ...safeContext(context, sensitive),\n level,\n timestamp: instant.toISOString(),\n event,\n mode: options.mode,\n ...(requestId ? { requestId } : {}),\n }));\n }\n\n return Object.freeze({\n debug: (event: string, context?: unknown) =>\n write(\"debug\", event, context),\n info: (event: string, context?: unknown) => write(\"info\", event, context),\n warn: (event: string, context?: unknown) => write(\"warn\", event, context),\n error: (event: string, context?: unknown) =>\n write(\"error\", event, context),\n withRequest(nextRequestId: string) {\n if (!REQUEST_ID.test(nextRequestId)) {\n throw new TypeError(\"Request ID is invalid\");\n }\n return build(nextRequestId);\n },\n });\n }\n\n return build();\n}\n","import type { NormalizedRoute, RouteOperation } from \"../routing/mod.ts\";\nimport { z } from \"../validation/mod.ts\";\nimport {\n type ConfigurationDiagnostic,\n ConfigurationError,\n type OperationalRouteOptions,\n type ReadinessCheck,\n} from \"./types.ts\";\n\nconst SAFE_NAME = /^[A-Za-z0-9._:-]{1,128}$/;\n\nfunction diagnostic(\n code: string,\n expected: string,\n correction: string,\n key?: string,\n): ConfigurationDiagnostic {\n return {\n code,\n severity: \"error\",\n ...(key ? { key } : {}),\n expected,\n correction,\n };\n}\n\nfunction validPath(path: unknown): path is string {\n return typeof path === \"string\" && path.startsWith(\"/\") && path !== \"/\" &&\n !path.endsWith(\"/\") &&\n path.split(\"/\").slice(1).every((segment) =>\n /^[A-Za-z0-9._~-]+$/.test(segment)\n );\n}\n\nfunction operationRoute(\n path: string,\n source: string,\n responses: Readonly<Record<number, z.ZodType>>,\n handler: () => Response | Promise<Response>,\n): NormalizedRoute {\n return {\n method: \"GET\",\n path,\n source,\n parameterNames: [],\n operation: {\n schemas: { responses },\n security: { authentication: { mode: \"none\" } },\n contract: { summary: source },\n handler: handler as RouteOperation[\"handler\"],\n },\n };\n}\n\nasync function runCheck(check: ReadinessCheck): Promise<boolean> {\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<boolean>((resolve) => {\n timer = setTimeout(() => {\n controller.abort(\"Readiness check timed out\");\n resolve(false);\n }, check.timeoutMs);\n });\n const execution = Promise.resolve()\n .then(() => check.check(controller.signal))\n .then((ready) => ready === true)\n .catch(() => false);\n try {\n return await Promise.race([execution, timeout]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n\n/**\n * Builds liveness and readiness routes, ready to add to the route table.\n *\n * Liveness answers whether the process is up; readiness probes the declared\n * dependencies, each under its own timeout, and reports which one failed. A\n * probe that exceeds its timeout counts as failed rather than hanging.\n *\n * @param options The paths to expose, and the dependencies to probe.\n * @returns Routes to include alongside the discovered ones.\n * @throws {ConfigurationError} When a path or readiness check is malformed.\n */\nexport function createOperationalRoutes(\n options: OperationalRouteOptions,\n): readonly NormalizedRoute[] {\n const diagnostics: ConfigurationDiagnostic[] = [];\n if (!validPath(options.healthPath)) {\n diagnostics.push(diagnostic(\n \"SH_OPERATIONAL_PATH_INVALID\",\n \"an explicit absolute static health path\",\n \"Use a path such as /_health.\",\n \"healthPath\",\n ));\n }\n\n const checks = [...(options.readiness ?? [])];\n if (checks.length > 0 && !validPath(options.readinessPath)) {\n diagnostics.push(diagnostic(\n \"SH_OPERATIONAL_PATH_INVALID\",\n \"an explicit absolute static readiness path\",\n \"Declare readinessPath when readiness checks exist.\",\n \"readinessPath\",\n ));\n }\n if (\n checks.length > 0 && options.readinessPath === options.healthPath\n ) {\n diagnostics.push(diagnostic(\n \"SH_OPERATIONAL_PATH_DUPLICATE\",\n \"distinct health and readiness paths\",\n \"Choose a different readiness path.\",\n \"readinessPath\",\n ));\n }\n\n const names = new Set<string>();\n for (const check of checks) {\n if (\n !SAFE_NAME.test(check.name) || names.has(check.name) ||\n !Number.isSafeInteger(check.timeoutMs) || check.timeoutMs <= 0 ||\n typeof check.check !== \"function\"\n ) {\n diagnostics.push(diagnostic(\n names.has(check.name)\n ? \"SH_READINESS_CHECK_DUPLICATE\"\n : \"SH_READINESS_CHECK_INVALID\",\n \"a unique safe name, positive timeout, and async check\",\n \"Repair the readiness declaration before startup.\",\n check.name,\n ));\n }\n names.add(check.name);\n }\n if (diagnostics.length > 0) throw new ConfigurationError(diagnostics);\n\n const healthSchema = z.strictObject({\n status: z.enum([\"healthy\", \"unhealthy\"]),\n });\n const routes: NormalizedRoute[] = [operationRoute(\n options.healthPath,\n \"sleepyhollow:operational/health\",\n { 200: healthSchema, 503: healthSchema },\n () => {\n let healthy = false;\n try {\n healthy = options.isHealthy?.() ?? true;\n } catch {\n healthy = false;\n }\n return Response.json(\n { status: healthy ? \"healthy\" : \"unhealthy\" },\n { status: healthy ? 200 : 503 },\n );\n },\n )];\n\n if (checks.length > 0) {\n const readinessSchema = z.strictObject({\n status: z.enum([\"ready\", \"not-ready\"]),\n checks: z.array(z.strictObject({ name: z.string(), ready: z.boolean() })),\n });\n const sorted = checks.sort((left, right) =>\n left.name.localeCompare(right.name)\n );\n routes.push(operationRoute(\n options.readinessPath!,\n \"sleepyhollow:operational/readiness\",\n { 200: readinessSchema, 503: readinessSchema },\n async () => {\n const states = await Promise.all(sorted.map(async (check) => ({\n name: check.name,\n ready: await runCheck(check),\n })));\n const ready = states.every((state) => state.ready);\n return Response.json({\n status: ready ? \"ready\" : \"not-ready\",\n checks: states,\n }, { status: ready ? 200 : 503 });\n },\n ));\n }\n\n return Object.freeze(routes);\n}\n"],"mappings":"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","names":["diagnostic","z","z","diagnostic"]}
|