@typokit/core 0.1.4
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/database.d.ts +28 -0
- package/dist/adapters/database.d.ts.map +1 -0
- package/dist/adapters/database.js +2 -0
- package/dist/adapters/database.js.map +1 -0
- package/dist/adapters/server.d.ts +35 -0
- package/dist/adapters/server.d.ts.map +1 -0
- package/dist/adapters/server.js +2 -0
- package/dist/adapters/server.js.map +1 -0
- package/dist/app.d.ts +36 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +55 -0
- package/dist/app.js.map +1 -0
- package/dist/error-middleware.d.ts +17 -0
- package/dist/error-middleware.d.ts.map +1 -0
- package/dist/error-middleware.js +138 -0
- package/dist/error-middleware.js.map +1 -0
- package/dist/handler.d.ts +41 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +22 -0
- package/dist/handler.js.map +1 -0
- package/dist/hooks.d.ts +48 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +64 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +35 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +54 -0
- package/dist/middleware.js.map +1 -0
- package/dist/plugin.d.ts +74 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +29 -0
- package/src/adapters/database.ts +37 -0
- package/src/adapters/server.ts +55 -0
- package/src/app.test.ts +438 -0
- package/src/app.ts +118 -0
- package/src/error-middleware.test.ts +263 -0
- package/src/error-middleware.ts +186 -0
- package/src/handler.test.ts +346 -0
- package/src/handler.ts +64 -0
- package/src/hooks.test.ts +419 -0
- package/src/hooks.ts +114 -0
- package/src/index.ts +51 -0
- package/src/middleware.test.ts +253 -0
- package/src/middleware.ts +100 -0
- package/src/plugin.ts +108 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { GeneratedOutput, MigrationDraft, SchemaTypeMap } from "@typokit/types";
|
|
2
|
+
/**
|
|
3
|
+
* Represents the current state of a database schema,
|
|
4
|
+
* used by adapters to compute diffs against TypoKit types.
|
|
5
|
+
*/
|
|
6
|
+
export interface DatabaseState {
|
|
7
|
+
tables: Record<string, TableState>;
|
|
8
|
+
}
|
|
9
|
+
export interface TableState {
|
|
10
|
+
columns: Record<string, ColumnState>;
|
|
11
|
+
}
|
|
12
|
+
export interface ColumnState {
|
|
13
|
+
type: string;
|
|
14
|
+
nullable: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Every database adapter implements this interface.
|
|
18
|
+
* See typokit-arch.md Section 7.3.
|
|
19
|
+
*/
|
|
20
|
+
export interface DatabaseAdapter {
|
|
21
|
+
/** Generate DB schema artifacts from TypoKit types */
|
|
22
|
+
generate(types: SchemaTypeMap): GeneratedOutput[];
|
|
23
|
+
/** Diff current DB state against types, produce migration draft */
|
|
24
|
+
diff(types: SchemaTypeMap, currentState: DatabaseState): MigrationDraft;
|
|
25
|
+
/** Generate typed repository helpers (optional — adapters can skip this) */
|
|
26
|
+
generateRepositories?(types: SchemaTypeMap): GeneratedOutput[];
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/adapters/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAAC;IAElD,mEAAmE;IACnE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,GAAG,cAAc,CAAC;IAExE,4EAA4E;IAC5E,oBAAoB,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAAC;CAChE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/adapters/database.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CompiledRouteTable, HandlerMap, MiddlewareChain, SerializerMap, ServerHandle, TypoKitRequest, TypoKitResponse, ValidatorMap } from "@typokit/types";
|
|
2
|
+
export interface ServerAdapter {
|
|
3
|
+
/** Adapter name for logging and diagnostics */
|
|
4
|
+
name: string;
|
|
5
|
+
/**
|
|
6
|
+
* Register TypoKit's compiled routes into the server framework.
|
|
7
|
+
* The adapter translates the route table into framework-native registrations.
|
|
8
|
+
* Each route handler receives a normalized TypoKitRequest and must return
|
|
9
|
+
* a TypoKitResponse.
|
|
10
|
+
*/
|
|
11
|
+
registerRoutes(routeTable: CompiledRouteTable, handlerMap: HandlerMap, middlewareChain: MiddlewareChain, validatorMap?: ValidatorMap, serializerMap?: SerializerMap): void;
|
|
12
|
+
/**
|
|
13
|
+
* Start the server. Returns a handle for shutdown.
|
|
14
|
+
*/
|
|
15
|
+
listen(port: number): Promise<ServerHandle>;
|
|
16
|
+
/**
|
|
17
|
+
* Normalize the framework's native request into TypoKit's standard format.
|
|
18
|
+
* This is where Fastify's `req`, Hono's `c`, or raw `http.IncomingMessage`
|
|
19
|
+
* get normalized into a consistent shape for TypoKit's validation/handler
|
|
20
|
+
* pipeline.
|
|
21
|
+
*/
|
|
22
|
+
normalizeRequest(raw: unknown): TypoKitRequest;
|
|
23
|
+
/**
|
|
24
|
+
* Write TypoKit's response back through the framework's native response
|
|
25
|
+
* mechanism.
|
|
26
|
+
*/
|
|
27
|
+
writeResponse(raw: unknown, response: TypoKitResponse): void;
|
|
28
|
+
/**
|
|
29
|
+
* Optional: expose the underlying framework instance for escape hatches.
|
|
30
|
+
* e.g., the raw Fastify instance so users can register Fastify-native plugins.
|
|
31
|
+
* Returns `unknown` — consumers cast to the specific framework type.
|
|
32
|
+
*/
|
|
33
|
+
getNativeServer?(): unknown;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/adapters/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,UAAU,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,eAAe,EACf,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,cAAc,CACZ,UAAU,EAAE,kBAAkB,EAC9B,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,eAAe,EAChC,YAAY,CAAC,EAAE,YAAY,EAC3B,aAAa,CAAC,EAAE,aAAa,GAC5B,IAAI,CAAC;IAER;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAE5C;;;;;OAKG;IACH,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAAC;IAE/C;;;OAGG;IACH,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IAE7D;;;;OAIG;IACH,eAAe,CAAC,IAAI,OAAO,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/adapters/server.ts"],"names":[],"mappings":""}
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ServerHandle, Logger, TypoKitRequest, RequestContext, TypoKitResponse } from "@typokit/types";
|
|
2
|
+
import type { ServerAdapter } from "./adapters/server.js";
|
|
3
|
+
import type { TypoKitPlugin } from "./plugin.js";
|
|
4
|
+
import type { MiddlewareEntry } from "./middleware.js";
|
|
5
|
+
/** A group of route handlers registered under a common prefix */
|
|
6
|
+
export interface RouteGroup {
|
|
7
|
+
prefix: string;
|
|
8
|
+
handlers: Record<string, unknown>;
|
|
9
|
+
middleware?: MiddlewareEntry[];
|
|
10
|
+
}
|
|
11
|
+
/** Options accepted by the createApp() factory function */
|
|
12
|
+
export interface CreateAppOptions {
|
|
13
|
+
server: ServerAdapter;
|
|
14
|
+
middleware?: MiddlewareEntry[];
|
|
15
|
+
routes: RouteGroup[];
|
|
16
|
+
plugins?: TypoKitPlugin[];
|
|
17
|
+
logging?: Partial<Logger>;
|
|
18
|
+
telemetry?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** The application instance returned by createApp() */
|
|
21
|
+
export interface TypoKitApp {
|
|
22
|
+
/** Start the server on the given port */
|
|
23
|
+
listen(port: number): Promise<ServerHandle>;
|
|
24
|
+
/** Expose the underlying server framework instance */
|
|
25
|
+
getNativeServer(): unknown;
|
|
26
|
+
/** Gracefully shut down the application */
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
/** The auto-registered error middleware for use by server adapters */
|
|
29
|
+
errorMiddleware: (req: TypoKitRequest, ctx: RequestContext, next: () => Promise<TypoKitResponse>) => Promise<TypoKitResponse>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a TypoKit application from a server adapter, middleware,
|
|
33
|
+
* routes, and plugins.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createApp(options: CreateAppOptions): TypoKitApp;
|
|
36
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,cAAc,EACd,cAAc,EACd,eAAe,EAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;CAChC;AAID,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAID,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5C,sDAAsD;IACtD,eAAe,IAAI,OAAO,CAAC;IAC3B,2CAA2C;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,sEAAsE;IACtE,eAAe,EAAE,CACf,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KACjC,OAAO,CAAC,eAAe,CAAC,CAAC;CAC/B;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,UAAU,CA0D/D"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @typokit/core — App Factory (createApp)
|
|
2
|
+
import { createErrorMiddleware } from "./error-middleware.js";
|
|
3
|
+
// ─── createApp Factory ──────────────────────────────────────
|
|
4
|
+
/**
|
|
5
|
+
* Create a TypoKit application from a server adapter, middleware,
|
|
6
|
+
* routes, and plugins.
|
|
7
|
+
*/
|
|
8
|
+
export function createApp(options) {
|
|
9
|
+
const { server, plugins = [] } = options;
|
|
10
|
+
const appInstance = {
|
|
11
|
+
name: server.name,
|
|
12
|
+
plugins,
|
|
13
|
+
services: {},
|
|
14
|
+
};
|
|
15
|
+
// Auto-register error middleware for the request pipeline
|
|
16
|
+
const errorMiddleware = createErrorMiddleware();
|
|
17
|
+
let serverHandle = null;
|
|
18
|
+
const app = {
|
|
19
|
+
errorMiddleware,
|
|
20
|
+
async listen(port) {
|
|
21
|
+
// 1. Call plugin onStart hooks
|
|
22
|
+
for (const plugin of plugins) {
|
|
23
|
+
if (plugin.onStart) {
|
|
24
|
+
await plugin.onStart(appInstance);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 2. Delegate to server adapter
|
|
28
|
+
serverHandle = await server.listen(port);
|
|
29
|
+
// 3. Call plugin onReady hooks
|
|
30
|
+
for (const plugin of plugins) {
|
|
31
|
+
if (plugin.onReady) {
|
|
32
|
+
await plugin.onReady(appInstance);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return serverHandle;
|
|
36
|
+
},
|
|
37
|
+
getNativeServer() {
|
|
38
|
+
return server.getNativeServer?.() ?? null;
|
|
39
|
+
},
|
|
40
|
+
async close() {
|
|
41
|
+
// Call plugin onStop hooks
|
|
42
|
+
for (const plugin of plugins) {
|
|
43
|
+
if (plugin.onStop) {
|
|
44
|
+
await plugin.onStop(appInstance);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (serverHandle) {
|
|
48
|
+
await serverHandle.close();
|
|
49
|
+
serverHandle = null;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
return app;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAY1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAyC9D,+DAA+D;AAE/D;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAyB;IACjD,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEzC,MAAM,WAAW,GAAgB;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO;QACP,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,0DAA0D;IAC1D,MAAM,eAAe,GAAG,qBAAqB,EAAE,CAAC;IAEhD,IAAI,YAAY,GAAwB,IAAI,CAAC;IAE7C,MAAM,GAAG,GAAe;QACtB,eAAe;QAEf,KAAK,CAAC,MAAM,CAAC,IAAY;YACvB,+BAA+B;YAC/B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YAED,gCAAgC;YAChC,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAEzC,+BAA+B;YAC/B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YAED,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,eAAe;YACb,OAAO,MAAM,CAAC,eAAe,EAAE,EAAE,IAAI,IAAI,CAAC;QAC5C,CAAC;QAED,KAAK,CAAC,KAAK;YACT,2BAA2B;YAC3B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClB,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;YAED,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC;gBAC3B,YAAY,GAAG,IAAI,CAAC;YACtB,CAAC;QACH,CAAC;KACF,CAAC;IAEF,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TypoKitRequest, RequestContext, TypoKitResponse } from "@typokit/types";
|
|
2
|
+
/** Options for configuring the error middleware */
|
|
3
|
+
export interface ErrorMiddlewareOptions {
|
|
4
|
+
/** Override dev mode detection (defaults to NODE_ENV === "development") */
|
|
5
|
+
isDev?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Built-in error middleware — catches all thrown errors and serializes
|
|
9
|
+
* them into the ErrorResponse schema.
|
|
10
|
+
*
|
|
11
|
+
* - AppError: serialized with correct status, code, message, details, traceId
|
|
12
|
+
* - Typia validation errors: 400 with field-level failure details
|
|
13
|
+
* - Unknown errors (prod): 500 generic message, full details logged
|
|
14
|
+
* - Unknown errors (dev): 500 with stack trace and message exposed
|
|
15
|
+
*/
|
|
16
|
+
export declare function createErrorMiddleware(options?: ErrorMiddlewareOptions): (req: TypoKitRequest, ctx: RequestContext, next: () => Promise<TypoKitResponse>) => Promise<TypoKitResponse>;
|
|
17
|
+
//# sourceMappingURL=error-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-middleware.d.ts","sourceRoot":"","sources":["../src/error-middleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EAGhB,MAAM,gBAAgB,CAAC;AAKxB,mDAAmD;AACnD,MAAM,WAAW,sBAAsB;IACrC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA8DD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,CACD,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KACjC,OAAO,CAAC,eAAe,CAAC,CAiF5B"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @typokit/core — Error Middleware
|
|
2
|
+
import { AppError } from "@typokit/errors";
|
|
3
|
+
/** Duck-type check for Typia TypeGuardError (thrown by typia.assert) */
|
|
4
|
+
function isTypiaTypeGuardError(error) {
|
|
5
|
+
if (!(error instanceof Error))
|
|
6
|
+
return false;
|
|
7
|
+
return error.name === "TypeGuardError";
|
|
8
|
+
}
|
|
9
|
+
/** Duck-type check for objects with Typia-style errors array */
|
|
10
|
+
function hasTypiaErrors(error) {
|
|
11
|
+
if (!(error instanceof Error))
|
|
12
|
+
return false;
|
|
13
|
+
const candidate = error;
|
|
14
|
+
return (Array.isArray(candidate.errors) &&
|
|
15
|
+
candidate.errors.length > 0 &&
|
|
16
|
+
typeof candidate.errors[0].path === "string");
|
|
17
|
+
}
|
|
18
|
+
/** Extract field-level details from a Typia validation error */
|
|
19
|
+
function extractTypiaDetails(error) {
|
|
20
|
+
if (hasTypiaErrors(error)) {
|
|
21
|
+
return {
|
|
22
|
+
fields: error.errors.map((e) => ({
|
|
23
|
+
path: e.path,
|
|
24
|
+
expected: e.expected,
|
|
25
|
+
value: e.value,
|
|
26
|
+
})),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const guard = error;
|
|
30
|
+
if (guard.path !== undefined) {
|
|
31
|
+
return {
|
|
32
|
+
fields: [
|
|
33
|
+
{
|
|
34
|
+
path: guard.path,
|
|
35
|
+
expected: guard.expected,
|
|
36
|
+
value: guard.value,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
// ─── Error Middleware Factory ─────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Built-in error middleware — catches all thrown errors and serializes
|
|
46
|
+
* them into the ErrorResponse schema.
|
|
47
|
+
*
|
|
48
|
+
* - AppError: serialized with correct status, code, message, details, traceId
|
|
49
|
+
* - Typia validation errors: 400 with field-level failure details
|
|
50
|
+
* - Unknown errors (prod): 500 generic message, full details logged
|
|
51
|
+
* - Unknown errors (dev): 500 with stack trace and message exposed
|
|
52
|
+
*/
|
|
53
|
+
export function createErrorMiddleware(options) {
|
|
54
|
+
const isDev = options?.isDev ??
|
|
55
|
+
(typeof globalThis !== "undefined" &&
|
|
56
|
+
globalThis
|
|
57
|
+
.process?.env?.NODE_ENV === "development");
|
|
58
|
+
return async (_req, ctx, next) => {
|
|
59
|
+
try {
|
|
60
|
+
return await next();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const traceId = ctx.requestId;
|
|
64
|
+
// ── AppError instances ──
|
|
65
|
+
if (error instanceof AppError) {
|
|
66
|
+
const json = error.toJSON();
|
|
67
|
+
json.error.traceId = traceId;
|
|
68
|
+
return {
|
|
69
|
+
status: error.status,
|
|
70
|
+
headers: { "content-type": "application/json" },
|
|
71
|
+
body: json,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// ── Typia validation errors ──
|
|
75
|
+
if (isTypiaTypeGuardError(error) || hasTypiaErrors(error)) {
|
|
76
|
+
const details = extractTypiaDetails(error);
|
|
77
|
+
const body = {
|
|
78
|
+
error: {
|
|
79
|
+
code: "VALIDATION_ERROR",
|
|
80
|
+
message: error.message || "Validation failed",
|
|
81
|
+
details,
|
|
82
|
+
traceId,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
status: 400,
|
|
87
|
+
headers: { "content-type": "application/json" },
|
|
88
|
+
body,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// ── Unknown errors ──
|
|
92
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
93
|
+
if (isDev) {
|
|
94
|
+
// Development: expose stack trace and message
|
|
95
|
+
const body = {
|
|
96
|
+
error: {
|
|
97
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
98
|
+
message: err.message,
|
|
99
|
+
details: {
|
|
100
|
+
stack: err.stack,
|
|
101
|
+
name: err.name,
|
|
102
|
+
},
|
|
103
|
+
traceId,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
status: 500,
|
|
108
|
+
headers: { "content-type": "application/json" },
|
|
109
|
+
body,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Production: log full details, return redacted response
|
|
113
|
+
logUnknownError(ctx.log, err, traceId);
|
|
114
|
+
const body = {
|
|
115
|
+
error: {
|
|
116
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
117
|
+
message: "Internal Server Error",
|
|
118
|
+
traceId,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
status: 500,
|
|
123
|
+
headers: { "content-type": "application/json" },
|
|
124
|
+
body,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Log full error details in production mode */
|
|
130
|
+
function logUnknownError(log, err, traceId) {
|
|
131
|
+
log.error("Unhandled error", {
|
|
132
|
+
traceId,
|
|
133
|
+
name: err.name,
|
|
134
|
+
message: err.message,
|
|
135
|
+
stack: err.stack,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=error-middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-middleware.js","sourceRoot":"","sources":["../src/error-middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RouteContract, RequestContext } from "@typokit/types";
|
|
2
|
+
/**
|
|
3
|
+
* Input received by each handler function.
|
|
4
|
+
* Types are inferred from the corresponding RouteContract.
|
|
5
|
+
*/
|
|
6
|
+
export type HandlerInput<TContract extends RouteContract<unknown, unknown, unknown, unknown>> = {
|
|
7
|
+
params: TContract["params"];
|
|
8
|
+
query: TContract["query"];
|
|
9
|
+
body: TContract["body"];
|
|
10
|
+
ctx: RequestContext;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* A handler function that receives typed input and returns the contract's response type.
|
|
14
|
+
*/
|
|
15
|
+
export type HandlerFn<TContract extends RouteContract<unknown, unknown, unknown, unknown>> = (input: HandlerInput<TContract>) => Promise<TContract["response"]> | TContract["response"];
|
|
16
|
+
/**
|
|
17
|
+
* Maps each route key in TRoutes to a handler function whose input/output
|
|
18
|
+
* types are inferred from the corresponding RouteContract.
|
|
19
|
+
*/
|
|
20
|
+
export type HandlerDefs<TRoutes extends Record<string, RouteContract<unknown, unknown, unknown, unknown>>> = {
|
|
21
|
+
[K in keyof TRoutes]: HandlerFn<TRoutes[K]>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Define typed handler implementations for a set of route contracts.
|
|
25
|
+
* The type system enforces that every route key has a handler and that
|
|
26
|
+
* each handler's signature matches its contract.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* export default defineHandlers<UsersRoutes>({
|
|
31
|
+
* "GET /users": async ({ query, ctx }) => {
|
|
32
|
+
* return userService.list(query, ctx);
|
|
33
|
+
* },
|
|
34
|
+
* "POST /users": async ({ body, ctx }) => {
|
|
35
|
+
* return userService.create(body, ctx);
|
|
36
|
+
* },
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function defineHandlers<TRoutes extends Record<string, RouteContract<unknown, unknown, unknown, unknown>>>(handlers: HandlerDefs<TRoutes>): HandlerDefs<TRoutes>;
|
|
41
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,YAAY,CACtB,SAAS,SAAS,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,IACjE;IACF,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1B,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACxB,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,CACnB,SAAS,SAAS,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,IACjE,CACF,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,KAC3B,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,WAAW,CACrB,OAAO,SAAS,MAAM,CACpB,MAAM,EACN,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAClD,IACC;KACD,CAAC,IAAI,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,SAAS,MAAM,CACpB,MAAM,EACN,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAClD,EACD,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAEtD"}
|
package/dist/handler.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @typokit/core — Handler System
|
|
2
|
+
/**
|
|
3
|
+
* Define typed handler implementations for a set of route contracts.
|
|
4
|
+
* The type system enforces that every route key has a handler and that
|
|
5
|
+
* each handler's signature matches its contract.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* export default defineHandlers<UsersRoutes>({
|
|
10
|
+
* "GET /users": async ({ query, ctx }) => {
|
|
11
|
+
* return userService.list(query, ctx);
|
|
12
|
+
* },
|
|
13
|
+
* "POST /users": async ({ body, ctx }) => {
|
|
14
|
+
* return userService.create(body, ctx);
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function defineHandlers(handlers) {
|
|
20
|
+
return handlers;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAuCjC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAK5B,QAA8B;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { BuildContext, BuildResult, CompiledRouteTable, GeneratedOutput, SchemaTypeMap } from "@typokit/types";
|
|
2
|
+
/** A tap registration entry */
|
|
3
|
+
export interface TapEntry<T extends unknown[]> {
|
|
4
|
+
name: string;
|
|
5
|
+
fn: (...args: T) => void | Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Tapable-style async series hook.
|
|
9
|
+
* Hooks execute in registration order; each receives the same args.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AsyncSeriesHookImpl<T extends unknown[]> {
|
|
12
|
+
readonly taps: TapEntry<T>[];
|
|
13
|
+
/** Register a named tap */
|
|
14
|
+
tap(name: string, fn: (...args: T) => void | Promise<void>): void;
|
|
15
|
+
/** Execute all taps in series, in registration order */
|
|
16
|
+
call(...args: T): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Concrete build pipeline with all 6 hook phases */
|
|
19
|
+
export interface BuildPipelineInstance {
|
|
20
|
+
hooks: {
|
|
21
|
+
beforeTransform: AsyncSeriesHookImpl<[BuildContext]>;
|
|
22
|
+
afterTypeParse: AsyncSeriesHookImpl<[SchemaTypeMap, BuildContext]>;
|
|
23
|
+
afterValidators: AsyncSeriesHookImpl<[GeneratedOutput[], BuildContext]>;
|
|
24
|
+
afterRouteTable: AsyncSeriesHookImpl<[CompiledRouteTable, BuildContext]>;
|
|
25
|
+
emit: AsyncSeriesHookImpl<[GeneratedOutput[], BuildContext]>;
|
|
26
|
+
done: AsyncSeriesHookImpl<[BuildResult]>;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a new build pipeline with empty hooks for all 6 phases.
|
|
31
|
+
* Plugins call `onBuild(pipeline)` to tap into specific phases.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createBuildPipeline(): BuildPipelineInstance;
|
|
34
|
+
/** Hook phase names in execution order */
|
|
35
|
+
export declare const BUILD_HOOK_PHASES: readonly ["beforeTransform", "afterTypeParse", "afterValidators", "afterRouteTable", "emit", "done"];
|
|
36
|
+
export type BuildHookPhase = (typeof BUILD_HOOK_PHASES)[number];
|
|
37
|
+
/** Metadata about a registered tap for introspection */
|
|
38
|
+
export interface TapInfo {
|
|
39
|
+
hookName: string;
|
|
40
|
+
tapName: string;
|
|
41
|
+
order: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get introspection info about all registered taps in a build pipeline.
|
|
45
|
+
* Used by `typokit inspect build-pipeline --json`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getPipelineTaps(pipeline: BuildPipelineInstance): TapInfo[];
|
|
48
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAIxB,+BAA+B;AAC/B,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE;IAClD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAM;IAElC,2BAA2B;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIjE,wDAAwD;IAClD,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAKtC;AAID,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE;QACL,eAAe,EAAE,mBAAmB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;QACrD,cAAc,EAAE,mBAAmB,CAAC,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;QACnE,eAAe,EAAE,mBAAmB,CAAC,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;QACxE,eAAe,EAAE,mBAAmB,CAAC,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC,CAAC;QACzE,IAAI,EAAE,mBAAmB,CAAC,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;QAC7D,IAAI,EAAE,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,qBAAqB,CAe3D;AAED,0CAA0C;AAC1C,eAAO,MAAM,iBAAiB,sGAOpB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,wDAAwD;AACxD,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,EAAE,CAiB1E"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// @typokit/core — Tapable Hook System Implementation
|
|
2
|
+
/**
|
|
3
|
+
* Tapable-style async series hook.
|
|
4
|
+
* Hooks execute in registration order; each receives the same args.
|
|
5
|
+
*/
|
|
6
|
+
export class AsyncSeriesHookImpl {
|
|
7
|
+
taps = [];
|
|
8
|
+
/** Register a named tap */
|
|
9
|
+
tap(name, fn) {
|
|
10
|
+
this.taps.push({ name, fn });
|
|
11
|
+
}
|
|
12
|
+
/** Execute all taps in series, in registration order */
|
|
13
|
+
async call(...args) {
|
|
14
|
+
for (const entry of this.taps) {
|
|
15
|
+
await entry.fn(...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a new build pipeline with empty hooks for all 6 phases.
|
|
21
|
+
* Plugins call `onBuild(pipeline)` to tap into specific phases.
|
|
22
|
+
*/
|
|
23
|
+
export function createBuildPipeline() {
|
|
24
|
+
return {
|
|
25
|
+
hooks: {
|
|
26
|
+
beforeTransform: new AsyncSeriesHookImpl(),
|
|
27
|
+
afterTypeParse: new AsyncSeriesHookImpl(),
|
|
28
|
+
afterValidators: new AsyncSeriesHookImpl(),
|
|
29
|
+
afterRouteTable: new AsyncSeriesHookImpl(),
|
|
30
|
+
emit: new AsyncSeriesHookImpl(),
|
|
31
|
+
done: new AsyncSeriesHookImpl(),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Hook phase names in execution order */
|
|
36
|
+
export const BUILD_HOOK_PHASES = [
|
|
37
|
+
"beforeTransform",
|
|
38
|
+
"afterTypeParse",
|
|
39
|
+
"afterValidators",
|
|
40
|
+
"afterRouteTable",
|
|
41
|
+
"emit",
|
|
42
|
+
"done",
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Get introspection info about all registered taps in a build pipeline.
|
|
46
|
+
* Used by `typokit inspect build-pipeline --json`.
|
|
47
|
+
*/
|
|
48
|
+
export function getPipelineTaps(pipeline) {
|
|
49
|
+
const taps = [];
|
|
50
|
+
for (const phase of BUILD_HOOK_PHASES) {
|
|
51
|
+
const hook = pipeline.hooks[phase];
|
|
52
|
+
// Use type assertion since AsyncSeriesHookImpl always has taps
|
|
53
|
+
const hookImpl = hook;
|
|
54
|
+
for (let i = 0; i < hookImpl.taps.length; i++) {
|
|
55
|
+
taps.push({
|
|
56
|
+
hookName: phase,
|
|
57
|
+
tapName: hookImpl.taps[i].name,
|
|
58
|
+
order: i,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return taps;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAkBrD;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAkB,EAAE,CAAC;IAElC,2BAA2B;IAC3B,GAAG,CAAC,IAAY,EAAE,EAAwC;QACxD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,IAAI,CAAC,GAAG,IAAO;QACnB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;CACF;AAgBD;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,KAAK,EAAE;YACL,eAAe,EAAE,IAAI,mBAAmB,EAAkB;YAC1D,cAAc,EAAE,IAAI,mBAAmB,EAAiC;YACxE,eAAe,EAAE,IAAI,mBAAmB,EAErC;YACH,eAAe,EAAE,IAAI,mBAAmB,EAErC;YACH,IAAI,EAAE,IAAI,mBAAmB,EAAqC;YAClE,IAAI,EAAE,IAAI,mBAAmB,EAAiB;SAC/C;KACF,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,MAAM;IACN,MAAM;CACE,CAAC;AAWX;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAA+B;IAC7D,MAAM,IAAI,GAAc,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACnC,+DAA+D;QAC/D,MAAM,QAAQ,GAAG,IAAsC,CAAC;QACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,IAAI,CAAC,IAAI,CAAC;gBACR,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC9B,KAAK,EAAE,CAAC;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type ServerAdapter } from "./adapters/server.js";
|
|
2
|
+
export { type DatabaseAdapter, type DatabaseState, type TableState, type ColumnState, } from "./adapters/database.js";
|
|
3
|
+
export { type AsyncSeriesHook, type BuildPipeline, type CliCommand, type InspectEndpoint, type AppInstance, type TypoKitPlugin, } from "./plugin.js";
|
|
4
|
+
export { AsyncSeriesHookImpl, createBuildPipeline, getPipelineTaps, BUILD_HOOK_PHASES, type BuildPipelineInstance, type TapEntry, type TapInfo, type BuildHookPhase, } from "./hooks.js";
|
|
5
|
+
export { type MiddlewareInput, type Middleware, type MiddlewareEntry, defineMiddleware, createPlaceholderLogger, createRequestContext, executeMiddlewareChain, } from "./middleware.js";
|
|
6
|
+
export { type HandlerInput, type HandlerFn, type HandlerDefs, defineHandlers, } from "./handler.js";
|
|
7
|
+
export { type RouteGroup, type CreateAppOptions, type TypoKitApp, createApp, } from "./app.js";
|
|
8
|
+
export { type ErrorMiddlewareOptions, createErrorMiddleware, } from "./error-middleware.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,sBAAsB,EAC3B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AsyncSeriesHookImpl, createBuildPipeline, getPipelineTaps, BUILD_HOOK_PHASES, } from "./hooks.js";
|
|
2
|
+
export { defineMiddleware, createPlaceholderLogger, createRequestContext, executeMiddlewareChain, } from "./middleware.js";
|
|
3
|
+
export { defineHandlers, } from "./handler.js";
|
|
4
|
+
export { createApp, } from "./app.js";
|
|
5
|
+
export { createErrorMiddleware, } from "./error-middleware.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,GAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAIL,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIL,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAIL,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAEL,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TypoKitRequest, RequestContext, Logger } from "@typokit/types";
|
|
2
|
+
/** Input received by a defineMiddleware handler */
|
|
3
|
+
export interface MiddlewareInput {
|
|
4
|
+
headers: TypoKitRequest["headers"];
|
|
5
|
+
body: TypoKitRequest["body"];
|
|
6
|
+
query: TypoKitRequest["query"];
|
|
7
|
+
params: TypoKitRequest["params"];
|
|
8
|
+
ctx: RequestContext;
|
|
9
|
+
}
|
|
10
|
+
/** A typed middleware created by defineMiddleware */
|
|
11
|
+
export interface Middleware<TAdded extends Record<string, unknown> = Record<string, unknown>> {
|
|
12
|
+
handler: (input: MiddlewareInput) => Promise<TAdded>;
|
|
13
|
+
}
|
|
14
|
+
/** An entry in the middleware chain with name and optional priority */
|
|
15
|
+
export interface MiddlewareEntry {
|
|
16
|
+
name: string;
|
|
17
|
+
middleware: Middleware;
|
|
18
|
+
priority?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Define a typed middleware that receives request properties and returns
|
|
22
|
+
* additional context properties. Supports context type narrowing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function defineMiddleware<TAdded extends Record<string, unknown>>(handler: (input: MiddlewareInput) => Promise<TAdded>): Middleware<TAdded>;
|
|
25
|
+
/** Create a no-op placeholder logger (actual implementation in observability phase) */
|
|
26
|
+
export declare function createPlaceholderLogger(): Logger;
|
|
27
|
+
/** Create a RequestContext with ctx.fail() and ctx.log placeholder */
|
|
28
|
+
export declare function createRequestContext(overrides?: Partial<RequestContext>): RequestContext;
|
|
29
|
+
/**
|
|
30
|
+
* Execute a middleware chain in priority order (lower priority runs first).
|
|
31
|
+
* Each middleware's returned properties are accumulated onto the context.
|
|
32
|
+
* Middleware can short-circuit by throwing an error.
|
|
33
|
+
*/
|
|
34
|
+
export declare function executeMiddlewareChain(req: TypoKitRequest, ctx: RequestContext, entries: MiddlewareEntry[]): Promise<RequestContext>;
|
|
35
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7E,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC7B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,GAAG,EAAE,cAAc,CAAC;CACrB;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU,CACzB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEhE,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACtD;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,GACnD,UAAU,CAAC,MAAM,CAAC,CAEpB;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,IAAI,MAAM,CAUhD;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,CAClC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAClC,cAAc,CAgBhB;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,eAAe,EAAE,GACzB,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
|