@theokit/http 0.5.3 → 0.6.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/LICENSE +201 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.js +5 -5
- package/dist/{chunk-AVSB6444.js → chunk-3PGQVQWG.js} +2 -2
- package/dist/{chunk-474GXFYG.js → chunk-6W4T4DPJ.js} +4 -4
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/{chunk-UVV6THUP.js → chunk-ELCXHPAD.js} +3 -3
- package/dist/{chunk-H7V4WQAM.js → chunk-GQ2UH554.js} +127 -9
- package/dist/chunk-GQ2UH554.js.map +1 -0
- package/dist/{chunk-IHUNGY64.js → chunk-HLW7YKZE.js} +2 -2
- package/dist/{chunk-XPFP4PQO.js → chunk-MQAJWR3K.js} +2 -2
- package/dist/chunk-MQAJWR3K.js.map +1 -0
- package/dist/{chunk-5T6NAG62.js → chunk-QGB5YC4T.js} +3 -3
- package/dist/chunk-RC4V75DI.js +257 -0
- package/dist/chunk-RC4V75DI.js.map +1 -0
- package/dist/exception-filter-chain-O45FXGEB.js +10 -0
- package/dist/index.d.ts +280 -5
- package/dist/index.js +200 -22
- package/dist/index.js.map +1 -1
- package/dist/interceptor-chain-ELSL6KZT.js +9 -0
- package/dist/{middleware-consumer-ljxK1fU_.d.ts → middleware-consumer-DcaksawH.d.ts} +1 -1
- package/dist/runtime-node.js +2 -2
- package/dist/theokit-plugin.d.ts +1 -1
- package/dist/theokit-plugin.js +9 -169
- package/dist/theokit-plugin.js.map +1 -1
- package/package.json +24 -14
- package/dist/chunk-H7V4WQAM.js.map +0 -1
- package/dist/chunk-I2ADDC6C.js +0 -87
- package/dist/chunk-I2ADDC6C.js.map +0 -1
- package/dist/chunk-XPFP4PQO.js.map +0 -1
- package/dist/chunk-Z56OUB3Z.js +0 -19
- package/dist/exception-filter-chain-GU547TYA.js +0 -10
- package/dist/interceptor-chain-BEI3ROM7.js +0 -9
- package/dist/server.node-HU6QI5YI.js +0 -23373
- package/dist/server.node-HU6QI5YI.js.map +0 -1
- /package/dist/{chunk-AVSB6444.js.map → chunk-3PGQVQWG.js.map} +0 -0
- /package/dist/{chunk-474GXFYG.js.map → chunk-6W4T4DPJ.js.map} +0 -0
- /package/dist/{chunk-Z56OUB3Z.js.map → chunk-7QVYU63E.js.map} +0 -0
- /package/dist/{chunk-UVV6THUP.js.map → chunk-ELCXHPAD.js.map} +0 -0
- /package/dist/{chunk-IHUNGY64.js.map → chunk-HLW7YKZE.js.map} +0 -0
- /package/dist/{chunk-5T6NAG62.js.map → chunk-QGB5YC4T.js.map} +0 -0
- /package/dist/{exception-filter-chain-GU547TYA.js.map → exception-filter-chain-O45FXGEB.js.map} +0 -0
- /package/dist/{interceptor-chain-BEI3ROM7.js.map → interceptor-chain-ELSL6KZT.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { D as DiContainer, M as MiddlewareConsumerImpl } from './middleware-consumer-
|
|
3
|
-
export { a as MiddlewareConfigProxy, b as MiddlewareFn, N as NestMiddleware, R as ResolvedMiddleware, m as middlewareMatchesPath, r as resolveOrNew, c as runMiddleware } from './middleware-consumer-
|
|
2
|
+
import { D as DiContainer, M as MiddlewareConsumerImpl } from './middleware-consumer-DcaksawH.js';
|
|
3
|
+
export { a as MiddlewareConfigProxy, b as MiddlewareFn, N as NestMiddleware, R as ResolvedMiddleware, m as middlewareMatchesPath, r as resolveOrNew, c as runMiddleware } from './middleware-consumer-DcaksawH.js';
|
|
4
4
|
import { S as ServerHandle } from './types-CGthbcon.js';
|
|
5
5
|
export { ReadinessCheck, TheoApp, TheoAppOptions } from './app.js';
|
|
6
|
+
import * as ReactTypes from 'react';
|
|
6
7
|
|
|
7
8
|
interface ControllerOptions {
|
|
8
9
|
host?: string;
|
|
@@ -354,6 +355,63 @@ declare function joinPath(prefix: string, path: string): string;
|
|
|
354
355
|
*/
|
|
355
356
|
declare function walkControllerMetadata(ControllerClass: Function): WalkResult[];
|
|
356
357
|
|
|
358
|
+
/**
|
|
359
|
+
* SWC-powered module loader for controller files with parameter decorators.
|
|
360
|
+
*
|
|
361
|
+
* esbuild (used by tsx/Vite SSR) fundamentally cannot parse TypeScript
|
|
362
|
+
* parameter decorators (`@Body()`, `@Param()`, `@Query()`). This loader
|
|
363
|
+
* uses @swc/core to transform controller files with full decorator support
|
|
364
|
+
* (legacyDecorator + decoratorMetadata), then imports them via a temp .mjs
|
|
365
|
+
* file written in the SAME directory (preserving relative import resolution).
|
|
366
|
+
*
|
|
367
|
+
* Pattern: follows Next.js's approach (read tsconfig → configure SWC)
|
|
368
|
+
* but scoped to the http-decorators package, not the framework core.
|
|
369
|
+
*
|
|
370
|
+
* @see references/next.js/packages/next/src/build/swc/options.ts
|
|
371
|
+
*/
|
|
372
|
+
|
|
373
|
+
interface SwcCore {
|
|
374
|
+
transformSync: (src: string, opts: unknown) => {
|
|
375
|
+
code: string;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Transform TypeScript controller SOURCE (with parameter decorators) into
|
|
380
|
+
* ESM code, emitting the `design:paramtypes` / decorator metadata that esbuild
|
|
381
|
+
* cannot produce. Pure code→code — no file I/O, no module load — so it is
|
|
382
|
+
* reusable both by {@link loadControllerWithSwc} (which then temp-writes +
|
|
383
|
+
* imports) and by a build-tool transform hook that returns `{ code }` directly.
|
|
384
|
+
*
|
|
385
|
+
* The `@swc/core` loader is injectable (`loadSwc`) for testability; it defaults
|
|
386
|
+
* to the cached singleton.
|
|
387
|
+
*
|
|
388
|
+
* @throws HttpDecoratorsConfigError when @swc/core is unavailable.
|
|
389
|
+
*/
|
|
390
|
+
declare function transformControllerSource(source: string, filename: string, loadSwc?: () => Promise<SwcCore | null>): Promise<string>;
|
|
391
|
+
/**
|
|
392
|
+
* Load a TypeScript controller file using @swc/core for decorator support.
|
|
393
|
+
*
|
|
394
|
+
* Strategy:
|
|
395
|
+
* 1. Read source .ts file
|
|
396
|
+
* 2. Transform via {@link transformControllerSource} (legacyDecorator + metadata)
|
|
397
|
+
* 3. Write temp .mjs in SAME directory (relative imports resolve correctly)
|
|
398
|
+
* 4. Dynamic import() the .mjs — transitive .ts imports go through
|
|
399
|
+
* tsx/Vite's global hook (they don't have parameter decorators)
|
|
400
|
+
* 5. Cleanup temp file
|
|
401
|
+
*/
|
|
402
|
+
declare function loadControllerWithSwc(absoluteFilePath: string): Promise<Record<string, unknown>>;
|
|
403
|
+
/**
|
|
404
|
+
* Scan a glob pattern for controller files and load them all via SWC.
|
|
405
|
+
* Returns an array of controller class constructors found.
|
|
406
|
+
*/
|
|
407
|
+
declare function loadControllersFromGlob(rootDir: string, pattern: string): Promise<Function[]>;
|
|
408
|
+
/**
|
|
409
|
+
* Check if a function has @Controller metadata.
|
|
410
|
+
* Uses Symbol.for() global registry key — same Symbol instance across
|
|
411
|
+
* module boundaries (SWC-loaded controllers share the global registry).
|
|
412
|
+
*/
|
|
413
|
+
declare function isControllerClass(fn: Function): boolean;
|
|
414
|
+
|
|
357
415
|
interface RouteRegistration {
|
|
358
416
|
verb: HttpVerb;
|
|
359
417
|
fullPath: string;
|
|
@@ -377,6 +435,25 @@ interface CreateDecoratorServerOptions {
|
|
|
377
435
|
container?: DiContainer;
|
|
378
436
|
configure?: (consumer: MiddlewareConsumerImpl) => void;
|
|
379
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* A pure Web-Standard controller handler: callable as `(request) => Response | null`
|
|
440
|
+
* plus a non-executing `matches(method, pathname)` route probe (so a host can gate
|
|
441
|
+
* — e.g. CSRF — before dispatch runs a handler).
|
|
442
|
+
*/
|
|
443
|
+
interface DecoratorHandler {
|
|
444
|
+
(request: Request): Promise<Response | null>;
|
|
445
|
+
/** True when a controller route owns `method` + `pathname` (no handler executed). */
|
|
446
|
+
matches(method: string, pathname: string): boolean;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Build a pure Web-Standard request handler from decorated controller classes,
|
|
450
|
+
* WITHOUT binding a network listener. Returns a {@link DecoratorHandler} whose
|
|
451
|
+
* call returns `null` when no controller route matched — the caller decides the
|
|
452
|
+
* miss (a standalone server answers 404; a host middleware falls through to its
|
|
453
|
+
* own routing). This is the reusable dispatch seam consumed by the framework's
|
|
454
|
+
* controller dispatch (#122) so it never re-implements match/bind/validate.
|
|
455
|
+
*/
|
|
456
|
+
declare function createDecoratorHandler(controllersOrOpts: Function[] | CreateDecoratorServerOptions): DecoratorHandler;
|
|
380
457
|
declare function createDecoratorServer(controllersOrOpts: Function[] | CreateDecoratorServerOptions): ServerHandle;
|
|
381
458
|
|
|
382
459
|
/**
|
|
@@ -998,10 +1075,10 @@ type InferResponse<D> = D extends {
|
|
|
998
1075
|
response: infer R;
|
|
999
1076
|
} ? R : unknown;
|
|
1000
1077
|
interface TypedClient<M extends RouteMap> {
|
|
1001
|
-
get<P extends string
|
|
1078
|
+
get<P extends string>(path: P, opts?: {
|
|
1002
1079
|
query?: Record<string, string>;
|
|
1003
1080
|
headers?: Record<string, string>;
|
|
1004
|
-
}): Promise<InferResponse<M[`GET ${P}`]
|
|
1081
|
+
}): Promise<InferResponse<M[`GET ${P}`]>>;
|
|
1005
1082
|
post<P extends string>(path: P, body?: InferBody<M[`POST ${P}`]>, opts?: {
|
|
1006
1083
|
headers?: Record<string, string>;
|
|
1007
1084
|
}): Promise<InferResponse<M[`POST ${P}`]>>;
|
|
@@ -1091,4 +1168,202 @@ declare function isSafePath(pathname: string): boolean;
|
|
|
1091
1168
|
*/
|
|
1092
1169
|
declare function createStaticHandler(options?: StaticOptions): (request: Request) => Promise<Response | null>;
|
|
1093
1170
|
|
|
1094
|
-
|
|
1171
|
+
interface TheoRequestContext {
|
|
1172
|
+
/** The raw Web Standard Request object. */
|
|
1173
|
+
request: Request;
|
|
1174
|
+
/** URL pathname (e.g., '/api/tasks'). */
|
|
1175
|
+
pathname: string;
|
|
1176
|
+
/** HTTP method (e.g., 'GET', 'POST'). */
|
|
1177
|
+
method: string;
|
|
1178
|
+
/** Route params extracted from URL (e.g., { id: '42' }). */
|
|
1179
|
+
params: Record<string, string>;
|
|
1180
|
+
/** Matched controller or agent class name (if resolved). */
|
|
1181
|
+
handler?: string;
|
|
1182
|
+
/** Request start time (ms). */
|
|
1183
|
+
startedAt: number;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Get the current request context.
|
|
1187
|
+
*
|
|
1188
|
+
* @throws Error if called outside a request (e.g., at module load time).
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```typescript
|
|
1192
|
+
* import { getRequestContext } from '@theokit/http'
|
|
1193
|
+
*
|
|
1194
|
+
* class AuthGuard {
|
|
1195
|
+
* canActivate() {
|
|
1196
|
+
* const { request } = getRequestContext()
|
|
1197
|
+
* return request.headers.get('authorization') !== null
|
|
1198
|
+
* }
|
|
1199
|
+
* }
|
|
1200
|
+
* ```
|
|
1201
|
+
*/
|
|
1202
|
+
declare function getRequestContext(): TheoRequestContext;
|
|
1203
|
+
/**
|
|
1204
|
+
* Try to get the current request context, or null if not in a request.
|
|
1205
|
+
* Useful for code that may run both inside and outside requests.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function tryGetRequestContext(): TheoRequestContext | null;
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Error digestion — converts any thrown value into a stable hash + context.
|
|
1211
|
+
*
|
|
1212
|
+
* Inspired by Next.js `create-error-handler.tsx`. Produces a deterministic
|
|
1213
|
+
* digest ID suitable for logging and client-safe error references without
|
|
1214
|
+
* leaking stack traces in production.
|
|
1215
|
+
*
|
|
1216
|
+
* Uses djb2 hash (sync, no crypto dependency) per ADR D3.
|
|
1217
|
+
*/
|
|
1218
|
+
interface ErrorContext {
|
|
1219
|
+
route?: string;
|
|
1220
|
+
phase?: 'guard' | 'interceptor' | 'handler' | 'filter' | 'agent';
|
|
1221
|
+
source?: string;
|
|
1222
|
+
}
|
|
1223
|
+
interface DigestedError {
|
|
1224
|
+
digest: string;
|
|
1225
|
+
message: string;
|
|
1226
|
+
status: number;
|
|
1227
|
+
context: ErrorContext;
|
|
1228
|
+
stack?: string;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Converts any thrown value into a structured {@link DigestedError}.
|
|
1232
|
+
*
|
|
1233
|
+
* - Sync (never async) — safe to call inside catch blocks.
|
|
1234
|
+
* - Stack trace stripped when `process.env.NODE_ENV === 'production'`.
|
|
1235
|
+
* - Preserves {@link HttpException} status codes.
|
|
1236
|
+
* - Handles non-Error throws (string, number, object).
|
|
1237
|
+
*/
|
|
1238
|
+
declare function digestError(err: unknown, context?: ErrorContext): DigestedError;
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Component tree composition — recursive wrapping of file-convention
|
|
1242
|
+
* components (layout, page, loading, error, not-found) into a React
|
|
1243
|
+
* element tree with Suspense and error boundaries.
|
|
1244
|
+
*
|
|
1245
|
+
* Inspired by Next.js `create-component-tree.tsx`.
|
|
1246
|
+
*
|
|
1247
|
+
* React is loaded via dynamic `import('react')` because it is an
|
|
1248
|
+
* optional peerDep of @theokit/http (EC-2).
|
|
1249
|
+
*/
|
|
1250
|
+
|
|
1251
|
+
interface RouteTree {
|
|
1252
|
+
layout?: ReactTypes.ComponentType<{
|
|
1253
|
+
children: ReactTypes.ReactNode;
|
|
1254
|
+
}>;
|
|
1255
|
+
page?: ReactTypes.ComponentType;
|
|
1256
|
+
loading?: ReactTypes.ComponentType;
|
|
1257
|
+
error?: ReactTypes.ComponentType;
|
|
1258
|
+
notFound?: ReactTypes.ComponentType;
|
|
1259
|
+
children?: Record<string, RouteTree>;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Composes a {@link RouteTree} into a nested React element tree.
|
|
1263
|
+
*
|
|
1264
|
+
* Wrapping order (outermost → innermost):
|
|
1265
|
+
* layout → ErrorBoundary(error) → Suspense(loading) → page
|
|
1266
|
+
*
|
|
1267
|
+
* Returns `null` when no `page` component is found in the tree.
|
|
1268
|
+
*
|
|
1269
|
+
* @param tree - The route tree describing file conventions found.
|
|
1270
|
+
* @returns A React element or `null`.
|
|
1271
|
+
*/
|
|
1272
|
+
declare function composeComponentTree(tree: RouteTree): Promise<ReactTypes.ReactElement | null>;
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Streaming SSR — renders a React element tree to a `ReadableStream<Uint8Array>`.
|
|
1276
|
+
*
|
|
1277
|
+
* Inspired by Next.js `stream-ops.ts`. Uses Web Standard `renderToReadableStream`
|
|
1278
|
+
* (works on Node 18+, Bun, Deno). Falls back to `renderToString` wrapped in a
|
|
1279
|
+
* ReadableStream when `renderToReadableStream` is not available (React 17 — EC-4).
|
|
1280
|
+
*
|
|
1281
|
+
* React is loaded via dynamic `import('react-dom/server')` because react-dom
|
|
1282
|
+
* is an optional peerDep of @theokit/http (ADR D2).
|
|
1283
|
+
*/
|
|
1284
|
+
|
|
1285
|
+
interface StreamRenderOptions {
|
|
1286
|
+
/** React element to render */
|
|
1287
|
+
root: ReactTypes.ReactElement;
|
|
1288
|
+
/** Whether to wait for all Suspense to resolve (default: false — stream immediately) */
|
|
1289
|
+
waitForAll?: boolean;
|
|
1290
|
+
}
|
|
1291
|
+
interface StreamRenderResult {
|
|
1292
|
+
/** The HTML stream */
|
|
1293
|
+
stream: ReadableStream<Uint8Array>;
|
|
1294
|
+
/** Promise that resolves when all content has been flushed */
|
|
1295
|
+
allReady: Promise<void>;
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Renders a React element to a `ReadableStream<Uint8Array>` (Web Standard).
|
|
1299
|
+
*
|
|
1300
|
+
* 1. Tries `renderToReadableStream` first (React 18+ — Web Standard API).
|
|
1301
|
+
* 2. Falls back to `renderToString` wrapped in a ReadableStream when
|
|
1302
|
+
* `renderToReadableStream` is not available (React 17 compat — EC-4).
|
|
1303
|
+
* 3. Prepends `<!DOCTYPE html>` to the stream.
|
|
1304
|
+
*
|
|
1305
|
+
* **EC-7 — Streaming error handling:** In streaming mode, errors thrown inside
|
|
1306
|
+
* Suspense boundaries are caught by React's streaming error handler and result
|
|
1307
|
+
* in a client-side error boundary activation (the shell is already sent). In
|
|
1308
|
+
* string mode (`renderToString`), errors throw synchronously before any bytes
|
|
1309
|
+
* are sent, allowing a full 500 error page. Choose streaming when you want
|
|
1310
|
+
* progressive rendering; choose string mode when you want atomic error handling.
|
|
1311
|
+
*/
|
|
1312
|
+
declare function renderToStream(options: StreamRenderOptions): Promise<StreamRenderResult>;
|
|
1313
|
+
/**
|
|
1314
|
+
* Converts a {@link StreamRenderResult} into a Web Standard `Response`.
|
|
1315
|
+
*
|
|
1316
|
+
* Convenience wrapper for use in request handlers:
|
|
1317
|
+
* ```ts
|
|
1318
|
+
* const result = await renderToStream({ root: <App /> })
|
|
1319
|
+
* return streamToResponse(result)
|
|
1320
|
+
* ```
|
|
1321
|
+
*/
|
|
1322
|
+
declare function streamToResponse(result: StreamRenderResult): Response;
|
|
1323
|
+
|
|
1324
|
+
interface RevalidationSignal {
|
|
1325
|
+
kind: 'tag' | 'path';
|
|
1326
|
+
value: string;
|
|
1327
|
+
timestamp: number;
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Signal that a cache tag should be revalidated.
|
|
1331
|
+
*
|
|
1332
|
+
* Safe to call from any request handler (controller, agent, action).
|
|
1333
|
+
* Signals are accumulated per-request and consumed by the cache engine.
|
|
1334
|
+
*
|
|
1335
|
+
* @example
|
|
1336
|
+
* ```typescript
|
|
1337
|
+
* import { revalidateTag } from '@theokit/http'
|
|
1338
|
+
*
|
|
1339
|
+
* @Post()
|
|
1340
|
+
* async createTask(@Body(schema) body) {
|
|
1341
|
+
* const task = await db.tasks.create(body)
|
|
1342
|
+
* revalidateTag('tasks') // invalidate cached task lists
|
|
1343
|
+
* return task
|
|
1344
|
+
* }
|
|
1345
|
+
* ```
|
|
1346
|
+
*/
|
|
1347
|
+
declare function revalidateTag(tag: string): void;
|
|
1348
|
+
/**
|
|
1349
|
+
* Signal that a cached path should be revalidated.
|
|
1350
|
+
*
|
|
1351
|
+
* @example
|
|
1352
|
+
* ```typescript
|
|
1353
|
+
* import { revalidatePath } from '@theokit/http'
|
|
1354
|
+
*
|
|
1355
|
+
* @Delete(':id')
|
|
1356
|
+
* async removeTask(@Param('id') id: string) {
|
|
1357
|
+
* await db.tasks.delete(id)
|
|
1358
|
+
* revalidatePath('/api/tasks')
|
|
1359
|
+
* }
|
|
1360
|
+
* ```
|
|
1361
|
+
*/
|
|
1362
|
+
declare function revalidatePath(path: string): void;
|
|
1363
|
+
/**
|
|
1364
|
+
* Get all revalidation signals collected during the current request.
|
|
1365
|
+
* Called by the cache engine after the handler completes.
|
|
1366
|
+
*/
|
|
1367
|
+
declare function getRevalidationSignals(): RevalidationSignal[];
|
|
1368
|
+
|
|
1369
|
+
export { All, type ArgumentsHost, BadGatewayException, BadRequestException, Body, CATCH_EXCEPTIONS, CONTROLLER_PREFIX, type CanActivate, Catch, ConflictException, Controller, type ControllerMeta, type ControllerOptions, type DecoratorHandler, Delete, DiContainer, type DigestedError, type ErrorContext, type ExceptionFilter, type ExecutionContext, ForbiddenException, GatewayTimeoutException, Get, GoneException, Head, Header, Headers, HostParam, HttpCode, HttpDecoratorsConfigError, HttpException, type HttpExceptionOptions, HttpStatus, type HttpStatusCode, type HttpVerb, HttpVersionNotSupportedException, ImATeapotException, type Interceptor, InternalServerErrorException, Ip, type MetadataKey, MethodNotAllowedException, MiddlewareConsumerImpl, NotAcceptableException, NotFoundException, NotImplementedException, Options, Param, type ParamEntry, type ParamSource, Patch, PayloadTooLargeException, Post, PreconditionFailedException, Put, Query, ROUTE_HEADERS, ROUTE_METHODS, ROUTE_PARAMS, ROUTE_REDIRECT, ROUTE_STATUS, Redirect, type RedirectMeta, Reflector, Req, RequestTimeoutException, Res, type RevalidationSignal, type RouteDefinition, type RouteMap, type RouteMethodEntry, type RouteRegistration, type RouteTree, ServiceUnavailableException, Session, SetMetadata, SkipThrottle, type StaticOptions, type StreamRenderOptions, type StreamRenderResult, type SwcCore, type TheoRequestContext, Throttle, type ThrottleOptions, TooManyRequestsException, type TypedClient, TypedClientError, USE_FILTERS, USE_GUARDS, USE_INTERCEPTORS, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UseFilters, UseGuards, UseInterceptors, type WalkResult, composeComponentTree, contract, createDecorator, createDecoratorHandler, createDecoratorServer, createExecutionContext, createStaticHandler, createTypedClient, digestError, getMeta, getMimeType, getRequestContext, getRevalidationSignals, getThrottleOptions, isControllerClass, isSafePath, isThrottleSkipped, joinPath, loadControllerWithSwc, loadControllersFromGlob, registerControllers, renderToStream, resolveDtoSchema, revalidatePath, revalidateTag, runExceptionFilters, runInterceptors, setMeta, streamToResponse, transformControllerSource, tryGetRequestContext, walkControllerMetadata };
|
package/dist/index.js
CHANGED
|
@@ -2,29 +2,37 @@ import {
|
|
|
2
2
|
TheoApp,
|
|
3
3
|
createStaticHandler,
|
|
4
4
|
getMimeType,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
getRequestContext,
|
|
6
|
+
isSafePath,
|
|
7
|
+
renderToStream,
|
|
8
|
+
streamToResponse,
|
|
9
|
+
tryGetRequestContext
|
|
10
|
+
} from "./chunk-GQ2UH554.js";
|
|
7
11
|
import {
|
|
8
12
|
MiddlewareConsumerImpl,
|
|
13
|
+
isControllerClass,
|
|
14
|
+
loadControllerWithSwc,
|
|
15
|
+
loadControllersFromGlob,
|
|
9
16
|
middlewareMatchesPath,
|
|
10
|
-
runMiddleware
|
|
11
|
-
|
|
17
|
+
runMiddleware,
|
|
18
|
+
transformControllerSource
|
|
19
|
+
} from "./chunk-RC4V75DI.js";
|
|
12
20
|
import {
|
|
13
21
|
HttpDecoratorsConfigError,
|
|
14
22
|
createExecutionContext,
|
|
15
23
|
joinPath,
|
|
16
24
|
resolveDtoSchema,
|
|
17
25
|
walkControllerMetadata
|
|
18
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-QGB5YC4T.js";
|
|
19
27
|
import {
|
|
20
28
|
createNodeAdapter
|
|
21
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-HLW7YKZE.js";
|
|
22
30
|
import {
|
|
23
31
|
runInterceptors
|
|
24
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-ELCXHPAD.js";
|
|
25
33
|
import {
|
|
26
34
|
runExceptionFilters
|
|
27
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-6W4T4DPJ.js";
|
|
28
36
|
import {
|
|
29
37
|
BadGatewayException,
|
|
30
38
|
BadRequestException,
|
|
@@ -61,13 +69,13 @@ import {
|
|
|
61
69
|
UnsupportedMediaTypeException,
|
|
62
70
|
getMeta,
|
|
63
71
|
setMeta
|
|
64
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-3PGQVQWG.js";
|
|
65
73
|
import {
|
|
66
74
|
resolveOrNew
|
|
67
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-MQAJWR3K.js";
|
|
68
76
|
import {
|
|
69
77
|
__name
|
|
70
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-7QVYU63E.js";
|
|
71
79
|
|
|
72
80
|
// src/decorators/controller.ts
|
|
73
81
|
function inferPrefix(className) {
|
|
@@ -415,7 +423,7 @@ __name(registerControllers, "registerControllers");
|
|
|
415
423
|
|
|
416
424
|
// src/bridge/create-server.ts
|
|
417
425
|
import "reflect-metadata";
|
|
418
|
-
function
|
|
426
|
+
function createDecoratorHandler(controllersOrOpts) {
|
|
419
427
|
const { controllers, container, configure } = Array.isArray(controllersOrOpts) ? {
|
|
420
428
|
controllers: controllersOrOpts,
|
|
421
429
|
container: void 0,
|
|
@@ -448,9 +456,25 @@ function createDecoratorServer(controllersOrOpts) {
|
|
|
448
456
|
if (aP !== bP) return aP ? 1 : -1;
|
|
449
457
|
return 0;
|
|
450
458
|
});
|
|
451
|
-
const adapter = createNodeAdapter();
|
|
452
459
|
const handler = /* @__PURE__ */ __name((request) => handleRequest(routes, request, container, middlewareEntries), "handler");
|
|
453
|
-
|
|
460
|
+
handler.matches = (method, pathname) => findRoute(routes, method.toUpperCase(), pathname) !== null;
|
|
461
|
+
return handler;
|
|
462
|
+
}
|
|
463
|
+
__name(createDecoratorHandler, "createDecoratorHandler");
|
|
464
|
+
function createDecoratorServer(controllersOrOpts) {
|
|
465
|
+
const handle = createDecoratorHandler(controllersOrOpts);
|
|
466
|
+
const adapter = createNodeAdapter();
|
|
467
|
+
return adapter.createServer(async (request) => {
|
|
468
|
+
const res = await handle(request);
|
|
469
|
+
if (res) return res;
|
|
470
|
+
const { pathname } = new URL(request.url);
|
|
471
|
+
return jsonResponse(404, {
|
|
472
|
+
error: {
|
|
473
|
+
code: "NOT_FOUND",
|
|
474
|
+
message: `No route for ${request.method.toUpperCase()} ${pathname}`
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
});
|
|
454
478
|
}
|
|
455
479
|
__name(createDecoratorServer, "createDecoratorServer");
|
|
456
480
|
async function handleRequest(routes, request, container, middlewareEntries = []) {
|
|
@@ -458,14 +482,7 @@ async function handleRequest(routes, request, container, middlewareEntries = [])
|
|
|
458
482
|
const method = request.method.toUpperCase();
|
|
459
483
|
const pathname = url.pathname;
|
|
460
484
|
const match = findRoute(routes, method, pathname);
|
|
461
|
-
if (!match)
|
|
462
|
-
return jsonResponse(404, {
|
|
463
|
-
error: {
|
|
464
|
-
code: "NOT_FOUND",
|
|
465
|
-
message: `No route for ${method} ${pathname}`
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
}
|
|
485
|
+
if (!match) return null;
|
|
469
486
|
const { walk, instance, params } = match;
|
|
470
487
|
try {
|
|
471
488
|
const mwResponse = await runMiddleware(middlewareEntries, request, pathname);
|
|
@@ -491,6 +508,7 @@ async function handleRequest(routes, request, container, middlewareEntries = [])
|
|
|
491
508
|
}
|
|
492
509
|
const handlerFn = instance[walk.propertyKey];
|
|
493
510
|
const result = await runInterceptors(walk.interceptors, () => handlerFn.apply(instance, args), request, container);
|
|
511
|
+
if (result instanceof Response) return result;
|
|
494
512
|
return buildResponse(result, walk, method);
|
|
495
513
|
} catch (err) {
|
|
496
514
|
return runExceptionFilters(err, walk.filters, request, container);
|
|
@@ -690,6 +708,152 @@ function contract(routes) {
|
|
|
690
708
|
return routes;
|
|
691
709
|
}
|
|
692
710
|
__name(contract, "contract");
|
|
711
|
+
|
|
712
|
+
// src/error-digest.ts
|
|
713
|
+
function djb2(input) {
|
|
714
|
+
let hash = 5381;
|
|
715
|
+
for (let i = 0; i < input.length; i++) {
|
|
716
|
+
hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
|
|
717
|
+
}
|
|
718
|
+
return (hash >>> 0).toString(16);
|
|
719
|
+
}
|
|
720
|
+
__name(djb2, "djb2");
|
|
721
|
+
function digestError(err, context = {}) {
|
|
722
|
+
const message = extractMessage(err);
|
|
723
|
+
const status = extractStatus(err);
|
|
724
|
+
const rawStack = extractStack(err);
|
|
725
|
+
const digest = djb2(message);
|
|
726
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
727
|
+
return {
|
|
728
|
+
digest,
|
|
729
|
+
message,
|
|
730
|
+
status,
|
|
731
|
+
context,
|
|
732
|
+
...rawStack && !isProduction ? {
|
|
733
|
+
stack: rawStack
|
|
734
|
+
} : {}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
__name(digestError, "digestError");
|
|
738
|
+
function extractMessage(err) {
|
|
739
|
+
if (err instanceof Error) {
|
|
740
|
+
return err.message;
|
|
741
|
+
}
|
|
742
|
+
if (typeof err === "string") {
|
|
743
|
+
return err;
|
|
744
|
+
}
|
|
745
|
+
if (typeof err === "number") {
|
|
746
|
+
return String(err);
|
|
747
|
+
}
|
|
748
|
+
try {
|
|
749
|
+
return JSON.stringify(err);
|
|
750
|
+
} catch {
|
|
751
|
+
return "Unknown error";
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
__name(extractMessage, "extractMessage");
|
|
755
|
+
function extractStatus(err) {
|
|
756
|
+
if (err instanceof HttpException) {
|
|
757
|
+
return err.statusCode;
|
|
758
|
+
}
|
|
759
|
+
return 500;
|
|
760
|
+
}
|
|
761
|
+
__name(extractStatus, "extractStatus");
|
|
762
|
+
function extractStack(err) {
|
|
763
|
+
if (err instanceof Error) {
|
|
764
|
+
return err.stack;
|
|
765
|
+
}
|
|
766
|
+
return void 0;
|
|
767
|
+
}
|
|
768
|
+
__name(extractStack, "extractStack");
|
|
769
|
+
|
|
770
|
+
// src/component-tree.ts
|
|
771
|
+
function createErrorBoundary(React, FallbackComponent) {
|
|
772
|
+
return class ErrorBoundary extends React.Component {
|
|
773
|
+
static {
|
|
774
|
+
__name(this, "ErrorBoundary");
|
|
775
|
+
}
|
|
776
|
+
constructor(props) {
|
|
777
|
+
super(props);
|
|
778
|
+
this.state = {
|
|
779
|
+
hasError: false
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
static getDerivedStateFromError() {
|
|
783
|
+
return {
|
|
784
|
+
hasError: true
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
render() {
|
|
788
|
+
if (this.state.hasError) {
|
|
789
|
+
return React.createElement(FallbackComponent);
|
|
790
|
+
}
|
|
791
|
+
return React.createElement(React.Fragment, null, this.props.children);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
__name(createErrorBoundary, "createErrorBoundary");
|
|
796
|
+
async function composeComponentTree(tree) {
|
|
797
|
+
const React = await import("react");
|
|
798
|
+
return composeNode(React, tree);
|
|
799
|
+
}
|
|
800
|
+
__name(composeComponentTree, "composeComponentTree");
|
|
801
|
+
function composeNode(React, node) {
|
|
802
|
+
const { layout: Layout, page: Page, loading: Loading, error: ErrorFallback } = node;
|
|
803
|
+
if (!Page) {
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
let element = React.createElement(Page);
|
|
807
|
+
if (Loading) {
|
|
808
|
+
element = React.createElement(React.Suspense, {
|
|
809
|
+
fallback: React.createElement(Loading)
|
|
810
|
+
}, element);
|
|
811
|
+
}
|
|
812
|
+
if (ErrorFallback) {
|
|
813
|
+
const Boundary = createErrorBoundary(React, ErrorFallback);
|
|
814
|
+
element = React.createElement(Boundary, null, element);
|
|
815
|
+
}
|
|
816
|
+
if (Layout) {
|
|
817
|
+
element = React.createElement(Layout, null, element);
|
|
818
|
+
}
|
|
819
|
+
return element;
|
|
820
|
+
}
|
|
821
|
+
__name(composeNode, "composeNode");
|
|
822
|
+
|
|
823
|
+
// src/cache-signal.ts
|
|
824
|
+
function revalidateTag(tag) {
|
|
825
|
+
collectSignal({
|
|
826
|
+
kind: "tag",
|
|
827
|
+
value: tag,
|
|
828
|
+
timestamp: Date.now()
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
__name(revalidateTag, "revalidateTag");
|
|
832
|
+
function revalidatePath(path) {
|
|
833
|
+
collectSignal({
|
|
834
|
+
kind: "path",
|
|
835
|
+
value: path,
|
|
836
|
+
timestamp: Date.now()
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
__name(revalidatePath, "revalidatePath");
|
|
840
|
+
function getRevalidationSignals() {
|
|
841
|
+
const ctx = tryGetRequestContext();
|
|
842
|
+
if (!ctx) return [];
|
|
843
|
+
return ctx._revalidationSignals ?? [];
|
|
844
|
+
}
|
|
845
|
+
__name(getRevalidationSignals, "getRevalidationSignals");
|
|
846
|
+
function collectSignal(signal) {
|
|
847
|
+
const ctx = tryGetRequestContext();
|
|
848
|
+
if (!ctx) {
|
|
849
|
+
console.warn("[theokit] revalidateTag/revalidatePath called outside a request context. Signal ignored.");
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const extended = ctx;
|
|
853
|
+
extended._revalidationSignals ??= [];
|
|
854
|
+
extended._revalidationSignals.push(signal);
|
|
855
|
+
}
|
|
856
|
+
__name(collectSignal, "collectSignal");
|
|
693
857
|
export {
|
|
694
858
|
All,
|
|
695
859
|
BadGatewayException,
|
|
@@ -757,26 +921,40 @@ export {
|
|
|
757
921
|
UseFilters,
|
|
758
922
|
UseGuards,
|
|
759
923
|
UseInterceptors,
|
|
924
|
+
composeComponentTree,
|
|
760
925
|
contract,
|
|
761
926
|
createDecorator,
|
|
927
|
+
createDecoratorHandler,
|
|
762
928
|
createDecoratorServer,
|
|
763
929
|
createExecutionContext,
|
|
764
930
|
createStaticHandler,
|
|
765
931
|
createTypedClient,
|
|
932
|
+
digestError,
|
|
766
933
|
getMeta,
|
|
767
934
|
getMimeType,
|
|
935
|
+
getRequestContext,
|
|
936
|
+
getRevalidationSignals,
|
|
768
937
|
getThrottleOptions,
|
|
938
|
+
isControllerClass,
|
|
769
939
|
isSafePath,
|
|
770
940
|
isThrottleSkipped,
|
|
771
941
|
joinPath,
|
|
942
|
+
loadControllerWithSwc,
|
|
943
|
+
loadControllersFromGlob,
|
|
772
944
|
middlewareMatchesPath,
|
|
773
945
|
registerControllers,
|
|
946
|
+
renderToStream,
|
|
774
947
|
resolveDtoSchema,
|
|
775
948
|
resolveOrNew,
|
|
949
|
+
revalidatePath,
|
|
950
|
+
revalidateTag,
|
|
776
951
|
runExceptionFilters,
|
|
777
952
|
runInterceptors,
|
|
778
953
|
runMiddleware,
|
|
779
954
|
setMeta,
|
|
955
|
+
streamToResponse,
|
|
956
|
+
transformControllerSource,
|
|
957
|
+
tryGetRequestContext,
|
|
780
958
|
walkControllerMetadata
|
|
781
959
|
};
|
|
782
960
|
//# sourceMappingURL=index.js.map
|