@tetrascience-npm/request 0.2.0-beta.106.2

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 (81) hide show
  1. package/README.md +169 -0
  2. package/client.d.ts +1 -0
  3. package/client.js +1 -0
  4. package/dist/cli/generate-client.d.ts +3 -0
  5. package/dist/cli/generate-client.d.ts.map +1 -0
  6. package/dist/cli/generate-client.js +208 -0
  7. package/dist/cli/generate-schemas.d.ts +7 -0
  8. package/dist/cli/generate-schemas.d.ts.map +1 -0
  9. package/dist/cli/generate-schemas.js +210 -0
  10. package/dist/cli/templates.d.ts +27 -0
  11. package/dist/cli/templates.d.ts.map +1 -0
  12. package/dist/cli/templates.js +165 -0
  13. package/dist/client/console-logger.d.ts +10 -0
  14. package/dist/client/console-logger.d.ts.map +1 -0
  15. package/dist/client/console-logger.js +42 -0
  16. package/dist/client/index.d.ts +4 -0
  17. package/dist/client/index.d.ts.map +1 -0
  18. package/dist/client/index.js +19 -0
  19. package/dist/client/install-middleware.d.ts +31 -0
  20. package/dist/client/install-middleware.d.ts.map +1 -0
  21. package/dist/client/install-middleware.js +117 -0
  22. package/dist/client/types.d.ts +13 -0
  23. package/dist/client/types.d.ts.map +1 -0
  24. package/dist/client/types.js +2 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +17 -0
  28. package/dist/server/index.d.ts +5 -0
  29. package/dist/server/index.d.ts.map +1 -0
  30. package/dist/server/index.js +24 -0
  31. package/dist/server/request-context.d.ts +27 -0
  32. package/dist/server/request-context.d.ts.map +1 -0
  33. package/dist/server/request-context.js +42 -0
  34. package/dist/server/request-middleware.d.ts +14 -0
  35. package/dist/server/request-middleware.d.ts.map +1 -0
  36. package/dist/server/request-middleware.js +82 -0
  37. package/dist/server/types.d.ts +13 -0
  38. package/dist/server/types.d.ts.map +1 -0
  39. package/dist/server/types.js +2 -0
  40. package/dist/shared/client-types.d.ts +63 -0
  41. package/dist/shared/client-types.d.ts.map +1 -0
  42. package/dist/shared/client-types.js +7 -0
  43. package/dist/shared/constants.d.ts +7 -0
  44. package/dist/shared/constants.d.ts.map +1 -0
  45. package/dist/shared/constants.js +9 -0
  46. package/dist/shared/generate-request-id.d.ts +10 -0
  47. package/dist/shared/generate-request-id.d.ts.map +1 -0
  48. package/dist/shared/generate-request-id.js +21 -0
  49. package/dist/shared/index.d.ts +8 -0
  50. package/dist/shared/index.d.ts.map +1 -0
  51. package/dist/shared/index.js +29 -0
  52. package/dist/shared/middleware/auth.d.ts +39 -0
  53. package/dist/shared/middleware/auth.d.ts.map +1 -0
  54. package/dist/shared/middleware/auth.js +164 -0
  55. package/dist/shared/middleware/default.d.ts +29 -0
  56. package/dist/shared/middleware/default.d.ts.map +1 -0
  57. package/dist/shared/middleware/default.js +67 -0
  58. package/dist/shared/middleware/index.d.ts +6 -0
  59. package/dist/shared/middleware/index.d.ts.map +1 -0
  60. package/dist/shared/middleware/index.js +21 -0
  61. package/dist/shared/middleware/safe-response.d.ts +22 -0
  62. package/dist/shared/middleware/safe-response.d.ts.map +1 -0
  63. package/dist/shared/middleware/safe-response.js +41 -0
  64. package/dist/shared/middleware/tracing.d.ts +23 -0
  65. package/dist/shared/middleware/tracing.d.ts.map +1 -0
  66. package/dist/shared/middleware/tracing.js +67 -0
  67. package/dist/shared/middleware/utils.d.ts +4 -0
  68. package/dist/shared/middleware/utils.d.ts.map +1 -0
  69. package/dist/shared/middleware/utils.js +13 -0
  70. package/dist/shared/middleware/validation.d.ts +43 -0
  71. package/dist/shared/middleware/validation.d.ts.map +1 -0
  72. package/dist/shared/middleware/validation.js +42 -0
  73. package/dist/shared/sanitize-url.d.ts +3 -0
  74. package/dist/shared/sanitize-url.d.ts.map +1 -0
  75. package/dist/shared/sanitize-url.js +12 -0
  76. package/dist/shared/types.d.ts +10 -0
  77. package/dist/shared/types.d.ts.map +1 -0
  78. package/dist/shared/types.js +2 -0
  79. package/package.json +98 -0
  80. package/server.d.ts +1 -0
  81. package/server.js +1 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Request-scoped context that persists throughout the lifecycle
3
+ * of a single request via AsyncLocalStorage.
4
+ */
5
+ export interface RequestContext {
6
+ requestId: string;
7
+ sessionId: string;
8
+ /** Organization slug from the incoming request. */
9
+ orgSlug?: string;
10
+ /** Auth token from the incoming request. */
11
+ authToken?: string;
12
+ }
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,63 @@
1
+ import type { HeaderValue, RequestTrackingLogger } from './types';
2
+ /** Tracing options — identity, correlation, and logging. */
3
+ export interface TracingOptions {
4
+ /** Request ID for end-to-end correlation. Defaults to a new UUID per request. */
5
+ requestId?: HeaderValue;
6
+ /** Session ID for cross-request correlation. */
7
+ sessionId?: HeaderValue;
8
+ /** Service name injected as ts-initiating-service-name (audit/identity). */
9
+ serviceName?: HeaderValue;
10
+ /** If provided, logs outgoing requests, responses, and errors with the request ID. */
11
+ logger?: RequestTrackingLogger;
12
+ }
13
+ /**
14
+ * Base auth fields shared by both auth modes.
15
+ * Values can be static strings or functions resolved per-request.
16
+ */
17
+ export interface AuthBase {
18
+ /** Auth token (ts-auth-token). Auto-resolves from context if not provided. */
19
+ authToken?: HeaderValue;
20
+ /** Organization slug (x-org-slug). Auto-resolves from context if not provided. */
21
+ orgSlug?: HeaderValue;
22
+ }
23
+ /**
24
+ * Internal (service-to-service) authentication with explicit config.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * auth: { internalApiKey: process.env.INTERNAL_API_KEY, orgSlug: 'my-org' }
29
+ * ```
30
+ */
31
+ export interface InternalAuthExplicit extends AuthBase {
32
+ /** Internal API key for service-to-service auth. */
33
+ internalApiKey: HeaderValue;
34
+ }
35
+ /**
36
+ * Internal or direct authentication.
37
+ *
38
+ * Use string shorthands for auto-resolve, or explicit objects for manual config.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * auth: 'internal' // reads INTERNAL_API_KEY env var, orgSlug + authToken from context
43
+ * auth: 'direct' // reads authToken + orgSlug from context/cookies
44
+ * auth: { internalApiKey: env.KEY } // explicit API key, rest from context
45
+ * auth: { authToken: jwt, orgSlug: 'my-org' } // explicit direct auth
46
+ * ```
47
+ */
48
+ export type InternalAuth = InternalAuthExplicit | 'internal';
49
+ export type DirectAuth = AuthBase | 'direct';
50
+ /** Base options shared by all generated service clients. */
51
+ export interface ServiceClientOptions extends TracingOptions {
52
+ /** API base URL. Required on server. Defaults to relative URLs (current origin) in browser. */
53
+ baseUrl?: string;
54
+ /** Custom headers. For anything outside the standard auth/tracing fields. */
55
+ headers?: Record<string, string>;
56
+ /** Disable request body validation (default: enabled). */
57
+ skipValidation?: boolean;
58
+ /** Authentication mode. */
59
+ auth: InternalAuth | DirectAuth;
60
+ }
61
+ /** Type guard to distinguish InternalAuth from DirectAuth. */
62
+ export declare function isInternalAuth(auth: InternalAuth | DirectAuth): auth is InternalAuth;
63
+ //# sourceMappingURL=client-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-types.d.ts","sourceRoot":"","sources":["../../src/shared/client-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,qBAAqB,EAAC,MAAM,SAAS,CAAC;AAEhE,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,gDAAgD;IAChD,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sFAAsF;IACtF,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACxB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kFAAkF;IAClF,OAAO,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAqB,SAAQ,QAAQ;IACrD,oDAAoD;IACpD,cAAc,EAAE,WAAW,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,4DAA4D;AAC5D,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC3D,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0DAA0D;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2BAA2B;IAC3B,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;CAChC;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,IAAI,YAAY,CAEpF"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isInternalAuth = isInternalAuth;
4
+ /** Type guard to distinguish InternalAuth from DirectAuth. */
5
+ function isInternalAuth(auth) {
6
+ return auth === 'internal' || (typeof auth === 'object' && 'internalApiKey' in auth);
7
+ }
@@ -0,0 +1,7 @@
1
+ export declare const ORG_SLUG_HEADER = "x-org-slug";
2
+ export declare const AUTH_TOKEN_HEADER = "ts-auth-token";
3
+ export declare const REQUEST_ID_HEADER = "ts-request-id";
4
+ export declare const INTERNAL_API_KEY_HEADER = "ts-internal-api-key";
5
+ export declare const INITIATING_SERVICE_NAME_HEADER = "ts-initiating-service-name";
6
+ export declare const SESSION_ID_HEADER = "ts-session-id";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,eAAe,CAAC;AAC5C,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AACjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AACjD,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAC7D,eAAO,MAAM,8BAA8B,+BAA+B,CAAC;AAC3E,eAAO,MAAM,iBAAiB,kBAAkB,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SESSION_ID_HEADER = exports.INITIATING_SERVICE_NAME_HEADER = exports.INTERNAL_API_KEY_HEADER = exports.REQUEST_ID_HEADER = exports.AUTH_TOKEN_HEADER = exports.ORG_SLUG_HEADER = void 0;
4
+ exports.ORG_SLUG_HEADER = 'x-org-slug';
5
+ exports.AUTH_TOKEN_HEADER = 'ts-auth-token';
6
+ exports.REQUEST_ID_HEADER = 'ts-request-id';
7
+ exports.INTERNAL_API_KEY_HEADER = 'ts-internal-api-key';
8
+ exports.INITIATING_SERVICE_NAME_HEADER = 'ts-initiating-service-name';
9
+ exports.SESSION_ID_HEADER = 'ts-session-id';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generate a fresh UUID v4 request ID. Always creates a new ID — does not
3
+ * read from AsyncLocalStorage context. For context-aware request IDs on
4
+ * the server, use `getRequestId()` from the `/server` entrypoint instead.
5
+ *
6
+ * Uses crypto.randomUUID() when available (modern browsers and Node).
7
+ * Falls back to a Math.random()-based implementation for insecure contexts (HTTP in dev).
8
+ */
9
+ export declare function generateRequestId(): string;
10
+ //# sourceMappingURL=generate-request-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-request-id.d.ts","sourceRoot":"","sources":["../../src/shared/generate-request-id.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAU1C"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateRequestId = generateRequestId;
4
+ /**
5
+ * Generate a fresh UUID v4 request ID. Always creates a new ID — does not
6
+ * read from AsyncLocalStorage context. For context-aware request IDs on
7
+ * the server, use `getRequestId()` from the `/server` entrypoint instead.
8
+ *
9
+ * Uses crypto.randomUUID() when available (modern browsers and Node).
10
+ * Falls back to a Math.random()-based implementation for insecure contexts (HTTP in dev).
11
+ */
12
+ function generateRequestId() {
13
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
14
+ return crypto.randomUUID();
15
+ }
16
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
17
+ const r = (Math.random() * 16) | 0;
18
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
19
+ return v.toString(16);
20
+ });
21
+ }
@@ -0,0 +1,8 @@
1
+ export * from './constants';
2
+ export * from './types';
3
+ export * from './client-types';
4
+ export * from './generate-request-id';
5
+ export * from './middleware';
6
+ export { default as createClient } from 'openapi-fetch';
7
+ export type { Client as OpenAPIClient, Middleware } from 'openapi-fetch';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAI7B,OAAO,EAAC,OAAO,IAAI,YAAY,EAAC,MAAM,eAAe,CAAC;AAEtD,YAAY,EAAC,MAAM,IAAI,aAAa,EAAE,UAAU,EAAC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createClient = void 0;
21
+ __exportStar(require("./constants"), exports);
22
+ __exportStar(require("./types"), exports);
23
+ __exportStar(require("./client-types"), exports);
24
+ __exportStar(require("./generate-request-id"), exports);
25
+ __exportStar(require("./middleware"), exports);
26
+ // Re-export openapi-fetch for generated clients (renamed, can't use export *)
27
+ // eslint-disable-next-line no-restricted-syntax
28
+ var openapi_fetch_1 = require("openapi-fetch");
29
+ Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return __importDefault(openapi_fetch_1).default; } });
@@ -0,0 +1,39 @@
1
+ import type { Middleware } from 'openapi-fetch';
2
+ import type { InternalAuth, DirectAuth } from '../client-types';
3
+ /**
4
+ * Create middleware that injects internal (service-to-service) auth headers.
5
+ *
6
+ * This mode is server-only. In browser context, use 'direct' instead.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // Auto — reads INTERNAL_API_KEY env var, orgSlug + authToken from context
11
+ * client.use(createInternalAuthMiddleware('internal'))
12
+ *
13
+ * // Explicit API key, rest from context
14
+ * client.use(createInternalAuthMiddleware({ internalApiKey: env.KEY }))
15
+ * ```
16
+ */
17
+ export declare function createInternalAuthMiddleware(auth: InternalAuth): Middleware;
18
+ /**
19
+ * Create middleware that injects direct (user/browser) auth headers.
20
+ *
21
+ * In browser context, `'direct'` is a no-op — the browser sends
22
+ * `ts-auth-token` and `x-org-slug` cookies automatically with
23
+ * same-origin `fetch()` requests. No JS cookie reading needed.
24
+ *
25
+ * On the server, `'direct'` reads from AsyncLocalStorage context
26
+ * (set by `createRequestMiddleware`).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * // Browser — no-op, cookies handle auth
31
+ * // Server — reads from RequestContext
32
+ * client.use(createDirectAuthMiddleware('direct'))
33
+ *
34
+ * // Explicit (E2E tests, non-cookie flows)
35
+ * client.use(createDirectAuthMiddleware({ authToken: jwt, orgSlug: 'my-org' }))
36
+ * ```
37
+ */
38
+ export declare function createDirectAuthMiddleware(auth: DirectAuth): Middleware;
39
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/shared/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAC,YAAY,EAAE,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAoF9D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAyB3E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CA2BvE"}
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInternalAuthMiddleware = createInternalAuthMiddleware;
4
+ exports.createDirectAuthMiddleware = createDirectAuthMiddleware;
5
+ const constants_1 = require("../constants");
6
+ const utils_1 = require("./utils");
7
+ const isBrowser = typeof document !== 'undefined';
8
+ const isLocalhost = (hostname) => hostname === 'localhost' || hostname === '127.0.0.1';
9
+ /**
10
+ * Get the trusted internal domain from INTERNAL_DOMAIN env var.
11
+ * e.g. "tetrascience-dev.int" → allows https://{service}.tetrascience-dev.int
12
+ */
13
+ function getTrustedDomain() {
14
+ if (typeof process !== 'undefined') {
15
+ return process.env?.INTERNAL_DOMAIN;
16
+ }
17
+ return undefined;
18
+ }
19
+ /**
20
+ * Guard against leaking auth tokens over insecure connections.
21
+ * Allows HTTPS and localhost (for local dev).
22
+ */
23
+ function assertSecureUrl(request) {
24
+ const url = new URL(request.url);
25
+ if (url.protocol !== 'https:' && !isLocalhost(url.hostname)) {
26
+ throw new Error(`Refusing to send auth credentials over insecure connection: ${url.protocol}//${url.hostname}. ` +
27
+ 'Use HTTPS or localhost.');
28
+ }
29
+ }
30
+ /**
31
+ * Stricter guard for internal API keys.
32
+ *
33
+ * If INTERNAL_DOMAIN is set (e.g. "tetrascience-dev.int"), only
34
+ * allows requests to that exact domain or its subdomains.
35
+ *
36
+ * If not set, falls back to requiring a .int TLD.
37
+ *
38
+ * Always allows localhost for local dev.
39
+ */
40
+ function assertInternalUrl(request) {
41
+ assertSecureUrl(request);
42
+ const url = new URL(request.url);
43
+ if (isLocalhost(url.hostname))
44
+ return;
45
+ const trustedDomain = getTrustedDomain();
46
+ if (trustedDomain) {
47
+ // Exact match or subdomain match
48
+ if (url.hostname !== trustedDomain && !url.hostname.endsWith(`.${trustedDomain}`)) {
49
+ throw new Error(`Refusing to send internal API key to untrusted domain: ${url.hostname}. ` +
50
+ `Expected ${trustedDomain} (set by INTERNAL_DOMAIN).`);
51
+ }
52
+ }
53
+ else {
54
+ // Fallback: require .int or .internal TLD (both used across TDP environments)
55
+ if (!url.hostname.endsWith('.int') && !url.hostname.endsWith('.internal')) {
56
+ throw new Error(`Refusing to send internal API key to non-internal domain: ${url.hostname}. ` +
57
+ 'Set INTERNAL_DOMAIN or use a .int/.internal domain.');
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * Try to read auth values from server context (Node only).
63
+ * Returns empty object in browser — auth is handled by cookies automatically.
64
+ */
65
+ function tryGetServerAuthContext() {
66
+ if (isBrowser)
67
+ return {};
68
+ try {
69
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
70
+ const { getRequestContext } = require('@tetrascience-npm/request/server');
71
+ const ctx = getRequestContext();
72
+ return { orgSlug: ctx.orgSlug, authToken: ctx.authToken };
73
+ }
74
+ catch {
75
+ return {};
76
+ }
77
+ }
78
+ /**
79
+ * Create middleware that injects internal (service-to-service) auth headers.
80
+ *
81
+ * This mode is server-only. In browser context, use 'direct' instead.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * // Auto — reads INTERNAL_API_KEY env var, orgSlug + authToken from context
86
+ * client.use(createInternalAuthMiddleware('internal'))
87
+ *
88
+ * // Explicit API key, rest from context
89
+ * client.use(createInternalAuthMiddleware({ internalApiKey: env.KEY }))
90
+ * ```
91
+ */
92
+ function createInternalAuthMiddleware(auth) {
93
+ if (auth === 'internal') {
94
+ return {
95
+ onRequest({ request }) {
96
+ assertInternalUrl(request);
97
+ const apiKey = process.env.INTERNAL_API_KEY;
98
+ if (apiKey)
99
+ (0, utils_1.setIfAbsent)(request, constants_1.INTERNAL_API_KEY_HEADER, apiKey);
100
+ const ctx = tryGetServerAuthContext();
101
+ (0, utils_1.setIfAbsent)(request, constants_1.ORG_SLUG_HEADER, ctx.orgSlug);
102
+ (0, utils_1.setIfAbsent)(request, constants_1.AUTH_TOKEN_HEADER, ctx.authToken);
103
+ return request;
104
+ },
105
+ };
106
+ }
107
+ return {
108
+ onRequest({ request }) {
109
+ assertInternalUrl(request);
110
+ (0, utils_1.setIfAbsent)(request, constants_1.INTERNAL_API_KEY_HEADER, auth.internalApiKey);
111
+ const ctx = tryGetServerAuthContext();
112
+ (0, utils_1.setIfAbsent)(request, constants_1.ORG_SLUG_HEADER, auth.orgSlug ?? ctx.orgSlug);
113
+ (0, utils_1.setIfAbsent)(request, constants_1.AUTH_TOKEN_HEADER, auth.authToken ?? ctx.authToken);
114
+ return request;
115
+ },
116
+ };
117
+ }
118
+ /**
119
+ * Create middleware that injects direct (user/browser) auth headers.
120
+ *
121
+ * In browser context, `'direct'` is a no-op — the browser sends
122
+ * `ts-auth-token` and `x-org-slug` cookies automatically with
123
+ * same-origin `fetch()` requests. No JS cookie reading needed.
124
+ *
125
+ * On the server, `'direct'` reads from AsyncLocalStorage context
126
+ * (set by `createRequestMiddleware`).
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * // Browser — no-op, cookies handle auth
131
+ * // Server — reads from RequestContext
132
+ * client.use(createDirectAuthMiddleware('direct'))
133
+ *
134
+ * // Explicit (E2E tests, non-cookie flows)
135
+ * client.use(createDirectAuthMiddleware({ authToken: jwt, orgSlug: 'my-org' }))
136
+ * ```
137
+ */
138
+ function createDirectAuthMiddleware(auth) {
139
+ if (auth === 'direct') {
140
+ // Browser: no-op. Cookies are sent automatically by fetch().
141
+ // Server: read from AsyncLocalStorage context.
142
+ if (isBrowser) {
143
+ return { onRequest: ({ request }) => request };
144
+ }
145
+ return {
146
+ onRequest({ request }) {
147
+ assertSecureUrl(request);
148
+ const ctx = tryGetServerAuthContext();
149
+ (0, utils_1.setIfAbsent)(request, constants_1.AUTH_TOKEN_HEADER, ctx.authToken);
150
+ (0, utils_1.setIfAbsent)(request, constants_1.ORG_SLUG_HEADER, ctx.orgSlug);
151
+ return request;
152
+ },
153
+ };
154
+ }
155
+ // Explicit values — always set headers (E2E tests, non-cookie flows)
156
+ return {
157
+ onRequest({ request }) {
158
+ assertSecureUrl(request);
159
+ (0, utils_1.setIfAbsent)(request, constants_1.AUTH_TOKEN_HEADER, auth.authToken);
160
+ (0, utils_1.setIfAbsent)(request, constants_1.ORG_SLUG_HEADER, auth.orgSlug);
161
+ return request;
162
+ },
163
+ };
164
+ }
@@ -0,0 +1,29 @@
1
+ import type { Client } from 'openapi-fetch';
2
+ import type { ServiceClientOptions } from '../client-types';
3
+ import type { Parseable } from './validation';
4
+ /**
5
+ * Apply the default middleware pipeline to an openapi-fetch client.
6
+ *
7
+ * Pipeline order:
8
+ * 1. Tracing — ts-request-id, ts-session-id, ts-initiating-service-name
9
+ * 2. Auth — internal (API key + context) or direct (JWT)
10
+ * 3. Validation — Zod request body validation (unless skipValidation)
11
+ * 4. Safe response — wraps non-JSON success responses
12
+ *
13
+ * On Node servers, request IDs are automatically propagated from
14
+ * AsyncLocalStorage context (set by requestContextMiddleware) when
15
+ * no explicit requestId option is provided. In browsers, a fresh
16
+ * UUID is generated per request.
17
+ *
18
+ * This is the standard pipeline used by all generated service clients.
19
+ * New middleware added here will be picked up by existing clients on
20
+ * their next dependency update — no client regeneration needed.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const client = createClient<paths>({ baseUrl, headers })
25
+ * applyDefaultMiddleware(client, options, requestBodySchemas)
26
+ * ```
27
+ */
28
+ export declare function applyDefaultMiddleware(client: Client<any>, options: ServiceClientOptions, schemas?: Record<string, Parseable>): void;
29
+ //# sourceMappingURL=default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../src/shared/middleware/default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyB5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EACnB,OAAO,EAAE,oBAAoB,EAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GACjC,IAAI,CAoBN"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyDefaultMiddleware = applyDefaultMiddleware;
4
+ const client_types_1 = require("../client-types");
5
+ const tracing_1 = require("./tracing");
6
+ const auth_1 = require("./auth");
7
+ const validation_1 = require("./validation");
8
+ const safe_response_1 = require("./safe-response");
9
+ /**
10
+ * Try to resolve getRequestId from the /server entrypoint at runtime.
11
+ * On Node, this reads the request ID from AsyncLocalStorage context.
12
+ * In browsers, the require fails and we return undefined (falls back
13
+ * to generating a new UUID).
14
+ */
15
+ function tryGetServerContext() {
16
+ try {
17
+ // Dynamic require so this module stays browser-safe at import time.
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ const { getRequestContext, getRequestId } = require('@tetrascience-npm/request/server');
20
+ const ctx = getRequestContext();
21
+ return { requestId: getRequestId(), sessionId: ctx.sessionId, orgSlug: ctx.orgSlug, authToken: ctx.authToken };
22
+ }
23
+ catch {
24
+ return {};
25
+ }
26
+ }
27
+ /**
28
+ * Apply the default middleware pipeline to an openapi-fetch client.
29
+ *
30
+ * Pipeline order:
31
+ * 1. Tracing — ts-request-id, ts-session-id, ts-initiating-service-name
32
+ * 2. Auth — internal (API key + context) or direct (JWT)
33
+ * 3. Validation — Zod request body validation (unless skipValidation)
34
+ * 4. Safe response — wraps non-JSON success responses
35
+ *
36
+ * On Node servers, request IDs are automatically propagated from
37
+ * AsyncLocalStorage context (set by requestContextMiddleware) when
38
+ * no explicit requestId option is provided. In browsers, a fresh
39
+ * UUID is generated per request.
40
+ *
41
+ * This is the standard pipeline used by all generated service clients.
42
+ * New middleware added here will be picked up by existing clients on
43
+ * their next dependency update — no client regeneration needed.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const client = createClient<paths>({ baseUrl, headers })
48
+ * applyDefaultMiddleware(client, options, requestBodySchemas)
49
+ * ```
50
+ */
51
+ function applyDefaultMiddleware(client, options, schemas) {
52
+ // If no explicit requestId/sessionId, try to read from server context (AsyncLocalStorage).
53
+ // This makes tracing propagation automatic for server-side consumers.
54
+ const tracingOptions = (options.requestId && options.sessionId)
55
+ ? options
56
+ : { ...options,
57
+ requestId: options.requestId ?? (() => tryGetServerContext().requestId),
58
+ sessionId: options.sessionId ?? (() => tryGetServerContext().sessionId),
59
+ };
60
+ client.use((0, tracing_1.createTracingMiddleware)(tracingOptions));
61
+ const { auth } = options;
62
+ client.use((0, client_types_1.isInternalAuth)(auth) ? (0, auth_1.createInternalAuthMiddleware)(auth) : (0, auth_1.createDirectAuthMiddleware)(auth));
63
+ if (!options.skipValidation && schemas) {
64
+ client.use((0, validation_1.createRequestValidationMiddleware)({ schemas }));
65
+ }
66
+ client.use((0, safe_response_1.createSafeResponseMiddleware)());
67
+ }
@@ -0,0 +1,6 @@
1
+ export * from './tracing';
2
+ export * from './auth';
3
+ export * from './validation';
4
+ export * from './safe-response';
5
+ export * from './default';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/middleware/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./tracing"), exports);
18
+ __exportStar(require("./auth"), exports);
19
+ __exportStar(require("./validation"), exports);
20
+ __exportStar(require("./safe-response"), exports);
21
+ __exportStar(require("./default"), exports);
@@ -0,0 +1,22 @@
1
+ import type { Middleware } from 'openapi-fetch';
2
+ /**
3
+ * Create openapi-fetch middleware that prevents crashes when the server
4
+ * returns a non-JSON body (e.g. plain text "success") on a successful response.
5
+ *
6
+ * openapi-fetch's default parser calls `response.json()` which throws on
7
+ * non-JSON bodies. This middleware detects that case and wraps the body in
8
+ * a `{ message: "<text>" }` JSON envelope so parsing succeeds.
9
+ *
10
+ * 204 No Content responses are left untouched (no body to parse).
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import createClient from 'openapi-fetch';
15
+ * import {createSafeResponseMiddleware} from '@tetrascience-npm/request';
16
+ *
17
+ * const client = createClient<paths>({baseUrl: '...'});
18
+ * client.use(createSafeResponseMiddleware());
19
+ * ```
20
+ */
21
+ export declare function createSafeResponseMiddleware(): Middleware;
22
+ //# sourceMappingURL=safe-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-response.d.ts","sourceRoot":"","sources":["../../../src/shared/middleware/safe-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,IAAI,UAAU,CAkBzD"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSafeResponseMiddleware = createSafeResponseMiddleware;
4
+ /**
5
+ * Create openapi-fetch middleware that prevents crashes when the server
6
+ * returns a non-JSON body (e.g. plain text "success") on a successful response.
7
+ *
8
+ * openapi-fetch's default parser calls `response.json()` which throws on
9
+ * non-JSON bodies. This middleware detects that case and wraps the body in
10
+ * a `{ message: "<text>" }` JSON envelope so parsing succeeds.
11
+ *
12
+ * 204 No Content responses are left untouched (no body to parse).
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import createClient from 'openapi-fetch';
17
+ * import {createSafeResponseMiddleware} from '@tetrascience-npm/request';
18
+ *
19
+ * const client = createClient<paths>({baseUrl: '...'});
20
+ * client.use(createSafeResponseMiddleware());
21
+ * ```
22
+ */
23
+ function createSafeResponseMiddleware() {
24
+ return {
25
+ async onResponse({ response }) {
26
+ const contentType = response.headers.get('content-type') || '';
27
+ const isJson = contentType.includes('/json') || contentType.includes('+json');
28
+ if (response.ok && response.status !== 204 && !isJson) {
29
+ const text = await response.clone().text();
30
+ const headers = new Headers(response.headers);
31
+ headers.set('content-type', 'application/json');
32
+ return new Response(JSON.stringify({ message: text }), {
33
+ status: response.status,
34
+ statusText: response.statusText,
35
+ headers,
36
+ });
37
+ }
38
+ return undefined;
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,23 @@
1
+ import type { Middleware } from 'openapi-fetch';
2
+ import type { TracingOptions } from '../client-types';
3
+ /**
4
+ * Create middleware that injects tracing headers and optionally logs
5
+ * requests, responses, and errors.
6
+ *
7
+ * Headers injected (only if not already present):
8
+ * - ts-request-id (auto-generates UUID if not provided)
9
+ * - ts-session-id (if provided)
10
+ * - ts-initiating-service-name (if provided)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * client.use(createTracingMiddleware({
15
+ * requestId: () => getRequestId(),
16
+ * sessionId: () => getRequestContext().sessionId,
17
+ * serviceName: 'my-service',
18
+ * logger: console,
19
+ * }))
20
+ * ```
21
+ */
22
+ export declare function createTracingMiddleware(options?: TracingOptions): Middleware;
23
+ //# sourceMappingURL=tracing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../../src/shared/middleware/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAMpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,CA6C5E"}