stitchkit 0.0.1 → 0.2.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 +21 -0
- package/README.md +376 -4
- package/dist/browser/client.d.ts +30 -0
- package/dist/browser/client.d.ts.map +1 -0
- package/dist/browser/http.d.ts +77 -0
- package/dist/browser/http.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +69 -0
- package/dist/browser/socket-io.d.ts.map +1 -0
- package/dist/browser/stream.d.ts +2 -0
- package/dist/browser/stream.d.ts.map +1 -0
- package/dist/contract/define.d.ts +94 -0
- package/dist/contract/define.d.ts.map +1 -0
- package/dist/contract/errors.d.ts +47 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/index.d.ts +4 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +26 -0
- package/dist/contract/pagination.d.ts +19 -0
- package/dist/contract/pagination.d.ts.map +1 -0
- package/dist/index-5sxnvwb1.js +368 -0
- package/dist/index-809wc1tt.js +18 -0
- package/dist/index-a35v22fh.js +29 -0
- package/dist/index-kckky6zw.js +96 -0
- package/dist/index-ke4mx4ea.js +38 -0
- package/dist/index-n7bmdwmz.js +68 -0
- package/dist/index-v2z2v3mq.js +587 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/internal/errors.d.ts +5 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/typed.d.ts +9 -0
- package/dist/internal/typed.d.ts.map +1 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +26 -0
- package/dist/observability/audit.d.ts +32 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/context.d.ts +60 -0
- package/dist/observability/context.d.ts.map +1 -0
- package/dist/observability/event.d.ts +49 -0
- package/dist/observability/event.d.ts.map +1 -0
- package/dist/observability/index.d.ts +14 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +266 -0
- package/dist/observability/sanitize.d.ts +42 -0
- package/dist/observability/sanitize.d.ts.map +1 -0
- package/dist/observability/trace.d.ts +42 -0
- package/dist/observability/trace.d.ts.map +1 -0
- package/dist/react/cache-bridge.d.ts +53 -0
- package/dist/react/cache-bridge.d.ts.map +1 -0
- package/dist/react/cursor-query.d.ts +37 -0
- package/dist/react/cursor-query.d.ts.map +1 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +52 -0
- package/dist/server/cache.d.ts +15 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/context.d.ts +9 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/create.d.ts +4 -0
- package/dist/server/create.d.ts.map +1 -0
- package/dist/server/event-bus.d.ts +24 -0
- package/dist/server/event-bus.d.ts.map +1 -0
- package/dist/server/implement.d.ts +16 -0
- package/dist/server/implement.d.ts.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +399 -0
- package/dist/server/logger.d.ts +21 -0
- package/dist/server/logger.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +50 -0
- package/dist/server/middleware/auth.d.ts.map +1 -0
- package/dist/server/middleware/cookies.d.ts +34 -0
- package/dist/server/middleware/cookies.d.ts.map +1 -0
- package/dist/server/middleware/cors.d.ts +9 -0
- package/dist/server/middleware/cors.d.ts.map +1 -0
- package/dist/server/multipart.d.ts +14 -0
- package/dist/server/multipart.d.ts.map +1 -0
- package/dist/server/node.d.ts +12 -0
- package/dist/server/node.d.ts.map +1 -0
- package/dist/server/rate-limit.d.ts +17 -0
- package/dist/server/rate-limit.d.ts.map +1 -0
- package/dist/server/request.d.ts +22 -0
- package/dist/server/request.d.ts.map +1 -0
- package/dist/server/router.d.ts +42 -0
- package/dist/server/router.d.ts.map +1 -0
- package/dist/server/socket-io.d.ts +42 -0
- package/dist/server/socket-io.d.ts.map +1 -0
- package/dist/server/stream.d.ts +18 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/dist/server/swept-map.d.ts +22 -0
- package/dist/server/swept-map.d.ts.map +1 -0
- package/dist/server/types.d.ts +127 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/tools/agent.d.ts +27 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/coerce.d.ts +8 -0
- package/dist/tools/coerce.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +45 -0
- package/dist/tools/execute.d.ts.map +1 -0
- package/dist/tools/flatten.d.ts +15 -0
- package/dist/tools/flatten.d.ts.map +1 -0
- package/dist/tools/json-schema.d.ts +18 -0
- package/dist/tools/json-schema.d.ts.map +1 -0
- package/dist/tools/manifest.d.ts +13 -0
- package/dist/tools/manifest.d.ts.map +1 -0
- package/dist/tools/mcp-handler.d.ts +21 -0
- package/dist/tools/mcp-handler.d.ts.map +1 -0
- package/dist/tools/mcp-stdio.d.ts +25 -0
- package/dist/tools/mcp-stdio.d.ts.map +1 -0
- package/dist/tools/mcp.d.ts +92 -0
- package/dist/tools/mcp.d.ts.map +1 -0
- package/dist/tools/mount.d.ts +73 -0
- package/dist/tools/mount.d.ts.map +1 -0
- package/dist/tools/names.d.ts +2 -0
- package/dist/tools/names.d.ts.map +1 -0
- package/dist/tools/remote.d.ts +22 -0
- package/dist/tools/remote.d.ts.map +1 -0
- package/dist/tools/schema.d.ts +21 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/view-file.d.ts +57 -0
- package/dist/tools/view-file.d.ts.map +1 -0
- package/dist/tools.d.ts +12 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +837 -0
- package/package.json +147 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor-paginated infinite query — the family's canonical pagination hook.
|
|
3
|
+
*
|
|
4
|
+
* Built on `react-query-kit`'s `createInfiniteQuery` (the family's hook layer),
|
|
5
|
+
* so the result keeps the full rqk surface: `.getKey()`, `.fetcher`,
|
|
6
|
+
* `useSuspenseInfiniteQuery`, every option. Cursor pagination is immune to
|
|
7
|
+
* concurrent inserts — offset paging drops or duplicates rows when a new row
|
|
8
|
+
* lands on top of the list.
|
|
9
|
+
*
|
|
10
|
+
* Pass the typed contract method as `endpoint` (e.g. `apiClient.media.list`).
|
|
11
|
+
* The helper injects `cursor` from the page param and bakes in
|
|
12
|
+
* `getNextPageParam` / `initialPageParam`. An infinite hook is therefore just
|
|
13
|
+
* `queryKey + endpoint` — no per-hook fetcher, no pagination plumbing. Every
|
|
14
|
+
* other rqk option (`staleTime`, `gcTime`, `refetch*`, …) passes through.
|
|
15
|
+
*
|
|
16
|
+
* Page size is NOT a client concern: the helper never sends `limit`, so the
|
|
17
|
+
* server applies the contract's `limit` default (`z.coerce.number().default(N)`).
|
|
18
|
+
* That default is the single source of truth — the client cannot diverge.
|
|
19
|
+
*/
|
|
20
|
+
import { type CompatibleError, type CreateInfiniteQueryOptions } from 'react-query-kit';
|
|
21
|
+
/** The cursor field the helper owns — the caller never passes it. */
|
|
22
|
+
interface CursorParam {
|
|
23
|
+
cursor?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Options the helper computes itself; the caller cannot override them. */
|
|
26
|
+
type ControlledKeys = 'fetcher' | 'initialPageParam' | 'getNextPageParam';
|
|
27
|
+
export type CursorQueryConfig<TVars, TPage extends {
|
|
28
|
+
nextCursor: string | null;
|
|
29
|
+
}, TError> = Omit<CreateInfiniteQueryOptions<TPage, TVars, TError, string | null>, ControlledKeys> & {
|
|
30
|
+
/** Contract method, e.g. `apiClient.media.list`. */
|
|
31
|
+
endpoint: (args: TVars & CursorParam) => Promise<TPage>;
|
|
32
|
+
};
|
|
33
|
+
export declare function createCursorQuery<TVars, TPage extends {
|
|
34
|
+
nextCursor: string | null;
|
|
35
|
+
}, TError = CompatibleError>(config: CursorQueryConfig<TVars, TPage, TError>): import("react-query-kit").InfiniteQueryHook<TPage, TVars, TError, string | null>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=cursor-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-query.d.ts","sourceRoot":"","sources":["../../src/react/cursor-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAEhC,MAAM,iBAAiB,CAAC;AAEzB,qEAAqE;AACrE,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2EAA2E;AAC3E,KAAK,cAAc,GAAG,SAAS,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAE1E,MAAM,MAAM,iBAAiB,CAC3B,KAAK,EACL,KAAK,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3C,MAAM,IACJ,IAAI,CAAC,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAAE,cAAc,CAAC,GAAG;IAC1F,oDAAoD;IACpD,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,GAAG,WAAW,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;CACzD,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,KAAK,EACL,KAAK,SAAS;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3C,MAAM,GAAG,eAAe,EACxB,MAAM,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,oFAYhD"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type CacheBridge, type CacheBridgeConfig, type CacheBridgeContext, type CacheBridgeHandler, type CacheBridgeHandlers, type CacheBridgeSocket, createCacheBridge, } from './react/cache-bridge';
|
|
2
|
+
export { type CursorQueryConfig, createCursorQuery } from './react/cursor-query';
|
|
3
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/react/cache-bridge.ts
|
|
2
|
+
function createCacheBridge(config) {
|
|
3
|
+
const freshWindow = config.freshWindow ?? 500;
|
|
4
|
+
const freshAt = new Map;
|
|
5
|
+
let unsubscribers = [];
|
|
6
|
+
const isFresh = (key) => {
|
|
7
|
+
const ts = freshAt.get(JSON.stringify(key));
|
|
8
|
+
return ts !== undefined && Date.now() - ts < freshWindow;
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
connect() {
|
|
12
|
+
if (unsubscribers.length > 0)
|
|
13
|
+
return;
|
|
14
|
+
const queryClient = typeof config.queryClient === "function" ? config.queryClient() : config.queryClient;
|
|
15
|
+
const ctx = { queryClient, isFresh };
|
|
16
|
+
const handlers = config.handlers;
|
|
17
|
+
const socket = config.socket;
|
|
18
|
+
for (const event of Object.keys(handlers)) {
|
|
19
|
+
const handler = handlers[event];
|
|
20
|
+
if (!handler)
|
|
21
|
+
continue;
|
|
22
|
+
const off = socket.on(event, (data) => handler(data, ctx));
|
|
23
|
+
unsubscribers.push(off);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
disconnect() {
|
|
27
|
+
for (const off of unsubscribers)
|
|
28
|
+
off();
|
|
29
|
+
unsubscribers = [];
|
|
30
|
+
},
|
|
31
|
+
markFresh(key) {
|
|
32
|
+
freshAt.set(JSON.stringify(key), Date.now());
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// src/react/cursor-query.ts
|
|
37
|
+
import {
|
|
38
|
+
createInfiniteQuery
|
|
39
|
+
} from "react-query-kit";
|
|
40
|
+
function createCursorQuery(config) {
|
|
41
|
+
const { endpoint, ...rest } = config;
|
|
42
|
+
return createInfiniteQuery({
|
|
43
|
+
...rest,
|
|
44
|
+
fetcher: (variables, { pageParam }) => endpoint({ ...variables, cursor: pageParam ?? undefined }),
|
|
45
|
+
initialPageParam: null,
|
|
46
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
createCursorQuery,
|
|
51
|
+
createCacheBridge
|
|
52
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory key/value cache with a per-entry TTL. Expired entries are swept on
|
|
3
|
+
* a 30-second timer; `destroy()` stops it. For response `Cache-Control`
|
|
4
|
+
* headers use `cacheHeaders()` instead.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCache(): {
|
|
7
|
+
destroy: () => void;
|
|
8
|
+
get(key: string): unknown | null;
|
|
9
|
+
set(key: string, data: unknown, maxAgeSeconds: number): void;
|
|
10
|
+
invalidate(prefix: string): void;
|
|
11
|
+
clear(): void;
|
|
12
|
+
};
|
|
13
|
+
/** Build a `Cache-Control` header capping freshness at `maxAge` seconds. */
|
|
14
|
+
export declare function cacheHeaders(maxAge: number, scope?: 'public' | 'private'): Record<string, string>;
|
|
15
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/server/cache.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,wBAAgB,WAAW;;aAQd,MAAM,GAAG,OAAO,GAAG,IAAI;aAUvB,MAAM,QAAQ,OAAO,iBAAiB,MAAM;uBAIlC,MAAM;;EAU5B;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,QAAQ,GAAG,SAAoB,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RuntimeContext` assembly — parses params / body / multipart against the
|
|
3
|
+
* endpoint schemas and gathers request metadata (trace id, client info).
|
|
4
|
+
*/
|
|
5
|
+
import { type RuntimeContext } from '../contract';
|
|
6
|
+
import type { MethodDef } from './types';
|
|
7
|
+
export declare function buildContext(req: Request, url: URL, method: MethodDef, pathParams: Record<string, string>, traceId: string): Promise<RuntimeContext>;
|
|
8
|
+
export declare function buildErrorContext(req: Request, url: URL, traceId: string): RuntimeContext;
|
|
9
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA2BzC,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAyCzB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAWzF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BunServerConfig, HandlerConfig } from './types';
|
|
2
|
+
export declare function createHandler(config: HandlerConfig): (req: Request) => Promise<Response>;
|
|
3
|
+
export declare function createServer(config: BunServerConfig): Bun.Server<unknown>;
|
|
4
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiLxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type EventHandler<T = unknown> = (data: T) => void | Promise<void>;
|
|
2
|
+
/** Default event map — untyped string-keyed events (ad-hoc buses). */
|
|
3
|
+
export type DefaultEventMap = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* In-process pub/sub bus.
|
|
6
|
+
*
|
|
7
|
+
* Pass an event map for full type safety — `emit`/`on`/`once`/`off` are then
|
|
8
|
+
* typed per event:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const bus = createEventBus<{ 'user:created': { id: string } }>();
|
|
12
|
+
* bus.emit('user:created', { id: '1' }); // typed
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface EventBus<M extends Record<string, unknown> = DefaultEventMap> {
|
|
16
|
+
emit<K extends keyof M & string>(event: K, data: M[K]): void;
|
|
17
|
+
on<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): () => void;
|
|
18
|
+
once<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): () => void;
|
|
19
|
+
off<K extends keyof M & string>(event: K, handler: EventHandler<M[K]>): void;
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function createEventBus<M extends Record<string, unknown> = DefaultEventMap>(): EventBus<M>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=event-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/server/event-bus.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe;IAC3E,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7D,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IAClF,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACpF,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7E,KAAK,IAAI,IAAI,CAAC;CACf;AAYD,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,KAChD,QAAQ,CAAC,CAAC,CAAC,CAuDf"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ContractDef, EndpointDef, RuntimeContext } from '../contract';
|
|
2
|
+
import type { Handlers, ServiceDef } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Bind a contract to its typed `handlers`, producing a `ServiceDef` to mount on
|
|
5
|
+
* `createServer`. Every handler is type-checked against its endpoint's schemas.
|
|
6
|
+
* Pass `TCtx` for a typed handler context — or use `createImplement` to fix it
|
|
7
|
+
* once.
|
|
8
|
+
*/
|
|
9
|
+
export declare function implement<T extends Record<string, EndpointDef>, TCtx extends RuntimeContext = RuntimeContext>(contract: ContractDef<T, string>, handlers: Handlers<T, TCtx>): ServiceDef;
|
|
10
|
+
/**
|
|
11
|
+
* Fix the handler context type once — `const implement =
|
|
12
|
+
* createImplement<MyContext>()` — so each `implement()` call site stays free of
|
|
13
|
+
* the generic. The application declares its context shape in a single place.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createImplement<TCtx extends RuntimeContext>(): <T extends Record<string, EndpointDef>>(contract: ContractDef<T, string>, handlers: Handlers<T, TCtx>) => ServiceDef;
|
|
16
|
+
//# sourceMappingURL=implement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/server/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAa,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,EAC5C,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CAoC3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,MACjD,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC3C,UAAU,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,UAAU,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,KAC1B,UAAU,CACd"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { AppError, appError, badRequest, conflict, forbidden, notFound, rateLimited, unauthorized, } from '../contract';
|
|
2
|
+
export { cacheHeaders, createCache } from './cache';
|
|
3
|
+
export { createHandler, createServer } from './create';
|
|
4
|
+
export { createEventBus, type EventBus } from './event-bus';
|
|
5
|
+
export { createImplement, implement } from './implement';
|
|
6
|
+
export { type AuthHook, type AuthHookConfig, type AuthRule, type BearerResolverConfig, createAuthHook, createBearerResolver, extractToken, type JwtPayload, verifyJwt, } from './middleware/auth';
|
|
7
|
+
export { type CookieDef, type CookieOptions, defineCookie, parseCookies, serializeCookie, } from './middleware/cookies';
|
|
8
|
+
export { type CorsConfig, corsHeaders, corsPreflightResponse, } from './middleware/cors';
|
|
9
|
+
export { parseMultipart } from './multipart';
|
|
10
|
+
export { createRateLimiter, type RateLimitConfig } from './rate-limit';
|
|
11
|
+
export { extractIp, generateTraceId, getClientInfo, resolveTraceId, } from './request';
|
|
12
|
+
export { staticRoute } from './router';
|
|
13
|
+
export type { SocketIOServerConfig, SocketIOServerHandle } from './socket-io';
|
|
14
|
+
export { createSocketIOServer } from './socket-io';
|
|
15
|
+
export { type ParseSSEOptions, parseSSE, streamSSE } from './stream';
|
|
16
|
+
export type { BunServer, BunServerConfig, HandlerConfig, Handlers, LifecycleHooks, MethodDef, RawRoute, RawRouteContext, RouteGroup, ServerPassthrough, ServiceDef, StitchLogger, } from './types';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,KAAK,UAAU,EACf,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,UAAU,EACf,WAAW,EACX,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EACL,SAAS,EACT,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrE,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,EACd,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseSSE,
|
|
3
|
+
streamSSE
|
|
4
|
+
} from "../index-n7bmdwmz.js";
|
|
5
|
+
import {
|
|
6
|
+
corsHeaders,
|
|
7
|
+
corsPreflightResponse,
|
|
8
|
+
createHandler,
|
|
9
|
+
createServer,
|
|
10
|
+
parseMultipart,
|
|
11
|
+
staticRoute
|
|
12
|
+
} from "../index-v2z2v3mq.js";
|
|
13
|
+
import"../index-a35v22fh.js";
|
|
14
|
+
import {
|
|
15
|
+
AppError,
|
|
16
|
+
appError,
|
|
17
|
+
badRequest,
|
|
18
|
+
conflict,
|
|
19
|
+
forbidden,
|
|
20
|
+
notFound,
|
|
21
|
+
rateLimited,
|
|
22
|
+
unauthorized
|
|
23
|
+
} from "../index-kckky6zw.js";
|
|
24
|
+
import {
|
|
25
|
+
extractIp,
|
|
26
|
+
generateTraceId,
|
|
27
|
+
getClientInfo,
|
|
28
|
+
resolveTraceId
|
|
29
|
+
} from "../index-ke4mx4ea.js";
|
|
30
|
+
// src/server/swept-map.ts
|
|
31
|
+
function createSweptMap(options) {
|
|
32
|
+
const store = new Map;
|
|
33
|
+
const timer = setInterval(() => {
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
for (const [key, value] of store) {
|
|
36
|
+
if (options.isExpired(value, now))
|
|
37
|
+
store.delete(key);
|
|
38
|
+
}
|
|
39
|
+
}, options.intervalMs);
|
|
40
|
+
timer.unref();
|
|
41
|
+
return {
|
|
42
|
+
store,
|
|
43
|
+
destroy() {
|
|
44
|
+
clearInterval(timer);
|
|
45
|
+
store.clear();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/server/cache.ts
|
|
51
|
+
function createCache() {
|
|
52
|
+
const { store, destroy } = createSweptMap({
|
|
53
|
+
intervalMs: 30000,
|
|
54
|
+
isExpired: (entry, now) => entry.expiresAt < now
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
destroy,
|
|
58
|
+
get(key) {
|
|
59
|
+
const entry = store.get(key);
|
|
60
|
+
if (!entry)
|
|
61
|
+
return null;
|
|
62
|
+
if (entry.expiresAt < Date.now()) {
|
|
63
|
+
store.delete(key);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return entry.data;
|
|
67
|
+
},
|
|
68
|
+
set(key, data, maxAgeSeconds) {
|
|
69
|
+
store.set(key, { data, expiresAt: Date.now() + maxAgeSeconds * 1000 });
|
|
70
|
+
},
|
|
71
|
+
invalidate(prefix) {
|
|
72
|
+
for (const key of store.keys()) {
|
|
73
|
+
if (key.startsWith(prefix))
|
|
74
|
+
store.delete(key);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
clear() {
|
|
78
|
+
store.clear();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function cacheHeaders(maxAge, scope = "public") {
|
|
83
|
+
return {
|
|
84
|
+
"Cache-Control": `${scope}, max-age=${maxAge}`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// src/server/event-bus.ts
|
|
88
|
+
function createEventBus() {
|
|
89
|
+
const subscriptions = new Map;
|
|
90
|
+
function add(event, fn, once) {
|
|
91
|
+
let set = subscriptions.get(event);
|
|
92
|
+
if (!set) {
|
|
93
|
+
set = new Set;
|
|
94
|
+
subscriptions.set(event, set);
|
|
95
|
+
}
|
|
96
|
+
const sub = { fn, once };
|
|
97
|
+
set.add(sub);
|
|
98
|
+
return () => {
|
|
99
|
+
set.delete(sub);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
emit(event, data) {
|
|
104
|
+
const set = subscriptions.get(event);
|
|
105
|
+
if (!set)
|
|
106
|
+
return;
|
|
107
|
+
for (const sub of [...set]) {
|
|
108
|
+
if (sub.once)
|
|
109
|
+
set.delete(sub);
|
|
110
|
+
try {
|
|
111
|
+
const result = sub.fn(data);
|
|
112
|
+
if (result instanceof Promise)
|
|
113
|
+
result.catch(() => {
|
|
114
|
+
return;
|
|
115
|
+
});
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
on(event, handler) {
|
|
120
|
+
return add(event, handler, false);
|
|
121
|
+
},
|
|
122
|
+
once(event, handler) {
|
|
123
|
+
return add(event, handler, true);
|
|
124
|
+
},
|
|
125
|
+
off(event, handler) {
|
|
126
|
+
const set = subscriptions.get(event);
|
|
127
|
+
if (!set)
|
|
128
|
+
return;
|
|
129
|
+
for (const sub of set) {
|
|
130
|
+
if (sub.fn === handler)
|
|
131
|
+
set.delete(sub);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
clear() {
|
|
135
|
+
subscriptions.clear();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// src/server/implement.ts
|
|
140
|
+
function implement(contract, handlers) {
|
|
141
|
+
const methods = {};
|
|
142
|
+
const groupScope = contract.meta.scope ?? "public";
|
|
143
|
+
for (const [key, endpoint] of Object.entries(contract.endpoints)) {
|
|
144
|
+
const typedHandler = handlers[key];
|
|
145
|
+
methods[key] = {
|
|
146
|
+
method: endpoint.method,
|
|
147
|
+
path: endpoint.path,
|
|
148
|
+
desc: endpoint.desc,
|
|
149
|
+
toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
|
|
150
|
+
expose: endpoint.expose,
|
|
151
|
+
scope: endpoint.scope ?? groupScope,
|
|
152
|
+
paramsSchema: endpoint.params,
|
|
153
|
+
inputSchema: endpoint.input,
|
|
154
|
+
outputSchema: endpoint.output,
|
|
155
|
+
multipart: endpoint.multipart,
|
|
156
|
+
handler: (ctx) => typedHandler(ctx)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
name: contract.meta.prefix,
|
|
161
|
+
prefix: contract.meta.prefix,
|
|
162
|
+
scope: groupScope,
|
|
163
|
+
methods
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function createImplement() {
|
|
167
|
+
return (contract, handlers) => implement(contract, handlers);
|
|
168
|
+
}
|
|
169
|
+
// src/server/middleware/cookies.ts
|
|
170
|
+
function parseCookies(header) {
|
|
171
|
+
if (!header)
|
|
172
|
+
return {};
|
|
173
|
+
const cookies = {};
|
|
174
|
+
for (const pair of header.split(";")) {
|
|
175
|
+
const [key, ...rest] = pair.split("=");
|
|
176
|
+
if (!key)
|
|
177
|
+
continue;
|
|
178
|
+
const raw = rest.join("=").trim();
|
|
179
|
+
try {
|
|
180
|
+
cookies[key.trim()] = decodeURIComponent(raw);
|
|
181
|
+
} catch {
|
|
182
|
+
cookies[key.trim()] = raw;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return cookies;
|
|
186
|
+
}
|
|
187
|
+
function serializeCookie(name, value, opts = {}) {
|
|
188
|
+
let cookie = `${name}=${encodeURIComponent(value)}`;
|
|
189
|
+
if (opts.maxAge !== undefined)
|
|
190
|
+
cookie += `; Max-Age=${opts.maxAge}`;
|
|
191
|
+
if (opts.path)
|
|
192
|
+
cookie += `; Path=${opts.path}`;
|
|
193
|
+
if (opts.domain)
|
|
194
|
+
cookie += `; Domain=${opts.domain}`;
|
|
195
|
+
if (opts.secure)
|
|
196
|
+
cookie += "; Secure";
|
|
197
|
+
if (opts.httpOnly)
|
|
198
|
+
cookie += "; HttpOnly";
|
|
199
|
+
if (opts.sameSite)
|
|
200
|
+
cookie += `; SameSite=${opts.sameSite}`;
|
|
201
|
+
return cookie;
|
|
202
|
+
}
|
|
203
|
+
function defineCookie(config) {
|
|
204
|
+
const { name, ...options } = config;
|
|
205
|
+
return {
|
|
206
|
+
get(req) {
|
|
207
|
+
return parseCookies(req.headers.get("cookie"))[name];
|
|
208
|
+
},
|
|
209
|
+
set(value) {
|
|
210
|
+
return serializeCookie(name, value, options);
|
|
211
|
+
},
|
|
212
|
+
clear() {
|
|
213
|
+
return serializeCookie(name, "", { ...options, maxAge: 0 });
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/server/middleware/auth.ts
|
|
219
|
+
function decodeBase64Url(segment) {
|
|
220
|
+
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
221
|
+
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
222
|
+
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
223
|
+
}
|
|
224
|
+
async function verifyJwt(token, secret) {
|
|
225
|
+
const [headerB64, payloadB64, signatureB64] = token.split(".");
|
|
226
|
+
if (!headerB64 || !payloadB64 || !signatureB64)
|
|
227
|
+
throw unauthorized("Invalid token format");
|
|
228
|
+
let header;
|
|
229
|
+
let payload;
|
|
230
|
+
let signature;
|
|
231
|
+
try {
|
|
232
|
+
const decoder = new TextDecoder;
|
|
233
|
+
header = JSON.parse(decoder.decode(decodeBase64Url(headerB64)));
|
|
234
|
+
payload = JSON.parse(decoder.decode(decodeBase64Url(payloadB64)));
|
|
235
|
+
signature = decodeBase64Url(signatureB64);
|
|
236
|
+
} catch {
|
|
237
|
+
throw unauthorized("Malformed token");
|
|
238
|
+
}
|
|
239
|
+
if (header.alg !== "HS256")
|
|
240
|
+
throw unauthorized("Unsupported token algorithm");
|
|
241
|
+
const encoder = new TextEncoder;
|
|
242
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
|
|
243
|
+
const data = encoder.encode(`${headerB64}.${payloadB64}`);
|
|
244
|
+
const valid = await crypto.subtle.verify("HMAC", key, signature, data);
|
|
245
|
+
if (!valid)
|
|
246
|
+
throw unauthorized("Invalid token signature");
|
|
247
|
+
const now = Date.now() / 1000;
|
|
248
|
+
if (typeof payload.exp === "number" && payload.exp < now) {
|
|
249
|
+
throw unauthorized("Token expired");
|
|
250
|
+
}
|
|
251
|
+
if (typeof payload.nbf === "number" && payload.nbf > now) {
|
|
252
|
+
throw unauthorized("Token not yet valid");
|
|
253
|
+
}
|
|
254
|
+
return payload;
|
|
255
|
+
}
|
|
256
|
+
function extractToken(req, cookieName) {
|
|
257
|
+
const authHeader = req.headers.get("authorization");
|
|
258
|
+
if (authHeader?.startsWith("Bearer "))
|
|
259
|
+
return authHeader.slice(7);
|
|
260
|
+
if (cookieName) {
|
|
261
|
+
const cookies = parseCookies(req.headers.get("cookie"));
|
|
262
|
+
return cookies[cookieName] ?? null;
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
function createAuthHook(config) {
|
|
267
|
+
const onAnonymous = config.onAnonymous ?? (() => unauthorized());
|
|
268
|
+
const onForbidden = config.onForbidden ?? (() => forbidden());
|
|
269
|
+
return async (ctx, endpoint) => {
|
|
270
|
+
if (!(ctx.req instanceof Request))
|
|
271
|
+
return;
|
|
272
|
+
const identity = await config.resolve(ctx);
|
|
273
|
+
config.inject?.(ctx, identity);
|
|
274
|
+
const scope = endpoint.scope ?? config.defaultScope;
|
|
275
|
+
if (!scope)
|
|
276
|
+
return;
|
|
277
|
+
const rule = config.rules[scope];
|
|
278
|
+
if (!rule || rule === "public")
|
|
279
|
+
return;
|
|
280
|
+
if (!identity) {
|
|
281
|
+
onAnonymous();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (rule === "authenticated")
|
|
285
|
+
return;
|
|
286
|
+
if (!await rule(identity, ctx))
|
|
287
|
+
onForbidden();
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function createBearerResolver(config) {
|
|
291
|
+
return async (req) => {
|
|
292
|
+
const token = extractToken(req);
|
|
293
|
+
if (!token)
|
|
294
|
+
return null;
|
|
295
|
+
return config.lookup(token, req);
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
// src/server/rate-limit.ts
|
|
299
|
+
function refillBucket(entry, config, now) {
|
|
300
|
+
const elapsed = (now - entry.lastRefill) / 1000;
|
|
301
|
+
entry.tokens = Math.min(config.max, entry.tokens + elapsed / config.window * config.max);
|
|
302
|
+
entry.lastRefill = now;
|
|
303
|
+
}
|
|
304
|
+
function createRateLimiter() {
|
|
305
|
+
const { store: buckets, destroy } = createSweptMap({
|
|
306
|
+
intervalMs: 60000,
|
|
307
|
+
isExpired: (entry, now) => now - entry.lastRefill > 300000
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
destroy,
|
|
311
|
+
check(key, config) {
|
|
312
|
+
const now = Date.now();
|
|
313
|
+
let entry = buckets.get(key);
|
|
314
|
+
if (!entry) {
|
|
315
|
+
entry = { tokens: config.max, lastRefill: now };
|
|
316
|
+
buckets.set(key, entry);
|
|
317
|
+
}
|
|
318
|
+
refillBucket(entry, config, now);
|
|
319
|
+
if (entry.tokens < 1)
|
|
320
|
+
return false;
|
|
321
|
+
entry.tokens -= 1;
|
|
322
|
+
return true;
|
|
323
|
+
},
|
|
324
|
+
remaining(key, config) {
|
|
325
|
+
const entry = buckets.get(key);
|
|
326
|
+
if (!entry)
|
|
327
|
+
return config.max;
|
|
328
|
+
refillBucket(entry, config, Date.now());
|
|
329
|
+
return Math.floor(entry.tokens);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// src/server/socket-io.ts
|
|
334
|
+
import { Server as BunEngine } from "@socket.io/bun-engine";
|
|
335
|
+
import { Server as SocketIOServer } from "socket.io";
|
|
336
|
+
function createSocketIOServer(config) {
|
|
337
|
+
const path = config.path ?? "/socket.io/";
|
|
338
|
+
const io = new SocketIOServer({
|
|
339
|
+
path,
|
|
340
|
+
cors: {
|
|
341
|
+
origin: config.cors.origin,
|
|
342
|
+
credentials: config.cors.credentials ?? true,
|
|
343
|
+
methods: ["GET", "POST"]
|
|
344
|
+
},
|
|
345
|
+
transports: config.transports ?? ["websocket", "polling"],
|
|
346
|
+
pingTimeout: config.pingTimeout ?? 20000,
|
|
347
|
+
pingInterval: config.pingInterval ?? 1e4
|
|
348
|
+
});
|
|
349
|
+
const engine = new BunEngine({ path });
|
|
350
|
+
io.bind(engine);
|
|
351
|
+
const { websocket } = engine.handler();
|
|
352
|
+
const route = {
|
|
353
|
+
method: "ALL",
|
|
354
|
+
path: `${path.replace(/\/+$/, "")}/*`,
|
|
355
|
+
handler: (req, ctx) => {
|
|
356
|
+
if (!ctx.server) {
|
|
357
|
+
throw new Error("[stitchkit] createSocketIOServer route needs a running Bun server — mount it via createServer.");
|
|
358
|
+
}
|
|
359
|
+
return engine.handleRequest(req, ctx.server);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
return { io, websocket, route };
|
|
363
|
+
}
|
|
364
|
+
export {
|
|
365
|
+
verifyJwt,
|
|
366
|
+
unauthorized,
|
|
367
|
+
streamSSE,
|
|
368
|
+
staticRoute,
|
|
369
|
+
serializeCookie,
|
|
370
|
+
resolveTraceId,
|
|
371
|
+
rateLimited,
|
|
372
|
+
parseSSE,
|
|
373
|
+
parseMultipart,
|
|
374
|
+
parseCookies,
|
|
375
|
+
notFound,
|
|
376
|
+
implement,
|
|
377
|
+
getClientInfo,
|
|
378
|
+
generateTraceId,
|
|
379
|
+
forbidden,
|
|
380
|
+
extractToken,
|
|
381
|
+
extractIp,
|
|
382
|
+
defineCookie,
|
|
383
|
+
createSocketIOServer,
|
|
384
|
+
createServer,
|
|
385
|
+
createRateLimiter,
|
|
386
|
+
createImplement,
|
|
387
|
+
createHandler,
|
|
388
|
+
createEventBus,
|
|
389
|
+
createCache,
|
|
390
|
+
createBearerResolver,
|
|
391
|
+
createAuthHook,
|
|
392
|
+
corsPreflightResponse,
|
|
393
|
+
corsHeaders,
|
|
394
|
+
conflict,
|
|
395
|
+
cacheHeaders,
|
|
396
|
+
badRequest,
|
|
397
|
+
appError,
|
|
398
|
+
AppError
|
|
399
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function elapsedMs(startTime: bigint): number;
|
|
2
|
+
/** Map an HTTP status to a log level. */
|
|
3
|
+
export declare function levelForStatus(status: number): 'error' | 'warn' | 'info';
|
|
4
|
+
/** The structured fields shared by every completed-request log line. */
|
|
5
|
+
export declare function buildLogFields(method: string, path: string, status: number, durationMs: number, traceId: string): {
|
|
6
|
+
traceId: string;
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
status: number;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
};
|
|
12
|
+
export interface RequestLog {
|
|
13
|
+
traceId: string;
|
|
14
|
+
startTime: bigint;
|
|
15
|
+
}
|
|
16
|
+
export declare function shouldLog(pathname: string, method: string): boolean;
|
|
17
|
+
/** Open the timing window for a request. Development prints a `→` line. */
|
|
18
|
+
export declare function logIncoming(req: Request, pathname: string, traceId: string): RequestLog;
|
|
19
|
+
/** Close a request. Development: `←` line. Production: one structured JSON line. */
|
|
20
|
+
export declare function logOutgoing(req: Request, pathname: string, status: number, log: RequestLog): void;
|
|
21
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AA0CA,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAIxE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAEvF;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CASvF;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,UAAU,GACd,IAAI,CAoBN"}
|