@jini-ai/http-kit 0.3.2 → 0.3.3

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.
Files changed (73) hide show
  1. package/dist/delegated-tools.d.ts +89 -0
  2. package/dist/delegated-tools.d.ts.map +1 -1
  3. package/dist/delegated-tools.js +106 -1
  4. package/dist/delegated-tools.js.map +1 -1
  5. package/package.json +6 -6
  6. package/dist/express/run-stream.d.ts +0 -15
  7. package/dist/express/run-stream.d.ts.map +0 -1
  8. package/dist/express/run-stream.js +0 -11
  9. package/dist/express/run-stream.js.map +0 -1
  10. package/dist/express-index.d.ts +0 -32
  11. package/dist/express-index.d.ts.map +0 -1
  12. package/dist/express-index.js +0 -14
  13. package/dist/express-index.js.map +0 -1
  14. package/dist/fastify/adapter.d.ts +0 -31
  15. package/dist/fastify/adapter.d.ts.map +0 -1
  16. package/dist/fastify/adapter.js +0 -64
  17. package/dist/fastify/adapter.js.map +0 -1
  18. package/dist/fastify/agents.d.ts +0 -13
  19. package/dist/fastify/agents.d.ts.map +0 -1
  20. package/dist/fastify/agents.js +0 -7
  21. package/dist/fastify/agents.js.map +0 -1
  22. package/dist/fastify/api-security-middleware.d.ts +0 -64
  23. package/dist/fastify/api-security-middleware.d.ts.map +0 -1
  24. package/dist/fastify/api-security-middleware.js +0 -139
  25. package/dist/fastify/api-security-middleware.js.map +0 -1
  26. package/dist/fastify/compat.d.ts +0 -22
  27. package/dist/fastify/compat.d.ts.map +0 -1
  28. package/dist/fastify/compat.js +0 -16
  29. package/dist/fastify/compat.js.map +0 -1
  30. package/dist/fastify/daemon-status.d.ts +0 -22
  31. package/dist/fastify/daemon-status.d.ts.map +0 -1
  32. package/dist/fastify/daemon-status.js +0 -9
  33. package/dist/fastify/daemon-status.js.map +0 -1
  34. package/dist/fastify/host-tools.d.ts +0 -13
  35. package/dist/fastify/host-tools.d.ts.map +0 -1
  36. package/dist/fastify/host-tools.js +0 -8
  37. package/dist/fastify/host-tools.js.map +0 -1
  38. package/dist/fastify/index.d.ts +0 -36
  39. package/dist/fastify/index.d.ts.map +0 -1
  40. package/dist/fastify/index.js +0 -18
  41. package/dist/fastify/index.js.map +0 -1
  42. package/dist/fastify/local-daemon-request.d.ts +0 -43
  43. package/dist/fastify/local-daemon-request.d.ts.map +0 -1
  44. package/dist/fastify/local-daemon-request.js +0 -155
  45. package/dist/fastify/local-daemon-request.js.map +0 -1
  46. package/dist/fastify/origin.d.ts +0 -21
  47. package/dist/fastify/origin.d.ts.map +0 -1
  48. package/dist/fastify/origin.js +0 -14
  49. package/dist/fastify/origin.js.map +0 -1
  50. package/dist/fastify/request.d.ts +0 -20
  51. package/dist/fastify/request.d.ts.map +0 -1
  52. package/dist/fastify/request.js +0 -25
  53. package/dist/fastify/request.js.map +0 -1
  54. package/dist/fastify/response.d.ts +0 -20
  55. package/dist/fastify/response.d.ts.map +0 -1
  56. package/dist/fastify/response.js +0 -41
  57. package/dist/fastify/response.js.map +0 -1
  58. package/dist/fastify/route-registration-guard.d.ts +0 -70
  59. package/dist/fastify/route-registration-guard.d.ts.map +0 -1
  60. package/dist/fastify/route-registration-guard.js +0 -69
  61. package/dist/fastify/route-registration-guard.js.map +0 -1
  62. package/dist/fastify/run-stream.d.ts +0 -18
  63. package/dist/fastify/run-stream.d.ts.map +0 -1
  64. package/dist/fastify/run-stream.js +0 -10
  65. package/dist/fastify/run-stream.js.map +0 -1
  66. package/dist/fastify/runs.d.ts +0 -17
  67. package/dist/fastify/runs.d.ts.map +0 -1
  68. package/dist/fastify/runs.js +0 -33
  69. package/dist/fastify/runs.js.map +0 -1
  70. package/dist/run-stream.d.ts +0 -60
  71. package/dist/run-stream.d.ts.map +0 -1
  72. package/dist/run-stream.js +0 -108
  73. package/dist/run-stream.js.map +0 -1
@@ -1,139 +0,0 @@
1
- import { apiTokenFromEnv, isApiTokenMiddlewareEnabled, } from '@jini/core';
2
- import { isLoopbackPeerAddress } from './local-daemon-request.js';
3
- import { allowedBrowserPorts, isAllowedBrowserOrigin } from '../origin-validation.js';
4
- /** Health/readiness/version probes stay reachable without a bearer token so monitoring never needs one. Both the mount-relative and `/api`-prefixed forms are kept for parity with the Express version's set (only the `/api`-prefixed forms are ever reachable here, since both hooks below already require the path to start with `/api` before this set is even consulted). */
5
- const OPEN_PROBE_PATHS = new Set([
6
- '/health',
7
- '/api/health',
8
- '/ready',
9
- '/api/ready',
10
- '/version',
11
- '/api/version',
12
- ]);
13
- const BEARER_TOKEN_PATTERN = /^Bearer\s+(\S+)\s*$/i;
14
- const DEFAULT_TOKEN_CONFIG = {
15
- tokenEnvVar: 'JINI_API_TOKEN',
16
- disableEnvVar: 'JINI_DISABLE_API_AUTH',
17
- };
18
- /**
19
- * The request path with no query string — Fastify's `request.url` (unlike Express's
20
- * `request.path`) always includes the query string, so this strips it to match Express's
21
- * comparison semantics. `req.url` is typed as a required `string` on `FastifyRequest`
22
- * (never `undefined`), and `String.prototype.split` on a string separator always returns an
23
- * array with at least one element, so the `[0]` index is provably defined — the non-null
24
- * assertion documents that guarantee instead of masking it behind an unreachable `?? ''` fallback
25
- * that could never be exercised by a real request.
26
- */
27
- function requestPath(req) {
28
- return req.url.split('?')[0];
29
- }
30
- /**
31
- * Registers a bearer-token gate on every `/api/*` route, active only when
32
- * {@link isApiTokenMiddlewareEnabled} says a token is configured and auth hasn't been disabled.
33
- * When active: open-probe paths and loopback peers skip the check unconditionally; every other
34
- * request must send `Authorization: Bearer <token>` matching the configured token exactly, or the
35
- * request is rejected with 401 before reaching any route handler.
36
- *
37
- * @param app - The Fastify instance to register the gate on.
38
- * @param deps - See {@link ApiBearerAuthMiddlewareDeps}. Both fields are optional; omitting `deps`
39
- * entirely reads `JINI_API_TOKEN`/`JINI_DISABLE_API_AUTH` from real `process.env`.
40
- * @returns Nothing. Registers zero hooks (a deliberate no-op, not a bug) when no token is configured.
41
- * @complexity Setup is O(1). Each gated request is O(1) (one Set lookup, one regex match).
42
- * @overallScore 100/100
43
- */
44
- export function registerApiBearerAuthMiddleware(app, deps = {}) {
45
- const tokenConfig = deps.tokenConfig ?? DEFAULT_TOKEN_CONFIG;
46
- const env = deps.env ?? process.env;
47
- if (!isApiTokenMiddlewareEnabled(tokenConfig, env))
48
- return;
49
- const apiToken = apiTokenFromEnv(tokenConfig, env);
50
- app.addHook('onRequest', (req, reply, done) => {
51
- const path = requestPath(req);
52
- if (!path.startsWith('/api')) {
53
- done();
54
- return;
55
- }
56
- if (OPEN_PROBE_PATHS.has(path)) {
57
- done();
58
- return;
59
- }
60
- // Loopback short-circuit: the desktop UI / local CLI never carry a bearer, and a reverse
61
- // proxy in front of a non-loopback bind must always forward the real bearer itself — so this
62
- // is intentionally not fooled by a proxied `X-Forwarded-For` header.
63
- if (isLoopbackPeerAddress(req.socket?.remoteAddress)) {
64
- done();
65
- return;
66
- }
67
- const match = BEARER_TOKEN_PATTERN.exec(req.headers.authorization ?? '');
68
- if (!match || match[1] !== apiToken) {
69
- reply.code(401).send({
70
- error: {
71
- code: 'API_TOKEN_REQUIRED',
72
- message: `Authorization: Bearer <${tokenConfig.tokenEnvVar}> required`,
73
- },
74
- });
75
- return;
76
- }
77
- done();
78
- });
79
- }
80
- /**
81
- * Chrome may strip the port from the `Origin` header on same-origin GET requests. Used only as a
82
- * narrow fallback for safe, idempotent GET requests once the exact-match check below has already
83
- * failed — mutating routes always require an exact origin/host match.
84
- */
85
- function isPortlessLoopbackOrigin(origin) {
86
- return /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])$/.test(origin);
87
- }
88
- /**
89
- * Registers an unconditional cross-origin gate on every `/api/*` route: non-browser clients (no
90
- * `Origin` header) and requests whose `Origin` resolves to a loopback, private-LAN, or explicitly
91
- * allow-listed origin are let through; everything else is rejected with 403. `Origin: null`
92
- * (typically a sandboxed iframe) is always rejected — see the Express version's doc for why the
93
- * origin daemon's safe-GET exemption for that case was dropped.
94
- *
95
- * @param app - The Fastify instance to register the gate on.
96
- * @param deps - See {@link ApiOriginGuardMiddlewareDeps}.
97
- * @returns Nothing. Unlike the bearer-token gate, this always registers its hook — there is
98
- * no "disabled" state.
99
- * @complexity Setup is O(1). Each gated request is O(p) in the number of allowed ports (typically 1-2).
100
- * @overallScore 100/100
101
- */
102
- export function registerApiOriginGuardMiddleware(app, deps) {
103
- const { host, getResolvedPort } = deps;
104
- const extraAllowedOrigins = deps.extraAllowedOrigins ?? [];
105
- const env = deps.env ?? process.env;
106
- app.addHook('onRequest', (req, reply, done) => {
107
- const path = requestPath(req);
108
- if (!path.startsWith('/api')) {
109
- done();
110
- return;
111
- }
112
- const origin = req.headers.origin;
113
- if (origin == null || origin === '') {
114
- done();
115
- return;
116
- }
117
- if (origin === 'null') {
118
- reply.code(403).send({ error: 'Origin: null not allowed for this route' });
119
- return;
120
- }
121
- // Fail-closed: block every browser origin until the daemon's real listen port is known, so a
122
- // request arriving in the brief window before `.listen()` resolves can never be compared
123
- // against a wrong or default port.
124
- const resolvedPort = getResolvedPort();
125
- if (!resolvedPort) {
126
- reply.code(403).send({ error: 'Server initializing' });
127
- return;
128
- }
129
- const ports = allowedBrowserPorts(resolvedPort, env);
130
- if (!isAllowedBrowserOrigin(origin, req.headers.host, ports, host, [...extraAllowedOrigins])) {
131
- if (req.method !== 'GET' || !isPortlessLoopbackOrigin(String(origin))) {
132
- reply.code(403).send({ error: 'Cross-origin requests are not allowed' });
133
- return;
134
- }
135
- }
136
- done();
137
- });
138
- }
139
- //# sourceMappingURL=api-security-middleware.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-security-middleware.js","sourceRoot":"","sources":["../../src/fastify/api-security-middleware.ts"],"names":[],"mappings":"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"}
@@ -1,22 +0,0 @@
1
- /**
2
- * Legacy-shaped error helpers: build/send an `ApiError` from separate `code`/`message`/`init`
3
- * arguments, matching call sites that predate `JsonRouteSpec`/`mountJsonRoute` (e.g. a host
4
- * application's own hand-mounted routes). No internal dependencies. `sendApiError` here is
5
- * deliberately the same name as `response.ts`'s `ApiError`-object-taking `sendApiError` — they
6
- * are different signatures for the same "send an error" job at two call-site generations; the
7
- * package's `fastify/index.ts` barrel re-exports this one as `sendCompatApiError` to keep both
8
- * addressable without a collision. Deliberately duplicated from `../express/compat.ts` — see
9
- * `./response.ts`'s top-of-module doc for why this subtree does not import from `express/`.
10
- */
11
- import type { ApiError, ApiErrorCode, ApiErrorResponse } from '@jini/protocol';
12
- import type { FastifyReply } from 'fastify';
13
- /** Builds an `ApiError` from separate `code`/`message`/`init` arguments (legacy call shape). */
14
- export declare function createCompatApiError(code: ApiErrorCode, message: string, init?: Omit<ApiError, 'code' | 'message'>): ApiError;
15
- /** Wraps `createCompatApiError`'s result in the standard `{ error }` envelope. */
16
- export declare function createCompatApiErrorResponse(code: ApiErrorCode, message: string, init?: Omit<ApiError, 'code' | 'message'>): ApiErrorResponse;
17
- /**
18
- * Writes an `ApiError` response built from separate `code`/`message`/`init` arguments (legacy
19
- * call shape) with the given status code. Exported from `fastify/index.ts` as `sendCompatApiError`.
20
- */
21
- export declare function sendApiError(reply: FastifyReply, status: number, code: ApiErrorCode, message: string, init?: Omit<ApiError, 'code' | 'message'>): FastifyReply;
22
- //# sourceMappingURL=compat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../../src/fastify/compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAM,GAC5C,QAAQ,CAEV;AAED,kFAAkF;AAClF,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAM,GAC5C,gBAAgB,CAElB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAM,GAC5C,YAAY,CAEd"}
@@ -1,16 +0,0 @@
1
- /** Builds an `ApiError` from separate `code`/`message`/`init` arguments (legacy call shape). */
2
- export function createCompatApiError(code, message, init = {}) {
3
- return { code, message, ...init };
4
- }
5
- /** Wraps `createCompatApiError`'s result in the standard `{ error }` envelope. */
6
- export function createCompatApiErrorResponse(code, message, init = {}) {
7
- return { error: createCompatApiError(code, message, init) };
8
- }
9
- /**
10
- * Writes an `ApiError` response built from separate `code`/`message`/`init` arguments (legacy
11
- * call shape) with the given status code. Exported from `fastify/index.ts` as `sendCompatApiError`.
12
- */
13
- export function sendApiError(reply, status, code, message, init = {}) {
14
- return reply.code(status).send(createCompatApiErrorResponse(code, message, init));
15
- }
16
- //# sourceMappingURL=compat.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compat.js","sourceRoot":"","sources":["../../src/fastify/compat.ts"],"names":[],"mappings":"AAaA,gGAAgG;AAChG,MAAM,UAAU,oBAAoB,CAClC,IAAkB,EAClB,OAAe,EACf,OAA2C,EAAE;IAE7C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,4BAA4B,CAC1C,IAAkB,EAClB,OAAe,EACf,OAA2C,EAAE;IAE7C,OAAO,EAAE,KAAK,EAAE,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,MAAc,EACd,IAAkB,EAClB,OAAe,EACf,OAA2C,EAAE;IAE7C,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;AACpF,CAAC"}
@@ -1,22 +0,0 @@
1
- /**
2
- * @module fastify/daemon-status
3
- *
4
- * Mounts the generic daemon status + shutdown routes through the Fastify Adapter. The route
5
- * SPECS themselves (`daemonStatusRoute`/`daemonShutdownRoute` — pure `defineJsonRoute`-shaped
6
- * data plus framework-agnostic `parse`/`handle` functions, see `../daemon-status.ts`'s own doc
7
- * for their full provenance) are intentionally NOT duplicated here: they never reference Express
8
- * at runtime (`../daemon-status.ts` only imports `type { Express }` for its own
9
- * `registerDaemonStatusRoutes`'s parameter — a type-only import that TypeScript's
10
- * `verbatimModuleSyntax` erases from the compiled output entirely, so importing the specs from
11
- * that module does not pull the `express` package into this subtree at runtime). This file's own
12
- * job is only the Fastify-specific mounting wrapper, calling `./adapter.js`'s Fastify
13
- * `mountJsonRoute` instead of the Express one.
14
- */
15
- import type { FastifyInstance } from 'fastify';
16
- import { daemonShutdownRoute, daemonStatusRoute, type DaemonShutdownResponse, type DaemonStatusDeps, type DaemonStatusResponse } from '../daemon-status.js';
17
- import { type AdapterContext } from './adapter.js';
18
- export { daemonShutdownRoute, daemonStatusRoute };
19
- export type { DaemonShutdownResponse, DaemonStatusDeps, DaemonStatusResponse };
20
- /** Mounts both daemon-status routes on `app`. A pack's `http(app, services)` calls this directly. */
21
- export declare function registerDaemonStatusRoutes(app: FastifyInstance, deps: DaemonStatusDeps, adapter: AdapterContext): void;
22
- //# sourceMappingURL=daemon-status.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-status.d.ts","sourceRoot":"","sources":["../../src/fastify/daemon-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;AAClD,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;AAE/E,qGAAqG;AACrG,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAGtH"}
@@ -1,9 +0,0 @@
1
- import { daemonShutdownRoute, daemonStatusRoute, } from '../daemon-status.js';
2
- import { mountJsonRoute } from './adapter.js';
3
- export { daemonShutdownRoute, daemonStatusRoute };
4
- /** Mounts both daemon-status routes on `app`. A pack's `http(app, services)` calls this directly. */
5
- export function registerDaemonStatusRoutes(app, deps, adapter) {
6
- mountJsonRoute(app, daemonStatusRoute, deps, adapter);
7
- mountJsonRoute(app, daemonShutdownRoute, deps, adapter);
8
- }
9
- //# sourceMappingURL=daemon-status.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-status.js","sourceRoot":"","sources":["../../src/fastify/daemon-status.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAIlB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAuB,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;AAGlD,qGAAqG;AACrG,MAAM,UAAU,0BAA0B,CAAC,GAAoB,EAAE,IAAsB,EAAE,OAAuB;IAC9G,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACtD,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC"}
@@ -1,13 +0,0 @@
1
- /**
2
- * @module fastify/host-tools
3
- *
4
- * Fastify-mounting glue for `../host-tools.js`'s `hostEditorsRoute`/`openResourceInEditorRoute` —
5
- * the exact same `JsonRouteSpec` objects the Express mounting in `../host-tools.js` uses, just
6
- * mounted through this subtree's own `mountJsonRoute` instead.
7
- */
8
- import type { FastifyInstance } from 'fastify';
9
- import { type HostToolsOpenInDeps } from '../host-tools.js';
10
- import { type AdapterContext } from './adapter.js';
11
- /** Mounts `GET /api/editors` and `POST /api/resources/:resourceRef/open-in` on `app` — the Fastify-native equivalent of `../host-tools.js`'s `registerHostToolsRoutes`. */
12
- export declare function registerHostToolsRoutes(app: FastifyInstance, adapter: AdapterContext, openInDeps?: HostToolsOpenInDeps): void;
13
- //# sourceMappingURL=host-tools.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-tools.d.ts","sourceRoot":"","sources":["../../src/fastify/host-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAA+C,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzG,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnE,2KAA2K;AAC3K,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,cAAc,EACvB,UAAU,GAAE,mBAAwB,GACnC,IAAI,CAGN"}
@@ -1,8 +0,0 @@
1
- import { hostEditorsRoute, openResourceInEditorRoute } from '../host-tools.js';
2
- import { mountJsonRoute } from './adapter.js';
3
- /** Mounts `GET /api/editors` and `POST /api/resources/:resourceRef/open-in` on `app` — the Fastify-native equivalent of `../host-tools.js`'s `registerHostToolsRoutes`. */
4
- export function registerHostToolsRoutes(app, adapter, openInDeps = {}) {
5
- mountJsonRoute(app, hostEditorsRoute, {}, adapter);
6
- mountJsonRoute(app, openResourceInEditorRoute, openInDeps, adapter);
7
- }
8
- //# sourceMappingURL=host-tools.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"host-tools.js","sourceRoot":"","sources":["../../src/fastify/host-tools.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAA4B,MAAM,kBAAkB,CAAC;AACzG,OAAO,EAAE,cAAc,EAAuB,MAAM,cAAc,CAAC;AAEnE,2KAA2K;AAC3K,MAAM,UAAU,uBAAuB,CACrC,GAAoB,EACpB,OAAuB,EACvB,aAAkC,EAAE;IAEpC,cAAc,CAAC,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACnD,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
@@ -1,36 +0,0 @@
1
- /**
2
- * @module @jini/http/fastify
3
- *
4
- * The Fastify-native transport implementation: request parsing, response serialization, the
5
- * Fastify-mounting Adapter (`defineJsonRoute`/`mountJsonRoute`, built on `fastify.route(...)`),
6
- * the `/api` bearer-auth and origin-guard `onRequest` hooks, a loopback-only request guard, a
7
- * route-registration inventory/guard built on Fastify's native `onRoute` application hook, legacy-
8
- * shaped compat error helpers, the generic daemon-status/shutdown routes, and full Fastify
9
- * mounting for the runs/agents/host-tools route packs (`registerRunRoutes` including its SSE
10
- * event stream, `registerAgentRoutes`, `registerHostToolsRoutes`) — every one mounting the exact
11
- * same `JsonRouteSpec` objects (and, for the SSE stream, the exact same
12
- * `handleRunEventStreamRequest` core) the `express`/root-barrel Express mounting uses, so
13
- * `transport: 'fastify'` serves identical routes to the Express default, not a reduced set. Every
14
- * export here operates on Fastify's `FastifyRequest`/`FastifyReply`/`FastifyInstance` types.
15
- * Import the shared, framework-agnostic pieces (`Result`, `JsonRouteSpec`, origin-validation
16
- * predicates, `mountPackHttp`) from the package root instead of duplicating them here.
17
- */
18
- export type { AdapterContext } from './adapter.js';
19
- export { defineJsonRoute, mountJsonRoute } from './adapter.js';
20
- export type { ApiBearerAuthMiddlewareDeps, ApiOriginGuardMiddlewareDeps } from './api-security-middleware.js';
21
- export { registerApiBearerAuthMiddleware, registerApiOriginGuardMiddleware } from './api-security-middleware.js';
22
- export { createCompatApiError, createCompatApiErrorResponse, sendApiError as sendCompatApiError, } from './compat.js';
23
- export type { DaemonShutdownResponse, DaemonStatusDeps, DaemonStatusResponse, } from './daemon-status.js';
24
- export { daemonShutdownRoute, daemonStatusRoute, registerDaemonStatusRoutes } from './daemon-status.js';
25
- export { isLoopbackHostname, isLoopbackPeerAddress, localOriginFromHeader, normalizeLocalAuthority, requireLocalDaemonRequest, validateLocalDaemonRequest, } from './local-daemon-request.js';
26
- export type { OriginContext } from './origin.js';
27
- export { guardSameOrigin } from './origin.js';
28
- export { rawInput, validationError } from './request.js';
29
- export { registerRunStreamRoute } from './run-stream.js';
30
- export { sendApiError, sendJson, statusForError } from './response.js';
31
- export type { InstallRouteRegistrationGuardOptions, RouteRegistration } from './route-registration-guard.js';
32
- export { getRouteRegistrationInventory, guardedRouteKey, installRouteRegistrationGuard, } from './route-registration-guard.js';
33
- export { registerRunEventStream, registerRunRoutes } from './runs.js';
34
- export { registerAgentRoutes } from './agents.js';
35
- export { registerHostToolsRoutes } from './host-tools.js';
36
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,YAAY,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC9G,OAAO,EAAE,+BAA+B,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAMjH,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,YAAY,IAAI,kBAAkB,GACnC,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAExG,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvE,YAAY,EAAE,oCAAoC,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAC7G,OAAO,EACL,6BAA6B,EAC7B,eAAe,EACf,6BAA6B,GAC9B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,18 +0,0 @@
1
- export { defineJsonRoute, mountJsonRoute } from './adapter.js';
2
- export { registerApiBearerAuthMiddleware, registerApiOriginGuardMiddleware } from './api-security-middleware.js';
3
- // Legacy-shaped compat error helpers (separate code/message/init call shape). `sendApiError`
4
- // below (from `response.ts`) takes a single `ApiError` object; this compat `sendApiError` takes
5
- // `(code, message, init)` — same job, different call-site generation, so it is re-exported here
6
- // under a distinct name to avoid a duplicate export.
7
- export { createCompatApiError, createCompatApiErrorResponse, sendApiError as sendCompatApiError, } from './compat.js';
8
- export { daemonShutdownRoute, daemonStatusRoute, registerDaemonStatusRoutes } from './daemon-status.js';
9
- export { isLoopbackHostname, isLoopbackPeerAddress, localOriginFromHeader, normalizeLocalAuthority, requireLocalDaemonRequest, validateLocalDaemonRequest, } from './local-daemon-request.js';
10
- export { guardSameOrigin } from './origin.js';
11
- export { rawInput, validationError } from './request.js';
12
- export { registerRunStreamRoute } from './run-stream.js';
13
- export { sendApiError, sendJson, statusForError } from './response.js';
14
- export { getRouteRegistrationInventory, guardedRouteKey, installRouteRegistrationGuard, } from './route-registration-guard.js';
15
- export { registerRunEventStream, registerRunRoutes } from './runs.js';
16
- export { registerAgentRoutes } from './agents.js';
17
- export { registerHostToolsRoutes } from './host-tools.js';
18
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fastify/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,+BAA+B,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAEjH,6FAA6F;AAC7F,gGAAgG;AAChG,gGAAgG;AAChG,qDAAqD;AACrD,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,YAAY,IAAI,kBAAkB,GACnC,MAAM,aAAa,CAAC;AAOrB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAExG,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGvE,OAAO,EACL,6BAA6B,EAC7B,eAAe,EACf,6BAA6B,GAC9B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -1,43 +0,0 @@
1
- import type { FastifyReply, FastifyRequest } from 'fastify';
2
- interface LocalAuthority {
3
- hostname: string;
4
- port: string;
5
- }
6
- interface LocalDaemonValidation {
7
- ok: true;
8
- origin: string | null;
9
- }
10
- interface LocalDaemonValidationError {
11
- ok: false;
12
- message: string;
13
- details: Record<string, string>;
14
- }
15
- /** Parses a `Host`-header-shaped authority string, rejecting anything with a path/userinfo/query. */
16
- export declare function normalizeLocalAuthority(value: unknown): LocalAuthority | null;
17
- /** True when `hostname` is `localhost`, an IPv6 loopback literal, or a `127.0.0.0/8` IPv4 literal. */
18
- export declare function isLoopbackHostname(hostname: unknown): boolean;
19
- /** True when `address` (typically `req.socket.remoteAddress`) is a loopback peer, unwrapping an IPv4-mapped IPv6 prefix first. */
20
- export declare function isLoopbackPeerAddress(address: unknown): boolean;
21
- /** Parses an `Origin` header value into its origin string, requiring a bare `scheme://loopback-host[:port]` with no path/query/userinfo. */
22
- export declare function localOriginFromHeader(value: unknown): string | null;
23
- /**
24
- * Validates that `req` is a loopback request: the TCP peer address, the `Host` header, and (when
25
- * present) the `Origin` header must all resolve to a loopback host.
26
- *
27
- * @param req - The incoming request.
28
- * @returns `{ ok: true, origin }` on success (`origin` is the validated `Origin` header value, or
29
- * `null` when the request carried none); otherwise `{ ok: false, message, details }` naming which
30
- * check failed.
31
- */
32
- export declare function validateLocalDaemonRequest(req: FastifyRequest): LocalDaemonValidation | LocalDaemonValidationError;
33
- /**
34
- * Fastify `onRequest` hook that rejects any non-loopback request with a 403, and otherwise sets
35
- * permissive same-machine CORS headers before calling `done()`.
36
- *
37
- * @param req - The incoming request.
38
- * @param reply - The reply to reject on, or set CORS headers on before continuing.
39
- * @param done - Called (with no error) when the request validates as loopback, continuing the hook chain.
40
- */
41
- export declare function requireLocalDaemonRequest(req: FastifyRequest, reply: FastifyReply, done: (err?: Error) => void): void;
42
- export {};
43
- //# sourceMappingURL=local-daemon-request.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-daemon-request.d.ts","sourceRoot":"","sources":["../../src/fastify/local-daemon-request.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG5D,UAAU,cAAc;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,qBAAqB;IAC7B,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,UAAU,0BAA0B;IAClC,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,qGAAqG;AACrG,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAkB7E;AAED,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAS7D;AAED,kIAAkI;AAClI,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED,4IAA4I;AAC5I,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAcnE;AAOD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,cAAc,GAAG,qBAAqB,GAAG,0BAA0B,CA4BlH;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAerH"}
@@ -1,155 +0,0 @@
1
- /**
2
- * @module fastify/local-daemon-request
3
- *
4
- * A loopback-only request guard: validates that a request's peer address, `Host` header, and (if
5
- * present) `Origin` header are all loopback, and sets the CORS headers a same-machine caller
6
- * needs. Distinct from `./origin.js`'s `guardSameOrigin` (which checks a browser tab's origin
7
- * against a configured allow-list) — this guard instead restricts an endpoint to same-machine
8
- * callers only (e.g. a local companion process), regardless of origin allow-list configuration.
9
- *
10
- * The header-parsing/classification predicates (`normalizeLocalAuthority`, `isLoopbackHostname`,
11
- * `isLoopbackPeerAddress`, `localOriginFromHeader`) are pure and operate on plain string/unknown
12
- * values, not a framework request object — deliberately duplicated from
13
- * `../express/local-daemon-request.ts` rather than shared; see `./response.ts`'s top-of-module
14
- * doc for why this subtree does not import from `express/`. Only `validateLocalDaemonRequest`
15
- * and `requireLocalDaemonRequest` below are Fastify-specific (they read `FastifyRequest`/
16
- * `FastifyReply` instead of Express's `Request`/`Response`/`req.get(...)`).
17
- */
18
- import net from 'node:net';
19
- import { sendApiError } from './compat.js';
20
- /** Parses a `Host`-header-shaped authority string, rejecting anything with a path/userinfo/query. */
21
- export function normalizeLocalAuthority(value) {
22
- if (typeof value !== 'string')
23
- return null;
24
- const trimmed = value.trim();
25
- if (!trimmed || /[\s/@]/.test(trimmed) || trimmed.includes(','))
26
- return null;
27
- try {
28
- // The guard above already rejects any `/`, `@`, or whitespace in `trimmed`, and the WHATWG
29
- // URL parser requires a non-empty host for an `http:` URL — so `parsed.hostname` is always
30
- // non-empty and `parsed.pathname` is always `/` for every `trimmed` value that reaches this
31
- // line without throwing. `hostname` below can still end up empty after stripping a trailing
32
- // dot (e.g. `trimmed === '.'` parses to `parsed.hostname === '.'`), so that check stays.
33
- const parsed = new URL(`http://${trimmed}`);
34
- const hostname = parsed.hostname.toLowerCase().replace(/\.$/, '');
35
- if (!hostname)
36
- return null;
37
- return { hostname, port: parsed.port };
38
- }
39
- catch {
40
- return null;
41
- }
42
- }
43
- /** True when `hostname` is `localhost`, an IPv6 loopback literal, or a `127.0.0.0/8` IPv4 literal. */
44
- export function isLoopbackHostname(hostname) {
45
- const normalized = String(hostname || '')
46
- .toLowerCase()
47
- .replace(/^\[|\]$/g, '')
48
- .replace(/\.$/, '');
49
- if (normalized === 'localhost')
50
- return true;
51
- if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1')
52
- return true;
53
- if (net.isIP(normalized) === 4)
54
- return normalized === '127.0.0.1' || normalized.startsWith('127.');
55
- return false;
56
- }
57
- /** True when `address` (typically `req.socket.remoteAddress`) is a loopback peer, unwrapping an IPv4-mapped IPv6 prefix first. */
58
- export function isLoopbackPeerAddress(address) {
59
- if (typeof address !== 'string')
60
- return false;
61
- const normalized = address.trim().toLowerCase().replace(/^\[|\]$/g, '');
62
- if (!normalized)
63
- return false;
64
- if (normalized.startsWith('::ffff:'))
65
- return isLoopbackPeerAddress(normalized.slice('::ffff:'.length));
66
- if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1')
67
- return true;
68
- if (net.isIP(normalized) === 4)
69
- return normalized === '127.0.0.1' || normalized.startsWith('127.');
70
- return false;
71
- }
72
- /** Parses an `Origin` header value into its origin string, requiring a bare `scheme://loopback-host[:port]` with no path/query/userinfo. */
73
- export function localOriginFromHeader(value) {
74
- if (typeof value !== 'string')
75
- return null;
76
- const trimmed = value.trim();
77
- if (!trimmed || trimmed === 'null' || trimmed.includes(','))
78
- return null;
79
- try {
80
- const parsed = new URL(trimmed);
81
- if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
82
- return null;
83
- if (parsed.pathname !== '/' || parsed.search || parsed.hash || parsed.username || parsed.password)
84
- return null;
85
- if (!isLoopbackHostname(parsed.hostname))
86
- return null;
87
- return parsed.origin;
88
- }
89
- catch {
90
- return null;
91
- }
92
- }
93
- /** Reads a single-value request header, collapsing Node's `string | string[] | undefined` shape (Fastify headers are Node's raw `IncomingHttpHeaders`) to the first value. */
94
- function headerValue(value) {
95
- return Array.isArray(value) ? value[0] : value;
96
- }
97
- /**
98
- * Validates that `req` is a loopback request: the TCP peer address, the `Host` header, and (when
99
- * present) the `Origin` header must all resolve to a loopback host.
100
- *
101
- * @param req - The incoming request.
102
- * @returns `{ ok: true, origin }` on success (`origin` is the validated `Origin` header value, or
103
- * `null` when the request carried none); otherwise `{ ok: false, message, details }` naming which
104
- * check failed.
105
- */
106
- export function validateLocalDaemonRequest(req) {
107
- if (!isLoopbackPeerAddress(req.socket?.remoteAddress)) {
108
- return {
109
- ok: false,
110
- message: 'request peer must be a loopback address',
111
- details: { peer: 'remoteAddress' },
112
- };
113
- }
114
- const host = normalizeLocalAuthority(headerValue(req.headers.host));
115
- if (!host || !isLoopbackHostname(host.hostname)) {
116
- return {
117
- ok: false,
118
- message: 'request host must be a loopback daemon address',
119
- details: { header: 'host' },
120
- };
121
- }
122
- const originHeader = headerValue(req.headers.origin);
123
- if (originHeader !== undefined && !localOriginFromHeader(originHeader)) {
124
- return {
125
- ok: false,
126
- message: 'request origin must be a loopback daemon origin',
127
- details: { header: 'origin' },
128
- };
129
- }
130
- return { ok: true, origin: localOriginFromHeader(originHeader) };
131
- }
132
- /**
133
- * Fastify `onRequest` hook that rejects any non-loopback request with a 403, and otherwise sets
134
- * permissive same-machine CORS headers before calling `done()`.
135
- *
136
- * @param req - The incoming request.
137
- * @param reply - The reply to reject on, or set CORS headers on before continuing.
138
- * @param done - Called (with no error) when the request validates as loopback, continuing the hook chain.
139
- */
140
- export function requireLocalDaemonRequest(req, reply, done) {
141
- const validation = validateLocalDaemonRequest(req);
142
- if (!validation.ok) {
143
- sendApiError(reply, 403, 'FORBIDDEN', validation.message, { details: validation.details });
144
- return;
145
- }
146
- reply.header('Vary', 'Origin');
147
- if (validation.origin) {
148
- reply.header('Access-Control-Allow-Origin', validation.origin);
149
- }
150
- reply.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
151
- reply.header('Access-Control-Allow-Headers', 'Content-Type');
152
- reply.header('Access-Control-Max-Age', '600');
153
- done();
154
- }
155
- //# sourceMappingURL=local-daemon-request.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-daemon-request.js","sourceRoot":"","sources":["../../src/fastify/local-daemon-request.ts"],"names":[],"mappings":"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"}
@@ -1,21 +0,0 @@
1
- /**
2
- * Same-origin security guard: wraps the shared, framework-agnostic `isLocalSameOrigin` in the
3
- * module's `Result` pipeline so the Fastify Adapter can treat an origin failure the same as a
4
- * parse/handle failure. `isLocalSameOrigin` is imported from the shared root (`../origin-validation.js`)
5
- * rather than duplicated, since it operates on plain header values (not a framework `Request`) and
6
- * is already framework-agnostic — see `../pack-http.ts`'s doc for the same reasoning applied there.
7
- */
8
- import type { FastifyRequest } from 'fastify';
9
- import { type Result } from '../types.js';
10
- /** The subset of server startup state `guardSameOrigin` needs: the resolved local port. */
11
- export interface OriginContext {
12
- resolvedPortRef: {
13
- current: number;
14
- };
15
- }
16
- /**
17
- * Wraps `isLocalSameOrigin` so the Fastify Adapter can fold the origin decision into the same
18
- * error-handling pipeline as parse/handle failures.
19
- */
20
- export declare function guardSameOrigin(req: FastifyRequest, origin: OriginContext): Result<void>;
21
- //# sourceMappingURL=origin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../../src/fastify/origin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAEnD,2FAA2F;AAC3F,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAKxF"}
@@ -1,14 +0,0 @@
1
- import { createApiError } from '@jini/protocol';
2
- import { isLocalSameOrigin } from '../origin-validation.js';
3
- import { err, ok } from '../types.js';
4
- /**
5
- * Wraps `isLocalSameOrigin` so the Fastify Adapter can fold the origin decision into the same
6
- * error-handling pipeline as parse/handle failures.
7
- */
8
- export function guardSameOrigin(req, origin) {
9
- if (isLocalSameOrigin(req, origin.resolvedPortRef.current)) {
10
- return ok(undefined);
11
- }
12
- return err(createApiError('FORBIDDEN', 'cross-origin request rejected'));
13
- }
14
- //# sourceMappingURL=origin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/fastify/origin.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,aAAa,CAAC;AAOnD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAmB,EAAE,MAAqB;IACxE,IAAI,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC,cAAc,CAAC,WAAW,EAAE,+BAA+B,CAAC,CAAC,CAAC;AAC3E,CAAC"}
@@ -1,20 +0,0 @@
1
- /**
2
- * Request-side normalization: turns a raw Fastify `FastifyRequest` into the framework-independent
3
- * `RouteInputContext` the rest of the module operates on, and builds a standard `BAD_REQUEST`
4
- * `ApiError` for validation failures. Deliberately duplicated from `../express/request.ts` — see
5
- * `./response.ts`'s top-of-module doc for why this subtree does not import from `express/`.
6
- */
7
- import type { FastifyRequest } from 'fastify';
8
- import { type ApiError, type ApiValidationIssue } from '@jini/protocol';
9
- import type { RouteInputContext } from '../types.js';
10
- /**
11
- * Extracts `body`/`query`/`params` from a Fastify `FastifyRequest` into a `RouteInputContext`, so
12
- * a route's `parse` function can be unit-tested without constructing a real `FastifyRequest`.
13
- */
14
- export declare function rawInput(request: FastifyRequest): RouteInputContext;
15
- /**
16
- * Builds a `BAD_REQUEST` `ApiError` for a failed parse. When `issues` is non-empty, attaches
17
- * them as structured validation details so clients can render per-field errors.
18
- */
19
- export declare function validationError(message: string, issues?: Pick<ApiValidationIssue, 'path' | 'message'>[]): ApiError;
20
- //# sourceMappingURL=request.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/fastify/request.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAMnE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC,EAAO,GAC1D,QAAQ,CAOV"}