theokit 0.12.0 → 0.12.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/adapters/web-shim.d.ts +67 -0
- package/dist/adapters/ws-shim.d.ts +55 -0
- package/dist/agent-events-DosDXkSV.d.ts +94 -0
- package/dist/audit-log-BQWM5YLG.d.ts +60 -0
- package/dist/boot/index.d.ts +39 -0
- package/dist/client/index.d.ts +362 -0
- package/dist/csrf-BBrEZSBW.d.ts +107 -0
- package/dist/csrf-readiness-store-CjIoub3U.d.ts +43 -0
- package/dist/define-websocket-CdK94O-D.d.ts +64 -0
- package/dist/devtools/entry.d.ts +5 -0
- package/dist/error-envelope-BsNzzAV5.d.ts +62 -0
- package/dist/health-route-C0hk64_U.d.ts +57 -0
- package/dist/index-B40qUSrQ.d.ts +575 -0
- package/dist/index.d.ts +361 -0
- package/dist/job-backend-CgC8Xf33.d.ts +68 -0
- package/dist/match-CfbEFRG4.d.ts +26 -0
- package/dist/plugin-runner-BGBkzgi0.d.ts +95 -0
- package/dist/plugin-types-DNJGxr4Z.d.ts +79 -0
- package/dist/rate-limit-BdNDZ3vt.d.ts +58 -0
- package/dist/rate-limit-store-BEJnhWdw.d.ts +72 -0
- package/dist/react-query/index.d.ts +33 -0
- package/dist/schema-BpH6ivDY.d.ts +74 -0
- package/dist/server/agent/index.d.ts +229 -0
- package/dist/server/auth/index.d.ts +419 -0
- package/dist/server/cost/index.d.ts +177 -0
- package/dist/server/cron/index.d.ts +208 -0
- package/dist/server/define/index.d.ts +313 -0
- package/dist/server/http/index.d.ts +11 -0
- package/dist/server/index.d.ts +848 -0
- package/dist/server/jobs/index.d.ts +348 -0
- package/dist/server/observability/index.d.ts +324 -0
- package/dist/server/plugins/index.d.ts +17 -0
- package/dist/server/rate-limit/index.d.ts +105 -0
- package/dist/server/realtime/index.d.ts +15 -0
- package/dist/server/scan/index.d.ts +106 -0
- package/dist/server/security/index.d.ts +193 -0
- package/dist/server/storage/index.d.ts +22 -0
- package/dist/server/webhook/index.d.ts +148 -0
- package/dist/storage-manager-C4jsO0Tp.d.ts +89 -0
- package/dist/storage-types-DsDTCPbp.d.ts +96 -0
- package/dist/vite-plugin/index.d.ts +115 -0
- package/package.json +4 -4
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { R as RateLimitConfig, a as RateLimitResult } from '../../rate-limit-BdNDZ3vt.js';
|
|
2
|
+
export { c as createRateLimiter, b as createRateLimiterWeb } from '../../rate-limit-BdNDZ3vt.js';
|
|
3
|
+
import { R as RateLimitStore } from '../../rate-limit-store-BEJnhWdw.js';
|
|
4
|
+
export { I as InMemoryStore, a as RateLimitState } from '../../rate-limit-store-BEJnhWdw.js';
|
|
5
|
+
import { IncomingMessage } from 'node:http';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* T2.2 — Per-route + per-user rate limiting.
|
|
9
|
+
*
|
|
10
|
+
* Layered on top of `rate-limit-store.ts`. The route map allows
|
|
11
|
+
* declarative policies ("strict /api/login, loose everything else")
|
|
12
|
+
* driven by config, not handler-decorated. `keyBy` selects what
|
|
13
|
+
* identifier the limiter buckets on.
|
|
14
|
+
*
|
|
15
|
+
* ADR D2: per-route via path matching, NOT per-handler decorator.
|
|
16
|
+
* Operators can tune policies without touching route definitions.
|
|
17
|
+
*/
|
|
18
|
+
type KeyByMode = 'ip' | 'session' | 'user' | ((req: IncomingMessage) => string);
|
|
19
|
+
interface RouteRateLimitConfig {
|
|
20
|
+
/** Fallback config used when no per-route entry matches. */
|
|
21
|
+
default?: RateLimitConfig;
|
|
22
|
+
/** Map of path pattern → config. Exact-string keys (RegExp via API). */
|
|
23
|
+
routes?: Record<string, RateLimitConfig>;
|
|
24
|
+
/** Same as `routes` but each entry is a [pattern, config] tuple, RegExp allowed. */
|
|
25
|
+
routePatterns?: readonly [string | RegExp, RateLimitConfig][];
|
|
26
|
+
/** Bucket identifier strategy. Default 'ip'. */
|
|
27
|
+
keyBy?: KeyByMode;
|
|
28
|
+
/** Cookie name used by keyBy='session'. Defaults to 'theo_session'. */
|
|
29
|
+
cookieName?: string;
|
|
30
|
+
/** Optional shared store (for multi-route correlation). Default per-limiter InMemoryStore. */
|
|
31
|
+
store?: RateLimitStore;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Test whether `path` matches `pattern`. String patterns are compared
|
|
35
|
+
* after trailing-slash normalization (EC-5). RegExp uses `.test` after
|
|
36
|
+
* resetting `lastIndex` (defensive against `/g` flag).
|
|
37
|
+
*/
|
|
38
|
+
declare function matchRoutePattern(path: string, pattern: string | RegExp): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Build the rate-limit bucket key for the request based on `keyBy`.
|
|
41
|
+
*
|
|
42
|
+
* EC-6: session mode reads the configured `cookieName`. With the wrong
|
|
43
|
+
* cookie name (e.g., default 'theo_session' but app uses 'app_session'),
|
|
44
|
+
* we fall back to IP so anonymous users still get rate-limited rather
|
|
45
|
+
* than sharing an empty bucket.
|
|
46
|
+
*/
|
|
47
|
+
declare function deriveKey(req: IncomingMessage, keyBy: KeyByMode, cookieName: string): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Per-route rate limiter factory. Returns a sync checker compatible with
|
|
50
|
+
* the existing api-middleware shape.
|
|
51
|
+
*
|
|
52
|
+
* Backwards-compatibility (ADR D2): a flat `{ windowMs, max }` config is
|
|
53
|
+
* accepted and treated as `default` (no per-route variants).
|
|
54
|
+
*/
|
|
55
|
+
declare function createRouteRateLimiter(config: RouteRateLimitConfig | RateLimitConfig): (req: IncomingMessage) => Promise<RateLimitResult>;
|
|
56
|
+
/**
|
|
57
|
+
* T5a.2 Phase D slice 1/3 — Web-Standards rate-limiter inputs context.
|
|
58
|
+
*
|
|
59
|
+
* Web `Request` has no equivalent of `req.socket.remoteAddress` (Node
|
|
60
|
+
* runtime concept) or `req.user` (set by upstream middleware). The
|
|
61
|
+
* Web-shaped rate-limiter requires the caller to pass these explicitly:
|
|
62
|
+
*
|
|
63
|
+
* - `clientIp` — resolved per-runtime (Node: `socket.remoteAddress`;
|
|
64
|
+
* CF Workers: `request.headers.get('cf-connecting-ip')`; Vercel:
|
|
65
|
+
* `x-forwarded-for` first hop; Bun/Deno: adapter-specific).
|
|
66
|
+
* - `userId` — resolved by auth middleware (Phase D slice 3/3 ships
|
|
67
|
+
* the Web-shaped session helper).
|
|
68
|
+
*
|
|
69
|
+
* Defaults: `clientIp = 'unknown'`, `userId = undefined` (matches the
|
|
70
|
+
* IncomingMessage path's fallback semantics).
|
|
71
|
+
*/
|
|
72
|
+
interface DeriveKeyRequestContext {
|
|
73
|
+
clientIp?: string;
|
|
74
|
+
userId?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* T5a.2 Phase D slice 1/3 — Web-Standards-shaped key derivation.
|
|
78
|
+
*
|
|
79
|
+
* Mirror of `deriveKey(req: IncomingMessage, keyBy, cookieName)` for the
|
|
80
|
+
* Web `Request` shape. Same `'ip' | 'session' | 'user'` enum cases (the
|
|
81
|
+
* `function` callback case is IncomingMessage-only because the existing
|
|
82
|
+
* `KeyByMode` callback type is Node-shaped; Web callers use the enum
|
|
83
|
+
* cases or call a future `KeyByModeWeb` shape — out of T5a.2 Phase D scope).
|
|
84
|
+
*
|
|
85
|
+
* Uses `getCookieFromRequest` (Phase B slice 6/6) for session-mode cookie
|
|
86
|
+
* lookup. Cookie parsing has the same CR-009 percent-encoding safety.
|
|
87
|
+
*/
|
|
88
|
+
declare function deriveKeyFromRequest(request: Request, keyBy: Exclude<KeyByMode, (req: IncomingMessage) => string>, cookieName: string, ctx?: DeriveKeyRequestContext): Promise<string>;
|
|
89
|
+
/**
|
|
90
|
+
* T5a.2 Phase D slice 1/3 — Web-Standards rate-limiter factory.
|
|
91
|
+
*
|
|
92
|
+
* Mirror of `createRouteRateLimiter(config)` returning a checker that
|
|
93
|
+
* accepts `(request: Request, ctx?: DeriveKeyRequestContext)` instead of
|
|
94
|
+
* `(req: IncomingMessage)`. Same `RouteRateLimitConfig` accepted; same
|
|
95
|
+
* `keyBy` enum cases; same `InMemoryStore` constraint (CR-005 guard).
|
|
96
|
+
*
|
|
97
|
+
* Same returned `RateLimitResult` shape (headers + limited boolean).
|
|
98
|
+
*
|
|
99
|
+
* Web `Request` has no `req.url` path-only property — uses
|
|
100
|
+
* `new URL(request.url).pathname + search` to derive the URL the way the
|
|
101
|
+
* IncomingMessage path's `req.url ?? ''` would.
|
|
102
|
+
*/
|
|
103
|
+
declare function createRouteRateLimiterWeb(config: RouteRateLimitConfig | RateLimitConfig): (request: Request, ctx?: DeriveKeyRequestContext) => Promise<RateLimitResult>;
|
|
104
|
+
|
|
105
|
+
export { type DeriveKeyRequestContext, type KeyByMode, RateLimitConfig, RateLimitResult, RateLimitStore, type RouteRateLimitConfig, createRouteRateLimiter, createRouteRateLimiterWeb, deriveKey, deriveKeyFromRequest, matchRoutePattern };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { b as WebSocketLike } from '../../define-websocket-CdK94O-D.js';
|
|
2
|
+
import 'node:http';
|
|
3
|
+
|
|
4
|
+
declare class ChannelManager {
|
|
5
|
+
private rooms;
|
|
6
|
+
private wsRooms;
|
|
7
|
+
subscribe(ws: WebSocketLike, room: string): void;
|
|
8
|
+
unsubscribe(ws: WebSocketLike, room: string): void;
|
|
9
|
+
broadcast(room: string, data: unknown, exclude?: WebSocketLike): void;
|
|
10
|
+
broadcastAll(data: unknown): void;
|
|
11
|
+
getRoomSize(room: string): number;
|
|
12
|
+
cleanup(ws: WebSocketLike): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { ChannelManager };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { S as ServerRouteNode } from '../../match-CfbEFRG4.js';
|
|
2
|
+
export { L as LoadModule, c as compilePattern, a as createProductionLoader, b as createViteLoader, m as matchRoute } from '../../match-CfbEFRG4.js';
|
|
3
|
+
import 'vite';
|
|
4
|
+
|
|
5
|
+
interface ActionNode {
|
|
6
|
+
filePath: string;
|
|
7
|
+
actionPath: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Enriched manifest entry per plan g3-server-actions-and-useaction v1.2
|
|
11
|
+
* § Phase 1 / T1.4 + ADR D4. Consumed by virtual module `@theo/actions`
|
|
12
|
+
* (T3.1) and G4 devtools "Actions" tab (T5.1).
|
|
13
|
+
*/
|
|
14
|
+
interface ActionManifestEntry {
|
|
15
|
+
name: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
urlPath: string;
|
|
18
|
+
accept: 'form' | 'json';
|
|
19
|
+
hasInput: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* P#4 plugin-forms shared-schema convention (per plan p4-plugin-forms v1.1 T1.1).
|
|
22
|
+
* When present, points to an isomorphic schema file at
|
|
23
|
+
* `<serverDir>/actions/schemas/<basename>.ts` exporting `export const schema = z.object(...)`.
|
|
24
|
+
* Virtual module emits `import {schema} from '<schemaFilePath>'` + attaches as
|
|
25
|
+
* `actions.X.__zodSchema` so client-side <TheoForm> can drive zodResolver.
|
|
26
|
+
* Undefined when convention not followed (graceful degrade — TheoForm still
|
|
27
|
+
* works via explicit `schema={...}` prop escape hatch).
|
|
28
|
+
*/
|
|
29
|
+
schemaFilePath?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* EC-2: structured error for scan-time defects (name collision, reserved
|
|
33
|
+
* identifier, etc.). Throw at scan time to fail loud — silent shadowing is
|
|
34
|
+
* a security/correctness footgun.
|
|
35
|
+
*/
|
|
36
|
+
declare class ActionScanError extends Error {
|
|
37
|
+
readonly code: 'NAME_COLLISION' | 'RESERVED_NAME';
|
|
38
|
+
readonly conflictingPaths: readonly string[];
|
|
39
|
+
constructor(code: 'NAME_COLLISION' | 'RESERVED_NAME', message: string, conflictingPaths: readonly string[]);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Backward-compatible: original simple-shape scanner used by existing
|
|
43
|
+
* consumers. Preserved verbatim; new consumers should call
|
|
44
|
+
* `scanServerActionsEnriched`.
|
|
45
|
+
*/
|
|
46
|
+
declare function scanServerActions(serverDir: string): ActionNode[];
|
|
47
|
+
/**
|
|
48
|
+
* Enriched scan: light AST detection of `accept: 'form'|'json'` + `input:`
|
|
49
|
+
* presence via regex-after-comment-stripping (EC-9). Throws ActionScanError
|
|
50
|
+
* on file/dir name collision (EC-2) or reserved JS identifier names.
|
|
51
|
+
*
|
|
52
|
+
* Output `ActionManifestEntry[]` is sorted by `name` for deterministic
|
|
53
|
+
* `.theokit/actions-manifest.json` emission.
|
|
54
|
+
*/
|
|
55
|
+
declare function scanServerActionsEnriched(serverDir: string): ActionManifestEntry[];
|
|
56
|
+
|
|
57
|
+
declare function scanServerRoutes(serverDir: string): ServerRouteNode[];
|
|
58
|
+
|
|
59
|
+
interface WebSocketRouteNode {
|
|
60
|
+
filePath: string;
|
|
61
|
+
wsPath: string;
|
|
62
|
+
}
|
|
63
|
+
declare function scanWebSocketRoutes(serverDir: string): WebSocketRouteNode[];
|
|
64
|
+
|
|
65
|
+
interface ManifestRoute {
|
|
66
|
+
filePath: string;
|
|
67
|
+
routePath: string;
|
|
68
|
+
paramNames: string[];
|
|
69
|
+
/** HTTP methods (uppercase) the route file exports. Optional — manifests
|
|
70
|
+
* generated before G1 omit this; loaders treat absence as "unknown". */
|
|
71
|
+
methods?: string[];
|
|
72
|
+
}
|
|
73
|
+
interface ManifestAction {
|
|
74
|
+
filePath: string;
|
|
75
|
+
actionPath: string;
|
|
76
|
+
}
|
|
77
|
+
interface ManifestWebSocket {
|
|
78
|
+
filePath: string;
|
|
79
|
+
wsPath: string;
|
|
80
|
+
}
|
|
81
|
+
interface TheoManifest {
|
|
82
|
+
version: 1;
|
|
83
|
+
generatedAt: string;
|
|
84
|
+
routes: ManifestRoute[];
|
|
85
|
+
actions: ManifestAction[];
|
|
86
|
+
websockets: ManifestWebSocket[];
|
|
87
|
+
}
|
|
88
|
+
interface LoadedManifest {
|
|
89
|
+
routes: ServerRouteNode[];
|
|
90
|
+
actions: ActionNode[];
|
|
91
|
+
websockets: WebSocketRouteNode[];
|
|
92
|
+
}
|
|
93
|
+
declare function generateManifest(serverDir: string): TheoManifest;
|
|
94
|
+
declare function writeManifest(manifest: TheoManifest, outputDir: string): void;
|
|
95
|
+
declare function loadManifest(distDir: string, serverDir: string): LoadedManifest;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Scan the server/middleware/ directory for middleware files.
|
|
99
|
+
* Files are returned sorted alphabetically — use numeric prefixes
|
|
100
|
+
* (e.g. 01-cors.ts, 02-auth.ts) to control execution order.
|
|
101
|
+
*
|
|
102
|
+
* Files starting with '_' or '.' are ignored (helpers, hidden files).
|
|
103
|
+
*/
|
|
104
|
+
declare function scanMiddlewares(serverDir: string): string[];
|
|
105
|
+
|
|
106
|
+
export { type ActionManifestEntry, type ActionNode, ActionScanError, type LoadedManifest, type ManifestAction, type ManifestRoute, type ManifestWebSocket, ServerRouteNode, type TheoManifest, type WebSocketRouteNode, generateManifest, loadManifest, scanMiddlewares, scanServerActions, scanServerActionsEnriched, scanServerRoutes, scanWebSocketRoutes, writeManifest };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { A as AuditLogger } from '../../audit-log-BQWM5YLG.js';
|
|
3
|
+
export { C as CSRF_WARN_CODE, a as CSRF_WARN_DOCS_URL, b as CsrfLogger, c as CsrfMode, d as CsrfWarnPayload, D as DisallowedConfig, e as enforceCsrf, m as matchDisallowed, v as validateCsrf, f as validateCsrfRequest } from '../../csrf-BBrEZSBW.js';
|
|
4
|
+
import { C as CsrfReadinessStore } from '../../csrf-readiness-store-CjIoub3U.js';
|
|
5
|
+
export { a as CsrfReadinessRouteSummary, b as CsrfReadinessSummary, c as CsrfWarnRecord } from '../../csrf-readiness-store-CjIoub3U.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* T5.1 — Built-in CSP report endpoint.
|
|
9
|
+
*
|
|
10
|
+
* Browsers POST violation reports to `report-uri` (legacy `application/csp-report`)
|
|
11
|
+
* or `Reporting API` (`application/reports+json`). Framework auto-registers this
|
|
12
|
+
* endpoint so `cspMode: 'report-only'` is actually useful out of the box.
|
|
13
|
+
*
|
|
14
|
+
* Forwards normalized violations to:
|
|
15
|
+
* - audit logger (`csp.violation`)
|
|
16
|
+
* - devtools dispatcher (dev only, for Errors tab)
|
|
17
|
+
* - optional user hook (Sentry, etc.)
|
|
18
|
+
*
|
|
19
|
+
* EC-2: browser MAY send `{"csp-report": null}`, empty `{}`, or
|
|
20
|
+
* reports+json entries lacking `body`. Handler MUST short-circuit to
|
|
21
|
+
* 204 (valid format, no violation), NEVER crash via null deref.
|
|
22
|
+
*/
|
|
23
|
+
declare const CSP_REPORT_PATH = "/__theo/csp-report";
|
|
24
|
+
interface CspViolation {
|
|
25
|
+
blockedUrl: string;
|
|
26
|
+
documentUrl: string;
|
|
27
|
+
violatedDirective: string;
|
|
28
|
+
effectiveDirective?: string;
|
|
29
|
+
originalPolicy?: string;
|
|
30
|
+
disposition?: 'enforce' | 'report';
|
|
31
|
+
statusCode?: number;
|
|
32
|
+
sourceFile?: string;
|
|
33
|
+
lineNumber?: number;
|
|
34
|
+
columnNumber?: number;
|
|
35
|
+
}
|
|
36
|
+
interface CspReportHandlerOptions {
|
|
37
|
+
auditLogger?: AuditLogger;
|
|
38
|
+
devtoolsDispatcher?: {
|
|
39
|
+
onCspViolation?: (v: CspViolation) => void;
|
|
40
|
+
};
|
|
41
|
+
/** Optional user-provided sink (Sentry, custom log router). Errors here are swallowed. */
|
|
42
|
+
onViolation?: (v: CspViolation) => void;
|
|
43
|
+
}
|
|
44
|
+
declare function normalizeLegacy(raw: Record<string, unknown>): CspViolation;
|
|
45
|
+
/**
|
|
46
|
+
* Map a `reports+json` entry to the internal shape. Returns `null` if
|
|
47
|
+
* the entry lacks a usable `body` object (EC-2).
|
|
48
|
+
*/
|
|
49
|
+
declare function normalizeNew(entry: unknown): CspViolation | null;
|
|
50
|
+
declare function handleCspReport(req: IncomingMessage, res: ServerResponse, opts: CspReportHandlerOptions): Promise<void>;
|
|
51
|
+
declare function handleCspReportRequest(request: Request, opts: CspReportHandlerOptions): Promise<Response>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* T2.2 — `/__theo/csrf-readiness` endpoint.
|
|
55
|
+
*
|
|
56
|
+
* GET /__theo/csrf-readiness → 200 + JSON summary
|
|
57
|
+
* POST /__theo/csrf-readiness/reset → 204; clears the store
|
|
58
|
+
*
|
|
59
|
+
* The reset endpoint enforces CSRF (own dog food) — requires
|
|
60
|
+
* `X-Theo-Action: 1` AND a matching Origin header (EC-15). This avoids
|
|
61
|
+
* the endpoint being weaponizable from a cross-origin page when the
|
|
62
|
+
* endpoint is opt-in exposed in production.
|
|
63
|
+
*
|
|
64
|
+
* Mount opt-in: in dev mode, the host wires this in unconditionally. In
|
|
65
|
+
* production, only mount when `config.security.csrfTelemetry.exposeReadinessEndpoint === true`.
|
|
66
|
+
* EC-10 parallel: returns 404 (via boolean false return) when the URL
|
|
67
|
+
* does not match — the caller continues normal request handling.
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
declare const CSRF_READINESS_PATH = "/__theo/csrf-readiness";
|
|
71
|
+
declare const CSRF_READINESS_RESET_PATH = "/__theo/csrf-readiness/reset";
|
|
72
|
+
declare function handleCsrfReadiness(req: IncomingMessage, res: ServerResponse, store: CsrfReadinessStore): Promise<boolean>;
|
|
73
|
+
declare function handleCsrfReadinessRequest(request: Request, store: CsrfReadinessStore): Promise<Response | null>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Phase 6 — Default Security Headers (D4 / EC-2).
|
|
77
|
+
*
|
|
78
|
+
* Per-response security baseline. The framework applies these BEFORE the
|
|
79
|
+
* route handler runs so a handler can still override via `res.setHeader`.
|
|
80
|
+
*
|
|
81
|
+
* EC-2 backward-compatibility: CSP ships in `report-only` mode by default
|
|
82
|
+
* for 0.2.0. Existing apps with inline scripts or third-party CDN scripts
|
|
83
|
+
* keep working but consumers see violation reports via their CSP report
|
|
84
|
+
* collector (or browser DevTools). 0.3.0 will flip the default to
|
|
85
|
+
* `enforce` after a release of visibility.
|
|
86
|
+
*/
|
|
87
|
+
/**
|
|
88
|
+
* Default Content-Security-Policy. Conservative-but-not-paralyzing:
|
|
89
|
+
*
|
|
90
|
+
* - default-src 'self' — every fetch falls back to same-origin
|
|
91
|
+
* - script-src 'self' — T6.1 (0.3.0): `'unsafe-inline'`
|
|
92
|
+
* dropped. The SSR pipeline issues a
|
|
93
|
+
* per-request nonce that REPLACES
|
|
94
|
+
* this directive at runtime
|
|
95
|
+
* (`'nonce-<token>'`). For static /
|
|
96
|
+
* non-SSR contexts where no nonce is
|
|
97
|
+
* available, the policy is strict-
|
|
98
|
+
* no-inline — user inline scripts
|
|
99
|
+
* must be migrated to external
|
|
100
|
+
* `<script src="...">` or threaded
|
|
101
|
+
* through `ctx.nonce`.
|
|
102
|
+
* - style-src 'self' 'unsafe-inline' — Tailwind + TheoUI use style attrs
|
|
103
|
+
* in animation directives
|
|
104
|
+
* - img-src 'self' data: blob: — supports inline data URIs, blobs
|
|
105
|
+
* from canvas exports
|
|
106
|
+
* - font-src 'self' data: — Geist fonts inline-base64
|
|
107
|
+
* - connect-src 'self' ws: wss: — WebSocket dev HMR + agent streams
|
|
108
|
+
* - frame-ancestors 'none' — clickjacking defense
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* T5.1 — default CSP includes the built-in report endpoint so violations
|
|
112
|
+
* are visible without extra config. Apps that ship a custom `csp` string
|
|
113
|
+
* override the report-uri.
|
|
114
|
+
*/
|
|
115
|
+
declare const DEFAULT_CSP: string;
|
|
116
|
+
/**
|
|
117
|
+
* T1.1 — Default Permissions-Policy header (default-deny stance).
|
|
118
|
+
*
|
|
119
|
+
* Disables sensitive Web APIs that the vast majority of apps don't use.
|
|
120
|
+
* Apps that DO need any of these opt in by overriding `permissionsPolicy`
|
|
121
|
+
* in `config.security.headers`.
|
|
122
|
+
*
|
|
123
|
+
* The seven features listed are the most-abused for: tracking
|
|
124
|
+
* (geolocation, accelerometer, gyroscope), spyware (camera, microphone),
|
|
125
|
+
* fraud (payment), and hardware exfiltration (usb).
|
|
126
|
+
*
|
|
127
|
+
* Aligns with Mozilla baseline + OWASP Secure Headers + Lighthouse PWA.
|
|
128
|
+
*/
|
|
129
|
+
declare const DEFAULT_PERMISSIONS_POLICY = "geolocation=(), camera=(), microphone=(), payment=(), usb=(), accelerometer=(), gyroscope=()";
|
|
130
|
+
type CspMode = 'enforce' | 'report-only' | 'off';
|
|
131
|
+
interface SecurityHeadersConfig {
|
|
132
|
+
/**
|
|
133
|
+
* Custom CSP policy string. When set, replaces the default verbatim.
|
|
134
|
+
* Pass `false` to disable CSP entirely (alias for `cspMode: 'off'`).
|
|
135
|
+
*/
|
|
136
|
+
csp?: string | false;
|
|
137
|
+
/**
|
|
138
|
+
* Enforcement mode for CSP. Default `report-only` for 0.2.0 (EC-2).
|
|
139
|
+
* 0.3.0 will default to `enforce`.
|
|
140
|
+
*/
|
|
141
|
+
cspMode?: CspMode;
|
|
142
|
+
/**
|
|
143
|
+
* Strict-Transport-Security value. Defaults to
|
|
144
|
+
* `max-age=31536000; includeSubDomains` in production. Pass `false` to
|
|
145
|
+
* suppress (e.g. internal LANs without TLS).
|
|
146
|
+
*/
|
|
147
|
+
hsts?: string | false;
|
|
148
|
+
/** X-Frame-Options. Default DENY. */
|
|
149
|
+
frameOptions?: 'DENY' | 'SAMEORIGIN';
|
|
150
|
+
/** X-Content-Type-Options. Default `nosniff`. */
|
|
151
|
+
contentTypeOptions?: 'nosniff';
|
|
152
|
+
/** Referrer-Policy. Default `strict-origin-when-cross-origin`. */
|
|
153
|
+
referrerPolicy?: string;
|
|
154
|
+
/**
|
|
155
|
+
* T1.1 — Permissions-Policy directive string. When set, replaces the
|
|
156
|
+
* default verbatim (no merge — see ADR-EC-12). Pass `false` to disable
|
|
157
|
+
* the header entirely. Schema-level refinement rejects any string
|
|
158
|
+
* containing CR/LF (EC-3 — CWE-113 HTTP Response Splitting mitigation).
|
|
159
|
+
*/
|
|
160
|
+
permissionsPolicy?: string | false;
|
|
161
|
+
}
|
|
162
|
+
interface SecurityEnv {
|
|
163
|
+
production: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* T4.1 — Per-request options passed by the SSR pipeline.
|
|
167
|
+
*
|
|
168
|
+
* - `nonce`: when set, the framework substitutes the `'unsafe-inline'`
|
|
169
|
+
* token in `script-src` with `'nonce-<nonce>'`. EC-3 forces
|
|
170
|
+
* `Cache-Control: private, no-store` so a CDN cannot cache the HTML
|
|
171
|
+
* (carrying one nonce) and re-serve it with a freshly-generated CSP
|
|
172
|
+
* header (carrying a different nonce).
|
|
173
|
+
* - `prerender`: when true, the nonce is IGNORED. Prerendered HTML is
|
|
174
|
+
* generated at build time with no nonce in the script tags; mixing a
|
|
175
|
+
* runtime nonce in the header would block every script. EC-4.
|
|
176
|
+
*/
|
|
177
|
+
interface SecurityHeadersOptions {
|
|
178
|
+
nonce?: string;
|
|
179
|
+
prerender?: boolean;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Build the security headers map for a given config + env. Pure function —
|
|
183
|
+
* returned object can be inspected, logged, or applied to a response.
|
|
184
|
+
*/
|
|
185
|
+
declare function buildSecurityHeaders(config: SecurityHeadersConfig, env: SecurityEnv, options?: SecurityHeadersOptions): Record<string, string>;
|
|
186
|
+
/**
|
|
187
|
+
* Apply security headers to a Node ServerResponse. Called by the
|
|
188
|
+
* api-middleware before the route handler runs. The handler can override
|
|
189
|
+
* any header via `res.setHeader()` — last write wins by Node convention.
|
|
190
|
+
*/
|
|
191
|
+
declare function applySecurityHeaders(res: ServerResponse, config: SecurityHeadersConfig, env: SecurityEnv, options?: SecurityHeadersOptions): void;
|
|
192
|
+
|
|
193
|
+
export { CSP_REPORT_PATH, CSRF_READINESS_PATH, CSRF_READINESS_RESET_PATH, type CspMode, type CspReportHandlerOptions, type CspViolation, CsrfReadinessStore, DEFAULT_CSP, DEFAULT_PERMISSIONS_POLICY, type SecurityEnv, type SecurityHeadersConfig, type SecurityHeadersOptions, applySecurityHeaders, buildSecurityHeaders, handleCspReport, handleCspReportRequest, handleCsrfReadiness, handleCsrfReadinessRequest, normalizeLegacy, normalizeNew };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { P as PostgresFactory, R as RedisFactory, S as StorageManager, g as getStorageManager } from '../../storage-manager-C4jsO0Tp.js';
|
|
2
|
+
export { G as GenericFactory, a as PoolLike, P as PostgresDatabaseConfig, c as RedisLike, R as RedisServerConfig, S as ServerConfig, d as StorageAdapter, b as StorageConfig, T as TlsConfig } from '../../storage-types-DsDTCPbp.js';
|
|
3
|
+
|
|
4
|
+
interface UnstorageInstance<T> {
|
|
5
|
+
getItem(key: string): Promise<T | null>;
|
|
6
|
+
setItem(key: string, value: T): Promise<void>;
|
|
7
|
+
removeItem(key: string): Promise<void>;
|
|
8
|
+
keys(prefix?: string): Promise<string[]>;
|
|
9
|
+
hasItem(key: string): Promise<boolean>;
|
|
10
|
+
clear(prefix?: string): Promise<void>;
|
|
11
|
+
dispose?(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
declare function useUnstorage<T = unknown>(name: string, driver?: unknown): Promise<UnstorageInstance<T>>;
|
|
14
|
+
|
|
15
|
+
interface Db0Database {
|
|
16
|
+
exec: (sql: string) => Promise<unknown>;
|
|
17
|
+
prepare: (sql: string) => unknown;
|
|
18
|
+
sql: (strings: TemplateStringsArray, ...args: unknown[]) => Promise<unknown>;
|
|
19
|
+
}
|
|
20
|
+
declare function useDatabase(name: string, connector: unknown): Promise<Db0Database>;
|
|
21
|
+
|
|
22
|
+
export { type Db0Database, type UnstorageInstance, useDatabase, useUnstorage };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time byte comparison.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `node:crypto.timingSafeEqual` when available (Node 6.6+) and falls
|
|
5
|
+
* back to a constant-time XOR loop for runtimes without `node:crypto`
|
|
6
|
+
* (Cloudflare Workers, Deno, Bun edge). Required to defeat timing attacks
|
|
7
|
+
* on webhook signature verification — a naive `===` or `Buffer.equals`
|
|
8
|
+
* would short-circuit on first mismatch byte and leak signature position
|
|
9
|
+
* via wall-clock timing.
|
|
10
|
+
*
|
|
11
|
+
* Web Crypto's `crypto.subtle.verify` would also work, but requires a
|
|
12
|
+
* `CryptoKey` object and is asymmetric (verify a signature against a key).
|
|
13
|
+
* For our case — comparing two pre-computed digests — raw byte equality
|
|
14
|
+
* is the correct primitive.
|
|
15
|
+
*
|
|
16
|
+
* @see https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b
|
|
17
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/verify
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Returns true iff `a` and `b` have identical bytes. Takes time
|
|
21
|
+
* proportional to `a.length` regardless of where bytes differ.
|
|
22
|
+
*
|
|
23
|
+
* @throws TypeError if either argument is not a Uint8Array.
|
|
24
|
+
*/
|
|
25
|
+
declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read a Web `Request` body as a raw string EXACTLY ONCE and expose it
|
|
29
|
+
* alongside the original request (which remains readable by downstream
|
|
30
|
+
* code). Enforces a configurable `maxBodyBytes` cap to prevent OOM via
|
|
31
|
+
* pathological POST.
|
|
32
|
+
*
|
|
33
|
+
* MUST be called FIRST in the webhook pipeline, before any other code
|
|
34
|
+
* touches the body (`request.text()`, `request.json()`, parsers). Once
|
|
35
|
+
* the body is consumed, `Request.clone()` throws and this function fails.
|
|
36
|
+
*
|
|
37
|
+
* EC-101: default `maxBodyBytes = 1_000_000` (1 MB) covers Stripe
|
|
38
|
+
* (256 KB max), Slack (4 MB but compressed), and is well below Node
|
|
39
|
+
* memory thresholds. GitHub webhooks up to 25 MB MUST opt in
|
|
40
|
+
* (`maxBodyBytes: 25_000_000`).
|
|
41
|
+
*
|
|
42
|
+
* @see https://docs.stripe.com/webhooks/signatures
|
|
43
|
+
* @see https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks
|
|
44
|
+
*/
|
|
45
|
+
declare const DEFAULT_MAX_BODY_BYTES = 1000000;
|
|
46
|
+
interface ReadRawBodyOptions {
|
|
47
|
+
/** Maximum bytes to read before throwing `BodyTooLargeError`. Defaults to 1 MB. */
|
|
48
|
+
maxBodyBytes?: number;
|
|
49
|
+
}
|
|
50
|
+
interface RawBodyResult {
|
|
51
|
+
/** UTF-8 decoded body (or empty string when no body present). */
|
|
52
|
+
rawBody: string;
|
|
53
|
+
/** The ORIGINAL request, still readable by downstream code. */
|
|
54
|
+
bodyClone: Request;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Thrown when the request body exceeds `maxBodyBytes`. Carries status
|
|
58
|
+
* 413 (Payload Too Large) and stable code `BODY_TOO_LARGE` so callers
|
|
59
|
+
* can map it to an HTTP response without sniffing the message.
|
|
60
|
+
*/
|
|
61
|
+
declare class BodyTooLargeError extends Error {
|
|
62
|
+
readonly limit: number;
|
|
63
|
+
readonly actualSeen: number;
|
|
64
|
+
readonly status = 413;
|
|
65
|
+
readonly code = "BODY_TOO_LARGE";
|
|
66
|
+
constructor(limit: number, actualSeen: number);
|
|
67
|
+
}
|
|
68
|
+
declare function readRawBody(request: Request, options?: ReadRawBodyOptions): Promise<RawBodyResult>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Webhook primitive types (R0.5.10).
|
|
72
|
+
*
|
|
73
|
+
* @see docs/adr/0005-webhook-verify-inline-function.md
|
|
74
|
+
*/
|
|
75
|
+
type VerifyResult = {
|
|
76
|
+
ok: true;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
reason: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* A verify function — pure, async-or-sync. Takes the cloned request
|
|
83
|
+
* (raw body NOT yet consumed by handler), returns ok/notOk + reason.
|
|
84
|
+
*
|
|
85
|
+
* Per ADR-0005, helper factories (`stripe`, `github`, `slack`) return
|
|
86
|
+
* this shape. Users can also write inline `verify: async (req) => ...`
|
|
87
|
+
* for custom providers.
|
|
88
|
+
*/
|
|
89
|
+
type VerifyFn = (req: Request) => Promise<VerifyResult> | VerifyResult;
|
|
90
|
+
interface WebhookContext {
|
|
91
|
+
/** The cloned Request (still readable by user code). */
|
|
92
|
+
readonly request: Request;
|
|
93
|
+
/** Raw body bytes as UTF-8 string — already-verified at this point. */
|
|
94
|
+
readonly rawBody: string;
|
|
95
|
+
/** W3C trace_id propagated from request. */
|
|
96
|
+
readonly traceId: string;
|
|
97
|
+
/** Abort signal triggered by client disconnect or server stop. */
|
|
98
|
+
readonly signal: AbortSignal;
|
|
99
|
+
}
|
|
100
|
+
interface DefineWebhookOptions {
|
|
101
|
+
verify: VerifyFn;
|
|
102
|
+
handler: (ctx: WebhookContext) => unknown;
|
|
103
|
+
/** Override `readRawBody` body size cap (EC-101). Default 1MB. */
|
|
104
|
+
maxBodyBytes?: number;
|
|
105
|
+
}
|
|
106
|
+
interface WebhookDefinition {
|
|
107
|
+
readonly verify: VerifyFn;
|
|
108
|
+
readonly handler: (ctx: WebhookContext) => unknown;
|
|
109
|
+
readonly maxBodyBytes?: number;
|
|
110
|
+
/** Discriminator for runtime dispatch. */
|
|
111
|
+
readonly __theokit_kind: 'webhook';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Declare a webhook handler with first-class signature verification
|
|
116
|
+
* (R0.5.10, ADR-0005). Pure identity helper — returns the definition
|
|
117
|
+
* unchanged for downstream dispatch.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* // server/webhooks/stripe.ts
|
|
122
|
+
* import { defineWebhook } from 'theokit/server'
|
|
123
|
+
* import { stripe } from 'theokit/server/webhook/providers'
|
|
124
|
+
*
|
|
125
|
+
* export default defineWebhook({
|
|
126
|
+
* verify: stripe({ secret: process.env.STRIPE_WEBHOOK_SECRET! }),
|
|
127
|
+
* async handler({ rawBody }) {
|
|
128
|
+
* const event = JSON.parse(rawBody)
|
|
129
|
+
* // ...
|
|
130
|
+
* return new Response('ok')
|
|
131
|
+
* },
|
|
132
|
+
* })
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
declare function defineWebhook(opts: DefineWebhookOptions): WebhookDefinition;
|
|
136
|
+
/**
|
|
137
|
+
* Dispatch a webhook request through the definition's verify → handler
|
|
138
|
+
* pipeline. Returns the handler's `Response` (or a wrapped one), or a
|
|
139
|
+
* 401/413 response when verification or body size guards trip.
|
|
140
|
+
*
|
|
141
|
+
* EC-101: enforces `maxBodyBytes` (default 1MB via readRawBody).
|
|
142
|
+
* EC-103: every throw from `verify` (sync or async) is treated as
|
|
143
|
+
* `{ok: false, reason: 'verify threw: <message>'}`. Handler NEVER
|
|
144
|
+
* invoked on verify failure.
|
|
145
|
+
*/
|
|
146
|
+
declare function dispatchWebhook(def: WebhookDefinition, request: Request): Promise<Response>;
|
|
147
|
+
|
|
148
|
+
export { BodyTooLargeError, DEFAULT_MAX_BODY_BYTES, type DefineWebhookOptions, type RawBodyResult, type ReadRawBodyOptions, type VerifyFn, type VerifyResult, type WebhookContext, type WebhookDefinition, defineWebhook, dispatchWebhook, readRawBody, timingSafeEqual };
|