@oscharko-dev/test-intelligence 0.0.1-beta.0 → 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1250 @@
1
+ import { createRequire } from 'node:module';
2
+ import { createServer } from 'http';
3
+ import { createHash, timingSafeEqual, randomUUID } from 'crypto';
4
+
5
+ createRequire(import.meta.url);
6
+
7
+ // packages/server/src/package-identity.ts
8
+ var PACKAGE_NAME = "@oscharko-dev/test-intelligence";
9
+ var PACKAGE_VERSION = "0.0.1-beta.0";
10
+ function resolveReleaseStage(version) {
11
+ const separatorIndex = version.indexOf("-");
12
+ if (separatorIndex === -1) {
13
+ return "stable";
14
+ }
15
+ const preRelease = version.slice(separatorIndex + 1);
16
+ return preRelease.startsWith("beta") ? "beta" : "pre-beta";
17
+ }
18
+ function getPackageIdentity() {
19
+ return {
20
+ name: PACKAGE_NAME,
21
+ version: PACKAGE_VERSION,
22
+ stage: resolveReleaseStage(PACKAGE_VERSION)
23
+ };
24
+ }
25
+
26
+ // packages/contracts/dist/index.js
27
+ var TEST_INTELLIGENCE_ENV = "TEST_INTELLIGENCE_ENABLED";
28
+
29
+ // packages/server/src/constants.ts
30
+ var DEFAULT_HOST = "127.0.0.1";
31
+ var DEFAULT_PORT = 1983;
32
+ var DEFAULT_RATE_LIMIT_PER_MINUTE = 60;
33
+ var RATE_LIMIT_WINDOW_MS = 6e4;
34
+ var MAX_REQUEST_BODY_BYTES = 1048576;
35
+ var MAX_SUBMIT_BODY_BYTES = 8388608;
36
+ var ENABLE_HSTS_ENV = "TEST_INTELLIGENCE_ENABLE_HSTS";
37
+ var DEFAULT_STRICT_TRANSPORT_SECURITY = "max-age=31536000";
38
+ var DEFAULT_CONTENT_SECURITY_POLICY = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'";
39
+ var API_ROUTE_PREFIX = "/api/v1";
40
+ var resolveTestIntelligenceEnabled = (env = process.env) => {
41
+ return parseBooleanEnv(env[TEST_INTELLIGENCE_ENV]);
42
+ };
43
+ var resolveStrictTransportSecurity = (env = process.env) => {
44
+ const raw = env[ENABLE_HSTS_ENV];
45
+ if (raw === void 0) {
46
+ return void 0;
47
+ }
48
+ const normalized = raw.trim().toLowerCase();
49
+ if (normalized === "" || normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
50
+ return void 0;
51
+ }
52
+ return DEFAULT_STRICT_TRANSPORT_SECURITY;
53
+ };
54
+ var parseBooleanEnv = (raw) => {
55
+ if (raw === void 0) {
56
+ return false;
57
+ }
58
+ const normalized = raw.trim().toLowerCase();
59
+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
60
+ };
61
+
62
+ // packages/server/src/error-codes.ts
63
+ var statusForErrorCode = (code) => {
64
+ switch (code) {
65
+ case "BAD_REQUEST":
66
+ return 400;
67
+ case "UNAUTHORIZED":
68
+ case "BEARER_TOKEN_MISSING":
69
+ return 401;
70
+ case "FORBIDDEN_REQUEST_ORIGIN":
71
+ case "FEATURE_GATE_DISABLED":
72
+ return 403;
73
+ case "NOT_FOUND":
74
+ return 404;
75
+ case "METHOD_NOT_ALLOWED":
76
+ return 405;
77
+ case "PAYLOAD_TOO_LARGE":
78
+ return 413;
79
+ case "UNSUPPORTED_MEDIA_TYPE":
80
+ return 415;
81
+ case "RATE_LIMITED":
82
+ return 429;
83
+ case "INTERNAL_ERROR":
84
+ case "VERIFICATION_FAILED":
85
+ return 500;
86
+ case "AUTHENTICATION_UNAVAILABLE":
87
+ case "LLM_GATEWAY_UNCONFIGURED":
88
+ case "LLM_GATEWAY_FAILED":
89
+ return 503;
90
+ }
91
+ };
92
+
93
+ // packages/server/src/http-helpers.ts
94
+ var applySecurityHeaders = (response, options = {}) => {
95
+ response.setHeader("X-Content-Type-Options", "nosniff");
96
+ response.setHeader("Referrer-Policy", "no-referrer");
97
+ response.setHeader(
98
+ "Content-Security-Policy",
99
+ options.contentSecurityPolicy ?? DEFAULT_CONTENT_SECURITY_POLICY
100
+ );
101
+ const hsts = options.strictTransportSecurity ?? resolveStrictTransportSecurity();
102
+ if (hsts !== void 0) {
103
+ response.setHeader("Strict-Transport-Security", hsts);
104
+ }
105
+ };
106
+ var writeJsonResponse = ({
107
+ response,
108
+ statusCode,
109
+ payload,
110
+ extraHeaders
111
+ }) => {
112
+ applySecurityHeaders(response);
113
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
114
+ if (extraHeaders !== void 0) {
115
+ for (const [name, value] of Object.entries(extraHeaders)) {
116
+ response.setHeader(name, value);
117
+ }
118
+ }
119
+ response.statusCode = statusCode;
120
+ response.end(JSON.stringify(payload));
121
+ };
122
+ var writeErrorResponse = ({
123
+ response,
124
+ code,
125
+ message,
126
+ extraHeaders,
127
+ statusCode
128
+ }) => {
129
+ const envelope = { error: code, message };
130
+ writeJsonResponse({
131
+ response,
132
+ statusCode: statusCode ?? statusForErrorCode(code),
133
+ payload: envelope,
134
+ ...extraHeaders !== void 0 ? { extraHeaders } : {}
135
+ });
136
+ };
137
+ var readJsonBody = async ({
138
+ request,
139
+ maxBytes
140
+ }) => {
141
+ const chunks = [];
142
+ let received = 0;
143
+ for await (const chunk of request) {
144
+ const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
145
+ received += buf.length;
146
+ if (received > maxBytes) {
147
+ return {
148
+ ok: false,
149
+ code: "PAYLOAD_TOO_LARGE",
150
+ message: `Request body exceeds ${maxBytes} bytes.`
151
+ };
152
+ }
153
+ chunks.push(buf);
154
+ }
155
+ if (chunks.length === 0) {
156
+ return {
157
+ ok: false,
158
+ code: "BAD_REQUEST",
159
+ message: "Request body is empty."
160
+ };
161
+ }
162
+ const text = Buffer.concat(chunks).toString("utf8");
163
+ try {
164
+ return { ok: true, value: JSON.parse(text) };
165
+ } catch {
166
+ return {
167
+ ok: false,
168
+ code: "BAD_REQUEST",
169
+ message: "Request body is not valid JSON."
170
+ };
171
+ }
172
+ };
173
+ var beginSseResponse = (response, options = {}) => {
174
+ applySecurityHeaders(response, options);
175
+ response.setHeader("Content-Type", "text/event-stream; charset=utf-8");
176
+ response.setHeader("Cache-Control", "no-cache, no-transform");
177
+ response.setHeader("Connection", "keep-alive");
178
+ response.statusCode = 200;
179
+ response.flushHeaders();
180
+ return {
181
+ writeEvent({ event, data, id }) {
182
+ const lines = [];
183
+ if (id !== void 0) {
184
+ lines.push(`id: ${id}`);
185
+ }
186
+ lines.push(`event: ${event}`);
187
+ const serialized = JSON.stringify(data);
188
+ for (const line of serialized.split("\n")) {
189
+ lines.push(`data: ${line}`);
190
+ }
191
+ lines.push("", "");
192
+ response.write(lines.join("\n"));
193
+ },
194
+ writeRetry(retryMs) {
195
+ response.write(`retry: ${String(retryMs)}
196
+
197
+ `);
198
+ },
199
+ end() {
200
+ response.end();
201
+ }
202
+ };
203
+ };
204
+ var FORWARDED_FOR_HEADER = "x-forwarded-for";
205
+ var resolveClientKey = (request) => {
206
+ const forwarded = request.headers[FORWARDED_FOR_HEADER];
207
+ const raw = Array.isArray(forwarded) ? forwarded[0] : forwarded;
208
+ if (typeof raw === "string" && raw.length > 0) {
209
+ const first = raw.split(",")[0]?.trim();
210
+ if (first !== void 0 && first.length > 0) {
211
+ return first;
212
+ }
213
+ }
214
+ return request.socket.remoteAddress ?? "unknown";
215
+ };
216
+ var createRequestLogger = (logger) => {
217
+ return {
218
+ log({ requestId, method, path, statusCode, durationMs, clientKey, route }) {
219
+ const segments = [
220
+ `method=${method}`,
221
+ `path=${path}`,
222
+ `status=${String(statusCode)}`,
223
+ `durationMs=${String(durationMs)}`,
224
+ `client=${clientKey}`
225
+ ];
226
+ if (route !== void 0) {
227
+ segments.push(`route=${route}`);
228
+ }
229
+ logger.log({
230
+ level: statusCode >= 500 ? "error" : "info",
231
+ message: segments.join(" "),
232
+ requestId,
233
+ method,
234
+ path,
235
+ statusCode
236
+ });
237
+ },
238
+ newRequestId() {
239
+ return randomUUID();
240
+ }
241
+ };
242
+ };
243
+ var createAuditLogger = ({
244
+ write,
245
+ now = () => (/* @__PURE__ */ new Date()).toISOString()
246
+ }) => {
247
+ return {
248
+ record(event) {
249
+ const line = JSON.stringify({
250
+ ts: now(),
251
+ action: event.action,
252
+ subject: event.subject,
253
+ outcome: event.outcome,
254
+ ...event.principal !== void 0 ? { principal: event.principal } : {},
255
+ ...event.requestId !== void 0 ? { requestId: event.requestId } : {},
256
+ ...event.details !== void 0 ? { details: event.details } : {}
257
+ });
258
+ write(`${line}
259
+ `);
260
+ }
261
+ };
262
+ };
263
+
264
+ // packages/server/src/rate-limit-store.ts
265
+ var RateLimitStore = class {
266
+ buckets = /* @__PURE__ */ new Map();
267
+ get(key) {
268
+ return this.buckets.get(key);
269
+ }
270
+ set(key, bucket) {
271
+ this.buckets.set(key, bucket);
272
+ }
273
+ delete(key) {
274
+ this.buckets.delete(key);
275
+ }
276
+ clear() {
277
+ this.buckets.clear();
278
+ }
279
+ size() {
280
+ return this.buckets.size;
281
+ }
282
+ };
283
+
284
+ // packages/server/src/rate-limit.ts
285
+ var createRateLimiter = ({
286
+ requestsPerMinute,
287
+ store = new RateLimitStore()
288
+ }) => {
289
+ if (!Number.isFinite(requestsPerMinute) || requestsPerMinute < 1) {
290
+ throw new Error(
291
+ "createRateLimiter: requestsPerMinute must be a finite integer >= 1."
292
+ );
293
+ }
294
+ const limit = Math.floor(requestsPerMinute);
295
+ return {
296
+ check({ clientKey, routeKey, nowMs }) {
297
+ const key = `${clientKey}\0${routeKey}`;
298
+ const existing = store.get(key);
299
+ if (existing === void 0 || nowMs - existing.windowStartedAtMs >= RATE_LIMIT_WINDOW_MS) {
300
+ store.set(key, { count: 1, windowStartedAtMs: nowMs });
301
+ return {
302
+ ok: true,
303
+ remaining: limit - 1,
304
+ resetAtMs: nowMs + RATE_LIMIT_WINDOW_MS
305
+ };
306
+ }
307
+ if (existing.count >= limit) {
308
+ const resetAtMs = existing.windowStartedAtMs + RATE_LIMIT_WINDOW_MS;
309
+ return {
310
+ ok: false,
311
+ retryAfterSeconds: Math.max(1, Math.ceil((resetAtMs - nowMs) / 1e3)),
312
+ resetAtMs
313
+ };
314
+ }
315
+ existing.count += 1;
316
+ return {
317
+ ok: true,
318
+ remaining: Math.max(0, limit - existing.count),
319
+ resetAtMs: existing.windowStartedAtMs + RATE_LIMIT_WINDOW_MS
320
+ };
321
+ },
322
+ reset() {
323
+ store.clear();
324
+ }
325
+ };
326
+ };
327
+ var JSON_CONTENT_TYPE_PATTERN = /^application\/json(?:\s*(?:;|$))/i;
328
+ var ALLOWED_SEC_FETCH_SITE_VALUES = /* @__PURE__ */ new Set(["same-origin", "same-site"]);
329
+ var TEST_INTELLIGENCE_BEARER_REALM = "test-intelligence";
330
+ var getHeaderValue = (value) => {
331
+ if (typeof value === "string") {
332
+ return value;
333
+ }
334
+ if (Array.isArray(value)) {
335
+ return value[0];
336
+ }
337
+ return void 0;
338
+ };
339
+ var normalizeOrigin = (value) => {
340
+ if (value === void 0 || value.trim().length === 0) {
341
+ return void 0;
342
+ }
343
+ try {
344
+ return new URL(value).origin;
345
+ } catch {
346
+ return void 0;
347
+ }
348
+ };
349
+ var normalizeOriginHost = (host) => {
350
+ if (host.includes(":") && !host.startsWith("[")) {
351
+ return `[${host}]`;
352
+ }
353
+ return host;
354
+ };
355
+ var isLoopbackLikeHost = (host) => {
356
+ const normalized = host.trim().toLowerCase();
357
+ return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1" || normalized === "[::1]" || normalized === "0.0.0.0" || normalized === "::" || normalized === "[::]";
358
+ };
359
+ var getAllowedWriteOrigins = ({
360
+ host,
361
+ port
362
+ }) => {
363
+ const allowed = /* @__PURE__ */ new Set([
364
+ `http://${normalizeOriginHost(host)}:${port}`
365
+ ]);
366
+ if (isLoopbackLikeHost(host)) {
367
+ allowed.add(`http://127.0.0.1:${port}`);
368
+ allowed.add(`http://localhost:${port}`);
369
+ allowed.add(`http://[::1]:${port}`);
370
+ }
371
+ return allowed;
372
+ };
373
+ var validateWriteRequest = ({
374
+ request,
375
+ host,
376
+ port
377
+ }) => {
378
+ const contentType = getHeaderValue(request.headers["content-type"]);
379
+ if (contentType === void 0 || !JSON_CONTENT_TYPE_PATTERN.test(contentType)) {
380
+ return {
381
+ ok: false,
382
+ statusCode: 415,
383
+ payload: {
384
+ error: "UNSUPPORTED_MEDIA_TYPE",
385
+ message: "Write routes require 'Content-Type: application/json'."
386
+ }
387
+ };
388
+ }
389
+ const originHeader = getHeaderValue(request.headers.origin);
390
+ const refererHeader = getHeaderValue(request.headers.referer);
391
+ const secFetchSite = getHeaderValue(request.headers["sec-fetch-site"])?.trim().toLowerCase();
392
+ const allowedOrigins = getAllowedWriteOrigins({ host, port });
393
+ if (secFetchSite !== void 0 && !ALLOWED_SEC_FETCH_SITE_VALUES.has(secFetchSite)) {
394
+ return forbiddenOrigin(
395
+ "Cross-site browser requests to test-intelligence write routes are blocked."
396
+ );
397
+ }
398
+ if (originHeader !== void 0) {
399
+ const origin = normalizeOrigin(originHeader);
400
+ if (origin === void 0 || !allowedOrigins.has(origin)) {
401
+ return forbiddenOrigin(
402
+ "Only same-origin browser requests may access test-intelligence write routes."
403
+ );
404
+ }
405
+ }
406
+ if (refererHeader !== void 0) {
407
+ const refererOrigin = normalizeOrigin(refererHeader);
408
+ if (refererOrigin === void 0 || !allowedOrigins.has(refererOrigin)) {
409
+ return forbiddenOrigin(
410
+ "Only same-origin browser requests may access test-intelligence write routes."
411
+ );
412
+ }
413
+ }
414
+ return { ok: true };
415
+ };
416
+ var forbiddenOrigin = (message) => ({
417
+ ok: false,
418
+ statusCode: 403,
419
+ payload: { error: "FORBIDDEN_REQUEST_ORIGIN", message }
420
+ });
421
+ var normalizeConfiguredBearerToken = (value) => {
422
+ if (typeof value !== "string") {
423
+ return void 0;
424
+ }
425
+ const trimmed = value.trim();
426
+ return trimmed.length > 0 ? trimmed : void 0;
427
+ };
428
+ var readBearerToken = (request) => {
429
+ const authorization = getHeaderValue(request.headers.authorization);
430
+ if (authorization === void 0) {
431
+ return void 0;
432
+ }
433
+ const expectedScheme = "bearer";
434
+ if (authorization.length <= expectedScheme.length) {
435
+ return void 0;
436
+ }
437
+ for (let index = 0; index < expectedScheme.length; index += 1) {
438
+ const code = authorization.charCodeAt(index);
439
+ const lowered = code >= 65 && code <= 90 ? String.fromCharCode(code + 32) : authorization[index];
440
+ if (lowered !== expectedScheme[index]) {
441
+ return void 0;
442
+ }
443
+ }
444
+ let tokenStart = expectedScheme.length;
445
+ while (tokenStart < authorization.length) {
446
+ const code = authorization.charCodeAt(tokenStart);
447
+ if (code !== 32 && code !== 9) {
448
+ break;
449
+ }
450
+ tokenStart += 1;
451
+ }
452
+ if (tokenStart === expectedScheme.length || tokenStart >= authorization.length) {
453
+ return void 0;
454
+ }
455
+ let tokenEnd = authorization.length;
456
+ while (tokenEnd > tokenStart) {
457
+ const code = authorization.charCodeAt(tokenEnd - 1);
458
+ if (code !== 32 && code !== 9) {
459
+ break;
460
+ }
461
+ tokenEnd -= 1;
462
+ }
463
+ return tokenEnd > tokenStart ? authorization.slice(tokenStart, tokenEnd) : void 0;
464
+ };
465
+ var tokensMatch = (expected, candidate) => {
466
+ const expectedDigest = createHash("sha256").update(expected, "utf8").digest();
467
+ const candidateDigest = createHash("sha256").update(candidate, "utf8").digest();
468
+ return timingSafeEqual(expectedDigest, candidateDigest);
469
+ };
470
+ var validateBearerToken = ({
471
+ request,
472
+ bearerToken,
473
+ routeLabel
474
+ }) => {
475
+ const configuredToken = normalizeConfiguredBearerToken(bearerToken);
476
+ if (configuredToken === void 0) {
477
+ return {
478
+ ok: false,
479
+ statusCode: 503,
480
+ payload: {
481
+ error: "AUTHENTICATION_UNAVAILABLE",
482
+ message: `${routeLabel} writes are disabled until server bearer authentication is configured.`
483
+ }
484
+ };
485
+ }
486
+ const receivedToken = readBearerToken(request);
487
+ if (receivedToken !== void 0 && tokensMatch(configuredToken, receivedToken)) {
488
+ return { ok: true, principal: { scheme: "bearer" } };
489
+ }
490
+ return {
491
+ ok: false,
492
+ statusCode: 401,
493
+ payload: {
494
+ error: "UNAUTHORIZED",
495
+ message: `${routeLabel} writes require a valid Bearer token.`
496
+ },
497
+ wwwAuthenticate: `Bearer realm="${TEST_INTELLIGENCE_BEARER_REALM}"`
498
+ };
499
+ };
500
+ var STABLE_SEGMENT_RE = /^[A-Za-z0-9_.-]{1,128}$/u;
501
+ var isSafeIdSegment = (segment) => {
502
+ if (!STABLE_SEGMENT_RE.test(segment)) {
503
+ return false;
504
+ }
505
+ if (segment === "." || segment === "..") {
506
+ return false;
507
+ }
508
+ return true;
509
+ };
510
+ var stripTrailingSlash = (pathname) => {
511
+ if (pathname.length > 1 && pathname.endsWith("/")) {
512
+ return pathname.slice(0, -1);
513
+ }
514
+ return pathname;
515
+ };
516
+
517
+ // packages/server/src/test-intelligence-routes.ts
518
+ var parseTestIntelligenceRoute = ({
519
+ method,
520
+ pathname
521
+ }) => {
522
+ const upper = method.toUpperCase();
523
+ if (upper === "OPTIONS") {
524
+ return { ok: true, route: { kind: "cors_preflight" } };
525
+ }
526
+ const normalized = stripTrailingSlash(pathname);
527
+ if (normalized === "/healthz") {
528
+ return methodGuard(upper, "GET", { kind: "healthz" });
529
+ }
530
+ if (normalized === "/readyz") {
531
+ return methodGuard(upper, "GET", { kind: "readyz" });
532
+ }
533
+ if (normalized === "/openapi.json") {
534
+ return methodGuard(upper, "GET", { kind: "openapi" });
535
+ }
536
+ if (!normalized.startsWith(`${API_ROUTE_PREFIX}/`)) {
537
+ return { ok: false, reason: "unknown_route" };
538
+ }
539
+ const rest = normalized.slice(API_ROUTE_PREFIX.length + 1);
540
+ const segments = rest.split("/");
541
+ return dispatchSegments(upper, segments);
542
+ };
543
+ var methodGuard = (method, allowed, route) => {
544
+ if (method !== allowed) {
545
+ return {
546
+ ok: false,
547
+ reason: "method_not_allowed",
548
+ allowedMethods: [allowed]
549
+ };
550
+ }
551
+ return { ok: true, route };
552
+ };
553
+ var dispatchSegments = (method, segments) => {
554
+ if (segments.length === 0 || segments[0] === "") {
555
+ return { ok: false, reason: "unknown_route" };
556
+ }
557
+ const head = segments[0];
558
+ const tail = segments.slice(1);
559
+ switch (head) {
560
+ case "jobs":
561
+ return dispatchJobs(method, tail);
562
+ case "review":
563
+ return dispatchReview(method, tail);
564
+ case "tms":
565
+ return dispatchTms(method, tail);
566
+ case "execution":
567
+ return dispatchExecution(method, tail);
568
+ case "onboard":
569
+ return dispatchSingleton(method, tail, "POST", { kind: "onboard" });
570
+ case "figma-export":
571
+ return dispatchSingleton(method, tail, "POST", { kind: "figma_export" });
572
+ default:
573
+ return { ok: false, reason: "unknown_route" };
574
+ }
575
+ };
576
+ var dispatchJobs = (method, tail) => {
577
+ if (tail.length === 0) {
578
+ return methodGuard(method, "POST", { kind: "submit_job" });
579
+ }
580
+ const jobId = tail[0];
581
+ if (jobId === "") {
582
+ return { ok: false, reason: "empty_segment" };
583
+ }
584
+ if (!isSafeIdSegment(jobId)) {
585
+ return { ok: false, reason: "unsafe_id_segment" };
586
+ }
587
+ if (tail.length === 1) {
588
+ return methodGuard(method, "GET", { kind: "job_status", jobId });
589
+ }
590
+ if (tail.length === 2 && tail[1] === "events") {
591
+ return methodGuard(method, "GET", { kind: "job_events", jobId });
592
+ }
593
+ if (tail.length === 3 && tail[2] === "verify") {
594
+ return jobsVerifySubroute(method, jobId, tail[1]);
595
+ }
596
+ return { ok: false, reason: "unknown_route" };
597
+ };
598
+ var jobsVerifySubroute = (method, jobId, subject) => {
599
+ switch (subject) {
600
+ case "evidence":
601
+ return methodGuard(method, "POST", { kind: "verify_evidence", jobId });
602
+ case "audit-dossier":
603
+ return methodGuard(method, "POST", {
604
+ kind: "verify_audit_dossier",
605
+ jobId
606
+ });
607
+ case "provenance":
608
+ return methodGuard(method, "POST", { kind: "verify_provenance", jobId });
609
+ case "seal":
610
+ return methodGuard(method, "POST", { kind: "verify_seal", jobId });
611
+ default:
612
+ return { ok: false, reason: "unknown_route" };
613
+ }
614
+ };
615
+ var dispatchReview = (method, tail) => {
616
+ if (tail.length === 0) {
617
+ return { ok: false, reason: "unknown_route" };
618
+ }
619
+ const jobId = tail[0];
620
+ if (jobId === "") {
621
+ return { ok: false, reason: "empty_segment" };
622
+ }
623
+ if (!isSafeIdSegment(jobId)) {
624
+ return { ok: false, reason: "unsafe_id_segment" };
625
+ }
626
+ if (tail.length === 1) {
627
+ return methodGuard(method, "GET", { kind: "review_snapshot", jobId });
628
+ }
629
+ if (tail.length === 2 && tail[1] === "decision") {
630
+ return methodGuard(method, "POST", { kind: "review_decision", jobId });
631
+ }
632
+ return { ok: false, reason: "unknown_route" };
633
+ };
634
+ var dispatchTms = (method, tail) => {
635
+ return dispatchSingleton(method, tail, "POST", { kind: "tms_push" }, [
636
+ "push"
637
+ ]);
638
+ };
639
+ var dispatchExecution = (method, tail) => {
640
+ return dispatchSingleton(method, tail, "POST", { kind: "execution_pull" }, [
641
+ "pull"
642
+ ]);
643
+ };
644
+ var dispatchSingleton = (method, tail, allowedMethod, route, expectedTail = []) => {
645
+ if (tail.length !== expectedTail.length) {
646
+ return { ok: false, reason: "unknown_route" };
647
+ }
648
+ for (let i = 0; i < expectedTail.length; i += 1) {
649
+ if (tail[i] !== expectedTail[i]) {
650
+ return { ok: false, reason: "unknown_route" };
651
+ }
652
+ }
653
+ return methodGuard(method, allowedMethod, route);
654
+ };
655
+
656
+ // packages/server/src/route-handlers.ts
657
+ var buildHandlerTable = (options) => {
658
+ const defaults = {
659
+ healthz: handleHealthz,
660
+ readyz: ({ response }) => handleReadyz(response, options),
661
+ openapi: handleOpenapi,
662
+ cors_preflight: ({ response }) => handleCorsPreflight(response, options),
663
+ submit_job: handleNotImplemented("submit_job"),
664
+ job_status: handleNotImplemented("job_status"),
665
+ job_events: handleSseStub,
666
+ verify_evidence: handleNotImplemented("verify_evidence"),
667
+ verify_audit_dossier: handleNotImplemented("verify_audit_dossier"),
668
+ verify_provenance: handleNotImplemented("verify_provenance"),
669
+ verify_seal: handleNotImplemented("verify_seal"),
670
+ review_snapshot: handleNotImplemented("review_snapshot"),
671
+ review_decision: handleNotImplemented("review_decision"),
672
+ tms_push: handleNotImplemented("tms_push"),
673
+ execution_pull: handleNotImplemented("execution_pull"),
674
+ onboard: handleNotImplemented("onboard"),
675
+ figma_export: handleNotImplemented("figma_export")
676
+ };
677
+ const overrides = options.routeHandlers ?? {};
678
+ for (const [kind, handler] of Object.entries(overrides)) {
679
+ defaults[kind] = handler;
680
+ }
681
+ return defaults;
682
+ };
683
+ var handleHealthz = ({ response }) => {
684
+ writeJsonResponse({
685
+ response,
686
+ statusCode: 200,
687
+ payload: { status: "ok", checkedAt: (/* @__PURE__ */ new Date()).toISOString() }
688
+ });
689
+ };
690
+ var handleReadyz = (response, options) => {
691
+ writeJsonResponse({
692
+ response,
693
+ statusCode: 200,
694
+ payload: {
695
+ status: "ready",
696
+ featureGate: options.testIntelligenceEnabled ? "enabled" : "disabled",
697
+ authConfigured: options.bearerToken !== void 0,
698
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
699
+ }
700
+ });
701
+ };
702
+ var handleOpenapi = ({ response }) => {
703
+ writeJsonResponse({
704
+ response,
705
+ statusCode: 200,
706
+ payload: {
707
+ openapi: "3.1.0",
708
+ info: { title: "Test Intelligence API", version: "0.0.1" },
709
+ paths: {}
710
+ }
711
+ });
712
+ };
713
+ var handleCorsPreflight = (response, options) => {
714
+ const allowedOrigin = options.allowedCorsOrigins?.[0];
715
+ if (allowedOrigin !== void 0) {
716
+ response.setHeader("Access-Control-Allow-Origin", allowedOrigin);
717
+ }
718
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
719
+ response.setHeader(
720
+ "Access-Control-Allow-Headers",
721
+ "Content-Type, Authorization"
722
+ );
723
+ response.setHeader("Access-Control-Max-Age", "600");
724
+ response.statusCode = 204;
725
+ response.end();
726
+ };
727
+ var handleNotImplemented = (routeKind) => {
728
+ return ({
729
+ request,
730
+ response,
731
+ route,
732
+ audit,
733
+ requestId
734
+ }) => {
735
+ void readJsonBodyForRoute(request, route).then((body) => {
736
+ audit.record({
737
+ action: `${routeKind}.received`,
738
+ subject: routeKind,
739
+ outcome: body.ok ? "ok" : "denied",
740
+ requestId
741
+ });
742
+ writeErrorResponse({
743
+ response,
744
+ code: "LLM_GATEWAY_UNCONFIGURED",
745
+ message: `Route '${routeKind}' is reachable but no operator-supplied handler is wired.`
746
+ });
747
+ });
748
+ };
749
+ };
750
+ var handleSseStub = ({ response }) => {
751
+ const sse = beginSseResponse(response);
752
+ sse.writeRetry(15e3);
753
+ sse.writeEvent({
754
+ event: "noop",
755
+ data: { message: "No event source is wired for this job." },
756
+ id: "0"
757
+ });
758
+ sse.end();
759
+ };
760
+ var readJsonBodyForRoute = async (request, route) => {
761
+ const maxBytes = route.kind === "submit_job" ? MAX_SUBMIT_BODY_BYTES : MAX_REQUEST_BODY_BYTES;
762
+ const result = await readJsonBody({ request, maxBytes });
763
+ return { ok: result.ok };
764
+ };
765
+
766
+ // packages/server/src/request-handler.ts
767
+ var createTestIntelligenceRequestHandler = (options) => {
768
+ const rateLimiter = createRateLimiter({
769
+ requestsPerMinute: options.requestsPerMinute ?? DEFAULT_RATE_LIMIT_PER_MINUTE
770
+ });
771
+ const requestLog = createRequestLogger(options.logger);
772
+ const audit = createAuditLogger({
773
+ write: options.auditWrite ?? defaultAuditWrite(options.logger)
774
+ });
775
+ const handlers = buildHandlerTable(options);
776
+ const nowMs = options.nowMs ?? (() => Date.now());
777
+ return (request, response) => {
778
+ void runHandler({
779
+ request,
780
+ response,
781
+ options,
782
+ rateLimiter,
783
+ requestLog,
784
+ audit,
785
+ handlers,
786
+ nowMs
787
+ });
788
+ };
789
+ };
790
+ var runHandler = async (input) => {
791
+ const { request, response, options, requestLog } = input;
792
+ const start = Date.now();
793
+ const requestId = requestLog.newRequestId();
794
+ const clientKey = resolveClientKey(request);
795
+ const method = request.method ?? "GET";
796
+ const pathname = extractPathname(request.url ?? "/");
797
+ try {
798
+ const dispatched = await dispatch({
799
+ ...input,
800
+ method,
801
+ pathname,
802
+ requestId,
803
+ clientKey
804
+ });
805
+ requestLog.log({
806
+ requestId,
807
+ method,
808
+ path: pathname,
809
+ statusCode: response.statusCode,
810
+ durationMs: Date.now() - start,
811
+ clientKey,
812
+ route: dispatched
813
+ });
814
+ } catch (error) {
815
+ handleUnexpectedError({ response, error, options, requestId });
816
+ requestLog.log({
817
+ requestId,
818
+ method,
819
+ path: pathname,
820
+ statusCode: response.statusCode,
821
+ durationMs: Date.now() - start,
822
+ clientKey
823
+ });
824
+ }
825
+ };
826
+ var dispatch = async (input) => {
827
+ const {
828
+ request,
829
+ response,
830
+ method,
831
+ pathname,
832
+ options,
833
+ rateLimiter,
834
+ handlers,
835
+ requestId,
836
+ clientKey,
837
+ audit,
838
+ nowMs
839
+ } = input;
840
+ const parse = parseTestIntelligenceRoute({ method, pathname });
841
+ if (!parse.ok) {
842
+ emitParseFailure({
843
+ response,
844
+ reason: parse.reason,
845
+ allowed: parse.allowedMethods
846
+ });
847
+ return `parse:${parse.reason}`;
848
+ }
849
+ const route = parse.route;
850
+ const routeKey = `${method.toUpperCase()} ${route.kind}`;
851
+ const limit = rateLimiter.check({
852
+ clientKey,
853
+ routeKey,
854
+ nowMs: nowMs()
855
+ });
856
+ if (!limit.ok) {
857
+ writeErrorResponse({
858
+ response,
859
+ code: "RATE_LIMITED",
860
+ message: "Too many requests; retry after the indicated interval.",
861
+ extraHeaders: { "Retry-After": String(limit.retryAfterSeconds) }
862
+ });
863
+ return route.kind;
864
+ }
865
+ if (requiresWriteGate(route)) {
866
+ const gate = enforceWriteGate({ request, response, options, route });
867
+ if (!gate.ok) {
868
+ return route.kind;
869
+ }
870
+ }
871
+ await handlers[route.kind]({
872
+ request,
873
+ response,
874
+ route,
875
+ requestId,
876
+ clientKey,
877
+ audit,
878
+ logger: options.logger
879
+ });
880
+ return route.kind;
881
+ };
882
+ var requiresWriteGate = (route) => {
883
+ switch (route.kind) {
884
+ case "healthz":
885
+ case "readyz":
886
+ case "openapi":
887
+ case "cors_preflight":
888
+ case "job_status":
889
+ case "job_events":
890
+ case "review_snapshot":
891
+ return false;
892
+ default:
893
+ return true;
894
+ }
895
+ };
896
+ var enforceWriteGate = ({
897
+ request,
898
+ response,
899
+ options,
900
+ route
901
+ }) => {
902
+ if (!options.testIntelligenceEnabled) {
903
+ writeErrorResponse({
904
+ response,
905
+ code: "FEATURE_GATE_DISABLED",
906
+ message: "The test-intelligence feature gate is disabled on this server."
907
+ });
908
+ return { ok: false };
909
+ }
910
+ const writeCheck = validateWriteRequest({
911
+ request,
912
+ host: options.host,
913
+ port: options.port
914
+ });
915
+ if (!writeCheck.ok) {
916
+ writeErrorResponse({
917
+ response,
918
+ code: writeCheck.payload.error,
919
+ message: writeCheck.payload.message,
920
+ statusCode: writeCheck.statusCode
921
+ });
922
+ return { ok: false };
923
+ }
924
+ const auth = validateBearerToken({
925
+ request,
926
+ bearerToken: options.bearerToken,
927
+ routeLabel: route.kind
928
+ });
929
+ if (!auth.ok) {
930
+ writeErrorResponse({
931
+ response,
932
+ code: auth.payload.error,
933
+ message: auth.payload.message,
934
+ statusCode: auth.statusCode,
935
+ ...auth.wwwAuthenticate !== void 0 ? { extraHeaders: { "WWW-Authenticate": auth.wwwAuthenticate } } : {}
936
+ });
937
+ return { ok: false };
938
+ }
939
+ return { ok: true };
940
+ };
941
+ var emitParseFailure = ({
942
+ response,
943
+ reason,
944
+ allowed
945
+ }) => {
946
+ if (reason === "method_not_allowed") {
947
+ writeErrorResponse({
948
+ response,
949
+ code: "METHOD_NOT_ALLOWED",
950
+ message: "The requested method is not allowed on this route.",
951
+ ...allowed !== void 0 ? { extraHeaders: { Allow: allowed.join(", ") } } : {}
952
+ });
953
+ return;
954
+ }
955
+ const code = reason === "unsafe_id_segment" || reason === "empty_segment" ? "BAD_REQUEST" : "NOT_FOUND";
956
+ const message = code === "BAD_REQUEST" ? "The route contains an unsafe or empty path segment." : "The requested route does not exist.";
957
+ writeErrorResponse({ response, code, message });
958
+ };
959
+ var extractPathname = (url) => {
960
+ const queryIndex = url.indexOf("?");
961
+ return queryIndex === -1 ? url : url.slice(0, queryIndex);
962
+ };
963
+ var defaultAuditWrite = (logger) => (line) => {
964
+ logger.log({ level: "info", message: `audit ${line.trimEnd()}` });
965
+ };
966
+ var handleUnexpectedError = ({
967
+ response,
968
+ error,
969
+ options,
970
+ requestId
971
+ }) => {
972
+ const message = error instanceof Error ? error.message : "Unknown error.";
973
+ options.logger.log({
974
+ level: "error",
975
+ message: `request handler failure: ${message}`,
976
+ requestId
977
+ });
978
+ if (!response.headersSent) {
979
+ writeErrorResponse({
980
+ response,
981
+ code: "INTERNAL_ERROR",
982
+ message: "An unexpected error occurred handling the request."
983
+ });
984
+ } else if (!response.writableEnded) {
985
+ response.end();
986
+ }
987
+ };
988
+
989
+ // packages/server/src/openapi.ts
990
+ var ERROR_ENVELOPE_SCHEMA = {
991
+ type: "object",
992
+ required: ["error", "message"],
993
+ properties: {
994
+ error: { type: "string" },
995
+ message: { type: "string" }
996
+ },
997
+ additionalProperties: false
998
+ };
999
+ var READYZ_SCHEMA = {
1000
+ type: "object",
1001
+ required: ["status", "featureGate", "authConfigured", "checkedAt"],
1002
+ properties: {
1003
+ status: { type: "string", enum: ["ready"] },
1004
+ featureGate: { type: "string", enum: ["enabled", "disabled"] },
1005
+ authConfigured: { type: "boolean" },
1006
+ checkedAt: { type: "string", format: "date-time" }
1007
+ },
1008
+ additionalProperties: false
1009
+ };
1010
+ var HEALTHZ_SCHEMA = {
1011
+ type: "object",
1012
+ required: ["status", "checkedAt"],
1013
+ properties: {
1014
+ status: { type: "string", enum: ["ok"] },
1015
+ checkedAt: { type: "string", format: "date-time" }
1016
+ },
1017
+ additionalProperties: false
1018
+ };
1019
+ var errorResponse = (description) => ({
1020
+ description,
1021
+ content: {
1022
+ "application/json": {
1023
+ schema: { $ref: "#/components/schemas/Error" }
1024
+ }
1025
+ }
1026
+ });
1027
+ var writeRoute = (summary, operationId) => ({
1028
+ post: {
1029
+ summary,
1030
+ operationId,
1031
+ security: [{ bearerAuth: [] }],
1032
+ requestBody: {
1033
+ required: true,
1034
+ content: { "application/json": { schema: { type: "object" } } }
1035
+ },
1036
+ responses: {
1037
+ "200": { description: "Operation completed." },
1038
+ "401": errorResponse("Bearer token is missing or incorrect."),
1039
+ "403": errorResponse("Feature gate or origin policy denied the call."),
1040
+ "413": errorResponse("Request body exceeded the configured limit."),
1041
+ "415": errorResponse("Content-Type is not application/json."),
1042
+ "429": errorResponse("Per-client rate limit was exceeded."),
1043
+ "503": errorResponse("Server is not configured for this route.")
1044
+ }
1045
+ }
1046
+ });
1047
+ var readRoute = (summary, operationId, responseSchemaRef) => ({
1048
+ get: {
1049
+ summary,
1050
+ operationId,
1051
+ responses: {
1052
+ "200": {
1053
+ description: "Result.",
1054
+ content: {
1055
+ "application/json": { schema: { $ref: responseSchemaRef } }
1056
+ }
1057
+ },
1058
+ "404": errorResponse("Resource not found."),
1059
+ "429": errorResponse("Per-client rate limit was exceeded.")
1060
+ }
1061
+ }
1062
+ });
1063
+ var jobScopedWriteRoute = (summary, operationId) => {
1064
+ const route = writeRoute(summary, operationId);
1065
+ route.post["parameters"] = [
1066
+ {
1067
+ name: "jobId",
1068
+ in: "path",
1069
+ required: true,
1070
+ schema: { type: "string", pattern: "^[A-Za-z0-9_.-]{1,128}$" }
1071
+ }
1072
+ ];
1073
+ return route;
1074
+ };
1075
+ var buildPaths = () => ({
1076
+ "/healthz": readRoute(
1077
+ "Liveness probe.",
1078
+ "getHealthz",
1079
+ "#/components/schemas/Healthz"
1080
+ ),
1081
+ "/readyz": readRoute(
1082
+ "Readiness probe.",
1083
+ "getReadyz",
1084
+ "#/components/schemas/Readyz"
1085
+ ),
1086
+ "/openapi.json": readRoute(
1087
+ "Return this OpenAPI document.",
1088
+ "getOpenapi",
1089
+ "#/components/schemas/Error"
1090
+ ),
1091
+ [`${API_ROUTE_PREFIX}/jobs`]: writeRoute(
1092
+ "Submit a Test Intelligence run.",
1093
+ "submitJob"
1094
+ ),
1095
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}`]: readRoute(
1096
+ "Read a job's status.",
1097
+ "getJobStatus",
1098
+ "#/components/schemas/Error"
1099
+ ),
1100
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}/events`]: readRoute(
1101
+ "Server-Sent Events stream of a job's phase events.",
1102
+ "streamJobEvents",
1103
+ "#/components/schemas/Error"
1104
+ ),
1105
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}/evidence/verify`]: jobScopedWriteRoute(
1106
+ "Verify a job's evidence manifest.",
1107
+ "verifyJobEvidence"
1108
+ ),
1109
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}/audit-dossier/verify`]: jobScopedWriteRoute(
1110
+ "Verify a job's audit dossier bundle.",
1111
+ "verifyJobAuditDossier"
1112
+ ),
1113
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}/provenance/verify`]: jobScopedWriteRoute(
1114
+ "Verify a job's provenance document.",
1115
+ "verifyJobProvenance"
1116
+ ),
1117
+ [`${API_ROUTE_PREFIX}/jobs/{jobId}/seal/verify`]: jobScopedWriteRoute(
1118
+ "Verify a job's seal bundle.",
1119
+ "verifyJobSeal"
1120
+ ),
1121
+ [`${API_ROUTE_PREFIX}/review/{jobId}`]: readRoute(
1122
+ "Read the review snapshot for a job.",
1123
+ "getReviewSnapshot",
1124
+ "#/components/schemas/Error"
1125
+ ),
1126
+ [`${API_ROUTE_PREFIX}/review/{jobId}/decision`]: jobScopedWriteRoute(
1127
+ "Record a review decision.",
1128
+ "recordReviewDecision"
1129
+ ),
1130
+ [`${API_ROUTE_PREFIX}/tms/push`]: writeRoute(
1131
+ "Push completed test cases to a configured TMS adapter.",
1132
+ "pushToTms"
1133
+ ),
1134
+ [`${API_ROUTE_PREFIX}/execution/pull`]: writeRoute(
1135
+ "Pull execution evidence from a TMS adapter.",
1136
+ "pullExecutionEvidence"
1137
+ ),
1138
+ [`${API_ROUTE_PREFIX}/onboard`]: writeRoute(
1139
+ "Run tenant onboarding.",
1140
+ "runTenantOnboarding"
1141
+ ),
1142
+ [`${API_ROUTE_PREFIX}/figma-export`]: writeRoute(
1143
+ "Fetch a Figma file as a TI ingest payload.",
1144
+ "exportFigmaForTestIntelligence"
1145
+ )
1146
+ });
1147
+ var buildOpenApiDocument = () => ({
1148
+ openapi: "3.1.0",
1149
+ info: {
1150
+ title: "Test Intelligence API",
1151
+ version: PACKAGE_VERSION,
1152
+ description: "Standalone HTTP surface for the Test Intelligence runtime. Every write route is bearer-protected and fails closed when the test-intelligence feature gate is disabled."
1153
+ },
1154
+ servers: [{ url: "http://127.0.0.1:1983" }],
1155
+ components: {
1156
+ securitySchemes: {
1157
+ bearerAuth: {
1158
+ type: "http",
1159
+ scheme: "bearer"
1160
+ }
1161
+ },
1162
+ schemas: {
1163
+ Error: ERROR_ENVELOPE_SCHEMA,
1164
+ Readyz: READYZ_SCHEMA,
1165
+ Healthz: HEALTHZ_SCHEMA
1166
+ }
1167
+ },
1168
+ paths: buildPaths()
1169
+ });
1170
+
1171
+ // packages/server/src/server.ts
1172
+ var createTestIntelligenceServer = async (options) => {
1173
+ const host = options.host ?? DEFAULT_HOST;
1174
+ const requestedPort = options.port ?? DEFAULT_PORT;
1175
+ const testIntelligenceEnabled = options.testIntelligenceEnabled ?? resolveTestIntelligenceEnabled(options.env);
1176
+ const openApiHandler = ({ response }) => {
1177
+ writeJsonResponse({
1178
+ response,
1179
+ statusCode: 200,
1180
+ payload: buildOpenApiDocument()
1181
+ });
1182
+ };
1183
+ const mergedRouteHandlers = {
1184
+ openapi: openApiHandler,
1185
+ ...options.routeHandlers ?? {}
1186
+ };
1187
+ const handler = createTestIntelligenceRequestHandler({
1188
+ host,
1189
+ port: requestedPort,
1190
+ logger: options.logger,
1191
+ testIntelligenceEnabled,
1192
+ requestsPerMinute: options.requestsPerMinute ?? DEFAULT_RATE_LIMIT_PER_MINUTE,
1193
+ routeHandlers: mergedRouteHandlers,
1194
+ ...options.bearerToken !== void 0 ? { bearerToken: options.bearerToken } : {},
1195
+ ...options.auditWrite !== void 0 ? { auditWrite: options.auditWrite } : {},
1196
+ ...options.allowedCorsOrigins !== void 0 ? { allowedCorsOrigins: options.allowedCorsOrigins } : {}
1197
+ });
1198
+ const server = createServer((request, response) => {
1199
+ handler(request, response);
1200
+ });
1201
+ await listen(server, host, requestedPort);
1202
+ const address = server.address();
1203
+ const boundPort = address !== null && typeof address === "object" ? address.port : requestedPort;
1204
+ const url = `http://${formatHost(host)}:${String(boundPort)}`;
1205
+ const startedAt = Date.now();
1206
+ return {
1207
+ server,
1208
+ host,
1209
+ port: boundPort,
1210
+ url,
1211
+ startedAt,
1212
+ close: () => closeServer(server)
1213
+ };
1214
+ };
1215
+ var listen = (server, host, port) => {
1216
+ return new Promise((resolve, reject) => {
1217
+ const onError = (error) => {
1218
+ server.off("listening", onListening);
1219
+ reject(error);
1220
+ };
1221
+ const onListening = () => {
1222
+ server.off("error", onError);
1223
+ resolve();
1224
+ };
1225
+ server.once("error", onError);
1226
+ server.once("listening", onListening);
1227
+ server.listen(port, host);
1228
+ });
1229
+ };
1230
+ var closeServer = (server) => {
1231
+ return new Promise((resolve, reject) => {
1232
+ server.close((error) => {
1233
+ if (error !== void 0) {
1234
+ reject(error);
1235
+ return;
1236
+ }
1237
+ resolve();
1238
+ });
1239
+ });
1240
+ };
1241
+ var formatHost = (host) => {
1242
+ if (host.includes(":") && !host.startsWith("[")) {
1243
+ return `[${host}]`;
1244
+ }
1245
+ return host;
1246
+ };
1247
+
1248
+ export { PACKAGE_NAME, PACKAGE_VERSION, createTestIntelligenceServer, getPackageIdentity, resolveReleaseStage };
1249
+ //# sourceMappingURL=index.js.map
1250
+ //# sourceMappingURL=index.js.map