@pymthouse/builder-sdk 0.4.4 → 0.4.6

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 (44) hide show
  1. package/README.md +120 -5
  2. package/dist/{client-C0HgAugK.d.cts → client-CEBVgCD7.d.cts} +28 -4
  3. package/dist/{client-zCskUJag.d.ts → client-D-p6v8ju.d.ts} +28 -4
  4. package/dist/device.d.cts +1 -1
  5. package/dist/device.d.ts +1 -1
  6. package/dist/env.cjs +64 -9
  7. package/dist/env.cjs.map +1 -1
  8. package/dist/env.d.cts +2 -2
  9. package/dist/env.d.ts +2 -2
  10. package/dist/env.js +64 -9
  11. package/dist/env.js.map +1 -1
  12. package/dist/{index-CAIAYJv7.d.cts → index-M0tsyotJ.d.cts} +1 -1
  13. package/dist/{index-BL1wpOki.d.ts → index-rC8smShg.d.ts} +1 -1
  14. package/dist/index.cjs +64 -9
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +4 -4
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +64 -9
  19. package/dist/index.js.map +1 -1
  20. package/dist/{proxy-KrA1vEmh.d.ts → proxy-CZLY0IfL.d.cts} +5 -2
  21. package/dist/{proxy-0wa8QZIU.d.cts → proxy-D36SpZ6k.d.ts} +5 -2
  22. package/dist/signer/gateway.cjs +542 -0
  23. package/dist/signer/gateway.cjs.map +1 -0
  24. package/dist/signer/gateway.d.cts +81 -0
  25. package/dist/signer/gateway.d.ts +81 -0
  26. package/dist/signer/gateway.js +538 -0
  27. package/dist/signer/gateway.js.map +1 -0
  28. package/dist/signer/server.cjs +225 -0
  29. package/dist/signer/server.cjs.map +1 -1
  30. package/dist/signer/server.d.cts +35 -4
  31. package/dist/signer/server.d.ts +35 -4
  32. package/dist/signer/server.js +219 -1
  33. package/dist/signer/server.js.map +1 -1
  34. package/dist/signer/webhook/adapters/oidc.d.cts +2 -2
  35. package/dist/signer/webhook/adapters/oidc.d.ts +2 -2
  36. package/dist/signer/webhook.d.cts +3 -3
  37. package/dist/signer/webhook.d.ts +3 -3
  38. package/dist/tokens.d.cts +1 -1
  39. package/dist/tokens.d.ts +1 -1
  40. package/dist/{types-BORaHW_x.d.cts → types-CcP67AZm.d.cts} +2 -0
  41. package/dist/{types-BORaHW_x.d.ts → types-CcP67AZm.d.ts} +2 -0
  42. package/dist/verify.d.cts +1 -1
  43. package/dist/verify.d.ts +1 -1
  44. package/package.json +6 -1
@@ -1,4 +1,4 @@
1
- import { F as FetchLike } from './types-BORaHW_x.js';
1
+ import { F as FetchLike } from './types-CcP67AZm.cjs';
2
2
 
3
3
  type SignerDmzGate = "http" | "cli";
4
4
  interface M2MClientCredentials {
@@ -26,7 +26,8 @@ interface DirectSignerProxyConfig {
26
26
  resolveM2MCredentials?: (publicClientId: string) => M2MClientCredentials | Promise<M2MClientCredentials>;
27
27
  /**
28
28
  * When set, incoming request paths matching this prefix are rewritten to the remote signer base.
29
- * Example: `/api/signer/proxy` → remote `/generate-live-payment` when the suffix is empty.
29
+ * Example: `/api/my-bff/signer` → remote `/generate-live-payment` when the suffix is empty.
30
+ * This is for integrator-hosted BFF routes, not dashboard `/api/signer/*` proxy paths.
30
31
  */
31
32
  proxyPathPrefix?: string;
32
33
  /** Remote path used when the proxied suffix is empty. Defaults to `/generate-live-payment`. */
@@ -139,6 +140,7 @@ interface DeviceExchangeResponse {
139
140
  };
140
141
  }
141
142
  interface ExchangeDeviceTokenForSignerOptions {
143
+ /** Platform/dashboard origin for JWT exchange only (`POST .../api/signer/device/exchange`). */
142
144
  facadeUrl: string;
143
145
  deviceToken: string;
144
146
  scope?: string;
@@ -194,6 +196,7 @@ interface ApiKeyExchangeHandlerConfig {
194
196
  allowInsecureHttp?: boolean;
195
197
  }
196
198
  interface ExchangeApiKeyForSignerOptions {
199
+ /** Platform/dashboard origin for JWT exchange only (`POST .../api/pymthouse/keys/exchange`). */
197
200
  facadeUrl: string;
198
201
  apiKey: string;
199
202
  scope?: string;
@@ -1,4 +1,4 @@
1
- import { F as FetchLike } from './types-BORaHW_x.cjs';
1
+ import { F as FetchLike } from './types-CcP67AZm.js';
2
2
 
3
3
  type SignerDmzGate = "http" | "cli";
4
4
  interface M2MClientCredentials {
@@ -26,7 +26,8 @@ interface DirectSignerProxyConfig {
26
26
  resolveM2MCredentials?: (publicClientId: string) => M2MClientCredentials | Promise<M2MClientCredentials>;
27
27
  /**
28
28
  * When set, incoming request paths matching this prefix are rewritten to the remote signer base.
29
- * Example: `/api/signer/proxy` → remote `/generate-live-payment` when the suffix is empty.
29
+ * Example: `/api/my-bff/signer` → remote `/generate-live-payment` when the suffix is empty.
30
+ * This is for integrator-hosted BFF routes, not dashboard `/api/signer/*` proxy paths.
30
31
  */
31
32
  proxyPathPrefix?: string;
32
33
  /** Remote path used when the proxied suffix is empty. Defaults to `/generate-live-payment`. */
@@ -139,6 +140,7 @@ interface DeviceExchangeResponse {
139
140
  };
140
141
  }
141
142
  interface ExchangeDeviceTokenForSignerOptions {
143
+ /** Platform/dashboard origin for JWT exchange only (`POST .../api/signer/device/exchange`). */
142
144
  facadeUrl: string;
143
145
  deviceToken: string;
144
146
  scope?: string;
@@ -194,6 +196,7 @@ interface ApiKeyExchangeHandlerConfig {
194
196
  allowInsecureHttp?: boolean;
195
197
  }
196
198
  interface ExchangeApiKeyForSignerOptions {
199
+ /** Platform/dashboard origin for JWT exchange only (`POST .../api/pymthouse/keys/exchange`). */
197
200
  facadeUrl: string;
198
201
  apiKey: string;
199
202
  scope?: string;
@@ -0,0 +1,542 @@
1
+ 'use strict';
2
+
3
+ var oauth4webapi = require('oauth4webapi');
4
+
5
+ // src/errors.ts
6
+ var PmtHouseError = class extends Error {
7
+ status;
8
+ code;
9
+ details;
10
+ constructor(message, {
11
+ status = 500,
12
+ code = "pymthouse_error",
13
+ details
14
+ } = {}) {
15
+ super(message);
16
+ this.name = "PmtHouseError";
17
+ this.status = status;
18
+ this.code = code;
19
+ this.details = details;
20
+ }
21
+ };
22
+
23
+ // src/string-utils.ts
24
+ function stripTrailingSlashes(value) {
25
+ let end = value.length;
26
+ while (end > 0 && (value.codePointAt(end - 1) ?? 0) === 47) {
27
+ end--;
28
+ }
29
+ return value.slice(0, end);
30
+ }
31
+ function endsWithIgnoreCase(value, suffix) {
32
+ if (suffix.length > value.length) {
33
+ return false;
34
+ }
35
+ const start = value.length - suffix.length;
36
+ for (let i = 0; i < suffix.length; i++) {
37
+ const a = value.codePointAt(start + i) ?? 0;
38
+ const b = suffix.codePointAt(i) ?? 0;
39
+ if (a !== b && (a | 32) !== (b | 32)) {
40
+ return false;
41
+ }
42
+ }
43
+ return true;
44
+ }
45
+ function stripSuffixIgnoreCase(value, suffix) {
46
+ return endsWithIgnoreCase(value, suffix) ? value.slice(0, value.length - suffix.length) : value;
47
+ }
48
+ function stripIssuerOriginFromOidcUrl(issuerUrl) {
49
+ let base = stripTrailingSlashes(issuerUrl.trim());
50
+ base = stripSuffixIgnoreCase(base, "/api/v1/oidc");
51
+ base = stripSuffixIgnoreCase(base, "/oidc");
52
+ return stripTrailingSlashes(base);
53
+ }
54
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
55
+ var discoveryCache = /* @__PURE__ */ new Map();
56
+ function normalizedIssuerKey(issuerUrl) {
57
+ return stripTrailingSlashes(issuerUrl);
58
+ }
59
+ async function loadAuthorizationServer(issuerUrl, fetchImpl, options = {}) {
60
+ const key = normalizedIssuerKey(issuerUrl);
61
+ const now = Date.now();
62
+ const cached = discoveryCache.get(key);
63
+ if (!options.force && cached && now - cached.fetchedAt < CACHE_TTL_MS) {
64
+ return cached.as;
65
+ }
66
+ const issuerIdentifier = new URL(key);
67
+ const discoveryOpts = {
68
+ algorithm: "oidc",
69
+ [oauth4webapi.customFetch]: fetchImpl
70
+ };
71
+ if (options.allowInsecureHttp) {
72
+ discoveryOpts[oauth4webapi.allowInsecureRequests] = true;
73
+ }
74
+ let response;
75
+ try {
76
+ response = await oauth4webapi.discoveryRequest(issuerIdentifier, discoveryOpts);
77
+ } catch (e) {
78
+ throw mapDiscoveryNetworkError(e);
79
+ }
80
+ let as;
81
+ try {
82
+ as = await oauth4webapi.processDiscoveryResponse(issuerIdentifier, response);
83
+ } catch (e) {
84
+ throw mapOAuthDiscoveryError(e);
85
+ }
86
+ discoveryCache.set(key, { as, fetchedAt: now });
87
+ return as;
88
+ }
89
+ function mapOAuthDiscoveryError(error) {
90
+ if (error instanceof PmtHouseError) {
91
+ return error;
92
+ }
93
+ if (error instanceof Error) {
94
+ return new PmtHouseError(error.message, {
95
+ status: 500,
96
+ code: "oidc_discovery_invalid",
97
+ details: { cause: error.cause }
98
+ });
99
+ }
100
+ return new PmtHouseError("OIDC discovery failed", {
101
+ status: 500,
102
+ code: "oidc_discovery_invalid"
103
+ });
104
+ }
105
+ function mapDiscoveryNetworkError(error) {
106
+ if (error instanceof PmtHouseError) {
107
+ return error;
108
+ }
109
+ if (error instanceof Error) {
110
+ return new PmtHouseError(`Failed to load OIDC discovery: ${error.message}`, {
111
+ status: 502,
112
+ code: "oidc_discovery_failed"
113
+ });
114
+ }
115
+ return new PmtHouseError("Failed to load OIDC discovery", {
116
+ status: 502,
117
+ code: "oidc_discovery_failed"
118
+ });
119
+ }
120
+
121
+ // src/encoding.ts
122
+ function encodeClientSecretBasic(clientId, clientSecret) {
123
+ const raw = `${clientId}:${clientSecret}`;
124
+ const b64 = typeof Buffer !== "undefined" ? Buffer.from(raw, "utf8").toString("base64") : btoa(Array.from(new TextEncoder().encode(raw), (c) => String.fromCharCode(c)).join(""));
125
+ return `Basic ${b64}`;
126
+ }
127
+
128
+ // src/signer/fetch-json.ts
129
+ function oauthFailureDescription(parsed, failureLabel, status) {
130
+ if (typeof parsed.error_description === "string") {
131
+ return parsed.error_description;
132
+ }
133
+ if (typeof parsed.error === "string") {
134
+ return parsed.error;
135
+ }
136
+ return `${failureLabel} (${status})`;
137
+ }
138
+ async function readJsonObjectFromResponse(response, options) {
139
+ const text = await response.text();
140
+ let parsed;
141
+ try {
142
+ parsed = text ? JSON.parse(text) : {};
143
+ } catch {
144
+ throw new PmtHouseError(options.invalidJsonMessage, {
145
+ status: 502,
146
+ code: options.invalidJsonCode,
147
+ details: { status: response.status }
148
+ });
149
+ }
150
+ if (!response.ok) {
151
+ const description = oauthFailureDescription(parsed, options.failureLabel, response.status);
152
+ throw new PmtHouseError(description, {
153
+ status: response.status,
154
+ code: typeof parsed.error === "string" ? parsed.error : options.defaultErrorCode,
155
+ details: parsed
156
+ });
157
+ }
158
+ return parsed;
159
+ }
160
+
161
+ // src/signer/json-fields.ts
162
+ function readStringField(body, key, errorCode, messagePrefix = "Response") {
163
+ const value = body[key];
164
+ if (typeof value !== "string" || !value.trim()) {
165
+ throw new PmtHouseError(`${messagePrefix} missing ${key}`, {
166
+ status: 502,
167
+ code: errorCode
168
+ });
169
+ }
170
+ return value.trim();
171
+ }
172
+ function readExpiresIn(body, errorCode) {
173
+ const expiresIn = body.expires_in;
174
+ if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
175
+ throw new PmtHouseError("Response missing expires_in", {
176
+ status: 502,
177
+ code: errorCode
178
+ });
179
+ }
180
+ return Math.floor(expiresIn);
181
+ }
182
+
183
+ // src/signer/mint-token.ts
184
+ var SIGN_MINT_USER_TOKEN_SCOPE = "sign:mint_user_token";
185
+ var DEFAULT_TTL_REFRESH_RATIO = 0.8;
186
+ var TOKEN_RESPONSE_ERROR = "invalid_token_response";
187
+ function signerJwtAudience(issuerUrl) {
188
+ return stripTrailingSlashes(issuerUrl);
189
+ }
190
+ function parseMintUserSignerTokenResponse(body, ttlRefreshRatio = DEFAULT_TTL_REFRESH_RATIO) {
191
+ const accessToken = readStringField(body, "access_token", TOKEN_RESPONSE_ERROR, "Token response");
192
+ const expiresIn = readExpiresIn(body, TOKEN_RESPONSE_ERROR);
193
+ const balanceUsdMicros = readStringField(
194
+ body,
195
+ "balanceUsdMicros",
196
+ TOKEN_RESPONSE_ERROR,
197
+ "Token response"
198
+ );
199
+ const lifetimeGrantedUsdMicros = readStringField(
200
+ body,
201
+ "lifetimeGrantedUsdMicros",
202
+ TOKEN_RESPONSE_ERROR,
203
+ "Token response"
204
+ );
205
+ const now = Date.now();
206
+ const expiresAt = now + expiresIn * 1e3;
207
+ const refreshAt = now + Math.floor(expiresIn * 1e3 * ttlRefreshRatio);
208
+ return {
209
+ jwt: accessToken,
210
+ expiresAt,
211
+ refreshAt,
212
+ balanceUsdMicros,
213
+ lifetimeGrantedUsdMicros
214
+ };
215
+ }
216
+ async function mintUserSignerToken(options) {
217
+ const fetchImpl = options.fetch ?? fetch;
218
+ const issuerUrl = stripTrailingSlashes(options.issuerUrl);
219
+ const as = await loadAuthorizationServer(issuerUrl, fetchImpl, {
220
+ allowInsecureHttp: options.allowInsecureHttp
221
+ });
222
+ const tokenEndpoint = as.token_endpoint;
223
+ if (!tokenEndpoint) {
224
+ throw new PmtHouseError("OIDC discovery document is missing token_endpoint", {
225
+ status: 500,
226
+ code: "oidc_discovery_invalid"
227
+ });
228
+ }
229
+ const audience = signerJwtAudience(issuerUrl);
230
+ const body = new URLSearchParams({
231
+ grant_type: "client_credentials",
232
+ scope: SIGN_MINT_USER_TOKEN_SCOPE,
233
+ external_user_id: options.externalUserId,
234
+ audience
235
+ });
236
+ const response = await fetchImpl(tokenEndpoint, {
237
+ method: "POST",
238
+ headers: {
239
+ Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
240
+ "Content-Type": "application/x-www-form-urlencoded",
241
+ Accept: "application/json"
242
+ },
243
+ body: body.toString(),
244
+ cache: "no-store"
245
+ });
246
+ const parsed = await readJsonObjectFromResponse(response, {
247
+ invalidJsonMessage: "Token endpoint returned invalid JSON",
248
+ invalidJsonCode: TOKEN_RESPONSE_ERROR,
249
+ failureLabel: "Token mint failed",
250
+ defaultErrorCode: "token_mint_failed"
251
+ });
252
+ return parseMintUserSignerTokenResponse(parsed);
253
+ }
254
+
255
+ // src/signer/device-exchange.ts
256
+ var TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
257
+ var SUBJECT_ACCESS_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
258
+ var EXCHANGE_RESPONSE_ERROR = "invalid_exchange_response";
259
+ async function mintSignerTokenFromDeviceToken(options) {
260
+ const fetchImpl = options.fetch ?? fetch;
261
+ const issuerUrl = stripTrailingSlashes(options.issuerUrl);
262
+ const as = await loadAuthorizationServer(issuerUrl, fetchImpl, {
263
+ allowInsecureHttp: options.allowInsecureHttp
264
+ });
265
+ const tokenEndpoint = as.token_endpoint;
266
+ if (!tokenEndpoint) {
267
+ throw new PmtHouseError("OIDC discovery document is missing token_endpoint", {
268
+ status: 500,
269
+ code: "oidc_discovery_invalid"
270
+ });
271
+ }
272
+ const audience = options.audience?.trim() || issuerUrl;
273
+ const params = new URLSearchParams({
274
+ grant_type: TOKEN_EXCHANGE_GRANT,
275
+ subject_token: options.deviceToken,
276
+ subject_token_type: SUBJECT_ACCESS_TOKEN_TYPE,
277
+ audience,
278
+ resource: audience
279
+ });
280
+ if (options.scope?.trim()) {
281
+ params.set("scope", options.scope.trim());
282
+ }
283
+ const response = await fetchImpl(tokenEndpoint, {
284
+ method: "POST",
285
+ headers: {
286
+ Authorization: encodeClientSecretBasic(options.m2mClientId, options.m2mClientSecret),
287
+ "Content-Type": "application/x-www-form-urlencoded",
288
+ Accept: "application/json"
289
+ },
290
+ body: params.toString(),
291
+ cache: "no-store"
292
+ });
293
+ const parsed = await readJsonObjectFromResponse(response, {
294
+ invalidJsonMessage: "Token endpoint returned invalid JSON",
295
+ invalidJsonCode: "invalid_token_response",
296
+ failureLabel: "Signer JWT exchange failed",
297
+ defaultErrorCode: "token_exchange_failed"
298
+ });
299
+ const cached = parseMintUserSignerTokenResponse(parsed);
300
+ return {
301
+ access_token: cached.jwt,
302
+ expires_in: readExpiresIn(parsed, EXCHANGE_RESPONSE_ERROR),
303
+ scope: readStringField(parsed, "scope", EXCHANGE_RESPONSE_ERROR),
304
+ balanceUsdMicros: cached.balanceUsdMicros,
305
+ lifetimeGrantedUsdMicros: cached.lifetimeGrantedUsdMicros
306
+ };
307
+ }
308
+
309
+ // src/signer/api-key-exchange.ts
310
+ var EXCHANGE_RESPONSE_ERROR2 = "invalid_exchange_response";
311
+ async function mintUserAccessTokenFromApiKey(input) {
312
+ const fetchImpl = input.fetch ?? fetch;
313
+ const issuerOrigin = stripIssuerOriginFromOidcUrl(input.issuerUrl);
314
+ const url = `${issuerOrigin}/api/v1/apps/${encodeURIComponent(input.publicClientId)}/auth/api-key/token`;
315
+ const response = await fetchImpl(url, {
316
+ method: "POST",
317
+ headers: {
318
+ Authorization: `Bearer ${input.apiKey}`,
319
+ "Content-Type": "application/json",
320
+ Accept: "application/json"
321
+ },
322
+ body: JSON.stringify(input.scope ? { scope: input.scope } : {}),
323
+ cache: "no-store"
324
+ });
325
+ const parsed = await readJsonObjectFromResponse(response, {
326
+ invalidJsonMessage: "API key token exchange returned invalid JSON",
327
+ invalidJsonCode: "invalid_token_response",
328
+ failureLabel: "API key token exchange failed",
329
+ defaultErrorCode: "api_key_token_exchange_failed"
330
+ });
331
+ const accessToken = parsed.access_token;
332
+ if (typeof accessToken !== "string" || !accessToken.trim()) {
333
+ throw new PmtHouseError("API key token exchange missing access_token", {
334
+ status: 502,
335
+ code: EXCHANGE_RESPONSE_ERROR2
336
+ });
337
+ }
338
+ const expiresIn = typeof parsed.expires_in === "number" && Number.isFinite(parsed.expires_in) ? parsed.expires_in : 900;
339
+ const scope = typeof parsed.scope === "string" && parsed.scope.trim() ? parsed.scope.trim() : input.scope?.trim() || "sign:job";
340
+ return {
341
+ access_token: accessToken.trim(),
342
+ expires_in: expiresIn,
343
+ scope
344
+ };
345
+ }
346
+ async function mintSignerSessionFromApiKey(input) {
347
+ const userToken = await mintUserAccessTokenFromApiKey({
348
+ issuerUrl: input.issuerUrl,
349
+ publicClientId: input.publicClientId,
350
+ apiKey: input.apiKey,
351
+ scope: input.scope,
352
+ fetch: input.fetch
353
+ });
354
+ return mintSignerTokenFromDeviceToken({
355
+ issuerUrl: input.issuerUrl,
356
+ m2mClientId: input.m2mClientId,
357
+ m2mClientSecret: input.m2mClientSecret,
358
+ deviceToken: userToken.access_token,
359
+ scope: userToken.scope,
360
+ audience: input.audience,
361
+ fetch: input.fetch,
362
+ allowInsecureHttp: input.allowInsecureHttp
363
+ });
364
+ }
365
+
366
+ // src/signer/gateway-token.ts
367
+ function requireString(value, label) {
368
+ if (typeof value !== "string") {
369
+ throw new PmtHouseError(`${label} must be a string`, {
370
+ status: 400,
371
+ code: "invalid_gateway_token"
372
+ });
373
+ }
374
+ return value.trim();
375
+ }
376
+ function optionalString(value, label) {
377
+ if (value === void 0 || value === null) {
378
+ return void 0;
379
+ }
380
+ return requireString(value, label) || void 0;
381
+ }
382
+ function encodeBase64Json(value) {
383
+ const json = JSON.stringify(value);
384
+ if (typeof Buffer === "undefined") {
385
+ const binary = Array.from(
386
+ new TextEncoder().encode(json),
387
+ (c) => String.fromCodePoint(c)
388
+ ).join("");
389
+ return btoa(binary);
390
+ }
391
+ return Buffer.from(json, "utf8").toString("base64");
392
+ }
393
+ function decodeBase64Json(token) {
394
+ const trimmed = requireString(token, "gateway token");
395
+ let json;
396
+ try {
397
+ if (typeof Buffer === "undefined") {
398
+ json = new TextDecoder().decode(
399
+ Uint8Array.from(atob(trimmed), (c) => c.codePointAt(0) ?? 0)
400
+ );
401
+ } else {
402
+ json = Buffer.from(trimmed, "base64").toString("utf8");
403
+ }
404
+ } catch {
405
+ throw new PmtHouseError("Invalid gateway token: expected base64-encoded JSON", {
406
+ status: 400,
407
+ code: "invalid_gateway_token"
408
+ });
409
+ }
410
+ try {
411
+ return JSON.parse(json);
412
+ } catch {
413
+ throw new PmtHouseError("Invalid gateway token: expected UTF-8 JSON payload", {
414
+ status: 400,
415
+ code: "invalid_gateway_token"
416
+ });
417
+ }
418
+ }
419
+ function normalizeStringMap(map) {
420
+ if (!map) {
421
+ return void 0;
422
+ }
423
+ const entries = Object.entries(map).filter(
424
+ ([key, value]) => key.trim() && typeof value === "string"
425
+ );
426
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
427
+ }
428
+ function buildGatewayToken(input) {
429
+ if (input === null || typeof input !== "object") {
430
+ throw new PmtHouseError("buildGatewayToken requires an input object", {
431
+ status: 400,
432
+ code: "invalid_gateway_token"
433
+ });
434
+ }
435
+ const signer = requireString(input.signer, "signer URL");
436
+ if (!signer) {
437
+ throw new PmtHouseError("buildGatewayToken requires a non-empty signer URL", {
438
+ status: 400,
439
+ code: "invalid_gateway_token"
440
+ });
441
+ }
442
+ const signerHeaders = { ...input.signerHeaders };
443
+ const bundle = { signer };
444
+ const discovery = optionalString(input.discovery, "discovery URL");
445
+ if (discovery) {
446
+ bundle.discovery = discovery;
447
+ }
448
+ const rawOrchestrators = input.orchestrators ?? [];
449
+ if (!Array.isArray(rawOrchestrators)) {
450
+ throw new PmtHouseError("orchestrators must be an array of strings", {
451
+ status: 400,
452
+ code: "invalid_gateway_token"
453
+ });
454
+ }
455
+ const orchestrators = rawOrchestrators.map((entry) => requireString(entry, "orchestrator entry")).filter((entry) => entry.length > 0);
456
+ if (orchestrators.length > 0) {
457
+ bundle.orchestrators = orchestrators;
458
+ }
459
+ if (input.auth?.kind === "signerJwt") {
460
+ const accessToken = requireString(input.auth.accessToken, "signerJwt accessToken");
461
+ if (!accessToken) {
462
+ throw new PmtHouseError("signerJwt auth requires a non-empty accessToken", {
463
+ status: 400,
464
+ code: "invalid_gateway_token"
465
+ });
466
+ }
467
+ signerHeaders.Authorization = `Bearer ${accessToken}`;
468
+ } else if (input.auth?.kind === "pmthApiKey") {
469
+ const apiKey = requireString(input.auth.apiKey, "pmthApiKey apiKey");
470
+ if (!apiKey) {
471
+ throw new PmtHouseError("pmthApiKey auth requires a non-empty apiKey", {
472
+ status: 400,
473
+ code: "invalid_gateway_token"
474
+ });
475
+ }
476
+ bundle.api_key = apiKey;
477
+ const billing = optionalString(input.auth.billing, "pmthApiKey billing URL");
478
+ if (billing) {
479
+ bundle.billing = billing;
480
+ }
481
+ }
482
+ const normalizedSignerHeaders = normalizeStringMap(signerHeaders);
483
+ if (normalizedSignerHeaders) {
484
+ bundle.signer_headers = normalizedSignerHeaders;
485
+ }
486
+ const normalizedDiscoveryHeaders = normalizeStringMap(input.discoveryHeaders);
487
+ if (normalizedDiscoveryHeaders) {
488
+ bundle.discovery_headers = normalizedDiscoveryHeaders;
489
+ }
490
+ return encodeBase64Json(bundle);
491
+ }
492
+ function decodeGatewayToken(token) {
493
+ const payload = decodeBase64Json(token);
494
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
495
+ throw new PmtHouseError("Invalid gateway token: payload must be a JSON object", {
496
+ status: 400,
497
+ code: "invalid_gateway_token"
498
+ });
499
+ }
500
+ return payload;
501
+ }
502
+ async function mintGatewayToken(options) {
503
+ let accessToken;
504
+ if (options.source === "m2m") {
505
+ const minted = await mintUserSignerToken({
506
+ issuerUrl: options.issuerUrl,
507
+ m2mClientId: options.m2mClientId,
508
+ m2mClientSecret: options.m2mClientSecret,
509
+ externalUserId: options.externalUserId,
510
+ fetch: options.fetch,
511
+ allowInsecureHttp: options.allowInsecureHttp
512
+ });
513
+ accessToken = minted.jwt;
514
+ } else {
515
+ const minted = await mintSignerSessionFromApiKey({
516
+ issuerUrl: options.issuerUrl,
517
+ publicClientId: options.publicClientId,
518
+ m2mClientId: options.m2mClientId,
519
+ m2mClientSecret: options.m2mClientSecret,
520
+ apiKey: options.apiKey,
521
+ scope: options.scope,
522
+ audience: options.audience,
523
+ fetch: options.fetch,
524
+ allowInsecureHttp: options.allowInsecureHttp
525
+ });
526
+ accessToken = minted.access_token;
527
+ }
528
+ return buildGatewayToken({
529
+ signer: options.signer,
530
+ discovery: options.discovery,
531
+ orchestrators: options.orchestrators,
532
+ signerHeaders: options.signerHeaders,
533
+ discoveryHeaders: options.discoveryHeaders,
534
+ auth: { kind: "signerJwt", accessToken }
535
+ });
536
+ }
537
+
538
+ exports.buildGatewayToken = buildGatewayToken;
539
+ exports.decodeGatewayToken = decodeGatewayToken;
540
+ exports.mintGatewayToken = mintGatewayToken;
541
+ //# sourceMappingURL=gateway.cjs.map
542
+ //# sourceMappingURL=gateway.cjs.map