@mosano-product-framework/sdk 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.
Files changed (74) hide show
  1. package/README.md +827 -0
  2. package/README.react.md +348 -0
  3. package/dist/auth/claims-types.d.ts +89 -0
  4. package/dist/auth/claims.d.ts +125 -0
  5. package/dist/auth/cross-tab.d.ts +114 -0
  6. package/dist/auth/errors.d.ts +40 -0
  7. package/dist/auth/index.d.ts +18 -0
  8. package/dist/auth/index.js +5 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/oauth-state.d.ts +93 -0
  11. package/dist/auth/session-manager.d.ts +253 -0
  12. package/dist/auth/storage.d.ts +36 -0
  13. package/dist/auth/tenant-directory.d.ts +59 -0
  14. package/dist/auth/tenant-selection.d.ts +92 -0
  15. package/dist/chunk-7WAV52EO.js +621 -0
  16. package/dist/chunk-7WAV52EO.js.map +1 -0
  17. package/dist/chunk-AJWM5MDZ.js +410 -0
  18. package/dist/chunk-AJWM5MDZ.js.map +1 -0
  19. package/dist/chunk-EXPYHNPV.js +212 -0
  20. package/dist/chunk-EXPYHNPV.js.map +1 -0
  21. package/dist/chunk-GPWGOYCA.js +85 -0
  22. package/dist/chunk-GPWGOYCA.js.map +1 -0
  23. package/dist/chunk-GQJ3QQPH.js +339 -0
  24. package/dist/chunk-GQJ3QQPH.js.map +1 -0
  25. package/dist/chunk-K2ELAI2X.js +64 -0
  26. package/dist/chunk-K2ELAI2X.js.map +1 -0
  27. package/dist/chunk-LRM6JJ63.js +616 -0
  28. package/dist/chunk-LRM6JJ63.js.map +1 -0
  29. package/dist/chunk-XAXFIIRT.js +959 -0
  30. package/dist/chunk-XAXFIIRT.js.map +1 -0
  31. package/dist/client/core/client-factory.d.ts +61 -0
  32. package/dist/client/core/client.d.ts +144 -0
  33. package/dist/client/core/errors.d.ts +105 -0
  34. package/dist/client/core/index.d.ts +9 -0
  35. package/dist/client/core/middleware.d.ts +67 -0
  36. package/dist/client/core/types.d.ts +99 -0
  37. package/dist/client/graphql/client.d.ts +66 -0
  38. package/dist/client/graphql/factory.d.ts +84 -0
  39. package/dist/client/graphql/operation.d.ts +24 -0
  40. package/dist/client/graphql/types.d.ts +60 -0
  41. package/dist/client/graphql/ws-client.d.ts +116 -0
  42. package/dist/client/index.d.ts +17 -0
  43. package/dist/client/index.js +227 -0
  44. package/dist/client/index.js.map +1 -0
  45. package/dist/client/middlewares/admin-auth.d.ts +90 -0
  46. package/dist/client/middlewares/auth.d.ts +81 -0
  47. package/dist/client/middlewares/index.d.ts +12 -0
  48. package/dist/client/middlewares/logging.d.ts +102 -0
  49. package/dist/client/middlewares/retry.d.ts +138 -0
  50. package/dist/client/middlewares/tenant.d.ts +60 -0
  51. package/dist/client/middlewares/turnstile.d.ts +41 -0
  52. package/dist/client/peer-free.d.ts +25 -0
  53. package/dist/client/utils/url.d.ts +19 -0
  54. package/dist/identity/index.d.ts +85 -0
  55. package/dist/identity/index.js +6 -0
  56. package/dist/identity/index.js.map +1 -0
  57. package/dist/identity/types.d.ts +690 -0
  58. package/dist/identity/v0.d.ts +594 -0
  59. package/dist/index.d.ts +50 -0
  60. package/dist/index.js +24 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/react/context.d.ts +47 -0
  63. package/dist/react/hooks.d.ts +120 -0
  64. package/dist/react/index.d.ts +19 -0
  65. package/dist/react/index.js +308 -0
  66. package/dist/react/index.js.map +1 -0
  67. package/dist/react/provider.d.ts +68 -0
  68. package/dist/react/store.d.ts +85 -0
  69. package/dist/storage/index.d.ts +31 -0
  70. package/dist/storage/index.js +5 -0
  71. package/dist/storage/index.js.map +1 -0
  72. package/dist/storage/types.d.ts +107 -0
  73. package/dist/storage/v0.d.ts +120 -0
  74. package/package.json +99 -0
@@ -0,0 +1,339 @@
1
+ import { issueAdminToken, createTurnstileMiddleware, createAdminAuthMiddleware, AdminEmptyUserIDError, AdminEmptySecretError } from './chunk-K2ELAI2X.js';
2
+ import { shouldRetryRequest, requestRetry, isRetryDisabled, isReplayableBody, getRetryAttempt, executeResponseMiddlewares, executeRequestMiddlewares, executeErrorMiddlewares, disableRetry, createTenantMiddleware, createRetryMiddleware, createHttpClient, createAuthMiddleware, clearRetryFlag, attachStandardMiddlewares, TENANT_ROLE_HEADER, TENANT_HEADER, MPFClient, MAX_REQUEST_ATTEMPTS } from './chunk-LRM6JJ63.js';
3
+ import { __export, isMPFValidationError, isMPFNetworkError, isMPFError, isMPFAuthError, isMPFAPIError, MPFValidationError, MPFTimeoutError, MPFNetworkError, MPFError, MPFAuthError, MPFAPIError } from './chunk-AJWM5MDZ.js';
4
+
5
+ // src/client/middlewares/logging.ts
6
+ var DEFAULT_REDACT_HEADERS = [
7
+ "authorization",
8
+ "cookie",
9
+ "x-api-key",
10
+ "x-auth-token",
11
+ "x-access-token",
12
+ "set-cookie"
13
+ ];
14
+ function formatHeaders(headers, redactHeaders) {
15
+ const result = {};
16
+ if (!headers) {
17
+ return result;
18
+ }
19
+ const headersObj = headers instanceof Headers ? headers : new Headers(headers);
20
+ const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));
21
+ headersObj.forEach((value, key) => {
22
+ const lowerKey = key.toLowerCase();
23
+ if (redactSet.has(lowerKey)) {
24
+ result[key] = "[REDACTED]";
25
+ } else {
26
+ result[key] = value;
27
+ }
28
+ });
29
+ return result;
30
+ }
31
+ function getTimestamp() {
32
+ return (/* @__PURE__ */ new Date()).toISOString();
33
+ }
34
+ function getMethod(init) {
35
+ return (init.method ?? "GET").toUpperCase();
36
+ }
37
+ function truncateBody(body, maxLength = 1e3) {
38
+ if (body === void 0 || body === null) {
39
+ return "";
40
+ }
41
+ let bodyStr;
42
+ if (typeof body === "string") {
43
+ bodyStr = body;
44
+ } else if (body instanceof FormData) {
45
+ return "[FormData]";
46
+ } else if (body instanceof Blob) {
47
+ return `[Blob: ${body.size} bytes]`;
48
+ } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
49
+ const size = body instanceof ArrayBuffer ? body.byteLength : body.byteLength;
50
+ return `[Binary: ${size} bytes]`;
51
+ } else if (body instanceof URLSearchParams) {
52
+ bodyStr = body.toString();
53
+ } else if (body instanceof ReadableStream) {
54
+ return "[ReadableStream]";
55
+ } else {
56
+ try {
57
+ bodyStr = JSON.stringify(body);
58
+ } catch {
59
+ return "[Unserializable]";
60
+ }
61
+ }
62
+ if (bodyStr.length > maxLength) {
63
+ return bodyStr.substring(0, maxLength) + `... (${bodyStr.length} chars total)`;
64
+ }
65
+ return bodyStr;
66
+ }
67
+ function createLoggingMiddleware(options) {
68
+ const {
69
+ logger = console,
70
+ logBody = false,
71
+ prefix = "[MPF SDK]",
72
+ logHeaders = false,
73
+ redactHeaders = DEFAULT_REDACT_HEADERS,
74
+ minDuration = 0,
75
+ includeTimestamp = true,
76
+ formatRequest: customFormatRequest,
77
+ formatResponse: customFormatResponse,
78
+ formatError: customFormatError
79
+ } = options ?? {};
80
+ function buildPrefix() {
81
+ if (includeTimestamp) {
82
+ return `${prefix} [${getTimestamp()}]`;
83
+ }
84
+ return prefix;
85
+ }
86
+ return {
87
+ name: "logging",
88
+ onRequest(ctx) {
89
+ ctx.metadata["__requestStartTime"] = Date.now();
90
+ ctx.metadata["__requestMethod"] = getMethod(ctx.init);
91
+ ctx.metadata["__requestUrl"] = ctx.url;
92
+ const method = ctx.metadata["__requestMethod"];
93
+ const logPrefix = buildPrefix();
94
+ if (customFormatRequest) {
95
+ logger.log(customFormatRequest(ctx));
96
+ } else {
97
+ const parts = [`${logPrefix} --> ${method} ${ctx.url}`];
98
+ if (logHeaders && ctx.init.headers) {
99
+ const headers = formatHeaders(ctx.init.headers, redactHeaders);
100
+ parts.push(` Headers: ${JSON.stringify(headers)}`);
101
+ }
102
+ if (logBody && ctx.init.body) {
103
+ const body = truncateBody(ctx.init.body);
104
+ if (body) {
105
+ parts.push(` Body: ${body}`);
106
+ }
107
+ }
108
+ logger.log(parts.join("\n"));
109
+ }
110
+ return ctx;
111
+ },
112
+ onResponse(ctx) {
113
+ const startTime = ctx.metadata["__requestStartTime"];
114
+ const duration = startTime ? Date.now() - startTime : 0;
115
+ if (duration < minDuration) {
116
+ return ctx;
117
+ }
118
+ const method = ctx.metadata["__requestMethod"] || "GET";
119
+ const status = ctx.response.status;
120
+ const statusText = ctx.response.statusText;
121
+ const url = ctx.metadata["__requestUrl"] || ctx.response.url || "(unknown)";
122
+ const logPrefix = buildPrefix();
123
+ if (customFormatResponse) {
124
+ logger.log(customFormatResponse(ctx));
125
+ } else {
126
+ const statusIcon = status >= 200 && status < 300 ? "OK" : status >= 400 ? "ERR" : "WARN";
127
+ const parts = [
128
+ `${logPrefix} <-- ${method} ${url} ${status} ${statusText} (${duration}ms) [${statusIcon}]`
129
+ ];
130
+ if (logHeaders) {
131
+ const headers = formatHeaders(ctx.response.headers, redactHeaders);
132
+ parts.push(` Headers: ${JSON.stringify(headers)}`);
133
+ }
134
+ if (logBody && ctx.data !== void 0) {
135
+ const body = truncateBody(ctx.data);
136
+ if (body) {
137
+ parts.push(` Body: ${body}`);
138
+ }
139
+ }
140
+ logger.log(parts.join("\n"));
141
+ }
142
+ return ctx;
143
+ },
144
+ onError(error, ctx) {
145
+ const startTime = ctx.metadata["__requestStartTime"];
146
+ const duration = startTime ? Date.now() - startTime : 0;
147
+ const method = getMethod(ctx.init);
148
+ const logPrefix = buildPrefix();
149
+ if (customFormatError) {
150
+ logger.error(customFormatError(error, ctx));
151
+ } else {
152
+ const parts = [
153
+ `${logPrefix} <-- ${method} ${ctx.url} ERROR (${duration}ms)`,
154
+ ` Error: ${error.name}: ${error.message}`
155
+ ];
156
+ if (error.stack) {
157
+ const stackLines = error.stack.split("\n").slice(1, 4);
158
+ parts.push(` Stack: ${stackLines.join("\n ")}`);
159
+ }
160
+ logger.error(parts.join("\n"));
161
+ }
162
+ throw error;
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/client/utils/url.ts
168
+ function buildUrl(baseUrl, path, params) {
169
+ const normalizedBase = baseUrl.replace(/\/+$/, "");
170
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
171
+ const base = /^https?:\/\//.test(normalizedBase) ? normalizedBase : typeof window !== "undefined" ? window.location.origin : "http://localhost";
172
+ const url = new URL(`${normalizedBase}${normalizedPath}`, base);
173
+ if (params) {
174
+ for (const [key, value] of Object.entries(params)) {
175
+ if (value !== void 0 && value !== null) {
176
+ url.searchParams.append(key, String(value));
177
+ }
178
+ }
179
+ }
180
+ return url.toString();
181
+ }
182
+ function joinPath(...segments) {
183
+ return segments.map((segment, index) => {
184
+ if (index > 0) {
185
+ segment = segment.replace(/^\/+/, "");
186
+ }
187
+ if (index < segments.length - 1) {
188
+ segment = segment.replace(/\/+$/, "");
189
+ }
190
+ return segment;
191
+ }).filter(Boolean).join("/");
192
+ }
193
+
194
+ // src/client/graphql/client.ts
195
+ var MPFGraphQLClient = class extends MPFClient {
196
+ /** The GraphQL endpoint path */
197
+ graphqlEndpoint;
198
+ constructor(config) {
199
+ super(config);
200
+ this.graphqlEndpoint = config.endpoint ?? "/graphql";
201
+ }
202
+ /**
203
+ * Execute a GraphQL query operation
204
+ *
205
+ * @param document - The GraphQL query (string or typed document node)
206
+ * @param variables - Optional variables for the query
207
+ * @param options - Optional request options (headers, signal)
208
+ * @returns The query result data
209
+ * @throws {MPFAPIError} When GraphQL errors occur or no data is returned
210
+ */
211
+ async query(document, variables, options) {
212
+ return this.executeOperation(document, variables, options);
213
+ }
214
+ /**
215
+ * Execute a GraphQL mutation operation
216
+ *
217
+ * @param document - The GraphQL mutation (string or typed document node)
218
+ * @param variables - Optional variables for the mutation
219
+ * @param options - Optional request options (headers, signal)
220
+ * @returns The mutation result data
221
+ * @throws {MPFAPIError} When GraphQL errors occur or no data is returned
222
+ */
223
+ async mutate(document, variables, options) {
224
+ return this.executeOperation(document, variables, options);
225
+ }
226
+ /**
227
+ * Execute a GraphQL operation (query or mutation)
228
+ *
229
+ * @internal
230
+ */
231
+ async executeOperation(document, variables, options) {
232
+ const query = typeof document === "string" ? document : document.toString();
233
+ const response = await this.post(
234
+ this.graphqlEndpoint,
235
+ { query, variables },
236
+ options
237
+ );
238
+ if (response.errors && response.errors.length > 0) {
239
+ const firstError = response.errors[0];
240
+ const errorCode = firstError.extensions?.code ?? "GRAPHQL_ERROR";
241
+ throw new MPFAPIError(
242
+ firstError.message,
243
+ errorCode,
244
+ 200,
245
+ response.errors
246
+ );
247
+ }
248
+ if (response.data === void 0 || response.data === null) {
249
+ throw new MPFAPIError(
250
+ "No data returned from GraphQL operation",
251
+ "NO_DATA",
252
+ 200
253
+ );
254
+ }
255
+ return response.data;
256
+ }
257
+ };
258
+
259
+ // src/client/graphql/operation.ts
260
+ function stripLeadingTrivia(source) {
261
+ let rest = source;
262
+ for (; ; ) {
263
+ const trimmed = rest.replace(/^[\s,\uFEFF]+/, "");
264
+ if (trimmed.startsWith("#")) {
265
+ const newline = trimmed.indexOf("\n");
266
+ rest = newline === -1 ? "" : trimmed.slice(newline + 1);
267
+ continue;
268
+ }
269
+ if (trimmed !== rest) {
270
+ rest = trimmed;
271
+ continue;
272
+ }
273
+ return trimmed;
274
+ }
275
+ }
276
+ function getOperationKind(document) {
277
+ const source = stripLeadingTrivia(document);
278
+ if (source.startsWith("{")) {
279
+ return "query";
280
+ }
281
+ const match = /^(query|mutation|subscription)\b/.exec(source);
282
+ if (!match) {
283
+ return "unknown";
284
+ }
285
+ return match[1];
286
+ }
287
+ function isSubscription(document) {
288
+ return getOperationKind(document) === "subscription";
289
+ }
290
+
291
+ // src/client/peer-free.ts
292
+ var peer_free_exports = {};
293
+ __export(peer_free_exports, {
294
+ AdminEmptySecretError: () => AdminEmptySecretError,
295
+ AdminEmptyUserIDError: () => AdminEmptyUserIDError,
296
+ MAX_REQUEST_ATTEMPTS: () => MAX_REQUEST_ATTEMPTS,
297
+ MPFAPIError: () => MPFAPIError,
298
+ MPFAuthError: () => MPFAuthError,
299
+ MPFClient: () => MPFClient,
300
+ MPFError: () => MPFError,
301
+ MPFGraphQLClient: () => MPFGraphQLClient,
302
+ MPFNetworkError: () => MPFNetworkError,
303
+ MPFTimeoutError: () => MPFTimeoutError,
304
+ MPFValidationError: () => MPFValidationError,
305
+ TENANT_HEADER: () => TENANT_HEADER,
306
+ TENANT_ROLE_HEADER: () => TENANT_ROLE_HEADER,
307
+ attachStandardMiddlewares: () => attachStandardMiddlewares,
308
+ buildUrl: () => buildUrl,
309
+ clearRetryFlag: () => clearRetryFlag,
310
+ createAdminAuthMiddleware: () => createAdminAuthMiddleware,
311
+ createAuthMiddleware: () => createAuthMiddleware,
312
+ createHttpClient: () => createHttpClient,
313
+ createLoggingMiddleware: () => createLoggingMiddleware,
314
+ createRetryMiddleware: () => createRetryMiddleware,
315
+ createTenantMiddleware: () => createTenantMiddleware,
316
+ createTurnstileMiddleware: () => createTurnstileMiddleware,
317
+ disableRetry: () => disableRetry,
318
+ executeErrorMiddlewares: () => executeErrorMiddlewares,
319
+ executeRequestMiddlewares: () => executeRequestMiddlewares,
320
+ executeResponseMiddlewares: () => executeResponseMiddlewares,
321
+ getOperationKind: () => getOperationKind,
322
+ getRetryAttempt: () => getRetryAttempt,
323
+ isMPFAPIError: () => isMPFAPIError,
324
+ isMPFAuthError: () => isMPFAuthError,
325
+ isMPFError: () => isMPFError,
326
+ isMPFNetworkError: () => isMPFNetworkError,
327
+ isMPFValidationError: () => isMPFValidationError,
328
+ isReplayableBody: () => isReplayableBody,
329
+ isRetryDisabled: () => isRetryDisabled,
330
+ isSubscription: () => isSubscription,
331
+ issueAdminToken: () => issueAdminToken,
332
+ joinPath: () => joinPath,
333
+ requestRetry: () => requestRetry,
334
+ shouldRetryRequest: () => shouldRetryRequest
335
+ });
336
+
337
+ export { MPFGraphQLClient, buildUrl, createLoggingMiddleware, getOperationKind, isSubscription, joinPath, peer_free_exports };
338
+ //# sourceMappingURL=chunk-GQJ3QQPH.js.map
339
+ //# sourceMappingURL=chunk-GQJ3QQPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/middlewares/logging.ts","../src/client/utils/url.ts","../src/client/graphql/client.ts","../src/client/graphql/operation.ts","../src/client/peer-free.ts"],"names":[],"mappings":"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","file":"chunk-GQJ3QQPH.js","sourcesContent":["import type { Middleware } from '../core/middleware.js';\nimport type { RequestContext, ResponseContext } from '../core/types.js';\n\n/**\n * Logger interface compatible with console and custom loggers.\n */\nexport interface Logger {\n log: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Options for the logging middleware.\n */\nexport interface LoggingMiddlewareOptions {\n /**\n * Logger instance to use for output.\n * Defaults to console.\n */\n logger?: Logger;\n\n /**\n * Whether to log request/response bodies.\n * Default is false for security (bodies may contain sensitive data).\n * @default false\n */\n logBody?: boolean;\n\n /**\n * Prefix for all log messages.\n * @default '[MPF SDK]'\n */\n prefix?: string;\n\n /**\n * Whether to log request headers.\n * Sensitive headers like Authorization are always redacted.\n * @default false\n */\n logHeaders?: boolean;\n\n /**\n * List of header names to redact from logs (case-insensitive).\n * @default ['authorization', 'cookie', 'x-api-key', 'x-auth-token']\n */\n redactHeaders?: string[];\n\n /**\n * Minimum duration in ms to log. Requests faster than this are not logged.\n * Useful for reducing log volume in high-traffic scenarios.\n * @default 0 (log all requests)\n */\n minDuration?: number;\n\n /**\n * Whether to include timestamp in log messages.\n * @default true\n */\n includeTimestamp?: boolean;\n\n /**\n * Custom log formatter for requests.\n * If provided, overrides the default request logging format.\n */\n formatRequest?: (ctx: RequestContext) => string;\n\n /**\n * Custom log formatter for responses.\n * If provided, overrides the default response logging format.\n */\n formatResponse?: <T>(ctx: ResponseContext<T>) => string;\n\n /**\n * Custom log formatter for errors.\n * If provided, overrides the default error logging format.\n */\n formatError?: (error: Error, ctx: RequestContext) => string;\n}\n\n/** Default headers to redact from logs */\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'x-api-key',\n 'x-auth-token',\n 'x-access-token',\n 'set-cookie',\n];\n\n/**\n * Format headers for logging, redacting sensitive values.\n */\nfunction formatHeaders(\n headers: Headers | HeadersInit | undefined,\n redactHeaders: string[]\n): Record<string, string> {\n const result: Record<string, string> = {};\n\n if (!headers) {\n return result;\n }\n\n const headersObj = headers instanceof Headers ? headers : new Headers(headers);\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n\n headersObj.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n if (redactSet.has(lowerKey)) {\n result[key] = '[REDACTED]';\n } else {\n result[key] = value;\n }\n });\n\n return result;\n}\n\n/**\n * Get current timestamp in ISO format.\n */\nfunction getTimestamp(): string {\n return new Date().toISOString();\n}\n\n/**\n * Extract HTTP method from request init.\n */\nfunction getMethod(init: RequestInit): string {\n return (init.method ?? 'GET').toUpperCase();\n}\n\n/**\n * Truncate body for logging if too long.\n */\nfunction truncateBody(body: unknown, maxLength: number = 1000): string {\n if (body === undefined || body === null) {\n return '';\n }\n\n let bodyStr: string;\n if (typeof body === 'string') {\n bodyStr = body;\n } else if (body instanceof FormData) {\n return '[FormData]';\n } else if (body instanceof Blob) {\n return `[Blob: ${body.size} bytes]`;\n } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n const size = body instanceof ArrayBuffer ? body.byteLength : body.byteLength;\n return `[Binary: ${size} bytes]`;\n } else if (body instanceof URLSearchParams) {\n bodyStr = body.toString();\n } else if (body instanceof ReadableStream) {\n return '[ReadableStream]';\n } else {\n try {\n bodyStr = JSON.stringify(body);\n } catch {\n return '[Unserializable]';\n }\n }\n\n if (bodyStr.length > maxLength) {\n return bodyStr.substring(0, maxLength) + `... (${bodyStr.length} chars total)`;\n }\n\n return bodyStr;\n}\n\n/**\n * Creates a logging middleware for debugging and monitoring.\n *\n * The middleware logs:\n * - Request method, URL, and optionally body/headers\n * - Response status, URL, and duration\n * - Errors with context\n *\n * Sensitive headers (Authorization, Cookie, etc.) are automatically redacted.\n *\n * @param options - Configuration options for the logging middleware\n * @returns A middleware instance\n *\n * @example\n * ```typescript\n * const loggingMiddleware = createLoggingMiddleware({\n * prefix: '[MyApp API]',\n * logBody: false,\n * logHeaders: true,\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With custom logger\n * const loggingMiddleware = createLoggingMiddleware({\n * logger: {\n * log: (msg, ...args) => myLogger.info(msg, ...args),\n * error: (msg, ...args) => myLogger.error(msg, ...args),\n * },\n * minDuration: 100, // Only log requests taking > 100ms\n * });\n * ```\n */\nexport function createLoggingMiddleware(options?: LoggingMiddlewareOptions): Middleware {\n const {\n logger = console,\n logBody = false,\n prefix = '[MPF SDK]',\n logHeaders = false,\n redactHeaders = DEFAULT_REDACT_HEADERS,\n minDuration = 0,\n includeTimestamp = true,\n formatRequest: customFormatRequest,\n formatResponse: customFormatResponse,\n formatError: customFormatError,\n } = options ?? {};\n\n /**\n * Build log prefix with optional timestamp.\n */\n function buildPrefix(): string {\n if (includeTimestamp) {\n return `${prefix} [${getTimestamp()}]`;\n }\n return prefix;\n }\n\n return {\n name: 'logging',\n\n onRequest(ctx: RequestContext): RequestContext {\n // Store start time and request info for response logging\n ctx.metadata['__requestStartTime'] = Date.now();\n ctx.metadata['__requestMethod'] = getMethod(ctx.init);\n ctx.metadata['__requestUrl'] = ctx.url;\n\n const method = ctx.metadata['__requestMethod'] as string;\n const logPrefix = buildPrefix();\n\n if (customFormatRequest) {\n logger.log(customFormatRequest(ctx));\n } else {\n const parts: string[] = [`${logPrefix} --> ${method} ${ctx.url}`];\n\n if (logHeaders && ctx.init.headers) {\n const headers = formatHeaders(ctx.init.headers, redactHeaders);\n parts.push(` Headers: ${JSON.stringify(headers)}`);\n }\n\n if (logBody && ctx.init.body) {\n const body = truncateBody(ctx.init.body);\n if (body) {\n parts.push(` Body: ${body}`);\n }\n }\n\n logger.log(parts.join('\\n'));\n }\n\n return ctx;\n },\n\n onResponse<T>(ctx: ResponseContext<T>): ResponseContext<T> {\n const startTime = ctx.metadata['__requestStartTime'] as number | undefined;\n const duration = startTime ? Date.now() - startTime : 0;\n\n // Skip logging if duration is below threshold\n if (duration < minDuration) {\n return ctx;\n }\n\n const method = (ctx.metadata['__requestMethod'] as string) || 'GET';\n const status = ctx.response.status;\n const statusText = ctx.response.statusText;\n const url = (ctx.metadata['__requestUrl'] as string) || ctx.response.url || '(unknown)';\n const logPrefix = buildPrefix();\n\n if (customFormatResponse) {\n logger.log(customFormatResponse(ctx));\n } else {\n const statusIcon = status >= 200 && status < 300 ? 'OK' : status >= 400 ? 'ERR' : 'WARN';\n const parts: string[] = [\n `${logPrefix} <-- ${method} ${url} ${status} ${statusText} (${duration}ms) [${statusIcon}]`,\n ];\n\n if (logHeaders) {\n const headers = formatHeaders(ctx.response.headers, redactHeaders);\n parts.push(` Headers: ${JSON.stringify(headers)}`);\n }\n\n if (logBody && ctx.data !== undefined) {\n const body = truncateBody(ctx.data);\n if (body) {\n parts.push(` Body: ${body}`);\n }\n }\n\n logger.log(parts.join('\\n'));\n }\n\n return ctx;\n },\n\n onError(error: Error, ctx: RequestContext): never {\n const startTime = ctx.metadata['__requestStartTime'] as number | undefined;\n const duration = startTime ? Date.now() - startTime : 0;\n const method = getMethod(ctx.init);\n const logPrefix = buildPrefix();\n\n if (customFormatError) {\n logger.error(customFormatError(error, ctx));\n } else {\n const parts: string[] = [\n `${logPrefix} <-- ${method} ${ctx.url} ERROR (${duration}ms)`,\n ` Error: ${error.name}: ${error.message}`,\n ];\n\n if (error.stack) {\n // Only include first 3 lines of stack trace\n const stackLines = error.stack.split('\\n').slice(1, 4);\n parts.push(` Stack: ${stackLines.join('\\n ')}`);\n }\n\n logger.error(parts.join('\\n'));\n }\n\n throw error;\n },\n };\n}\n","/**\n * URL building utilities for the MPF SDK\n */\n\n/**\n * Build a URL with query parameters\n *\n * @param baseUrl - The base URL\n * @param path - The path to append\n * @param params - Optional query parameters\n * @returns The full URL with query string\n */\nexport function buildUrl(\n baseUrl: string,\n path: string,\n params?: Record<string, string | number | boolean | undefined | null>\n): string {\n // Remove trailing slash from base URL\n const normalizedBase = baseUrl.replace(/\\/+$/, '');\n\n // Ensure path starts with a slash\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n\n const base = /^https?:\\/\\//.test(normalizedBase)\n ? normalizedBase\n : (typeof window !== 'undefined' ? window.location.origin : 'http://localhost');\n const url = new URL(`${normalizedBase}${normalizedPath}`, base);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n }\n }\n\n return url.toString();\n}\n\n/**\n * Join URL segments safely\n *\n * @param segments - URL segments to join\n * @returns Joined URL path\n */\nexport function joinPath(...segments: string[]): string {\n return segments\n .map((segment, index) => {\n // Remove leading slash except for first segment\n if (index > 0) {\n segment = segment.replace(/^\\/+/, '');\n }\n // Remove trailing slash except for last segment\n if (index < segments.length - 1) {\n segment = segment.replace(/\\/+$/, '');\n }\n return segment;\n })\n .filter(Boolean)\n .join('/');\n}\n","/**\n * GraphQL client that extends the base MPF client\n */\n\nimport { MPFClient } from '../core/client.js';\nimport type { ClientConfig } from '../core/types.js';\nimport type {\n TypedDocumentNode,\n GraphQLResponse,\n GraphQLRequestOptions,\n} from './types.js';\nimport { MPFAPIError } from '../core/errors.js';\n\n/**\n * Configuration options for the GraphQL client\n */\nexport interface GraphQLClientConfig extends ClientConfig {\n /**\n * GraphQL endpoint path (default: '/graphql')\n */\n endpoint?: string;\n}\n\n/**\n * GraphQL client for MPF services\n *\n * Supports both raw query strings and typed document nodes for type-safe operations.\n *\n * @example\n * ```typescript\n * const client = new MPFGraphQLClient({ baseUrl: 'https://api.example.com' });\n *\n * // Using raw query string\n * const result = await client.query(`\n * query GetUser($id: ID!) {\n * user(id: $id) { id name email }\n * }\n * `, { id: '123' });\n *\n * // Using typed document node (from codegen)\n * const typedResult = await client.query(GetUserDocument, { id: '123' });\n * ```\n */\nexport class MPFGraphQLClient extends MPFClient {\n /** The GraphQL endpoint path */\n protected graphqlEndpoint: string;\n\n constructor(config: GraphQLClientConfig) {\n super(config);\n this.graphqlEndpoint = config.endpoint ?? '/graphql';\n }\n\n /**\n * Execute a GraphQL query operation\n *\n * @param document - The GraphQL query (string or typed document node)\n * @param variables - Optional variables for the query\n * @param options - Optional request options (headers, signal)\n * @returns The query result data\n * @throws {MPFAPIError} When GraphQL errors occur or no data is returned\n */\n async query<TData, TVariables = Record<string, unknown>>(\n document: TypedDocumentNode<TData, TVariables> | string,\n variables?: TVariables,\n options?: GraphQLRequestOptions\n ): Promise<TData> {\n return this.executeOperation<TData, TVariables>(document, variables, options);\n }\n\n /**\n * Execute a GraphQL mutation operation\n *\n * @param document - The GraphQL mutation (string or typed document node)\n * @param variables - Optional variables for the mutation\n * @param options - Optional request options (headers, signal)\n * @returns The mutation result data\n * @throws {MPFAPIError} When GraphQL errors occur or no data is returned\n */\n async mutate<TData, TVariables = Record<string, unknown>>(\n document: TypedDocumentNode<TData, TVariables> | string,\n variables?: TVariables,\n options?: GraphQLRequestOptions\n ): Promise<TData> {\n return this.executeOperation<TData, TVariables>(document, variables, options);\n }\n\n /**\n * Execute a GraphQL operation (query or mutation)\n *\n * @internal\n */\n private async executeOperation<TData, TVariables>(\n document: TypedDocumentNode<TData, TVariables> | string,\n variables?: TVariables,\n options?: GraphQLRequestOptions\n ): Promise<TData> {\n const query = typeof document === 'string' ? document : document.toString();\n\n const response = await this.post<GraphQLResponse<TData>>(\n this.graphqlEndpoint,\n { query, variables },\n options\n );\n\n // Handle GraphQL errors (they come in the response body, not HTTP status)\n if (response.errors && response.errors.length > 0) {\n const firstError = response.errors[0];\n const errorCode =\n (firstError.extensions?.code as string | undefined) ?? 'GRAPHQL_ERROR';\n\n throw new MPFAPIError(\n firstError.message,\n errorCode,\n 200,\n response.errors\n );\n }\n\n if (response.data === undefined || response.data === null) {\n throw new MPFAPIError(\n 'No data returned from GraphQL operation',\n 'NO_DATA',\n 200\n );\n }\n\n return response.data;\n }\n}\n","/**\n * Cheap operation-kind detection — no `graphql` parser dependency.\n *\n * The hybrid client only needs ONE bit: \"is this a subscription\". A full AST\n * parse (`graphql`'s `parse()` + `getOperationAST()`) would answer that too,\n * but it drags `graphql` into the HTTP-only path as a hard dependency, which\n * defeats the point of `graphql` staying an optional peer (see\n * `graphql/ws-client.ts`). A GraphQL document's operation keyword is always\n * the first non-trivia token — `query`/`mutation`/`subscription`, or omitted\n * entirely for the anonymous-query shorthand (`{ ... }`) — so a regex over the\n * leading trivia is exact, not a heuristic.\n */\n\n/** The three GraphQL operation kinds, plus `unknown` for an unparseable document. */\nexport type OperationKind = 'query' | 'mutation' | 'subscription' | 'unknown';\n\n/**\n * Strip GraphQL trivia (whitespace, commas, `#`-comments) from the START of a\n * document string, mirroring the lexer's own definition of \"ignored tokens\"\n * (GraphQL spec §2.1.4) closely enough to find the first real token.\n */\nfunction stripLeadingTrivia(source: string): string {\n let rest = source;\n for (;;) {\n const trimmed = rest.replace(/^[\\s,\\uFEFF]+/, '');\n if (trimmed.startsWith('#')) {\n const newline = trimmed.indexOf('\\n');\n rest = newline === -1 ? '' : trimmed.slice(newline + 1);\n continue;\n }\n if (trimmed !== rest) {\n rest = trimmed;\n continue;\n }\n return trimmed;\n }\n}\n\n/**\n * Determine a GraphQL document's operation kind from its source text.\n *\n * `{ ... }` (no keyword) is the anonymous-query shorthand and is always a\n * `query` — a `subscription` document is never allowed to omit its keyword,\n * per the GraphQL spec, so this cannot misclassify a subscription as a query.\n */\nexport function getOperationKind(document: string): OperationKind {\n const source = stripLeadingTrivia(document);\n\n if (source.startsWith('{')) {\n return 'query';\n }\n\n const match = /^(query|mutation|subscription)\\b/.exec(source);\n if (!match) {\n return 'unknown';\n }\n\n return match[1] as OperationKind;\n}\n\n/** Whether a GraphQL document is a subscription. */\nexport function isSubscription(document: string): boolean {\n return getOperationKind(document) === 'subscription';\n}\n","/**\n * The part of `./client` that reaches NO optional peer dependency.\n *\n * This file exists so the root barrel can expose a `client` namespace without\n * dragging `graphql-ws` into every consumer's module graph.\n *\n * `createGraphQLClient` (in `graphql/factory.ts`) and `createGraphQLWsTransport`\n * (in `graphql/ws-client.ts`) reach `graphql-ws`. It is only ever loaded through\n * a dynamic `import()`, so nothing breaks at runtime for an app that never\n * subscribes — but a *static* export chain from `src/index.ts` still puts those\n * modules (and the dynamic-import chunk a bundler emits for them) in the graph\n * of every consumer, including plain-HTTP ones. So they are reachable from the\n * `./client` subpath only, never from the root namespace.\n *\n * This is the same rule `./react` follows for its `react` peer. See\n * `tests/module-boundaries.test.ts` (\"depends on jose only\") for the guard.\n *\n * @packageDocumentation\n */\n\n// Core transport: client, middleware machinery, errors, shared types.\nexport * from './core/index.js';\n\n// Generic middlewares: auth, admin-auth, tenant, retry, logging, turnstile.\nexport * from './middlewares/index.js';\n\n// URL helpers.\nexport * from './utils/url.js';\n\n// GraphQL over plain HTTP. No optional peer involved: the request goes through\n// the same `fetch` path as every other client.\nexport { MPFGraphQLClient, type GraphQLClientConfig } from './graphql/client.js';\nexport type {\n TypedDocumentNode,\n GraphQLError,\n GraphQLResponse,\n GraphQLRequestOptions,\n} from './graphql/types.js';\n\n// Pure string inspection of a GraphQL document — no dependencies at all.\nexport { getOperationKind, isSubscription, type OperationKind } from './graphql/operation.js';\n"]}
@@ -0,0 +1,64 @@
1
+ import { createAuthMiddleware } from './chunk-LRM6JJ63.js';
2
+ import { SignJWT } from 'jose';
3
+
4
+ var AdminEmptySecretError = class extends Error {
5
+ constructor() {
6
+ super("auth: admin JWT signing secret must not be empty");
7
+ this.name = "AdminEmptySecretError";
8
+ }
9
+ };
10
+ var AdminEmptyUserIDError = class extends Error {
11
+ constructor() {
12
+ super("auth: act_on_behalf_of.user_id must not be empty");
13
+ this.name = "AdminEmptyUserIDError";
14
+ }
15
+ };
16
+ async function issueAdminToken(secret, actOnBehalfOf, ttlSeconds) {
17
+ if (!secret) {
18
+ throw new AdminEmptySecretError();
19
+ }
20
+ if (!actOnBehalfOf.userId) {
21
+ throw new AdminEmptyUserIDError();
22
+ }
23
+ const nowSeconds = Math.floor(Date.now() / 1e3);
24
+ const key = new TextEncoder().encode(secret);
25
+ const actClaim = {
26
+ user_id: actOnBehalfOf.userId,
27
+ ...actOnBehalfOf.onTenantId ? { on_tenant_id: actOnBehalfOf.onTenantId } : {}
28
+ };
29
+ return new SignJWT({ act_on_behalf_of: actClaim }).setProtectedHeader({ alg: "HS256" }).setIssuedAt(nowSeconds).setExpirationTime(nowSeconds + ttlSeconds).sign(key);
30
+ }
31
+ function createAdminAuthMiddleware(options) {
32
+ const getAccessToken = "token" in options && options.token !== void 0 ? () => options.token : () => {
33
+ const { secret, actOnBehalfOf, ttlSeconds } = options.config;
34
+ return issueAdminToken(secret, actOnBehalfOf, ttlSeconds);
35
+ };
36
+ return createAuthMiddleware({
37
+ getAccessToken,
38
+ tokenType: "Admin"
39
+ });
40
+ }
41
+
42
+ // src/client/middlewares/turnstile.ts
43
+ function createTurnstileMiddleware(options) {
44
+ const { getToken, protectedPaths } = options;
45
+ return {
46
+ name: "turnstile",
47
+ async onRequest(ctx) {
48
+ const url = new URL(ctx.url, "http://localhost");
49
+ const isProtected = protectedPaths.some((path) => url.pathname.endsWith(path));
50
+ if (!isProtected) {
51
+ return ctx;
52
+ }
53
+ const token = await getToken();
54
+ const headers = new Headers(ctx.init.headers);
55
+ headers.set("X-Turnstile-Token", token);
56
+ ctx.init.headers = headers;
57
+ return ctx;
58
+ }
59
+ };
60
+ }
61
+
62
+ export { AdminEmptySecretError, AdminEmptyUserIDError, createAdminAuthMiddleware, createTurnstileMiddleware, issueAdminToken };
63
+ //# sourceMappingURL=chunk-K2ELAI2X.js.map
64
+ //# sourceMappingURL=chunk-K2ELAI2X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/middlewares/admin-auth.ts","../src/client/middlewares/turnstile.ts"],"names":[],"mappings":";;;AAmBO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAC/C,WAAA,GAAc;AACZ,IAAA,KAAA,CAAM,kDAAkD,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF;AAGO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAC/C,WAAA,GAAc;AACZ,IAAA,KAAA,CAAM,kDAAkD,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AAAA,EACd;AACF;AA0BA,eAAsB,eAAA,CACpB,MAAA,EACA,aAAA,EACA,UAAA,EACiB;AACjB,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,qBAAA,EAAsB;AAAA,EAClC;AACA,EAAA,IAAI,CAAC,cAAc,MAAA,EAAQ;AACzB,IAAA,MAAM,IAAI,qBAAA,EAAsB;AAAA,EAClC;AAEA,EAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAC/C,EAAA,MAAM,GAAA,GAAM,IAAI,WAAA,EAAY,CAAE,OAAO,MAAM,CAAA;AAE3C,EAAA,MAAM,QAAA,GAAuD;AAAA,IAC3D,SAAS,aAAA,CAAc,MAAA;AAAA,IACvB,GAAI,cAAc,UAAA,GAAa,EAAE,cAAc,aAAA,CAAc,UAAA,KAAe;AAAC,GAC/E;AAEA,EAAA,OAAO,IAAI,QAAQ,EAAE,gBAAA,EAAkB,UAAU,CAAA,CAC9C,mBAAmB,EAAE,GAAA,EAAK,SAAS,CAAA,CACnC,YAAY,UAAU,CAAA,CACtB,kBAAkB,UAAA,GAAa,UAAU,CAAA,CACzC,IAAA,CAAK,GAAG,CAAA;AACb;AA+CO,SAAS,0BAA0B,OAAA,EAAiD;AACzF,EAAA,MAAM,cAAA,GACJ,WAAW,OAAA,IAAW,OAAA,CAAQ,UAAU,MAAA,GACpC,MAAM,OAAA,CAAQ,KAAA,GACd,MAAM;AACJ,IAAA,MAAM,EAAE,MAAA,EAAQ,aAAA,EAAe,UAAA,KAAe,OAAA,CAAQ,MAAA;AACtD,IAAA,OAAO,eAAA,CAAgB,MAAA,EAAQ,aAAA,EAAe,UAAU,CAAA;AAAA,EAC1D,CAAA;AAEN,EAAA,OAAO,oBAAA,CAAqB;AAAA,IAC1B,cAAA;AAAA,IACA,SAAA,EAAW;AAAA,GACZ,CAAA;AACH;;;ACnGO,SAAS,0BAA0B,OAAA,EAAiD;AACzF,EAAA,MAAM,EAAE,QAAA,EAAU,cAAA,EAAe,GAAI,OAAA;AAErC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IAEN,MAAM,UAAU,GAAA,EAA8C;AAE5D,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,GAAA,CAAI,KAAK,kBAAkB,CAAA;AAC/C,MAAA,MAAM,WAAA,GAAc,eAAe,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAI,QAAA,CAAS,QAAA,CAAS,IAAI,CAAC,CAAA;AAE3E,MAAA,IAAI,CAAC,WAAA,EAAa;AAChB,QAAA,OAAO,GAAA;AAAA,MACT;AAEA,MAAA,MAAM,KAAA,GAAQ,MAAM,QAAA,EAAS;AAC7B,MAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,GAAA,CAAI,KAAK,OAAO,CAAA;AAC5C,MAAA,OAAA,CAAQ,GAAA,CAAI,qBAAqB,KAAK,CAAA;AACtC,MAAA,GAAA,CAAI,KAAK,OAAA,GAAU,OAAA;AAEnB,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,GACF;AACF","file":"chunk-K2ELAI2X.js","sourcesContent":["import { SignJWT } from 'jose';\nimport type { Middleware } from '../core/middleware.js';\nimport { createAuthMiddleware } from './auth.js';\n\n/**\n * Impersonation payload embedded in an admin token.\n *\n * Mirrors the Go `ActOnBehalfOf` struct\n * (`internal/go-chi-middleware-auth/types.go`), which the Go verifier reads\n * from the `act_on_behalf_of` JWT claim.\n */\nexport interface AdminActOnBehalfOf {\n /** The subject (user) the admin token acts on behalf of. Required. */\n userId: string;\n /** Optionally scopes the impersonation to a tenant. */\n onTenantId?: string;\n}\n\n/** Error thrown when an admin token is minted without a signing secret. */\nexport class AdminEmptySecretError extends Error {\n constructor() {\n super('auth: admin JWT signing secret must not be empty');\n this.name = 'AdminEmptySecretError';\n }\n}\n\n/** Error thrown when an admin token is minted without a subject user id. */\nexport class AdminEmptyUserIDError extends Error {\n constructor() {\n super('auth: act_on_behalf_of.user_id must not be empty');\n this.name = 'AdminEmptyUserIDError';\n }\n}\n\n/**\n * Mints an HS256-signed Admin JWT that impersonates `actOnBehalfOf`.\n *\n * The minted token interoperates byte-for-byte with the Go verifier in\n * `internal/go-chi-middleware-auth/auth.go` (`parseAdmin`):\n * - Protected header `{ alg: 'HS256' }`.\n * - Required `exp` claim (`now + ttlSeconds`), which the Go side enforces via\n * `jwt.WithExpirationRequired()`.\n * - An `iat` claim of `now`.\n * - An `act_on_behalf_of` object claim with `user_id` (required) and an\n * optional `on_tenant_id`, matching the Go `ActOnBehalfOf` JSON tags.\n *\n * SECURITY: Minting an admin token requires the shared `ADMIN_JWT_SECRET`.\n * This secret grants the ability to impersonate ANY user, so it MUST ONLY be\n * used server-side or by trusted backend services. NEVER ship the secret to a\n * browser or any untrusted client, and never embed it in frontend bundles.\n *\n * @param secret - The shared HS256 signing secret (`ADMIN_JWT_SECRET`).\n * @param actOnBehalfOf - The principal to impersonate.\n * @param ttlSeconds - Token lifetime in seconds (added to the current time).\n * @returns The signed compact JWT string.\n * @throws {AdminEmptySecretError} when `secret` is empty.\n * @throws {AdminEmptyUserIDError} when `actOnBehalfOf.userId` is empty.\n */\nexport async function issueAdminToken(\n secret: string,\n actOnBehalfOf: AdminActOnBehalfOf,\n ttlSeconds: number\n): Promise<string> {\n if (!secret) {\n throw new AdminEmptySecretError();\n }\n if (!actOnBehalfOf.userId) {\n throw new AdminEmptyUserIDError();\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n const key = new TextEncoder().encode(secret);\n\n const actClaim: { user_id: string; on_tenant_id?: string } = {\n user_id: actOnBehalfOf.userId,\n ...(actOnBehalfOf.onTenantId ? { on_tenant_id: actOnBehalfOf.onTenantId } : {}),\n };\n\n return new SignJWT({ act_on_behalf_of: actClaim })\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt(nowSeconds)\n .setExpirationTime(nowSeconds + ttlSeconds)\n .sign(key);\n}\n\n/**\n * Options for the admin auth middleware.\n *\n * Exactly one of `token` (pre-minted) or `config` (mint-per-request) must be\n * provided.\n */\nexport type AdminAuthMiddlewareOptions =\n | {\n /** A pre-minted admin JWT to attach as `Authorization: Admin <token>`. */\n token: string;\n config?: never;\n }\n | {\n token?: never;\n /**\n * Per-request minting config (analogous to Go's `AdminRoundTripper`):\n * a fresh admin token is minted for every request.\n */\n config: {\n /** The shared HS256 signing secret (`ADMIN_JWT_SECRET`). */\n secret: string;\n /** The principal to impersonate. */\n actOnBehalfOf: AdminActOnBehalfOf;\n /** Token lifetime in seconds. */\n ttlSeconds: number;\n };\n };\n\n/**\n * Creates an admin authentication middleware that stamps an\n * `Authorization: Admin <token>` header on outgoing requests.\n *\n * Reuses {@link createAuthMiddleware} with `tokenType: 'Admin'`. Two ergonomics\n * are supported:\n * - `{ token }`: attach a pre-minted admin token.\n * - `{ config }`: mint a fresh admin token per request (like Go's\n * `AdminRoundTripper`).\n *\n * SECURITY: When using `config`, the `ADMIN_JWT_SECRET` is held in memory and\n * used to mint tokens. Only construct this middleware server-side / in trusted\n * services. NEVER ship the secret to a browser.\n *\n * @param options - Either a pre-minted `token` or a minting `config`.\n * @returns A middleware instance.\n */\nexport function createAdminAuthMiddleware(options: AdminAuthMiddlewareOptions): Middleware {\n const getAccessToken =\n 'token' in options && options.token !== undefined\n ? () => options.token\n : () => {\n const { secret, actOnBehalfOf, ttlSeconds } = options.config;\n return issueAdminToken(secret, actOnBehalfOf, ttlSeconds);\n };\n\n return createAuthMiddleware({\n getAccessToken,\n tokenType: 'Admin',\n });\n}\n","import type { Middleware } from '../core/middleware.js';\nimport type { RequestContext } from '../core/types.js';\n\n/**\n * Options for the Turnstile middleware.\n */\nexport interface TurnstileMiddlewareOptions {\n /**\n * Async function that mints a fresh Turnstile token.\n * Called once per protected request. Must return a valid token string.\n * Should throw if token cannot be obtained.\n */\n getToken: () => Promise<string>;\n\n /**\n * URL path suffixes that require a Turnstile token.\n * Matched against the end of the request URL path.\n * Example: ['/sign-up-email-password', '/sign-in-email-password']\n */\n protectedPaths: string[];\n}\n\n/**\n * Creates a Cloudflare Turnstile middleware that attaches `X-Turnstile-Token`\n * headers to requests matching the configured protected paths.\n *\n * The middleware only calls `getToken()` for requests whose URL path ends\n * with one of the `protectedPaths` entries — all other requests pass through\n * untouched with zero overhead.\n *\n * @param options - Configuration options for the turnstile middleware\n * @returns A middleware instance\n *\n * @example\n * ```typescript\n * const turnstileMiddleware = createTurnstileMiddleware({\n * getToken: async () => {\n * // Render Turnstile widget and return token\n * return await turnstile.execute(siteKey);\n * },\n * protectedPaths: ['/sign-up-email-password', '/sign-in-email-password'],\n * });\n * ```\n */\nexport function createTurnstileMiddleware(options: TurnstileMiddlewareOptions): Middleware {\n const { getToken, protectedPaths } = options;\n\n return {\n name: 'turnstile',\n\n async onRequest(ctx: RequestContext): Promise<RequestContext> {\n // Check if this request URL matches any protected path\n const url = new URL(ctx.url, 'http://localhost');\n const isProtected = protectedPaths.some(path => url.pathname.endsWith(path));\n\n if (!isProtected) {\n return ctx;\n }\n\n const token = await getToken();\n const headers = new Headers(ctx.init.headers);\n headers.set('X-Turnstile-Token', token);\n ctx.init.headers = headers;\n\n return ctx;\n },\n };\n}\n"]}