@c1m-public/platform-auth 0.8.4 → 0.9.1

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.
package/README.md CHANGED
@@ -116,6 +116,14 @@ await access.requireAccess("platform.tenants.manage");
116
116
  Omit `getSubjectId` and the loader derives the cache-tag subject from the access
117
117
  token, so per-user tagging cannot silently collapse to one shared tag.
118
118
 
119
+ ### Pipeline stages
120
+
121
+ `PIPELINE_STAGES` / `PipelineStage` / `isPipelineStage` under `./access` are
122
+ the onboarding funnel strings in order (`created`, `awaiting_payment`,
123
+ `provisioning`, `attention`, `active`), generated from the backend's
124
+ `schemas/api/pipeline-stages.json` the same way the permission keys are.
125
+ Labels and descriptions stay with the app; the strings are the backend's.
126
+
119
127
  ## Keys drift guard
120
128
 
121
129
  `src/access/generated/keys.ts` is generated from the backend authz catalog
@@ -1,148 +1 @@
1
- import { R as Resolvable } from '../internal-D6RafCm2.js';
2
-
3
- /** Platform-operator permission keys. */
4
- declare const PLATFORM_PERMISSIONS: readonly ["affiliates.finance.manage", "affiliates.partners.manage", "affiliates.programmes.manage", "affiliates.view", "linkservice.domains.manage", "linkservice.links.manage", "linkservice.qr.manage", "linkservice.view", "platform.admin.view", "platform.audit.view", "platform.auth.manage", "platform.billing.manage", "platform.channels.manage", "platform.channels.view", "platform.config.manage", "platform.docs.manage", "platform.docs.view", "platform.observability.manage", "platform.observability.view", "platform.offboarding.view", "platform.ops.manage", "platform.support.impersonate", "platform.support.sessions", "platform.tenants.archive", "platform.tenants.manage", "platform.tenants.restore", "platform.users.deactivate", "platform.users.delete", "platform.users.invite", "platform.users.manage_access", "platform.users.reactivate", "platform.users.view"];
5
- type PlatformPermissionKey = (typeof PLATFORM_PERMISSIONS)[number];
6
- /** Dash-owner permission keys (granted at dash scope, mirrored from the membership role). */
7
- declare const DASH_PERMISSIONS: readonly ["dash.apps.manage", "dash.billing.manage", "dash.channels.manage", "dash.customers.manage", "dash.domains.manage", "dash.members.manage", "dash.ownership.transfer", "dash.plans.manage", "dash.publish", "dash.settings.manage", "dash.workspaces.admin", "dash.workspaces.manage"];
8
- type DashPermissionKey = (typeof DASH_PERMISSIONS)[number];
9
- /** Membership-mirrored dash role bundles (grant keys at dash scope). */
10
- declare const DASH_ROLES: readonly ["dash.admin", "dash.owner"];
11
- type DashRole = (typeof DASH_ROLES)[number];
12
- /** Lead-broker workload permission keys. */
13
- declare const LEADBROKER_PERMISSIONS: readonly ["leadbroker.finance.manage", "leadbroker.leads.dispatch", "leadbroker.leads.review", "leadbroker.leads.view", "leadbroker.policy.manage"];
14
- type LeadBrokerPermissionKey = (typeof LEADBROKER_PERMISSIONS)[number];
15
- /** Workspace-staff permission keys. */
16
- declare const WORKSPACE_PERMISSIONS: readonly ["workspace.channels.manage", "workspace.channels.secrets", "workspace.channels.view", "workspace.crm.manage", "workspace.crm.view", "workspace.customers.manage", "workspace.customers.view", "workspace.orders.manage", "workspace.orders.view", "workspace.payments.record", "workspace.site.edit", "workspace.site.view", "workspace.staff.manage"];
17
- type WorkspacePermissionKey = (typeof WORKSPACE_PERMISSIONS)[number];
18
- /** Assignable workspace-staff role bundles (grant keys at workspace scope). */
19
- declare const WORKSPACE_ROLES: readonly ["workspace.editor", "workspace.frontdesk", "workspace.manager"];
20
- type WorkspaceRole = (typeof WORKSPACE_ROLES)[number];
21
- /** Every permission key the backend can grant, across all scopes. */
22
- type PermissionKey = PlatformPermissionKey | DashPermissionKey | LeadBrokerPermissionKey | WorkspacePermissionKey;
23
-
24
- type AccessErrorCode = "unauthenticated" | "forbidden" | "unavailable" | "invalid_snapshot";
25
- /**
26
- * Typed failure for loading or validating an access snapshot. `unauthenticated`
27
- * = no/expired session (401); `forbidden` = the backend refused the snapshot
28
- * (403; e.g. wrong audience); `unavailable` = transport/5xx; `invalid_snapshot`
29
- * = the payload did not match the contract.
30
- */
31
- declare class AccessSnapshotError extends Error {
32
- readonly code: AccessErrorCode;
33
- readonly status: number | undefined;
34
- constructor(code: AccessErrorCode, message: string, status?: number);
35
- }
36
- declare function isAccessSnapshotError(error: unknown, code?: AccessErrorCode): error is AccessSnapshotError;
37
-
38
- /** Scope a permission entry applies to. Mirrors c1m-server authz.Scope*. */
39
- type AccessScopeType = "platform" | "saas_app" | "workspace";
40
- interface AccessEntry {
41
- /** Permission key, e.g. "platform.users.manage_access". */
42
- key: string;
43
- scope_type: AccessScopeType;
44
- /** "" for platform scope; saas_app slug or "*" for saas_app scope; workspace id for workspace scope. */
45
- scope_id: string;
46
- }
47
- /**
48
- * The resolved authorization snapshot for one subject, as returned by
49
- * `GET /api/v1/platform/access`. This is the *output* of the backend resolver
50
- * (base-role grant ∪ scoped grants), never the grant rows themselves.
51
- */
52
- interface AccessSnapshot {
53
- subject_id: string;
54
- /** Monotonic; bumps on any grant/role change. Use for optimistic concurrency and cache keys. */
55
- access_version: number;
56
- is_owner: boolean;
57
- active: boolean;
58
- /** Platform operator base role (derived; display only — never authorize on it). */
59
- platform_role: string;
60
- permissions: AccessEntry[];
61
- /**
62
- * Dash bundle (`dash.owner` / `dash.admin`) per saas_app id, mirrored from
63
- * the subject's dash membership. This is the admission list for the dash
64
- * area: the dashes this subject runs. What they may *do* on one is a normal
65
- * `hasPermission(snapshot, key, { saasApp })` check — platform managers hold
66
- * their authority platform-wide and so appear here not at all.
67
- */
68
- dash_roles: Record<string, string>;
69
- /** Effective workspace-staff permission keys per workspace id. */
70
- workspace_permissions: Record<string, string[]>;
71
- /** Workspace-staff role bundle per workspace id (display only). */
72
- workspace_roles: Record<string, string>;
73
- }
74
- declare function emptySnapshot(subjectId?: string): AccessSnapshot;
75
- /** Runtime validator for the wire payload. Throws AccessSnapshotError("invalid_snapshot"). */
76
- declare function parseAccessSnapshot(input: unknown): AccessSnapshot;
77
-
78
- interface PermissionCheckOptions {
79
- /**
80
- * SaaS app (dash) slug the check is about. Omit for "anywhere": platform
81
- * entries and any saas_app entry satisfy the check.
82
- */
83
- saasApp?: string;
84
- }
85
- /**
86
- * Mirrors c1m-server authz.entryCoversApp: platform scope covers every app;
87
- * saas_app scope covers "*" or a case-insensitive slug match; workspace-scoped
88
- * entries never satisfy an app-level check.
89
- */
90
- declare function entryCoversApp(entry: AccessEntry, saasApp: string | undefined): boolean;
91
- /** True when the subject is active and holds `key` for the given app (or anywhere). */
92
- declare function hasPermission(snapshot: AccessSnapshot, key: string, opts?: PermissionCheckOptions): boolean;
93
- declare function hasAnyPermission(snapshot: AccessSnapshot, keys: readonly string[], opts?: PermissionCheckOptions): boolean;
94
- declare function hasAllPermissions(snapshot: AccessSnapshot, keys: readonly string[], opts?: PermissionCheckOptions): boolean;
95
- /**
96
- * Workspace-staff check. Platform owners always pass (they own every
97
- * workspace); otherwise the workspace's effective permission list decides.
98
- * Platform/saas_app entries never confer workspace-staff permissions.
99
- */
100
- declare function hasWorkspacePermission(snapshot: AccessSnapshot, workspaceId: string, key: string): boolean;
101
- /**
102
- * The dashes this subject runs, from the mirrored dash bundle grants — the
103
- * admission list for the Admin Center's dash area. Platform managers are not
104
- * listed: their reach is platform-wide, so admit them with
105
- * `hasPermission(snapshot, "platform.tenants.manage")` instead.
106
- */
107
- declare function managedDashes(snapshot: AccessSnapshot): Array<{
108
- saasApp: string;
109
- role: string;
110
- }>;
111
- /** The dash bundle held on one dash, or "" when the subject holds none there. */
112
- declare function dashRole(snapshot: AccessSnapshot, saasApp: string): string;
113
- declare function isPlatformOwner(snapshot: AccessSnapshot): boolean;
114
- /**
115
- * The saas_app scopes under which `key` is held. Returns `{ all: true }` when a
116
- * platform-scoped or "*" entry grants it everywhere; otherwise the explicit
117
- * app slugs (empty = nowhere). Mirrors authz.AppPermissionScope.
118
- */
119
- declare function appScopes(snapshot: AccessSnapshot, key: string): {
120
- all: boolean;
121
- apps: string[];
122
- };
123
- /** Distinct permission keys held for `saasApp` (or anywhere). Handy for nav/badges. */
124
- declare function permissionsFor(snapshot: AccessSnapshot, opts?: PermissionCheckOptions): string[];
125
-
126
- declare const DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
127
- interface FetchAccessSnapshotOptions {
128
- /** Backend origin, e.g. https://api.example.com (no trailing slash needed). */
129
- apiUrl: Resolvable;
130
- /** Bearer access token for the platform surface. */
131
- accessToken: string;
132
- /** Defaults to /api/v1/platform/access. */
133
- endpointPath?: string;
134
- fetchTimeoutMs?: number;
135
- fetchImpl?: typeof fetch;
136
- /** Extra headers. Authorization and Accept are always set here. */
137
- headers?: HeadersInit;
138
- /** Incoming BFF request; its client identity (IP, geo, UA) is forwarded to the backend. */
139
- request?: Request;
140
- }
141
- /**
142
- * Framework-neutral fetch of the resolved access snapshot. Throws
143
- * AccessSnapshotError: 401 → unauthenticated, 403 → forbidden, other non-2xx or
144
- * network failure → unavailable, malformed body → invalid_snapshot.
145
- */
146
- declare function fetchAccessSnapshot(opts: FetchAccessSnapshotOptions): Promise<AccessSnapshot>;
147
-
148
- export { type AccessEntry, type AccessErrorCode, type AccessScopeType, type AccessSnapshot, AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, type DashPermissionKey, type DashRole, type FetchAccessSnapshotOptions, LEADBROKER_PERMISSIONS, type LeadBrokerPermissionKey, PLATFORM_PERMISSIONS, type PermissionCheckOptions, type PermissionKey, type PlatformPermissionKey, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, type WorkspacePermissionKey, type WorkspaceRole, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor };
1
+ export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, A as AccessSnapshot, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, P as PermissionCheckOptions, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-CbsCWtku.js';
@@ -1,2 +1,307 @@
1
- export { AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, LEADBROKER_PERMISSIONS, PLATFORM_PERMISSIONS, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor } from '../chunk-73ZH67GM.js';
2
- import '../chunk-ZWSJVE5S.js';
1
+ // src/access/generated/keys.ts
2
+ var PLATFORM_PERMISSIONS = [
3
+ "affiliates.finance.manage",
4
+ "affiliates.partners.manage",
5
+ "affiliates.programmes.manage",
6
+ "affiliates.view",
7
+ "linkservice.domains.manage",
8
+ "linkservice.links.manage",
9
+ "linkservice.qr.manage",
10
+ "linkservice.view",
11
+ "platform.admin.view",
12
+ "platform.audit.view",
13
+ "platform.auth.manage",
14
+ "platform.billing.manage",
15
+ "platform.channels.manage",
16
+ "platform.channels.view",
17
+ "platform.config.manage",
18
+ "platform.docs.manage",
19
+ "platform.docs.view",
20
+ "platform.observability.manage",
21
+ "platform.observability.view",
22
+ "platform.offboarding.view",
23
+ "platform.ops.manage",
24
+ "platform.support.impersonate",
25
+ "platform.support.sessions",
26
+ "platform.tenants.archive",
27
+ "platform.tenants.manage",
28
+ "platform.tenants.restore",
29
+ "platform.users.deactivate",
30
+ "platform.users.delete",
31
+ "platform.users.invite",
32
+ "platform.users.manage_access",
33
+ "platform.users.reactivate",
34
+ "platform.users.view"
35
+ ];
36
+ var DASH_PERMISSIONS = [
37
+ "dash.apps.manage",
38
+ "dash.billing.manage",
39
+ "dash.channels.manage",
40
+ "dash.customers.manage",
41
+ "dash.domains.manage",
42
+ "dash.members.manage",
43
+ "dash.ownership.transfer",
44
+ "dash.plans.manage",
45
+ "dash.publish",
46
+ "dash.settings.manage",
47
+ "dash.workspaces.admin",
48
+ "dash.workspaces.manage"
49
+ ];
50
+ var DASH_ROLES = [
51
+ "dash.admin",
52
+ "dash.owner"
53
+ ];
54
+ var LEADBROKER_PERMISSIONS = [
55
+ "leadbroker.finance.manage",
56
+ "leadbroker.leads.dispatch",
57
+ "leadbroker.leads.review",
58
+ "leadbroker.leads.view",
59
+ "leadbroker.policy.manage"
60
+ ];
61
+ var WORKSPACE_PERMISSIONS = [
62
+ "workspace.channels.manage",
63
+ "workspace.channels.secrets",
64
+ "workspace.channels.view",
65
+ "workspace.crm.manage",
66
+ "workspace.crm.view",
67
+ "workspace.customers.manage",
68
+ "workspace.customers.view",
69
+ "workspace.orders.manage",
70
+ "workspace.orders.view",
71
+ "workspace.payments.record",
72
+ "workspace.site.edit",
73
+ "workspace.site.view",
74
+ "workspace.staff.manage"
75
+ ];
76
+ var WORKSPACE_ROLES = [
77
+ "workspace.editor",
78
+ "workspace.frontdesk",
79
+ "workspace.manager"
80
+ ];
81
+
82
+ // src/access/generated/stages.ts
83
+ var PIPELINE_STAGES = ["created", "awaiting_payment", "provisioning", "attention", "active"];
84
+ function isPipelineStage(value) {
85
+ return typeof value === "string" && PIPELINE_STAGES.includes(value);
86
+ }
87
+
88
+ // src/access/errors.ts
89
+ var AccessSnapshotError = class extends Error {
90
+ code;
91
+ status;
92
+ constructor(code, message, status) {
93
+ super(message);
94
+ this.name = "AccessSnapshotError";
95
+ this.code = code;
96
+ this.status = status;
97
+ }
98
+ };
99
+ function isAccessSnapshotError(error, code) {
100
+ return error instanceof AccessSnapshotError && (code === void 0 || error.code === code);
101
+ }
102
+
103
+ // src/access/snapshot.ts
104
+ var SCOPE_TYPES = /* @__PURE__ */ new Set(["platform", "saas_app", "workspace"]);
105
+ function emptySnapshot(subjectId = "") {
106
+ return {
107
+ subject_id: subjectId,
108
+ access_version: 0,
109
+ is_owner: false,
110
+ active: false,
111
+ platform_role: "",
112
+ permissions: [],
113
+ dash_roles: {},
114
+ workspace_permissions: {},
115
+ workspace_roles: {}
116
+ };
117
+ }
118
+ function isRecord(value) {
119
+ return typeof value === "object" && value !== null && !Array.isArray(value);
120
+ }
121
+ function fail(field, why) {
122
+ throw new AccessSnapshotError("invalid_snapshot", `access snapshot: ${field} ${why}`);
123
+ }
124
+ function parseAccessSnapshot(input) {
125
+ if (!isRecord(input)) fail("payload", "is not an object");
126
+ const { subject_id, access_version, is_owner, active, platform_role, permissions, dash_roles, workspace_permissions, workspace_roles } = input;
127
+ if (typeof subject_id !== "string" || subject_id === "") fail("subject_id", "must be a non-empty string");
128
+ if (typeof access_version !== "number" || !Number.isInteger(access_version) || access_version < 0) fail("access_version", "must be a non-negative integer");
129
+ if (typeof is_owner !== "boolean") fail("is_owner", "must be a boolean");
130
+ if (typeof active !== "boolean") fail("active", "must be a boolean");
131
+ if (platform_role !== void 0 && platform_role !== null && typeof platform_role !== "string") fail("platform_role", "must be a string");
132
+ if (!Array.isArray(permissions)) fail("permissions", "must be an array");
133
+ const entries = permissions.map((raw, i) => {
134
+ if (!isRecord(raw)) fail(`permissions[${i}]`, "is not an object");
135
+ const { key, scope_type, scope_id } = raw;
136
+ if (typeof key !== "string" || key === "") fail(`permissions[${i}].key`, "must be a non-empty string");
137
+ if (typeof scope_type !== "string" || !SCOPE_TYPES.has(scope_type)) fail(`permissions[${i}].scope_type`, "must be platform|saas_app|workspace");
138
+ if (scope_id !== void 0 && scope_id !== null && typeof scope_id !== "string") fail(`permissions[${i}].scope_id`, "must be a string");
139
+ return { key, scope_type, scope_id: typeof scope_id === "string" ? scope_id : "" };
140
+ });
141
+ const dashRoles = {};
142
+ if (dash_roles !== void 0 && dash_roles !== null) {
143
+ if (!isRecord(dash_roles)) fail("dash_roles", "must be an object");
144
+ for (const [dash, role] of Object.entries(dash_roles)) {
145
+ if (typeof role !== "string") fail(`dash_roles[${dash}]`, "must be a string");
146
+ dashRoles[dash] = role;
147
+ }
148
+ }
149
+ const wsPerms = {};
150
+ if (workspace_permissions !== void 0 && workspace_permissions !== null) {
151
+ if (!isRecord(workspace_permissions)) fail("workspace_permissions", "must be an object");
152
+ for (const [ws, keys] of Object.entries(workspace_permissions)) {
153
+ if (!Array.isArray(keys) || keys.some((k) => typeof k !== "string")) fail(`workspace_permissions[${ws}]`, "must be a string array");
154
+ wsPerms[ws] = [...keys];
155
+ }
156
+ }
157
+ const wsRoles = {};
158
+ if (workspace_roles !== void 0 && workspace_roles !== null) {
159
+ if (!isRecord(workspace_roles)) fail("workspace_roles", "must be an object");
160
+ for (const [ws, role] of Object.entries(workspace_roles)) {
161
+ if (typeof role !== "string") fail(`workspace_roles[${ws}]`, "must be a string");
162
+ wsRoles[ws] = role;
163
+ }
164
+ }
165
+ return {
166
+ subject_id,
167
+ access_version,
168
+ is_owner,
169
+ active,
170
+ platform_role: typeof platform_role === "string" ? platform_role : "",
171
+ permissions: entries,
172
+ dash_roles: dashRoles,
173
+ workspace_permissions: wsPerms,
174
+ workspace_roles: wsRoles
175
+ };
176
+ }
177
+
178
+ // src/access/predicates.ts
179
+ function entryCoversApp(entry, saasApp) {
180
+ switch (entry.scope_type) {
181
+ case "platform":
182
+ return true;
183
+ case "saas_app":
184
+ if (saasApp === void 0) return true;
185
+ return entry.scope_id === "*" || entry.scope_id.toLowerCase() === saasApp.toLowerCase();
186
+ default:
187
+ return false;
188
+ }
189
+ }
190
+ function hasPermission(snapshot, key, opts = {}) {
191
+ if (!snapshot.active) return false;
192
+ return snapshot.permissions.some((entry) => entry.key === key && entryCoversApp(entry, opts.saasApp));
193
+ }
194
+ function hasAnyPermission(snapshot, keys, opts = {}) {
195
+ return keys.some((key) => hasPermission(snapshot, key, opts));
196
+ }
197
+ function hasAllPermissions(snapshot, keys, opts = {}) {
198
+ return keys.every((key) => hasPermission(snapshot, key, opts));
199
+ }
200
+ function hasWorkspacePermission(snapshot, workspaceId, key) {
201
+ if (!snapshot.active) return false;
202
+ if (snapshot.is_owner) return true;
203
+ return (snapshot.workspace_permissions[workspaceId] ?? []).includes(key);
204
+ }
205
+ function managedDashes(snapshot) {
206
+ if (!snapshot.active) return [];
207
+ return Object.entries(snapshot.dash_roles).map(([saasApp, role]) => ({ saasApp, role })).sort((a, b) => a.saasApp.localeCompare(b.saasApp));
208
+ }
209
+ function dashRole(snapshot, saasApp) {
210
+ if (!snapshot.active) return "";
211
+ return snapshot.dash_roles[saasApp] ?? "";
212
+ }
213
+ function isPlatformOwner(snapshot) {
214
+ return snapshot.active && snapshot.is_owner;
215
+ }
216
+ function appScopes(snapshot, key) {
217
+ if (!snapshot.active) return { all: false, apps: [] };
218
+ const apps = /* @__PURE__ */ new Set();
219
+ for (const entry of snapshot.permissions) {
220
+ if (entry.key !== key) continue;
221
+ if (entry.scope_type === "platform") return { all: true, apps: [] };
222
+ if (entry.scope_type === "saas_app") {
223
+ if (entry.scope_id === "*") return { all: true, apps: [] };
224
+ apps.add(entry.scope_id.toLowerCase());
225
+ }
226
+ }
227
+ return { all: false, apps: [...apps].sort() };
228
+ }
229
+ function permissionsFor(snapshot, opts = {}) {
230
+ if (!snapshot.active) return [];
231
+ const keys = /* @__PURE__ */ new Set();
232
+ for (const entry of snapshot.permissions) if (entryCoversApp(entry, opts.saasApp)) keys.add(entry.key);
233
+ return [...keys].sort();
234
+ }
235
+
236
+ // ../transport/src/forwarding.ts
237
+ function applyForwardedClientHeaders(incoming, headers = new Headers()) {
238
+ const forwardedFor = incoming.get("x-forwarded-for");
239
+ const realIp = incoming.get("x-real-ip") ?? forwardedFor?.split(",")[0]?.trim();
240
+ if (realIp && !headers.has("X-Client-IP")) headers.set("X-Client-IP", realIp);
241
+ if (realIp && !headers.has("X-Real-IP")) headers.set("X-Real-IP", realIp);
242
+ if (forwardedFor && !headers.has("X-Forwarded-For")) headers.set("X-Forwarded-For", forwardedFor);
243
+ const country = incoming.get("cf-ipcountry") ?? incoming.get("x-vercel-ip-country") ?? incoming.get("x-geo-country") ?? incoming.get("x-country-code");
244
+ if (country && country !== "XX" && !headers.has("X-Geo-Country")) {
245
+ headers.set("X-Geo-Country", country);
246
+ }
247
+ const host = incoming.get("host");
248
+ if (host && !headers.has("X-Forwarded-Host")) headers.set("X-Forwarded-Host", host);
249
+ const proto = incoming.get("x-forwarded-proto");
250
+ if (proto && !headers.has("X-Forwarded-Proto")) headers.set("X-Forwarded-Proto", proto);
251
+ const userAgent = incoming.get("user-agent");
252
+ if (userAgent && !headers.has("X-Client-User-Agent")) headers.set("X-Client-User-Agent", userAgent);
253
+ return headers;
254
+ }
255
+ function forwardedRequestHeaders(request, init) {
256
+ return applyForwardedClientHeaders(request.headers, new Headers(init));
257
+ }
258
+
259
+ // src/internal.ts
260
+ function resolveValue(value) {
261
+ return typeof value === "function" ? value() : value;
262
+ }
263
+ async function timedFetch(url, options = {}) {
264
+ const doFetch = options.fetch ?? options.fetchImpl ?? fetch;
265
+ const controller = options.timeoutMs ? new AbortController() : void 0;
266
+ const timer = controller ? setTimeout(() => controller.abort(), options.timeoutMs) : void 0;
267
+ try {
268
+ return await doFetch(url, {
269
+ ...options.init,
270
+ cache: "no-store",
271
+ ...controller ? { signal: controller.signal } : {}
272
+ });
273
+ } finally {
274
+ if (timer) clearTimeout(timer);
275
+ }
276
+ }
277
+
278
+ // src/access/fetch.ts
279
+ var DEFAULT_ACCESS_ENDPOINT_PATH = "/api/v1/platform/access";
280
+ async function fetchAccessSnapshot(opts) {
281
+ const url = `${resolveValue(opts.apiUrl).replace(/\/$/, "")}${opts.endpointPath ?? DEFAULT_ACCESS_ENDPOINT_PATH}`;
282
+ const headers = opts.request ? forwardedRequestHeaders(opts.request, opts.headers) : new Headers(opts.headers);
283
+ headers.set("Authorization", `Bearer ${opts.accessToken}`);
284
+ headers.set("Accept", "application/json");
285
+ let response;
286
+ try {
287
+ response = await timedFetch(url, {
288
+ init: { method: "GET", headers },
289
+ timeoutMs: opts.fetchTimeoutMs,
290
+ fetchImpl: opts.fetchImpl
291
+ });
292
+ } catch (error) {
293
+ throw new AccessSnapshotError("unavailable", `access snapshot request failed: ${error.message}`);
294
+ }
295
+ if (response.status === 401) throw new AccessSnapshotError("unauthenticated", "session expired", 401);
296
+ if (response.status === 403) throw new AccessSnapshotError("forbidden", "access snapshot refused", 403);
297
+ if (!response.ok) throw new AccessSnapshotError("unavailable", `access snapshot returned ${response.status}`, response.status);
298
+ let body;
299
+ try {
300
+ body = await response.json();
301
+ } catch {
302
+ throw new AccessSnapshotError("invalid_snapshot", "access snapshot body is not JSON", response.status);
303
+ }
304
+ return parseAccessSnapshot(body);
305
+ }
306
+
307
+ export { AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, LEADBROKER_PERMISSIONS, PIPELINE_STAGES, PLATFORM_PERMISSIONS, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPipelineStage, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor };
@@ -1,6 +1,5 @@
1
- import { R as Resolvable } from '../internal-D6RafCm2.js';
2
- import { AccessSnapshot, PermissionCheckOptions } from './index.js';
3
- export { AccessEntry, AccessErrorCode, AccessScopeType, AccessSnapshotError, DASH_PERMISSIONS, DASH_ROLES, DEFAULT_ACCESS_ENDPOINT_PATH, DashPermissionKey, DashRole, FetchAccessSnapshotOptions, LEADBROKER_PERMISSIONS, LeadBrokerPermissionKey, PLATFORM_PERMISSIONS, PermissionKey, PlatformPermissionKey, WORKSPACE_PERMISSIONS, WORKSPACE_ROLES, WorkspacePermissionKey, WorkspaceRole, appScopes, dashRole, emptySnapshot, entryCoversApp, fetchAccessSnapshot, hasAllPermissions, hasAnyPermission, hasPermission, hasWorkspacePermission, isAccessSnapshotError, isPlatformOwner, managedDashes, parseAccessSnapshot, permissionsFor } from './index.js';
1
+ import { A as AccessSnapshot, P as PermissionCheckOptions, R as Resolvable } from '../index-CbsCWtku.js';
2
+ export { a as AccessEntry, b as AccessErrorCode, c as AccessScopeType, d as AccessSnapshotError, D as DASH_PERMISSIONS, e as DASH_ROLES, f as DEFAULT_ACCESS_ENDPOINT_PATH, g as DashPermissionKey, h as DashRole, F as FetchAccessSnapshotOptions, L as LEADBROKER_PERMISSIONS, i as LeadBrokerPermissionKey, j as PIPELINE_STAGES, k as PLATFORM_PERMISSIONS, l as PermissionKey, m as PipelineStage, n as PlatformPermissionKey, W as WORKSPACE_PERMISSIONS, o as WORKSPACE_ROLES, p as WorkspacePermissionKey, q as WorkspaceRole, r as appScopes, s as dashRole, t as emptySnapshot, u as entryCoversApp, v as fetchAccessSnapshot, w as hasAllPermissions, x as hasAnyPermission, y as hasPermission, z as hasWorkspacePermission, B as isAccessSnapshotError, C as isPipelineStage, E as isPlatformOwner, G as managedDashes, H as parseAccessSnapshot, I as permissionsFor } from '../index-CbsCWtku.js';
4
3
 
5
4
  /** Default cache tag for one subject's snapshot: `access:<subjectId>`. */
6
5
  declare function accessCacheTag(subjectId: string): string;