@jeffusion/bungee-core 5.15.0 → 5.15.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.
@@ -168,11 +168,19 @@ function current(state) {
168
168
  return value === undefined ? empty() : validatePublication(value);
169
169
  }
170
170
  function routeKeyBindings(value) {
171
- return Object.fromEntries(value.protectedRouteIds.map((routeId) => [routeId, value.credentials.filter((key) => {
171
+ const routeIds = [...new Set([...value.protectedRouteIds, ...Object.values(value.byKey).flatMap((scope) => scope?.routes ?? [])])];
172
+ return Object.fromEntries(routeIds.map((routeId) => [routeId, value.credentials.filter((key) => {
172
173
  const scope = validatePolicy(value.byKey[key.id] ?? null);
173
174
  return scope?.routes == null || scope.routes.includes(routeId);
174
175
  }).map((key) => ({ id: key.id, name: key.name }))]));
175
176
  }
177
+ function routeAccess(value) {
178
+ return {
179
+ protectedRouteIds: value.protectedRouteIds,
180
+ routeKeyBindings: routeKeyBindings(value),
181
+ unrestrictedKeyIds: value.credentials.filter((key) => validatePolicy(value.byKey[key.id] ?? null)?.routes == null).map((key) => key.id)
182
+ };
183
+ }
176
184
  function metadata(key) {
177
185
  const { digest: _digest, ...value } = key;
178
186
  return value;
@@ -337,7 +345,7 @@ function createControl(host) {
337
345
  { path: "/routes", methods: ["GET", "PUT"], handler: "routeProtection", async invoke(ctx) {
338
346
  if (ctx.request.method === "GET") {
339
347
  const value = current(state);
340
- return json({ protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value), version: policy().version, ready: ready(), published: ready() });
348
+ return json({ ...routeAccess(value), version: policy().version, ready: ready(), published: ready() });
341
349
  }
342
350
  return write(async () => {
343
351
  const input = await ctx.request.json();
@@ -348,18 +356,14 @@ function createControl(host) {
348
356
  return json({ error: "reference_validator_unavailable" }, 503);
349
357
  if (!await host.validateRouteReferences(protectedRouteIds))
350
358
  return json({ error: "invalid_references" }, 422);
351
- const value = current(state), bindings = routeKeyBindings(value);
352
- const blockedRouteIds = value.protectedRouteIds.filter((id) => !protectedRouteIds.includes(id) && bindings[id].length > 0);
353
- if (blockedRouteIds.length)
354
- return json({ error: "route_has_api_keys", blockedRouteIds, routeKeyBindings: bindings }, 409);
355
- const next = { ...value, protectedRouteIds };
356
- return save(next, { protectedRouteIds, routeKeyBindings: routeKeyBindings(next) });
359
+ const next = { ...current(state), protectedRouteIds };
360
+ return save(next, routeAccess(next));
357
361
  });
358
362
  } },
359
363
  { path: "/route-key", methods: ["PUT"], handler: "applyRouteKey", async invoke(ctx) {
360
364
  return write(async () => {
361
365
  const input = await ctx.request.json();
362
- if (!input || Object.keys(input).some((k) => !["routeId", "keyId", "protect"].includes(k)) || typeof input.routeId !== "string" || typeof input.keyId !== "string" || typeof input.protect !== "boolean")
366
+ if (!input || Object.keys(input).some((k) => !["routeId", "keyId", "protect"].includes(k)) || typeof input.routeId !== "string" || typeof input.keyId !== "string" || input.protect !== undefined && typeof input.protect !== "boolean")
363
367
  return json({ error: "invalid_binding" }, 422);
364
368
  const next = current(state);
365
369
  if (!next.credentials.some((key) => key.id === input.keyId && key.revokedAt === null))
@@ -368,15 +372,12 @@ function createControl(host) {
368
372
  return json({ error: "reference_validator_unavailable" }, 503);
369
373
  if (!await host.validateRouteReferences([input.routeId]))
370
374
  return json({ error: "invalid_references" }, 422);
371
- if (!next.protectedRouteIds.includes(input.routeId) && !input.protect)
372
- return json({ error: "route_protection_confirmation_required" }, 409);
373
375
  const previous = validatePolicy(next.byKey[input.keyId] ?? null);
374
376
  const value = { routes: previous?.routes == null ? null : [...new Set([...previous.routes, input.routeId])], models: previous?.models ?? null };
375
377
  if (!await host.validateKeyPolicyReferences(input.keyId, value))
376
378
  return json({ error: "invalid_references" }, 422);
377
379
  next.byKey[input.keyId] = value;
378
- next.protectedRouteIds = [...new Set([...next.protectedRouteIds, input.routeId])];
379
- return save(next, { protectedRouteIds: next.protectedRouteIds, routeKeyBindings: routeKeyBindings(next) });
380
+ return save(next, routeAccess(next));
380
381
  });
381
382
  } },
382
383
  { path: "/keys/:keyId", methods: ["GET", "PUT"], handler: "keyPolicy", async invoke(ctx) {
@@ -434,4 +435,4 @@ export {
434
435
  verifyDataPrincipal
435
436
  };
436
437
 
437
- //# debugId=4AF8389FB39A168C64756E2164756E21
438
+ //# debugId=13B684B8C247D4F064756E2164756E21
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../../plugins/key-access/server/control.ts", "../../../src/plugin-logger.ts", "../../../src/hooks/impl.ts", "../../../src/plugin-extensions.ts", "../../../src/plugin-durable-state.ts", "../../../src/plugin-services/host-rpc.ts", "../../../src/plugin-services/wire-contract.ts", "../../../src/plugin-services/rpc-runtime.ts", "../../../src/plugin-services/peer-channel-protocol.ts", "../../../src/plugin-services/peer-channel-hub.ts", "../../../src/plugin-services/snapshot-store.ts", "../../../src/plugin-services.ts", "../../../src/gateway/retry-action.ts", "../../../src/token-stats-window.ts", "../../../../../plugins/key-access/server/policy.ts"],
4
4
  "sourcesContent": [
5
- "import { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport type { ControlHostContext, PluginControl } from '@jeffusion/bungee-core/plugin';\nimport type { PluginDurableState, DurableJson } from '@jeffusion/bungee-core/plugin';\nimport type { DataPrincipal } from '@jeffusion/bungee-core/plugin';\nimport { validatePolicy, validatePublication, stringIds, validPrincipal, type AccessPublication, type AccessCredential } from './policy';\n\ntype ReadState = Pick<PluginDurableState, 'get' | 'list'>;\nconst empty = (): AccessPublication => ({byKey: {}, protectedRouteIds: [], credentials: []});\nfunction current(state: ReadState): AccessPublication {\n const value = state.get('policies')?.value;\n return value === undefined ? empty() : validatePublication(value);\n}\nfunction routeKeyBindings(value: AccessPublication) {\n return Object.fromEntries(value.protectedRouteIds.map(routeId => [routeId, value.credentials.filter(key => {\n const scope = validatePolicy(value.byKey[key.id] ?? null);\n return scope?.routes == null || scope.routes.includes(routeId);\n }).map(key => ({id: key.id, name: key.name}))]));\n}\nfunction metadata(key: AccessCredential) { const {digest: _digest, ...value} = key; return value; }\nfunction json(value: unknown, status = 200) { return Response.json(value, {status, headers: {'cache-control': 'no-store'}}); }\nfunction keyId(request: Request): string {\n const id = decodeURIComponent(new URL(request.url).pathname.split('/').pop() ?? '');\n if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(id)) throw new Error('invalid_key_id');\n return id;\n}\nexport function createControl(host: ControlHostContext): PluginControl {\n if (!host.durableState) throw new Error('key-access.durable_state_required');\n const state = host.durableState;\n let running = false, publishedVersion = -1;\n let writes: Promise<unknown> = Promise.resolve();\n const policy = () => ({version: state.get('policies')?.version ?? 0, value: current(state) as unknown as DurableJson});\n const ready = () => running && !host.signal.aborted && publishedVersion === policy().version;\n async function publish() {\n if (!host.publishPolicy) throw new Error('publication_unavailable');\n const next = policy();\n await host.publishPolicy(next);\n publishedVersion = next.version;\n }\n async function save(value: AccessPublication, result: Record<string, unknown>, status = 200): Promise<Response> {\n const old = state.get('policies');\n state.execute({commandId: randomUUID(), mutations: [{key: 'policies', expectedVersion: old?.version ?? 0, value: value as unknown as DurableJson}]});\n try { await publish(); return json({...result, version: policy().version, ready: ready(), published: true}, status); }\n catch { return json({...result, error: 'key-access.publication_pending', persisted: true, version: policy().version, ready: false, active: false, published: false}, 503); }\n }\n function write(operation: () => Promise<Response>): Promise<Response> {\n const task = writes.catch(() => undefined).then(async () => {\n if (!running || host.signal.aborted) return json({error: 'key-access.inactive'}, 503);\n try { return await operation(); } catch { return json({error: 'key-access.invalid_write'}, 422); }\n });\n writes = task; return task;\n }\n return {policy, rpc: [], api: [\n {path: '/credentials', methods: ['GET', 'POST'], handler: 'credentials', async invoke(ctx) {\n if (ctx.request.method === 'GET') {\n const keys = await Promise.all(current(state).credentials.map(async key => ({...metadata(key),\n ...(host.readResourceExtensions ? {extensions: await host.readResourceExtensions(key.id)} : {})})));\n return json({keys, ready: ready(), published: ready()});\n }\n return write(async () => {\n const input = await ctx.request.json() as {name: string; expiresAt?: number | null};\n if (!input || Object.keys(input).some(k => !['name', 'expiresAt'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error: 'invalid_key_name'}, 422);\n const expiresAt = input.expiresAt ?? null, now = Date.now();\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || expiresAt <= now)) return json({error: 'invalid_expiration'}, 422);\n const next = current(state);\n if (next.credentials.some(key => key.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const token = `bng_data_${randomBytes(32).toString('base64url')}`;\n const key: AccessCredential = {id: randomUUID(), domain: 'data', name: input.name.trim(), prefix: token.slice(0, 17),\n digest: createHash('sha256').update(token).digest('hex'), createdAt: now, expiresAt, revokedAt: null, credentialVersion: 1};\n next.credentials.push(key);\n // A partially completed UI flow must never grant broader access.\n next.byKey[key.id] = {routes: [], models: null};\n try { await host.secretStore.compareAndSet('credential:' + key.id, null, token); }\n catch { return json({error: 'key_secret_unavailable'}, 503); }\n try { return await save(next, {key: metadata(key), token}, 201); }\n catch (error) {\n // A failed publication still persisted the credential; only clean up an uncommitted secret.\n if (!current(state).credentials.some(item => item.id === key.id)) {\n const stored = await host.secretStore.get('credential:' + key.id);\n if (stored) await host.secretStore.delete('credential:' + key.id, stored.version);\n }\n throw error;\n }\n });\n }},\n {path: '/credentials/:keyId', methods: ['GET', 'PUT', 'DELETE'], handler: 'credential', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') {\n const key = current(state).credentials.find(key => key.id === id);\n if (!key) return json({error: 'key_not_found'}, 404);\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (!stored) return json({error: 'key_secret_not_stored'}, 409);\n if (!current(state).credentials.some(key => key.id === id)) return json({error: 'key_not_found'}, 404);\n if (createHash('sha256').update(stored.value).digest('hex') !== key.digest) return json({error: 'key_secret_unavailable'}, 503);\n return json({token: stored.value});\n } catch { return json({error: 'key_secret_unavailable'}, 503); }\n }\n return write(async () => {\n const next = current(state), key = next.credentials.find(key => key.id === id);\n if (ctx.request.method === 'DELETE') {\n next.credentials = next.credentials.filter(key => key.id !== id);\n delete next.byKey[id];\n const result = await save(next, {deleted: true, id});\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (stored) await host.secretStore.delete('credential:' + id, stored.version);\n } catch { return json({...await result.json(),error:'key_secret_cleanup_pending', persisted:true, deleted:true, id}, 503); }\n return result;\n }\n if (!key || key.revokedAt !== null) return json({error:'key_not_found'}, 404);\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['name','expiresAt','routes','models'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error:'invalid_key_name'},422);\n if (next.credentials.some(other => other.id !== id && other.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const expiresAt = input.expiresAt ?? null;\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || (expiresAt <= Date.now() && expiresAt !== key.expiresAt))) return json({error:'invalid_expiration'},422);\n const value = validatePolicy({routes: input.routes, models: input.models});\n if (!host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateKeyPolicyReferences(id,value)) return json({error:'invalid_references'},422);\n key.name = input.name.trim(); key.expiresAt = expiresAt; next.byKey[id] = value;\n return save(next,{key: metadata(key),value});\n });\n }},\n {path: '/routes', methods: ['GET', 'PUT'], handler: 'routeProtection', async invoke(ctx) {\n if (ctx.request.method === 'GET') { const value = current(state); return json({protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value), version: policy().version, ready: ready(), published: ready()}); }\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).join() !== 'protectedRouteIds') return json({error: 'invalid_routes'}, 422);\n const protectedRouteIds = stringIds(input.protectedRouteIds);\n if (!host.validateRouteReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateRouteReferences(protectedRouteIds)) return json({error: 'invalid_references'}, 422);\n const value = current(state), bindings = routeKeyBindings(value);\n const blockedRouteIds = value.protectedRouteIds.filter(id => !protectedRouteIds.includes(id) && bindings[id]!.length > 0);\n if (blockedRouteIds.length) return json({error:'route_has_api_keys', blockedRouteIds, routeKeyBindings: bindings},409);\n const next = {...value, protectedRouteIds};\n return save(next, {protectedRouteIds, routeKeyBindings: routeKeyBindings(next)});\n });\n }},\n {path: '/route-key', methods: ['PUT'], handler: 'applyRouteKey', async invoke(ctx) {\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['routeId','keyId','protect'].includes(k))\n || typeof input.routeId !== 'string' || typeof input.keyId !== 'string' || typeof input.protect !== 'boolean') return json({error:'invalid_binding'},422);\n const next = current(state);\n if (!next.credentials.some(key => key.id === input.keyId && key.revokedAt === null)) return json({error:'key_not_found'},404);\n if (!host.validateRouteReferences || !host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateRouteReferences([input.routeId])) return json({error:'invalid_references'},422);\n if (!next.protectedRouteIds.includes(input.routeId) && !input.protect) return json({error:'route_protection_confirmation_required'},409);\n const previous = validatePolicy(next.byKey[input.keyId] ?? null);\n const value = {routes: previous?.routes == null ? null : [...new Set([...previous.routes,input.routeId])], models:previous?.models ?? null};\n if (!await host.validateKeyPolicyReferences(input.keyId,value)) return json({error:'invalid_references'},422);\n next.byKey[input.keyId] = value;\n next.protectedRouteIds = [...new Set([...next.protectedRouteIds,input.routeId])];\n return save(next,{protectedRouteIds:next.protectedRouteIds,routeKeyBindings:routeKeyBindings(next)});\n });\n }},\n {path: '/keys/:keyId', methods: ['GET', 'PUT'], handler: 'keyPolicy', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') return json({keyId: id, version: policy().version, value: current(state).byKey[id] ?? null, active: ready(), ready: ready()});\n return write(async () => {\n const next = current(state);\n if (!next.credentials.some(key => key.id === id && key.revokedAt === null)) return json({error: 'key_not_found'}, 404);\n const value = validatePolicy(await ctx.request.json());\n if (!host.validateKeyPolicyReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateKeyPolicyReferences(id, value)) return json({error: 'invalid_references'}, 422);\n next.byKey[id] = value;\n return save(next, {keyId: id, value, active: true});\n });\n }},\n ],\n async start() { if (host.signal.aborted) throw new Error('inactive'); await publish(); running = true; },\n dispose() { running = false; },\n };\n}\nexport function readResource(resource: string, keyId: string, state: ReadState) {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return {value: current(state).byKey[keyId] ?? null};\n}\nexport function readResourceCollection(resource: string, state: ReadState): readonly unknown[] {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return current(state).credentials.map(metadata);\n}\nexport function readAdmissionRequirements(state: ReadState): readonly string[] { return current(state).protectedRouteIds; }\nexport function verifyDataPrincipal(principal: DataPrincipal, state: ReadState): boolean { return validPrincipal(principal, current(state), Date.now()); }\nexport default {createControl, readResource, readResourceCollection, readAdmissionRequirements, verifyDataPrincipal};\n",
5
+ "import { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport type { ControlHostContext, PluginControl } from '@jeffusion/bungee-core/plugin';\nimport type { PluginDurableState, DurableJson } from '@jeffusion/bungee-core/plugin';\nimport type { DataPrincipal } from '@jeffusion/bungee-core/plugin';\nimport { validatePolicy, validatePublication, stringIds, validPrincipal, type AccessPublication, type AccessCredential } from './policy';\n\ntype ReadState = Pick<PluginDurableState, 'get' | 'list'>;\nconst empty = (): AccessPublication => ({byKey: {}, protectedRouteIds: [], credentials: []});\nfunction current(state: ReadState): AccessPublication {\n const value = state.get('policies')?.value;\n return value === undefined ? empty() : validatePublication(value);\n}\nfunction routeKeyBindings(value: AccessPublication) {\n const routeIds = [...new Set([...value.protectedRouteIds, ...Object.values(value.byKey).flatMap(scope => scope?.routes ?? [])])];\n return Object.fromEntries(routeIds.map(routeId => [routeId, value.credentials.filter(key => {\n const scope = validatePolicy(value.byKey[key.id] ?? null);\n return scope?.routes == null || scope.routes.includes(routeId);\n }).map(key => ({id: key.id, name: key.name}))]));\n}\nfunction routeAccess(value: AccessPublication) {\n return {protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value),\n unrestrictedKeyIds: value.credentials.filter(key => validatePolicy(value.byKey[key.id] ?? null)?.routes == null).map(key => key.id)};\n}\nfunction metadata(key: AccessCredential) { const {digest: _digest, ...value} = key; return value; }\nfunction json(value: unknown, status = 200) { return Response.json(value, {status, headers: {'cache-control': 'no-store'}}); }\nfunction keyId(request: Request): string {\n const id = decodeURIComponent(new URL(request.url).pathname.split('/').pop() ?? '');\n if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(id)) throw new Error('invalid_key_id');\n return id;\n}\nexport function createControl(host: ControlHostContext): PluginControl {\n if (!host.durableState) throw new Error('key-access.durable_state_required');\n const state = host.durableState;\n let running = false, publishedVersion = -1;\n let writes: Promise<unknown> = Promise.resolve();\n const policy = () => ({version: state.get('policies')?.version ?? 0, value: current(state) as unknown as DurableJson});\n const ready = () => running && !host.signal.aborted && publishedVersion === policy().version;\n async function publish() {\n if (!host.publishPolicy) throw new Error('publication_unavailable');\n const next = policy();\n await host.publishPolicy(next);\n publishedVersion = next.version;\n }\n async function save(value: AccessPublication, result: Record<string, unknown>, status = 200): Promise<Response> {\n const old = state.get('policies');\n state.execute({commandId: randomUUID(), mutations: [{key: 'policies', expectedVersion: old?.version ?? 0, value: value as unknown as DurableJson}]});\n try { await publish(); return json({...result, version: policy().version, ready: ready(), published: true}, status); }\n catch { return json({...result, error: 'key-access.publication_pending', persisted: true, version: policy().version, ready: false, active: false, published: false}, 503); }\n }\n function write(operation: () => Promise<Response>): Promise<Response> {\n const task = writes.catch(() => undefined).then(async () => {\n if (!running || host.signal.aborted) return json({error: 'key-access.inactive'}, 503);\n try { return await operation(); } catch { return json({error: 'key-access.invalid_write'}, 422); }\n });\n writes = task; return task;\n }\n return {policy, rpc: [], api: [\n {path: '/credentials', methods: ['GET', 'POST'], handler: 'credentials', async invoke(ctx) {\n if (ctx.request.method === 'GET') {\n const keys = await Promise.all(current(state).credentials.map(async key => ({...metadata(key),\n ...(host.readResourceExtensions ? {extensions: await host.readResourceExtensions(key.id)} : {})})));\n return json({keys, ready: ready(), published: ready()});\n }\n return write(async () => {\n const input = await ctx.request.json() as {name: string; expiresAt?: number | null};\n if (!input || Object.keys(input).some(k => !['name', 'expiresAt'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error: 'invalid_key_name'}, 422);\n const expiresAt = input.expiresAt ?? null, now = Date.now();\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || expiresAt <= now)) return json({error: 'invalid_expiration'}, 422);\n const next = current(state);\n if (next.credentials.some(key => key.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const token = `bng_data_${randomBytes(32).toString('base64url')}`;\n const key: AccessCredential = {id: randomUUID(), domain: 'data', name: input.name.trim(), prefix: token.slice(0, 17),\n digest: createHash('sha256').update(token).digest('hex'), createdAt: now, expiresAt, revokedAt: null, credentialVersion: 1};\n next.credentials.push(key);\n // A partially completed UI flow must never grant broader access.\n next.byKey[key.id] = {routes: [], models: null};\n try { await host.secretStore.compareAndSet('credential:' + key.id, null, token); }\n catch { return json({error: 'key_secret_unavailable'}, 503); }\n try { return await save(next, {key: metadata(key), token}, 201); }\n catch (error) {\n // A failed publication still persisted the credential; only clean up an uncommitted secret.\n if (!current(state).credentials.some(item => item.id === key.id)) {\n const stored = await host.secretStore.get('credential:' + key.id);\n if (stored) await host.secretStore.delete('credential:' + key.id, stored.version);\n }\n throw error;\n }\n });\n }},\n {path: '/credentials/:keyId', methods: ['GET', 'PUT', 'DELETE'], handler: 'credential', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') {\n const key = current(state).credentials.find(key => key.id === id);\n if (!key) return json({error: 'key_not_found'}, 404);\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (!stored) return json({error: 'key_secret_not_stored'}, 409);\n if (!current(state).credentials.some(key => key.id === id)) return json({error: 'key_not_found'}, 404);\n if (createHash('sha256').update(stored.value).digest('hex') !== key.digest) return json({error: 'key_secret_unavailable'}, 503);\n return json({token: stored.value});\n } catch { return json({error: 'key_secret_unavailable'}, 503); }\n }\n return write(async () => {\n const next = current(state), key = next.credentials.find(key => key.id === id);\n if (ctx.request.method === 'DELETE') {\n next.credentials = next.credentials.filter(key => key.id !== id);\n delete next.byKey[id];\n const result = await save(next, {deleted: true, id});\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (stored) await host.secretStore.delete('credential:' + id, stored.version);\n } catch { return json({...await result.json(),error:'key_secret_cleanup_pending', persisted:true, deleted:true, id}, 503); }\n return result;\n }\n if (!key || key.revokedAt !== null) return json({error:'key_not_found'}, 404);\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['name','expiresAt','routes','models'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error:'invalid_key_name'},422);\n if (next.credentials.some(other => other.id !== id && other.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const expiresAt = input.expiresAt ?? null;\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || (expiresAt <= Date.now() && expiresAt !== key.expiresAt))) return json({error:'invalid_expiration'},422);\n const value = validatePolicy({routes: input.routes, models: input.models});\n if (!host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateKeyPolicyReferences(id,value)) return json({error:'invalid_references'},422);\n key.name = input.name.trim(); key.expiresAt = expiresAt; next.byKey[id] = value;\n return save(next,{key: metadata(key),value});\n });\n }},\n {path: '/routes', methods: ['GET', 'PUT'], handler: 'routeProtection', async invoke(ctx) {\n if (ctx.request.method === 'GET') { const value = current(state); return json({...routeAccess(value), version: policy().version, ready: ready(), published: ready()}); }\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).join() !== 'protectedRouteIds') return json({error: 'invalid_routes'}, 422);\n const protectedRouteIds = stringIds(input.protectedRouteIds);\n if (!host.validateRouteReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateRouteReferences(protectedRouteIds)) return json({error: 'invalid_references'}, 422);\n const next = {...current(state), protectedRouteIds};\n return save(next, routeAccess(next));\n });\n }},\n {path: '/route-key', methods: ['PUT'], handler: 'applyRouteKey', async invoke(ctx) {\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['routeId','keyId','protect'].includes(k))\n || typeof input.routeId !== 'string' || typeof input.keyId !== 'string'\n || (input.protect !== undefined && typeof input.protect !== 'boolean')) return json({error:'invalid_binding'},422);\n const next = current(state);\n if (!next.credentials.some(key => key.id === input.keyId && key.revokedAt === null)) return json({error:'key_not_found'},404);\n if (!host.validateRouteReferences || !host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateRouteReferences([input.routeId])) return json({error:'invalid_references'},422);\n const previous = validatePolicy(next.byKey[input.keyId] ?? null);\n const value = {routes: previous?.routes == null ? null : [...new Set([...previous.routes,input.routeId])], models:previous?.models ?? null};\n if (!await host.validateKeyPolicyReferences(input.keyId,value)) return json({error:'invalid_references'},422);\n next.byKey[input.keyId] = value;\n // Legacy callers may send protect; Key grants never change route public access.\n return save(next,routeAccess(next));\n });\n }},\n {path: '/keys/:keyId', methods: ['GET', 'PUT'], handler: 'keyPolicy', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') return json({keyId: id, version: policy().version, value: current(state).byKey[id] ?? null, active: ready(), ready: ready()});\n return write(async () => {\n const next = current(state);\n if (!next.credentials.some(key => key.id === id && key.revokedAt === null)) return json({error: 'key_not_found'}, 404);\n const value = validatePolicy(await ctx.request.json());\n if (!host.validateKeyPolicyReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateKeyPolicyReferences(id, value)) return json({error: 'invalid_references'}, 422);\n next.byKey[id] = value;\n return save(next, {keyId: id, value, active: true});\n });\n }},\n ],\n async start() { if (host.signal.aborted) throw new Error('inactive'); await publish(); running = true; },\n dispose() { running = false; },\n };\n}\nexport function readResource(resource: string, keyId: string, state: ReadState) {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return {value: current(state).byKey[keyId] ?? null};\n}\nexport function readResourceCollection(resource: string, state: ReadState): readonly unknown[] {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return current(state).credentials.map(metadata);\n}\nexport function readAdmissionRequirements(state: ReadState): readonly string[] { return current(state).protectedRouteIds; }\nexport function verifyDataPrincipal(principal: DataPrincipal, state: ReadState): boolean { return validPrincipal(principal, current(state), Date.now()); }\nexport default {createControl, readResource, readResourceCollection, readAdmissionRequirements, verifyDataPrincipal};\n",
6
6
  "/** Shared process sink; the public SDK must not bundle file transports into plugins. */\ntype Context = Readonly<Record<string, unknown>>;\ntype LogMethod = { (context: Context, message?: string): void; (message: string): void };\nexport interface PluginLogSink {\n info: LogMethod; warn: LogMethod; error: LogMethod; debug: LogMethod; fatal: LogMethod;\n child(bindings: Context): PluginLogSink;\n}\nconst sinkKey = Symbol.for('@jeffusion/bungee/plugin-log-sink');\nexport function installPluginLogSink(sink: PluginLogSink): void {\n (globalThis as Record<symbol, unknown>)[sinkKey] = sink;\n}\nfunction createLogger(bindings: Context = {}): PluginLogSink {\n const method = (level: 'info' | 'warn' | 'error' | 'debug' | 'fatal'): LogMethod =>\n (value: Context | string, message?: string) => {\n const sink = (globalThis as Record<symbol, unknown>)[sinkKey] as PluginLogSink | undefined;\n if (sink) {\n if (typeof value === 'string') sink[level](bindings, value);\n else sink[level]({...bindings, ...value}, message);\n } else if (level !== 'debug' || process.env.LOG_LEVEL === 'debug') {\n const write = level === 'fatal' ? console.error : console[level];\n write(message ?? (typeof value === 'string' ? value : ''), typeof value === 'string' ? bindings : {...bindings, ...value});\n }\n };\n return {info: method('info'), warn: method('warn'), error: method('error'), debug: method('debug'),\n fatal: method('fatal'), child: extra => createLogger({...bindings, ...extra})};\n}\nexport const logger = createLogger();\n",
7
7
  "/**\n * Hook 实现类\n *\n * 提供 4 种 Hook 类型的实现:\n * - AsyncParallelHook: 异步并行执行\n * - AsyncSeriesHook: 异步串行执行\n * - AsyncSeriesBailHook: 异步串行可中断\n * - AsyncSeriesWaterfallHook: 异步串行瀑布\n */\n\nimport { logger } from '../plugin-logger';\n/** Host-supplied view refresh, run between every waterfall tap. */\nexport const WATERFALL_VIEW_TRANSFORM = Symbol('waterfall-view-transform');\nimport type {\n TapInfo,\n Tap,\n IAsyncParallelHook,\n IAsyncSeriesHook,\n IAsyncSeriesBailHook,\n IAsyncSeriesWaterfallHook,\n IAsyncSeriesMapHook,\n HookStats,\n} from './types';\n\n/**\n * 标准化 TapInfo\n */\nfunction normalizeTapInfo(info: TapInfo | string): TapInfo {\n if (typeof info === 'string') {\n return { name: info, stage: 0 };\n }\n return { ...info, stage: info.stage ?? 0 };\n}\n\n/**\n * 基础 Hook 类\n */\nabstract class BaseHook<T extends any[]> {\n protected taps: Tap<T, any>[] = [];\n protected name: string;\n protected callCount = 0;\n protected totalTimeMs = 0;\n\n constructor(name?: string) {\n this.name = name ?? 'anonymous';\n }\n\n /**\n * 同步注册\n */\n tap(info: TapInfo | string, fn: (...args: T) => any): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'sync', fn });\n this.sortTaps();\n }\n\n /**\n * 异步回调风格注册\n */\n tapAsync(info: TapInfo | string, fn: (...args: any[]) => void): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'async', fn });\n this.sortTaps();\n }\n\n /**\n * Promise 风格注册\n */\n tapPromise(info: TapInfo | string, fn: (...args: T) => Promise<any>): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'promise', fn });\n this.sortTaps();\n }\n\n /**\n * 检查是否有注册的回调\n */\n hasCallbacks(): boolean {\n return this.taps.length > 0;\n }\n\n /**\n * 获取统计信息\n */\n getStats(): HookStats {\n return {\n name: this.name,\n tapCount: this.taps.length,\n callCount: this.callCount,\n totalTimeMs: this.totalTimeMs,\n avgTimeMs: this.callCount > 0 ? this.totalTimeMs / this.callCount : 0,\n };\n }\n\n /**\n * 重置统计信息\n */\n resetStats(): void {\n this.callCount = 0;\n this.totalTimeMs = 0;\n }\n\n /**\n * 清空所有注册的回调\n */\n clear(): void {\n this.taps = [];\n }\n\n /**\n * 按 stage 排序(稳定排序)\n */\n private sortTaps(): void {\n this.taps.sort((a, b) => (a.info.stage ?? 0) - (b.info.stage ?? 0));\n }\n\n /**\n * 执行单个 tap\n */\n protected async executeTap<R>(\n tap: Tap<T, R>,\n args: T\n ): Promise<R | undefined> {\n try {\n if (tap.type === 'sync') {\n return tap.fn(...args);\n } else if (tap.type === 'promise') {\n return await tap.fn(...args);\n } else {\n // async (callback style)\n return await new Promise<R | undefined>((resolve, reject) => {\n const done = (err?: Error, result?: R) => {\n if (err) reject(err);\n else resolve(result);\n };\n tap.fn(...args, done);\n });\n }\n } catch (error) {\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in hook callback'\n );\n throw error;\n }\n }\n}\n\n/** Synchronous providers use the same registration, ordering and statistics as async hooks. */\nexport class SyncBailHook<T extends any[] = [], R = unknown> extends BaseHook<T> {\n override tapAsync(): never { throw new Error('synchronous provider requires tap'); }\n override tapPromise(): never { throw new Error('synchronous provider requires tap'); }\n call(...args: T): R | undefined {\n const started = performance.now();\n this.callCount++;\n try {\n for (const tap of this.taps) {\n const result = tap.fn(...args);\n if (result && typeof result.then === 'function') throw new Error('synchronous provider returned a Promise');\n if (result !== undefined) return result as R;\n }\n return undefined;\n } finally { this.totalTimeMs += performance.now() - started; }\n }\n}\n\n/**\n * 异步并行 Hook\n * 所有回调并行执行,全部完成后返回\n */\nexport class AsyncParallelHook<T extends any[] = []>\n extends BaseHook<T>\n implements IAsyncParallelHook<T>\n{\n async promise(...args: T): Promise<void> {\n if (this.taps.length === 0) return;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n // 并行执行所有回调,错误独立处理不影响其他\n await Promise.all(\n this.taps.map(async (tap) => {\n try {\n await this.executeTap(tap, args);\n } catch (error) {\n // 并行 Hook 中单个回调的错误不应影响其他回调\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in parallel hook callback (ignored)'\n );\n }\n })\n );\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行 Hook\n * 所有回调按顺序串行执行\n */\nexport class AsyncSeriesHook<T extends any[] = []>\n extends BaseHook<T>\n implements IAsyncSeriesHook<T>\n{\n async promise(...args: T): Promise<void> {\n if (this.taps.length === 0) return;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n for (const tap of this.taps) {\n await this.executeTap(tap, args);\n }\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行可中断 Hook\n * 任意回调返回非 undefined 值则停止执行并返回该值\n */\nexport class AsyncSeriesBailHook<T extends any[] = [], R = any>\n extends BaseHook<T>\n implements IAsyncSeriesBailHook<T, R>\n{\n async promise(...args: T): Promise<R | undefined> {\n if (this.taps.length === 0) return undefined;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n for (const tap of this.taps) {\n const result = await this.executeTap<R>(tap, args);\n if (result !== undefined) {\n logger.debug(\n { hookName: this.name, pluginName: tap.info.name },\n 'Hook bailed with result'\n );\n return result;\n }\n }\n return undefined;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行瀑布 Hook\n * 每个回调的返回值作为下一个回调的第一个参数\n */\nexport class AsyncSeriesWaterfallHook<T, A extends any[] = []>\n extends BaseHook<[T, ...A]>\n implements IAsyncSeriesWaterfallHook<T, A>\n{\n async promise(value: T, ...args: A): Promise<T> {\n if (this.taps.length === 0) return value;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n let current = value;\n for (const tap of this.taps) {\n const result = await this.executeTap<T>(tap, [current, ...args] as [T, ...A]);\n // 只有返回非 undefined 时才更新值\n if (result !== undefined) {\n const transform = args[0]?.[WATERFALL_VIEW_TRANSFORM];\n current = typeof transform === 'function' ? await transform(result,current) : result;\n }\n }\n return current;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行映射 Hook\n * 支持 N:M 转换:每个回调处理输入,可以返回 0 到多个输出\n *\n * 执行流程:\n * 1. 初始输入:单个 chunk\n * 2. 每个插件处理当前的所有 chunks,返回新的 chunks 数组\n * 3. 下一个插件处理上一步的所有输出\n *\n * 返回值约定:\n * - null/undefined: 不处理,原样输出当前 chunk\n * - []: 缓冲当前 chunk,不输出(N:0)\n * - [chunk]: 1:1 转换\n * - [chunk1, chunk2, ...]: 1:M 拆分\n *\n * 性能优化:\n * - 同步优先执行:sync tap 直接同步调用,跳过 Promise 开销\n * - 适用于流式响应处理等高频调用场景\n */\nexport class AsyncSeriesMapHook<T, A extends any[] = []>\n extends BaseHook<[T, ...A]>\n implements IAsyncSeriesMapHook<T, A>\n{\n /**\n * 同步执行单个 tap(跳过 Promise 开销)\n */\n private executeTapSync<R>(tap: Tap<[T, ...A], R>, args: [T, ...A]): R | undefined {\n try {\n return tap.fn(...args);\n } catch (error) {\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in hook callback'\n );\n throw error;\n }\n }\n\n async promise(value: T, ...args: A): Promise<T[]> {\n // 如果没有注册任何回调,直接返回原始值\n if (this.taps.length === 0) return [value];\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n // 初始输入为单个 chunk\n let chunks: T[] = [value];\n\n // 依次应用每个插件的转换\n for (const tap of this.taps) {\n const newChunks: T[] = [];\n\n // 对当前的每个 chunk 应用转换\n for (const chunk of chunks) {\n try {\n // 同步优先执行:sync tap 直接同步调用,跳过 Promise 开销\n const result = tap.type === 'sync'\n ? this.executeTapSync<T[] | null | undefined>(tap, [chunk, ...args] as [T, ...A])\n : await this.executeTap<T[] | null | undefined>(tap, [chunk, ...args] as [T, ...A]);\n\n if (result === null || result === undefined) {\n // 不处理,原样输出\n newChunks.push(chunk);\n } else if (Array.isArray(result)) {\n // N:M 转换\n // [] = 缓冲(不输出)\n // [item] = 1:1 转换\n // [item1, item2, ...] = 1:M 拆分\n newChunks.push(...result);\n } else {\n // 非法返回值,警告并原样输出\n logger.warn(\n { hookName: this.name, pluginName: tap.info.name, result },\n 'AsyncSeriesMapHook callback must return an array or null'\n );\n newChunks.push(chunk);\n }\n } catch (error) {\n // 单个 chunk 处理失败,记录错误并原样输出\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in AsyncSeriesMapHook callback'\n );\n if ((args[0] as { strict?: boolean } | undefined)?.strict) {\n throw error;\n }\n newChunks.push(chunk);\n }\n }\n\n chunks = newChunks;\n }\n\n return chunks;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n",
8
8
  "import type { PluginDurableState, DurableJson } from './plugin-durable-state';\n\n/** Anonymous is an explicit, signed data identity; it carries no key authority. */\nexport type DataPrincipal = { readonly domain: string; readonly keyId: string; readonly credentialVersion: number };\nexport const ANONYMOUS_PRINCIPAL: DataPrincipal = Object.freeze({domain: 'anonymous', keyId: '', credentialVersion: 0});\nexport interface AdmissionRequirement { readonly plugin: string; readonly routeIds: readonly string[] }\n/** Public contracts. Business rules belong to the implementing plugin. */\nexport interface ManagementSubject {\n readonly id: string;\n readonly provider: string;\n readonly capabilities: readonly string[];\n readonly requiresPasswordChange?: boolean;\n}\nexport interface ManagementProvider {\n authenticate(request: Request): Promise<ManagementSubject | null>;\n authorize(subject: ManagementSubject, capability: string): boolean;\n login(request: Request): Promise<Response>;\n logout(request: Request): Promise<Response>;\n bootstrap(input: unknown): Promise<void>;\n hasIdentity(): boolean;\n revokeSessions(): void;\n validateWrite?(request: Request): void;\n csrfToken?(request: Request): string | undefined;\n /** Optional browser cookie renewal after successful session verification. Must not extend the server-side absolute deadline. */\n sessionCookie?(request: Request): string | undefined;\n}\nexport interface AdmissionTarget {\n readonly requestId: string;\n readonly attemptId: string;\n readonly principal: DataPrincipal;\n readonly routeId: string;\n readonly serviceId: string | null;\n readonly upstreamId: string;\n readonly url: string;\n readonly model: string | null;\n readonly now: number;\n}\nexport interface AdmissionDenial {\n readonly error: string;\n readonly status: 401 | 403 | 429 | 503 | 422;\n readonly retryAfter?: number;\n}\nexport interface AdmissionPlan {\n /** A complete JSON state replacement, staged by Host before the common decision. */\n readonly state?: DurableJson;\n /** Immutable policy snapshot retained by the grant for subsequent attempts. */\n readonly snapshot?: DurableJson;\n readonly denial?: AdmissionDenial;\n}\nexport interface IngressPlugin {\n /** Effective policy demand; no policy means no content decoding. */\n bodyRequirements(target: AdmissionTarget, policy: DurableJson): { readonly request: 'none' | 'json-read' };\n /** Optional per-principal state. Policies are frozen; plans receive only this key's clones.\n * Expiry must mean absence is behaviorally equivalent to retaining the state. */\n keyedState?: {\n readonly capacity: number;\n policyForKey(policy: DurableJson, keyId: string): DurableJson;\n expiresAt(state: DurableJson): number;\n reconcile(state: DurableJson, policy: DurableJson, now: number): DurableJson;\n };\n authenticate?(request: Request, policy: DurableJson, now: number): DataPrincipal | null;\n resolveIdentity?(target: AdmissionTarget, policy: DurableJson): DataPrincipal;\n plan(target: AdmissionTarget, policy: DurableJson, state: DurableJson): AdmissionPlan;\n beforeAttempt?(target: AdmissionTarget, snapshot: DurableJson): AdmissionDenial | null;\n}\nexport interface IngressPluginModule { createIngress(): IngressPlugin }\nexport interface PluginStateRpcContext {\n readonly state: PluginDurableState;\n readonly requestId: string;\n readonly attemptId: string;\n readonly principal: DataPrincipal;\n}\nexport interface PluginPolicyPublication {\n readonly version: number;\n readonly value: DurableJson;\n}\n",
@@ -18,7 +18,7 @@
18
18
  "import type { TokenStatsRange } from './plugin.types';\n\nconst HOUR = 3_600_000;\nexport const TOKEN_STATS_RETENTION_MS = 31 * 24 * HOUR;\nexport const TOKEN_STATS_RANGES = ['1h', '12h', '24h', '1d', '7d', '30d', 'day', 'week', 'month'] as const;\n\n/** Recent page ranges are consecutive elapsed hours; calendar ranges use local dates. */\nexport function tokenStatsWindow(range: TokenStatsRange, asOfMs: number, timeZone = 'UTC') {\n if (range === '1h' || range === '12h' || range === '24h') {\n const duration = { '1h': HOUR, '12h': 12 * HOUR, '24h': 24 * HOUR };\n const bucketMs = { '1h': 300_000, '12h': HOUR, '24h': 2 * HOUR }[range];\n return { startMs: asOfMs - duration[range], endMs: asOfMs, bucketMs };\n }\n if (range === '1d' || range === '7d' || range === '30d') {\n const days = { '1d': 1, '7d': 7, '30d': 30 }[range];\n const bucketMs = range === '1d' ? HOUR : 24 * HOUR;\n const startMs = asOfMs - days * 24 * HOUR;\n const bucketStarts = Array.from({ length: range === '1d' ? 24 : days }, (_, i) => startMs + i * bucketMs);\n return { startMs, endMs: asOfMs, bucketMs, bucketStarts };\n }\n const format = new Intl.DateTimeFormat('en-US', {\n timeZone, year: 'numeric', month: 'numeric', day: 'numeric',\n hour: 'numeric', minute: 'numeric', second: 'numeric', hourCycle: 'h23',\n });\n const parts = (ms: number) => Object.fromEntries(format.formatToParts(ms)\n .filter(part => part.type !== 'literal').map(part => [part.type, Number(part.value)]));\n const today = parts(asOfMs);\n const day = Date.UTC(today.year!, today.month! - 1, today.day!);\n // Resolve each local midnight separately so DST days can contain 23 or 25 hours.\n const midnight = (date: number) => {\n let candidate = date;\n for (let i = 0; i < 4; i++) {\n const p = parts(candidate);\n const local = Date.UTC(p.year!, p.month! - 1, p.day!, p.hour!, p.minute!, p.second!);\n const next = candidate + date - local;\n if (next === candidate) return candidate;\n candidate = next;\n }\n return candidate;\n };\n if (range === 'day') {\n const startMs = midnight(day);\n const bucketEndMs = midnight(day + 24 * HOUR);\n const bucketStarts = Array.from({ length: Math.ceil((bucketEndMs - startMs) / HOUR) }, (_, i) => startMs + i * HOUR);\n return { startMs, endMs: asOfMs, bucketMs: HOUR, bucketStarts, bucketEndMs };\n }\n const firstDay = range === 'month' ? Date.UTC(today.year!, today.month! - 1, 1)\n : day - ((new Date(day).getUTCDay() + 6) % 7) * 24 * HOUR;\n const nextPeriod = range === 'month' ? Date.UTC(today.year!, today.month!, 1) : firstDay + 7 * 24 * HOUR;\n const days = (nextPeriod - firstDay) / (24 * HOUR);\n const bucketStarts = Array.from({ length: days }, (_, i) => midnight(firstDay + i * 24 * HOUR));\n return { startMs: bucketStarts[0]!, endMs: asOfMs, bucketMs: 24 * HOUR, bucketStarts, bucketEndMs: midnight(nextPeriod) };\n}\n",
19
19
  "import { createHash, timingSafeEqual } from 'node:crypto';\nimport { ANONYMOUS_PRINCIPAL, type DataPrincipal, type AdmissionTarget, type AdmissionDenial, type IngressPlugin } from '@jeffusion/bungee-core/plugin';\nimport { DataAdmissionError } from '@jeffusion/bungee-core/plugin';\n\nexport interface AccessPolicy { routes: string[] | null; models: string[] | null }\nexport interface AccessCredential {\n id: string; domain: 'data'; name: string; prefix: string; digest: string;\n createdAt: number; expiresAt: number | null; revokedAt: number | null; credentialVersion: number;\n}\nexport interface AccessPublication {\n protectedRouteIds: string[];\n byKey: Record<string, AccessPolicy | null>;\n credentials: AccessCredential[];\n}\nexport function stringIds(value: unknown): string[] {\n if (!Array.isArray(value) || value.length > 1000 || value.some(x => typeof x !== 'string' || !x.length || x.length > 256)\n || new Set(value).size !== value.length) throw new Error('invalid_policy');\n return [...value];\n}\nexport function validatePolicy(value: unknown): AccessPolicy | null {\n if (value === null) return null;\n if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(k => !['routes', 'models'].includes(k))) throw new Error('invalid_policy');\n const p = value as AccessPolicy;\n return {routes: p.routes === null ? null : stringIds(p.routes), models: p.models === null ? null : stringIds(p.models)};\n}\nexport function validatePublication(value: unknown): AccessPublication {\n const p = value as AccessPublication;\n if (!p || typeof p !== 'object' || !p.byKey || typeof p.byKey !== 'object' || Array.isArray(p.byKey)\n || !Array.isArray(p.credentials)) throw new Error('invalid_policy');\n const protectedRouteIds = stringIds(p.protectedRouteIds);\n for (const key of p.credentials) {\n if (!key || key.domain !== 'data' || typeof key.id !== 'string' || !key.id || !/^[a-f0-9]{64}$/.test(key.digest)\n || !Number.isSafeInteger(key.credentialVersion) || key.credentialVersion < 1\n || (key.expiresAt !== null && !Number.isSafeInteger(key.expiresAt))\n || (key.revokedAt !== null && !Number.isSafeInteger(key.revokedAt))) throw new Error('invalid_credentials');\n }\n return {...p, protectedRouteIds};\n}\nexport function validPrincipal(principal: DataPrincipal, value: AccessPublication, now: number): boolean {\n const key = value.credentials.find(key => key.id === principal.keyId);\n return principal.domain === 'data' && !!key && key.credentialVersion === principal.credentialVersion\n && key.revokedAt === null && (key.expiresAt === null || key.expiresAt > now);\n}\n/** Only * is special; every other character is literal. Match the entire model. */\nfunction matchesModel(pattern: string, model: string): boolean {\n const parts = pattern.split('*');\n if (parts.length === 1) return pattern === model;\n const first = parts[0]!, last = parts[parts.length - 1]!;\n if (!model.startsWith(first) || !model.endsWith(last)) return false;\n let cursor = first.length;\n const end = model.length - last.length;\n for (const part of parts.slice(1, -1)) {\n const index = model.indexOf(part, cursor);\n if (index < 0) return false;\n cursor = index + part.length;\n }\n return cursor <= end;\n}\nfunction check(target: AdmissionTarget, policy: AccessPolicy | null): AdmissionDenial | null {\n if (target.principal.domain === 'anonymous') return null;\n if (policy && ((policy.routes !== null && !policy.routes.includes(target.routeId))\n || (policy.models !== null && (!target.model || !policy.models.some(pattern => matchesModel(pattern, target.model!)))))) return {error: 'key-access.scope_denied', status: 403};\n return null;\n}\nexport function createIngress(): IngressPlugin { return {\n bodyRequirements(target, value) {\n const publication = validatePublication(value);\n if (!publication.protectedRouteIds.includes(target.routeId) || target.principal.domain !== 'data') return { request: 'none' };\n const policy = validatePolicy(publication.byKey[target.principal.keyId] ?? null);\n const urlModel = /\\/models\\/([^/:]+):(?:streamGenerateContent|generateContent)$/.test(new URL(target.url).pathname);\n return { request: policy?.models !== null && policy?.models !== undefined && policy.models.length > 0 && !urlModel ? 'json-read' : 'none' };\n },\n authenticate(request, value, now) {\n const p = validatePublication(value);\n const match = /^Bearer (bng_data_[A-Za-z0-9_-]{43})$/i.exec(request.headers.get('authorization') ?? '');\n if (!match) return null;\n const digest = createHash('sha256').update(match[1]!).digest('hex');\n const key = p.credentials.find(key => timingSafeEqual(Buffer.from(key.digest, 'hex'), Buffer.from(digest, 'hex')));\n if (!key) return null;\n const principal = {domain: 'data', keyId: key.id, credentialVersion: key.credentialVersion};\n return validPrincipal(principal, p, now) ? principal : null;\n },\n resolveIdentity(target, value) {\n const p = validatePublication(value);\n if (!p.protectedRouteIds.includes(target.routeId)) return ANONYMOUS_PRINCIPAL;\n if (!validPrincipal(target.principal, p, target.now)) throw new DataAdmissionError(401, 'unauthorized');\n return target.principal;\n },\n plan(target, value) {\n try {\n const p = validatePublication(value);\n if (!p.protectedRouteIds.includes(target.routeId)) return {snapshot: null};\n if (target.principal.domain === 'anonymous') return {denial: {error: 'unauthorized', status: 401}};\n const policy = validatePolicy(p.byKey[target.principal.keyId] ?? null);\n const denial = check(target, policy);\n return denial ? {denial} : {snapshot: {routeId: target.routeId, policy: policy as any}};\n } catch { return {denial: {error: 'key-access.policy_unavailable', status: 503}}; }\n },\n beforeAttempt(target, snapshot) {\n if (snapshot === null) return target.principal.domain === 'anonymous' ? null : {error: 'key-access.invalid_grant', status: 503};\n try {\n const s = snapshot as {routeId: string; policy: AccessPolicy | null};\n if (!s || s.routeId !== target.routeId) return {error: 'key-access.scope_denied', status: 403};\n return check(target, validatePolicy(s.policy));\n } catch { return {error: 'key-access.policy_unavailable', status: 503}; }\n },\n}; }\n"
20
20
  ],
21
- "mappings": "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",
22
- "debugId": "4AF8389FB39A168C64756E2164756E21",
21
+ "mappings": "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",
22
+ "debugId": "13B684B8C247D4F064756E2164756E21",
23
23
  "names": []
24
24
  }
@@ -107,8 +107,15 @@
107
107
  }
108
108
  }
109
109
  ],
110
- "settings": "/settings",
111
- "nativeSettingsComponent": "KeyAccessKeyPolicy"
110
+ "navigation": [
111
+ {
112
+ "label": "metadata.name",
113
+ "path": "/settings",
114
+ "component": "KeyAccessKeyPolicy",
115
+ "icon": "shield",
116
+ "target": "header"
117
+ }
118
+ ]
112
119
  },
113
120
  "metadata": {
114
121
  "name": "metadata.name",
@@ -125,12 +132,8 @@
125
132
  "ui.configureDescription": "Configure access credentials for route {path}.",
126
133
  "ui.keyCount": "{count, plural, one {# KEY} other {# KEYS}}",
127
134
  "ui.routeCount": "{count, plural, one {# ROUTE} other {# ROUTES}}",
128
- "ui.cannotMakePublic": "{path} cannot be made public",
129
135
  "ui.isRoutePublic": "Public access for {path}",
130
136
  "ui.deletedRoute": "Deleted route: {id} (remove it)",
131
- "ui.selectedPublicWarning": "{count, plural, one {The selected public route will stop} other {The # selected public routes will stop}} accepting anonymous requests after confirmation.",
132
- "ui.reviewWarning": "{count, plural, one {The following route will stop} other {The following # routes will stop}} accepting anonymous requests. Callers must provide a valid, authorized Key.",
133
- "ui.applyRouteWarning": "{path} will stop accepting anonymous requests and allow {name} to call it.",
134
137
  "ui.inactive": "The plugin is disabled; Keys are read-only. Enable it to manage Keys and route protection.",
135
138
  "ui.configurationPending": "Configuration saved; publication is still pending.",
136
139
  "ui.expirationFuture": "The expiration time must be later than now.",
@@ -138,31 +141,24 @@
138
141
  "ui.secretFailed": "Unable to read the Key. Please try again later.",
139
142
  "ui.deletedRouteError": "The allowed list contains deleted routes. Remove them before saving.",
140
143
  "ui.keyRoutesPending": "Allowed routes saved, but publication is still pending.",
141
- "ui.protectionPending": "Route protection saved, but publication is still pending.",
142
- "ui.createdPending": "Key created and saved, but publication is still pending. Allowed routes and route protection have not been configured. Save the plaintext, then edit this Key after publication recovers to finish setup.",
143
- "ui.policyPublished": "Allowed routes and selected protection settings saved and published.",
144
+ "ui.createdPending": "Key created and saved, but publication is still pending. Allowed routes have not been configured. Save the plaintext, then edit this Key after publication recovers to finish setup.",
145
+ "ui.policyPublished": "Key permissions saved and published. Route public access stays unchanged.",
144
146
  "ui.routeHelp": "Manage access to Bungee Routes. Public routes need no credentials; protected routes only accept valid, authorized Keys. Deleting a Key does not make routes public.",
145
- "ui.reviewHelp": "The access permissions for this Key will also be saved. Existing authorized Keys retain access; other routes keep their current access settings.",
146
147
  "ui.deleteHelp": "After deletion, this Key cannot start new requests; accepted requests continue. Route public access stays unchanged.",
147
- "ui.allRoutesHelp": "Allow access to current and future routes; only current public routes will be changed to require a Key now.",
148
+ "ui.allRoutesHelp": "Allow access to current and future routes. Route public access stays unchanged.",
148
149
  "ui.keyHelp": "Keys provide access to routes that require them. Public routes need no credentials.",
149
- "ui.allowedRoutesHelp": "Select the Bungee routes that this Key can call.",
150
+ "ui.allowedRoutesHelp": "Select the Bungee routes that this Key can call. This does not change public access.",
150
151
  "ui.modelsHelp": "Supports * wildcards and is case-sensitive. Validation uses the model actually sent upstream.",
151
152
  "ui.editHelp": "Change the name, expiration, and access permissions. Removing authorization does not make routes public.",
152
153
  "ui.noRoutesSelected": "No routes selected: this Key has no access to protected routes yet.",
153
154
  "ui.createHelp": "Assign credentials to an app. You can view the Key from the list at any time.",
154
- "ui.applyHelp": "Apply directly to this route while preserving the Key’s other permissions.",
155
- "ui.createReviewHelp": "Create the Key and apply the following access changes after confirmation.",
155
+ "ui.applyHelp": "Allow this Key to call the route while preserving its other permissions and the route’s public access setting.",
156
156
  "ui.notReady": "The plugin is not ready or configuration is pending publication. Refresh the runtime status.",
157
- "ui.publicBlockedTitle": "This route is linked to API Keys and cannot be made public",
158
- "ui.editReviewHelp": "Save permissions and apply the following access changes after confirmation.",
159
- "ui.publicBlockedHelp": "Remove authorization for this route or delete the Keys first.",
160
157
  "ui.allowedModels": "Allowed models (one per line; leave blank to allow all)",
161
158
  "ui.createRoutesFirst": "No routes yet. Create one in route management first.",
162
159
  "ui.noUsableKeys": "No Keys available to configure. Create one first.",
163
160
  "ui.chooseExistingKey": "Choose an existing API Key",
164
161
  "ui.noKeys": "No API Keys yet.",
165
- "ui.publicToProtected": "Public → Requires Key",
166
162
  "ui.createKey": "Create API Key",
167
163
  "ui.configureKey": "Configure API Key",
168
164
  "ui.viewKey": "View API Key",
@@ -173,13 +169,11 @@
173
169
  "ui.loadingKeys": "Loading Keys",
174
170
  "ui.expiration": "Expiration time (optional)",
175
171
  "ui.modelScopeOptional": "Model scope (optional)",
176
- "ui.reviewTitle": "Confirm route access changes",
177
172
  "ui.copied": "Copied to clipboard.",
178
173
  "ui.keys": "API Key",
179
174
  "ui.settings": "Access control settings",
180
175
  "ui.loadingRoutes": "Loading routes",
181
176
  "ui.requiresKey": "Requires Key",
182
- "ui.keyName": "Key name",
183
177
  "ui.loadingPolicy": "Loading policy",
184
178
  "ui.noRoutes": "No routes yet.",
185
179
  "ui.allowedRoutes": "Allowed routes",
@@ -189,15 +183,11 @@
189
183
  "ui.public": "Public access",
190
184
  "ui.basicInfo": "Basic information",
191
185
  "ui.allRoutes": "Any route",
192
- "ui.modelScope": "Model scope",
193
- "ui.allModels": "All models",
194
186
  "ui.saving": "Saving…",
195
187
  "ui.copyAgain": "Copy again",
196
188
  "ui.copyPlaintext": "Copy plaintext",
197
189
  "ui.confirmDelete": "Confirm deletion",
198
190
  "ui.validity": "Validity",
199
- "ui.back": "Back",
200
- "ui.next": "Next",
201
191
  "ui.applied": "Applied",
202
192
  "ui.refresh": "Refresh",
203
193
  "ui.name": "Name",
@@ -212,17 +202,13 @@
212
202
  "ui.cancel": "Cancel",
213
203
  "ui.save": "Save",
214
204
  "ui.apply": "Apply",
215
- "ui.publicBlockedReason": "Linked API Keys: {keys}. Remove authorization for this route or delete the Keys before making it public.",
216
205
  "ui.keyNameExists": "An API Key with this name already exists. Use another name.",
217
- "ui.routeHasKeys": "This route is linked to API Keys. Remove authorization or delete the Keys before making it public.",
218
206
  "ui.persistedPending": "Configuration saved, but publication is still pending. Refresh the runtime status and confirm it has taken effect before continuing.",
219
207
  "ui.operationFailed": "Operation failed",
220
208
  "ui.stageCreate": "Creating the Key",
221
- "ui.stageSavePolicy": "Saving allowed routes and route protection",
222
- "ui.createdProtected": "Key created; allowed routes saved and published. Protection for the selected public routes has been published.",
209
+ "ui.stageSavePolicy": "Saving Key permissions",
223
210
  "ui.createdPublic": "Key created; allowed routes saved and published. Public routes remain public.",
224
211
  "ui.createdIncomplete": "Key created. {stage} is incomplete: {reason} Save the plaintext, then edit this Key to finish setup.",
225
- "ui.keySavedProtectionIncomplete": "Allowed routes saved; route protection is incomplete: {reason}",
226
212
  "ui.clipboardUnavailable": "Clipboard unavailable. Copy the plaintext manually.",
227
213
  "ui.publicationPending": "Saved; publication is still pending. Refresh the runtime status.",
228
214
  "ui.publicationComplete": "Saved and published.",
@@ -246,12 +232,8 @@
246
232
  "ui.configureDescription": "为路由 {path} 配置访问凭证。",
247
233
  "ui.keyCount": "{count} 个 Key",
248
234
  "ui.routeCount": "{count} 条路由",
249
- "ui.cannotMakePublic": "{path} 无法设为公开",
250
235
  "ui.isRoutePublic": "{path} 是否公开",
251
236
  "ui.deletedRoute": "已删除路由:{id}(请移除)",
252
- "ui.selectedPublicWarning": "选中的 {count} 条公开路由将在提交确认后停止匿名访问。",
253
- "ui.reviewWarning": "以下 {count} 条路由将停止匿名访问。调用方必须携带有效且获授权的 Key。",
254
- "ui.applyRouteWarning": "{path} 将停止匿名访问,并允许 {name} 调用。",
255
237
  "ui.inactive": "插件已停用,Key 只读。启用插件后可管理 Key 与路由保护。",
256
238
  "ui.configurationPending": "配置已保存,当前发布尚未完成。",
257
239
  "ui.expirationFuture": "到期时间必须晚于当前时间。",
@@ -259,31 +241,24 @@
259
241
  "ui.secretFailed": "无法读取 Key,请稍后重试。",
260
242
  "ui.deletedRouteError": "允许列表包含已删除的路由,请移除后保存。",
261
243
  "ui.keyRoutesPending": "Key 允许路由已保存,但发布尚未完成。",
262
- "ui.protectionPending": "路由保护已保存,但发布尚未完成。",
263
- "ui.createdPending": "Key 已创建并保存,发布尚未完成;允许路由和路由保护步骤尚未执行。请保存明文,发布恢复后编辑此 Key 完成设置。",
264
- "ui.policyPublished": "Key 允许路由与所选保护设置已保存并发布。",
244
+ "ui.createdPending": "Key 已创建并保存,发布尚未完成;允许路由尚未配置。请保存明文,发布恢复后编辑此 Key 完成设置。",
245
+ "ui.policyPublished": "Key 权限已保存并发布,路由公开状态保持不变。",
265
246
  "ui.routeHelp": "管理 Bungee Route 的访问方式。公开路由无需凭证;需要 Key 的路由只接受有效且获授权的 Key。删除 Key 不会让路由变为公开。",
266
- "ui.reviewHelp": "本次同时保存此 Key 的访问权限。已有获授权的 Key 仍可访问;其他路由保持原来的访问方式。",
267
247
  "ui.deleteHelp": "删除后此 Key 无法再发起新请求,已接纳的请求继续。路由的公开状态保持不变。",
268
- "ui.allRoutesHelp": "包含当前和后续新增路由的访问权限;本次只将当前公开路由改为需要 Key。",
248
+ "ui.allRoutesHelp": "包含当前和后续新增路由的访问权限,路由公开状态保持不变。",
269
249
  "ui.keyHelp": "Key 用于访问需要 Key 的路由。公开路由无需凭证。",
270
- "ui.allowedRoutesHelp": "从 Bungee 路由中选择此 Key 可以调用的入口。",
250
+ "ui.allowedRoutesHelp": "从 Bungee 路由中选择此 Key 可以调用的入口,不改变路由的公开状态。",
271
251
  "ui.modelsHelp": "支持 * 通配符,区分大小写;按实际发往上游的模型校验。",
272
252
  "ui.editHelp": "修改名称、有效期和访问权限。取消授权不会将路由改为公开。",
273
253
  "ui.noRoutesSelected": "未选择路由:此 Key 暂无受保护路由的访问权限。",
274
254
  "ui.createHelp": "为应用分配调用凭证。可在列表中随时查看 Key。",
275
- "ui.applyHelp": "直接应用到当前路由,保留 Key 的其他权限。",
276
- "ui.createReviewHelp": "确认后创建 Key,并应用下列访问方式变更。",
255
+ "ui.applyHelp": "允许此 Key 调用当前路由,保留其他权限和路由的公开状态。",
277
256
  "ui.notReady": "插件尚未就绪或配置待发布;请刷新运行状态。",
278
- "ui.publicBlockedTitle": "此路由已关联 API Key,无法设为公开",
279
- "ui.editReviewHelp": "确认后保存权限,并应用下列访问方式变更。",
280
- "ui.publicBlockedHelp": "请先解除该路由的授权或删除 Key。",
281
257
  "ui.allowedModels": "允许模型(每行一个,留空允许全部)",
282
258
  "ui.createRoutesFirst": "尚无路由,请先在路由管理中创建。",
283
259
  "ui.noUsableKeys": "暂无可配置的 Key,请先新建。",
284
260
  "ui.chooseExistingKey": "选择已有 API Key",
285
261
  "ui.noKeys": "尚无 API Key。",
286
- "ui.publicToProtected": "公开 → 需要 Key",
287
262
  "ui.createKey": "创建 API Key",
288
263
  "ui.configureKey": "配置 API Key",
289
264
  "ui.viewKey": "查看 API Key",
@@ -294,13 +269,11 @@
294
269
  "ui.loadingKeys": "正在读取 Key",
295
270
  "ui.expiration": "到期时间(可选)",
296
271
  "ui.modelScopeOptional": "模型范围(可选)",
297
- "ui.reviewTitle": "确认路由访问变更",
298
272
  "ui.copied": "已复制到剪贴板。",
299
273
  "ui.keys": "API Key",
300
274
  "ui.settings": "访问控制设置",
301
275
  "ui.loadingRoutes": "正在读取路由",
302
276
  "ui.requiresKey": "需要 Key",
303
- "ui.keyName": "Key 名称",
304
277
  "ui.loadingPolicy": "正在读取策略",
305
278
  "ui.noRoutes": "尚无路由。",
306
279
  "ui.allowedRoutes": "可访问路由",
@@ -310,15 +283,11 @@
310
283
  "ui.public": "是否公开",
311
284
  "ui.basicInfo": "基本信息",
312
285
  "ui.allRoutes": "任意路由",
313
- "ui.modelScope": "模型范围",
314
- "ui.allModels": "全部模型",
315
286
  "ui.saving": "保存中…",
316
287
  "ui.copyAgain": "再次复制",
317
288
  "ui.copyPlaintext": "复制明文",
318
289
  "ui.confirmDelete": "确认删除",
319
290
  "ui.validity": "有效期",
320
- "ui.back": "上一步",
321
- "ui.next": "下一步",
322
291
  "ui.applied": "已应用",
323
292
  "ui.refresh": "刷新",
324
293
  "ui.name": "名称",
@@ -333,17 +302,13 @@
333
302
  "ui.cancel": "取消",
334
303
  "ui.save": "保存",
335
304
  "ui.apply": "应用",
336
- "ui.publicBlockedReason": "已关联 API Key:{keys}。请先解除该路由的授权或删除 Key,再设为公开。",
337
305
  "ui.keyNameExists": "API Key 名称已存在,请使用其他名称。",
338
- "ui.routeHasKeys": "此路由已关联 API Key,请先解除授权或删除 Key,再设为公开。",
339
306
  "ui.persistedPending": "配置已保存,但发布尚未完成。请刷新运行状态,确认生效后继续。",
340
307
  "ui.operationFailed": "操作失败",
341
308
  "ui.stageCreate": "创建 Key",
342
- "ui.stageSavePolicy": "保存 Key 允许路由与路由保护",
343
- "ui.createdProtected": "Key 已创建,允许路由已保存并发布。所选公开路由的保护已发布。",
309
+ "ui.stageSavePolicy": "保存 Key 权限",
344
310
  "ui.createdPublic": "Key 已创建,允许路由已保存并发布。公开路由保持公开。",
345
311
  "ui.createdIncomplete": "Key 已创建。{stage}未完成:{reason} 请保存明文,随后编辑此 Key 完成设置。",
346
- "ui.keySavedProtectionIncomplete": "Key 允许路由已保存;路由保护未完成:{reason}",
347
312
  "ui.clipboardUnavailable": "剪贴板不可用,请手动复制明文。",
348
313
  "ui.publicationPending": "已保存,发布尚未完成;请刷新运行状态。",
349
314
  "ui.publicationComplete": "已保存并发布。",