@reopt-ai/data-contract 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/client.cjs +228 -321
  2. package/dist/client.cjs.map +1 -1
  3. package/dist/client.d.cts +57 -71
  4. package/dist/client.d.cts.map +1 -0
  5. package/dist/client.d.ts +57 -71
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +228 -322
  8. package/dist/client.js.map +1 -1
  9. package/dist/control.cjs +158 -54
  10. package/dist/control.cjs.map +1 -1
  11. package/dist/control.d.cts +121 -137
  12. package/dist/control.d.cts.map +1 -0
  13. package/dist/control.d.ts +121 -137
  14. package/dist/control.d.ts.map +1 -0
  15. package/dist/control.js +134 -54
  16. package/dist/control.js.map +1 -1
  17. package/dist/events-BUIeU60t.d.cts +142 -0
  18. package/dist/events-BUIeU60t.d.cts.map +1 -0
  19. package/dist/events-BUIeU60t.d.ts +142 -0
  20. package/dist/events-BUIeU60t.d.ts.map +1 -0
  21. package/dist/events.cjs +110 -15
  22. package/dist/events.cjs.map +1 -1
  23. package/dist/events.d.cts +2 -59
  24. package/dist/events.d.ts +2 -59
  25. package/dist/events.js +101 -15
  26. package/dist/events.js.map +1 -1
  27. package/dist/identity.cjs +150 -55
  28. package/dist/identity.cjs.map +1 -1
  29. package/dist/identity.d.cts +21 -20
  30. package/dist/identity.d.cts.map +1 -0
  31. package/dist/identity.d.ts +21 -20
  32. package/dist/identity.d.ts.map +1 -0
  33. package/dist/identity.js +126 -56
  34. package/dist/identity.js.map +1 -1
  35. package/dist/index.cjs +46 -25
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +37 -54
  38. package/dist/index.d.cts.map +1 -0
  39. package/dist/index.d.ts +37 -54
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +36 -25
  42. package/dist/index.js.map +1 -1
  43. package/dist/ingest.cjs +152 -57
  44. package/dist/ingest.cjs.map +1 -1
  45. package/dist/ingest.d.cts +182 -193
  46. package/dist/ingest.d.cts.map +1 -0
  47. package/dist/ingest.d.ts +182 -193
  48. package/dist/ingest.d.ts.map +1 -0
  49. package/dist/ingest.js +126 -57
  50. package/dist/ingest.js.map +1 -1
  51. package/dist/query.cjs +284 -84
  52. package/dist/query.cjs.map +1 -1
  53. package/dist/query.d.cts +456 -469
  54. package/dist/query.d.cts.map +1 -0
  55. package/dist/query.d.ts +456 -469
  56. package/dist/query.d.ts.map +1 -0
  57. package/dist/query.js +245 -84
  58. package/dist/query.js.map +1 -1
  59. package/dist/report.cjs +104 -74
  60. package/dist/report.cjs.map +1 -1
  61. package/dist/report.d.cts +182 -136
  62. package/dist/report.d.cts.map +1 -0
  63. package/dist/report.d.ts +182 -136
  64. package/dist/report.d.ts.map +1 -0
  65. package/dist/report.js +93 -74
  66. package/dist/report.js.map +1 -1
  67. package/dist/version-B_0qb1_K.cjs +53 -0
  68. package/dist/version-B_0qb1_K.cjs.map +1 -0
  69. package/dist/version-C09Cyf4R.js +12 -0
  70. package/dist/version-C09Cyf4R.js.map +1 -0
  71. package/dist/{version-DJ2Grb_N.d.ts → version-CGujUGWd.d.cts} +5 -3
  72. package/dist/version-CGujUGWd.d.cts.map +1 -0
  73. package/dist/{version-DJ2Grb_N.d.cts → version-CGujUGWd.d.ts} +5 -3
  74. package/dist/version-CGujUGWd.d.ts.map +1 -0
  75. package/package.json +69 -28
  76. package/dist/chunk-3EEANNYN.cjs +0 -62
  77. package/dist/chunk-3EEANNYN.cjs.map +0 -1
  78. package/dist/chunk-7W33YGRO.js +0 -160
  79. package/dist/chunk-7W33YGRO.js.map +0 -1
  80. package/dist/chunk-F2RNXJGO.cjs +0 -19
  81. package/dist/chunk-F2RNXJGO.cjs.map +0 -1
  82. package/dist/chunk-FP2X3PRK.js +0 -62
  83. package/dist/chunk-FP2X3PRK.js.map +0 -1
  84. package/dist/chunk-JCEWUSUM.js +0 -53
  85. package/dist/chunk-JCEWUSUM.js.map +0 -1
  86. package/dist/chunk-L3JPDRVG.cjs +0 -160
  87. package/dist/chunk-L3JPDRVG.cjs.map +0 -1
  88. package/dist/chunk-L4KBQAOO.js +0 -204
  89. package/dist/chunk-L4KBQAOO.js.map +0 -1
  90. package/dist/chunk-NJ4JKNBX.js +0 -361
  91. package/dist/chunk-NJ4JKNBX.js.map +0 -1
  92. package/dist/chunk-ORNTN6AK.cjs +0 -53
  93. package/dist/chunk-ORNTN6AK.cjs.map +0 -1
  94. package/dist/chunk-PSUINLW7.js +0 -19
  95. package/dist/chunk-PSUINLW7.js.map +0 -1
  96. package/dist/chunk-RU26QFM2.js +0 -179
  97. package/dist/chunk-RU26QFM2.js.map +0 -1
  98. package/dist/chunk-XDH5QQYF.cjs +0 -361
  99. package/dist/chunk-XDH5QQYF.cjs.map +0 -1
  100. package/dist/chunk-YCTGHWTP.cjs +0 -204
  101. package/dist/chunk-YCTGHWTP.cjs.map +0 -1
  102. package/dist/chunk-ZI6A3WQP.cjs +0 -179
  103. package/dist/chunk-ZI6A3WQP.cjs.map +0 -1
@@ -1,160 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
-
3
- var _chunkF2RNXJGOcjs = require('./chunk-F2RNXJGO.cjs');
4
-
5
- // src/identity.ts
6
- var SESSION_ID_HEADER = "reopt-session-id";
7
- var IDENTITY_REQUEST_HEADERS = [_chunkF2RNXJGOcjs.DEVICE_ID_HEADER, SESSION_ID_HEADER];
8
- var COOKIE_MAX_AGE_SECONDS = 400 * 24 * 60 * 60;
9
- var OPT_OUT_CONSENT_CATEGORY = "analytics";
10
- var COOKIE_PREFIX = "reopt_";
11
- function cookieSafe(writeKey) {
12
- if (!writeKey) {
13
- throw new Error("[reopt] writeKey is required to derive cookie names");
14
- }
15
- return writeKey.replace(/[^A-Za-z0-9_-]/g, "_");
16
- }
17
- function deviceCookieName(writeKey) {
18
- return `${COOKIE_PREFIX}${cookieSafe(writeKey)}_device`;
19
- }
20
- function consentCookieName(writeKey) {
21
- return `${COOKIE_PREFIX}${cookieSafe(writeKey)}_consent`;
22
- }
23
- var IDENTITY_ID_PATTERN = /^[A-Za-z0-9._~-]{1,200}$/;
24
- function isValidIdentityId(value) {
25
- return typeof value === "string" && IDENTITY_ID_PATTERN.test(value);
26
- }
27
- function decode(raw) {
28
- let text = raw;
29
- for (let round = 0; round < 3 && /%[0-9A-Fa-f]{2}/.test(text); round++) {
30
- try {
31
- const next = decodeURIComponent(text);
32
- if (next === text) break;
33
- text = next;
34
- } catch (e) {
35
- break;
36
- }
37
- if (text.startsWith("{")) break;
38
- }
39
- return text;
40
- }
41
- function parseDeviceCookie(raw) {
42
- if (!raw) return null;
43
- const text = decode(raw.trim());
44
- if (!text) return null;
45
- if (!text.startsWith("{")) {
46
- return isValidIdentityId(text) ? { deviceId: text } : null;
47
- }
48
- let parsed;
49
- try {
50
- parsed = JSON.parse(text);
51
- } catch (e2) {
52
- return null;
53
- }
54
- if (typeof parsed !== "object" || parsed === null) return null;
55
- const candidate = parsed;
56
- if (!isValidIdentityId(candidate.deviceId)) return null;
57
- const state = { deviceId: candidate.deviceId };
58
- if (isValidIdentityId(candidate.sessionId)) state.sessionId = candidate.sessionId;
59
- if (isValidIdentityId(candidate.profileId)) state.profileId = candidate.profileId;
60
- return state;
61
- }
62
- function serializeDeviceCookie(state) {
63
- if (!isValidIdentityId(state.deviceId)) {
64
- throw new Error("[reopt] deviceId must be a non-empty string without control characters");
65
- }
66
- const envelope = { deviceId: state.deviceId };
67
- if (isValidIdentityId(state.sessionId)) envelope.sessionId = state.sessionId;
68
- if (isValidIdentityId(state.profileId)) envelope.profileId = state.profileId;
69
- return encodeURIComponent(JSON.stringify(envelope));
70
- }
71
- function parseConsentCookie(raw) {
72
- if (!raw) return null;
73
- const text = decode(raw.trim());
74
- if (!text.startsWith("{")) return null;
75
- let parsed;
76
- try {
77
- parsed = JSON.parse(text);
78
- } catch (e3) {
79
- return null;
80
- }
81
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
82
- const state = {};
83
- for (const [category, decision] of Object.entries(parsed)) {
84
- if (typeof decision === "boolean" && isValidIdentityId(category)) {
85
- state[category] = decision;
86
- }
87
- }
88
- return state;
89
- }
90
- function serializeConsentCookie(state) {
91
- const clean = {};
92
- for (const [category, decision] of Object.entries(state)) {
93
- if (typeof decision === "boolean" && isValidIdentityId(category)) {
94
- clean[category] = decision;
95
- }
96
- }
97
- return encodeURIComponent(JSON.stringify(clean));
98
- }
99
- function isOptedOut(consent) {
100
- return _optionalChain([consent, 'optionalAccess', _ => _[OPT_OUT_CONSENT_CATEGORY]]) === false;
101
- }
102
- var SESSION_HEADER_SEPARATOR = ".";
103
- function formatSessionHeader(credential) {
104
- return `${credential.id}${SESSION_HEADER_SEPARATOR}${credential.token}`;
105
- }
106
- function parseSessionHeader(value) {
107
- if (!value) return null;
108
- const at = value.lastIndexOf(SESSION_HEADER_SEPARATOR);
109
- if (at <= 0 || at === value.length - 1) return null;
110
- const id = value.slice(0, at);
111
- const token = value.slice(at + 1);
112
- return isValidIdentityId(id) && /^[A-Za-z0-9_-]{43}$/.test(token) ? { id, token } : null;
113
- }
114
- var encoder = new TextEncoder();
115
- function base64url(bytes) {
116
- let binary = "";
117
- for (const byte of new Uint8Array(bytes)) binary += String.fromCharCode(byte);
118
- const b64 = typeof btoa === "function" ? btoa(binary) : Buffer.from(binary, "binary").toString("base64");
119
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
120
- }
121
- async function hmacKey(secret) {
122
- return crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
123
- }
124
- async function signSessionToken(secret, sessionId) {
125
- if (!secret) throw new Error("[reopt] a session signing secret is required");
126
- const signature = await crypto.subtle.sign("HMAC", await hmacKey(secret), encoder.encode(sessionId));
127
- return base64url(signature);
128
- }
129
- async function verifySessionToken(secret, credential) {
130
- if (!secret || !credential) return false;
131
- const expected = await signSessionToken(secret, credential.id);
132
- if (expected.length !== credential.token.length) return false;
133
- let diff = 0;
134
- for (let index = 0; index < expected.length; index++) {
135
- diff |= expected.charCodeAt(index) ^ credential.token.charCodeAt(index);
136
- }
137
- return diff === 0;
138
- }
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
- exports.SESSION_ID_HEADER = SESSION_ID_HEADER; exports.IDENTITY_REQUEST_HEADERS = IDENTITY_REQUEST_HEADERS; exports.COOKIE_MAX_AGE_SECONDS = COOKIE_MAX_AGE_SECONDS; exports.OPT_OUT_CONSENT_CATEGORY = OPT_OUT_CONSENT_CATEGORY; exports.deviceCookieName = deviceCookieName; exports.consentCookieName = consentCookieName; exports.IDENTITY_ID_PATTERN = IDENTITY_ID_PATTERN; exports.isValidIdentityId = isValidIdentityId; exports.parseDeviceCookie = parseDeviceCookie; exports.serializeDeviceCookie = serializeDeviceCookie; exports.parseConsentCookie = parseConsentCookie; exports.serializeConsentCookie = serializeConsentCookie; exports.isOptedOut = isOptedOut; exports.SESSION_HEADER_SEPARATOR = SESSION_HEADER_SEPARATOR; exports.formatSessionHeader = formatSessionHeader; exports.parseSessionHeader = parseSessionHeader; exports.signSessionToken = signSessionToken; exports.verifySessionToken = verifySessionToken;
160
- //# sourceMappingURL=chunk-L3JPDRVG.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-L3JPDRVG.cjs","../src/identity.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACgCO,IAAM,kBAAA,EAAoB,kBAAA;AAG1B,IAAM,yBAAA,EAA2B,CAAC,kCAAA,EAAkB,iBAAiB,CAAA;AAMrE,IAAM,uBAAA,EAAyB,IAAA,EAAM,GAAA,EAAK,GAAA,EAAK,EAAA;AAG/C,IAAM,yBAAA,EAA2B,WAAA;AAExC,IAAM,cAAA,EAAgB,QAAA;AAOtB,SAAS,UAAA,CAAW,QAAA,EAA0B;AAC5C,EAAA,GAAA,CAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,qDAAqD,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,iBAAA,EAAmB,GAAG,CAAA;AAChD;AAUO,SAAS,gBAAA,CAAiB,QAAA,EAA0B;AACzD,EAAA,OAAO,CAAA,EAAA;AACT;AAGgB;AACP,EAAA;AACT;AAsCa;AAEG;AACP,EAAA;AACT;AAOS;AACH,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACI,MAAA;AACJ,MAAA;AACF,IAAA;AACE,MAAA;AACF,IAAA;AACI,IAAA;AACN,EAAA;AACO,EAAA;AACT;AASgB;AACT,EAAA;AACC,EAAA;AACD,EAAA;AAEA,EAAA;AACH,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACI,EAAA;AAEE,EAAA;AACD,EAAA;AAEC,EAAA;AACF,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAQgB;AACT,EAAA;AACG,IAAA;AACR,EAAA;AACM,EAAA;AACF,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAOgB;AACT,EAAA;AACC,EAAA;AACD,EAAA;AAED,EAAA;AACA,EAAA;AACF,IAAA;AACM,EAAA;AACN,IAAA;AACF,EAAA;AACI,EAAA;AAEE,EAAA;AACN,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEgB;AACR,EAAA;AACN,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAQgB;AACP,EAAA;AACT;AAgBa;AAQG;AACP,EAAA;AACT;AAGgB;AACT,EAAA;AACC,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAEM;AAEG;AACH,EAAA;AACJ,EAAA;AACM,EAAA;AACC,EAAA;AACT;AAEA;AACS,EAAA;AACT;AAGA;AACO,EAAA;AACC,EAAA;AACC,EAAA;AACT;AAOA;AACO,EAAA;AACC,EAAA;AACF,EAAA;AACA,EAAA;AACJ,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AD3KU;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/eric/reopt-ai/reopt-data/packages/data-contract/dist/chunk-L3JPDRVG.cjs","sourcesContent":[null,"/**\n * `@reopt-ai/data-contract/identity` — the names and encodings that carry a\n * visitor's identity between the browser SDK, the server SDK, the Next.js\n * proxy and the ingest pipeline.\n *\n * Every one of those parties reads or writes the same cookies and headers.\n * None of them depends on another (ingest does not import the SDKs), so this\n * module is the only place a name may be spelled out. A party that hardcodes\n * `reopt_<key>_device` instead of calling `deviceCookieName()` fails silently:\n * the browser sets one cookie, the server looks for another, and every\n * visitor is counted as new. `packages/db/src/__tests__/redis-namespace.test.ts`\n * guards Redis key prefixes for exactly the same reason.\n *\n * Nothing here touches `document`, `fetch` or Node built-ins — it runs in\n * browsers, edge runtimes and Node alike.\n */\nimport { DEVICE_ID_HEADER } from \"./version.js\";\n\nexport {\n CLIENT_ID_HEADER,\n CLIENT_SECRET_HEADER,\n CONTRACT_VERSION,\n CONTRACT_VERSION_HEADER,\n DEVICE_ID_HEADER,\n REQUEST_ID_HEADER,\n WRITE_KEY_HEADER,\n} from \"./version.js\";\n\n/**\n * Request header carrying the browser's current session id. The browser SDK\n * adds it to same-origin requests so a server-side `track()` can land in the\n * session the user is actually in; the server SDK forwards it to ingest.\n *\n * Ingest treats it as a *hint*, never as authority — a browser can put any\n * value here.\n */\nexport const SESSION_ID_HEADER = \"reopt-session-id\";\n\n/** Every request header the identity protocol may carry, for CORS allow-lists. */\nexport const IDENTITY_REQUEST_HEADERS = [DEVICE_ID_HEADER, SESSION_ID_HEADER] as const;\n\n/**\n * 400 days — the longest lifetime Chrome will honour. Anything longer is\n * silently clamped to this, so asking for more only hides the real expiry.\n */\nexport const COOKIE_MAX_AGE_SECONDS = 400 * 24 * 60 * 60;\n\n/** Consent category whose refusal means \"send nothing at all\". */\nexport const OPT_OUT_CONSENT_CATEGORY = \"analytics\";\n\nconst COOKIE_PREFIX = \"reopt_\";\n\n/**\n * Cookie names may not contain separators (`=`, `;`, `,`, whitespace) or\n * control characters. A write key is opaque to us, so anything outside the\n * safe set is folded to `_` — deterministically, so both ends agree.\n */\nfunction cookieSafe(writeKey: string): string {\n if (!writeKey) {\n throw new Error(\"[reopt] writeKey is required to derive cookie names\");\n }\n return writeKey.replace(/[^A-Za-z0-9_-]/g, \"_\");\n}\n\n/**\n * `reopt_<writeKey>_device` — device id plus session/profile hints.\n *\n * The write key is **sanitized**: any character outside `[A-Za-z0-9_-]`\n * becomes `_` (see `cookieSafe`), so do not derive prefix/suffix pieces by\n * probing this with a marker character — the marker is folded too. Always\n * call this with the real write key and compare whole names.\n */\nexport function deviceCookieName(writeKey: string): string {\n return `${COOKIE_PREFIX}${cookieSafe(writeKey)}_device`;\n}\n\n/** `reopt_<writeKey>_consent` — per-category consent decisions. Same sanitizing as `deviceCookieName`. */\nexport function consentCookieName(writeKey: string): string {\n return `${COOKIE_PREFIX}${cookieSafe(writeKey)}_consent`;\n}\n\n/** Everything the device cookie may carry. Only `deviceId` is required. */\nexport interface DeviceCookieState {\n deviceId: string;\n /** Current session, if the browser has one. */\n sessionId?: string | undefined;\n /** Last identified profile. A hint for the server SDK, never trusted by ingest. */\n profileId?: string | undefined;\n}\n\n/** Per-category consent. `false` withdraws, `true` grants, absent = undecided. */\nexport type ConsentCookieState = Record<string, boolean>;\n\n/**\n * What the server hands the browser SDK so the first render already agrees\n * with the server about who the visitor is. Plain JSON — it crosses the\n * RSC → client component boundary as a prop.\n */\nexport interface ReoptBootstrap {\n /** From the device cookie (seeded by the proxy on first visit). */\n deviceId: string;\n /** Session hint, or `null` when the browser has not started one. */\n sessionId: string | null;\n /** Result of the server's `getProfileId` resolver, or `null`. */\n profileId: string | null;\n consent: ConsentCookieState;\n /** Server clock at render time, so a skewed device clock can be corrected. */\n serverTimeMs: number;\n}\n\n/**\n * Ids are opaque, but a cookie is attacker-controlled input (a sibling\n * subdomain can set one). The value ends up in an HTTP header, and\n * `Headers.set` throws on anything outside ISO-8859-1 — so a single odd\n * cookie would break every `fetch` the tracing patch touches. Only the URL\n * \"unreserved\" set is accepted; every id this SDK mints is a UUID.\n */\nexport const IDENTITY_ID_PATTERN = /^[A-Za-z0-9._~-]{1,200}$/;\n\nexport function isValidIdentityId(value: unknown): value is string {\n return typeof value === \"string\" && IDENTITY_ID_PATTERN.test(value);\n}\n\n/**\n * Decodes the envelope. Tolerates one extra layer (`%257B…`) — a cookie\n * setter that encodes an already-encoded value is a mistake we have shipped\n * once, and a visitor's identity should survive it rather than be reminted.\n */\nfunction decode(raw: string): string {\n let text = raw;\n for (let round = 0; round < 3 && /%[0-9A-Fa-f]{2}/.test(text); round++) {\n try {\n const next = decodeURIComponent(text);\n if (next === text) break;\n text = next;\n } catch {\n break;\n }\n if (text.startsWith(\"{\")) break;\n }\n return text;\n}\n\n/**\n * Reads a device cookie value. Accepts the JSON envelope written by\n * `serializeDeviceCookie` and, for cookies seeded before the envelope existed,\n * a bare device id. Returns `null` for anything else — a malformed cookie is\n * treated as absent, never as an error, so a corrupted value cannot take the\n * page down.\n */\nexport function parseDeviceCookie(raw: string | null | undefined): DeviceCookieState | null {\n if (!raw) return null;\n const text = decode(raw.trim());\n if (!text) return null;\n\n if (!text.startsWith(\"{\")) {\n return isValidIdentityId(text) ? { deviceId: text } : null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n\n const candidate = parsed as Record<string, unknown>;\n if (!isValidIdentityId(candidate.deviceId)) return null;\n\n const state: DeviceCookieState = { deviceId: candidate.deviceId };\n if (isValidIdentityId(candidate.sessionId)) state.sessionId = candidate.sessionId;\n if (isValidIdentityId(candidate.profileId)) state.profileId = candidate.profileId;\n return state;\n}\n\n/**\n * Cookie values may not contain `;`, `,` or whitespace, and JSON contains\n * quotes and commas. The envelope is therefore URI-encoded; `parseDeviceCookie`\n * reverses it. Optional fields are omitted rather than written as `null` so\n * the cookie stays as small as the state it carries.\n */\nexport function serializeDeviceCookie(state: DeviceCookieState): string {\n if (!isValidIdentityId(state.deviceId)) {\n throw new Error(\"[reopt] deviceId must be a non-empty string without control characters\");\n }\n const envelope: DeviceCookieState = { deviceId: state.deviceId };\n if (isValidIdentityId(state.sessionId)) envelope.sessionId = state.sessionId;\n if (isValidIdentityId(state.profileId)) envelope.profileId = state.profileId;\n return encodeURIComponent(JSON.stringify(envelope));\n}\n\n/**\n * Reads a consent cookie. Only boolean entries survive; anything else in the\n * object is dropped rather than failing the whole cookie, because one odd key\n * must not erase a user's recorded opt-out.\n */\nexport function parseConsentCookie(raw: string | null | undefined): ConsentCookieState | null {\n if (!raw) return null;\n const text = decode(raw.trim());\n if (!text.startsWith(\"{\")) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return null;\n\n const state: ConsentCookieState = {};\n for (const [category, decision] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof decision === \"boolean\" && isValidIdentityId(category)) {\n state[category] = decision;\n }\n }\n return state;\n}\n\nexport function serializeConsentCookie(state: ConsentCookieState): string {\n const clean: ConsentCookieState = {};\n for (const [category, decision] of Object.entries(state)) {\n if (typeof decision === \"boolean\" && isValidIdentityId(category)) {\n clean[category] = decision;\n }\n }\n return encodeURIComponent(JSON.stringify(clean));\n}\n\n/**\n * \"Opted out\" means the visitor refused the `analytics` category. An absent\n * cookie or an undecided category is *not* an opt-out — it is the default\n * state of every first visit, and treating it as refusal would make consent\n * banners mandatory for every integration.\n */\nexport function isOptedOut(consent: ConsentCookieState | null | undefined): boolean {\n return consent?.[OPT_OUT_CONSENT_CATEGORY] === false;\n}\n\n// ─── Session tokens ───────────────────────────────────────────────────────────\n\n/**\n * Sessions are assigned by ingest, not by the browser. To let a later request\n * — from the same browser, or from a server acting for it — join that session\n * with authority, ingest hands the browser its session id together with an\n * HMAC over it, and the browser sends both back as `reopt-session-id`. An\n * unsigned or wrongly signed value is a hint at most: ingest falls back to\n * deciding the session from the device, exactly as if the header were absent.\n *\n * The token is `base64url(HMAC-SHA256(secret, sessionId))`. The secret is\n * per project and never leaves the server. WebCrypto only, so this runs in\n * browsers, edge runtimes and Node alike.\n */\nexport const SESSION_HEADER_SEPARATOR = \".\";\n\nexport interface SessionCredential {\n id: string;\n token: string;\n}\n\n/** `<id>.<token>` — what travels in `reopt-session-id`. */\nexport function formatSessionHeader(credential: SessionCredential): string {\n return `${credential.id}${SESSION_HEADER_SEPARATOR}${credential.token}`;\n}\n\n/** Splits a `reopt-session-id` value. `null` unless both parts are well-formed. */\nexport function parseSessionHeader(value: string | null | undefined): SessionCredential | null {\n if (!value) return null;\n const at = value.lastIndexOf(SESSION_HEADER_SEPARATOR);\n if (at <= 0 || at === value.length - 1) return null;\n const id = value.slice(0, at);\n const token = value.slice(at + 1);\n return isValidIdentityId(id) && /^[A-Za-z0-9_-]{43}$/.test(token) ? { id, token } : null;\n}\n\nconst encoder = new TextEncoder();\n\nfunction base64url(bytes: ArrayBuffer): string {\n let binary = \"\";\n for (const byte of new Uint8Array(bytes)) binary += String.fromCharCode(byte);\n const b64 = typeof btoa === \"function\" ? btoa(binary) : Buffer.from(binary, \"binary\").toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nasync function hmacKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\"raw\", encoder.encode(secret), { name: \"HMAC\", hash: \"SHA-256\" }, false, [\"sign\"]);\n}\n\n/** Issues the token for `sessionId`. Server-side only — needs the project secret. */\nexport async function signSessionToken(secret: string, sessionId: string): Promise<string> {\n if (!secret) throw new Error(\"[reopt] a session signing secret is required\");\n const signature = await crypto.subtle.sign(\"HMAC\", await hmacKey(secret), encoder.encode(sessionId));\n return base64url(signature);\n}\n\n/**\n * Constant-time check of a presented token. Recomputes and compares the\n * digests byte by byte rather than using `subtle.verify` with a decoded\n * token, so a malformed token can never throw its way past the check.\n */\nexport async function verifySessionToken(secret: string, credential: SessionCredential): Promise<boolean> {\n if (!secret || !credential) return false;\n const expected = await signSessionToken(secret, credential.id);\n if (expected.length !== credential.token.length) return false;\n let diff = 0;\n for (let index = 0; index < expected.length; index++) {\n diff |= expected.charCodeAt(index) ^ credential.token.charCodeAt(index);\n }\n return diff === 0;\n}\n"]}
@@ -1,204 +0,0 @@
1
- import {
2
- zTimezone
3
- } from "./chunk-NJ4JKNBX.js";
4
- import {
5
- zInstant
6
- } from "./chunk-JCEWUSUM.js";
7
-
8
- // src/control.ts
9
- import { z } from "zod";
10
- var PLATFORM_KEY_HEADER = "reopt-platform-key";
11
- var ORG_KEY_HEADER = "reopt-org-key";
12
- var zExternalId = z.string().min(1).max(191);
13
- var zCreateOrganizationInput = z.object({
14
- externalId: zExternalId,
15
- name: z.string().min(1).max(200),
16
- slug: z.string().min(1).max(100).regex(/^[a-z0-9-]+$/, "lowercase letters, digits and hyphens only").optional(),
17
- monthlyEventQuota: z.number().int().nonnegative().optional(),
18
- monthlyCreditQuota: z.number().int().nonnegative().optional()
19
- });
20
- var zOrganization = z.object({
21
- id: z.string(),
22
- externalId: zExternalId,
23
- name: z.string(),
24
- slug: z.string(),
25
- monthlyEventQuota: z.number().int(),
26
- monthlyCreditQuota: z.number().int()
27
- });
28
- var zCreateOrganizationResponse = z.object({
29
- /** `false` when this externalId already had an organization. */
30
- created: z.boolean(),
31
- organization: zOrganization,
32
- /**
33
- * Plaintext, returned here and nowhere else.
34
- *
35
- * Absent when `created` is `false`: re-issuing on every retry would let a
36
- * retry quietly multiply live credentials. A caller that lost the key rotates
37
- * instead — see `POST /api/v1/organizations/{id}/keys`.
38
- */
39
- orgKey: z.string().optional()
40
- });
41
- var zRotateOrganizationKeyResponse = z.object({
42
- organizationId: z.string(),
43
- keyPrefix: z.string(),
44
- /** Plaintext, returned once. Every previously issued key is revoked immediately. */
45
- orgKey: z.string()
46
- });
47
- var zUpdateOrganizationQuotaInput = z.object({
48
- monthlyEventQuota: z.number().int().nonnegative().optional(),
49
- monthlyCreditQuota: z.number().int().nonnegative().optional()
50
- }).refine((value) => value.monthlyEventQuota !== void 0 || value.monthlyCreditQuota !== void 0, {
51
- message: "at least one quota must be given"
52
- });
53
- var zUpdateOrganizationQuotaResponse = z.object({ organization: zOrganization });
54
- var zCreateProjectInput = z.object({
55
- externalId: zExternalId,
56
- name: z.string().min(1).max(200),
57
- domain: z.string().max(253).optional(),
58
- /** Becomes the project's default for every query that omits one. */
59
- timezone: zTimezone.optional(),
60
- retentionDays: z.number().int().min(1).max(3650).optional()
61
- });
62
- var zProject = z.object({
63
- id: z.string(),
64
- externalId: zExternalId,
65
- organizationId: z.string(),
66
- name: z.string(),
67
- /** The applied value — `"UTC"` when none was given. */
68
- timezone: z.string(),
69
- retentionDays: z.number().int().nullable()
70
- });
71
- var zCreateProjectResponse = z.object({
72
- created: z.boolean(),
73
- project: zProject,
74
- /**
75
- * Two credentials, not one. A key embedded in a browser bundle must not also
76
- * be able to read the project's analytics, so the browser write key and the
77
- * server secret are separate rows with different reach.
78
- */
79
- clients: z.object({
80
- browser: z.object({
81
- id: z.string(),
82
- /** Public by design, and recoverable — it ships in page source anyway. */
83
- writeKey: z.string(),
84
- scopes: z.array(z.string())
85
- }),
86
- server: z.object({
87
- id: z.string(),
88
- /** Plaintext, returned once. Absent when `created` is `false`. */
89
- clientSecret: z.string().optional(),
90
- scopes: z.array(z.string())
91
- })
92
- })
93
- });
94
- var zRotateClientSecretResponse = z.object({
95
- projectId: z.string(),
96
- clientId: z.string(),
97
- /** Plaintext, returned once. The previous secret stops working immediately. */
98
- clientSecret: z.string()
99
- });
100
- var zDeleteProjectInput = z.object({
101
- /**
102
- * A field in the body rather than `?purge=true`. This deletes the project's
103
- * Postgres rows and every analytics row behind it, with no undo — and a query
104
- * parameter is far too easy to fire from shell history or a mis-copied URL.
105
- */
106
- purge: z.literal(true),
107
- /** Checked against the project on record; a mismatch is refused. */
108
- externalId: zExternalId
109
- });
110
- var zDeleteProjectResponse = z.object({
111
- projectId: z.string(),
112
- /** Idempotent, but it still says which of the two happened. */
113
- purge: z.enum(["queued", "already-absent"]),
114
- /** When the purge was marked. Absent for `already-absent`. */
115
- purgingAt: zInstant.optional()
116
- });
117
- var EVENT_META_STATUSES = ["active", "deprecated", "archived", "internal"];
118
- var zEventMetaStatus = z.enum(EVENT_META_STATUSES);
119
- var zEventName = z.string().min(1).max(200);
120
- var zUpsertEventMetaInput = z.object({
121
- conversion: z.boolean().optional(),
122
- /** `internal` events are left out of every metric unless a query opts in. */
123
- status: zEventMetaStatus.optional(),
124
- /** Property keys the hourly rollups break this event down by. */
125
- rollupProperties: z.array(z.string().min(1).max(200)).max(20).optional()
126
- }).strict();
127
- var zEventMetaResponse = z.object({
128
- projectId: z.string(),
129
- name: z.string(),
130
- conversion: z.boolean(),
131
- status: zEventMetaStatus,
132
- rollupProperties: z.array(z.string()),
133
- /** Fields this call actually changed; empty on a repeat. */
134
- changed: z.array(z.string()),
135
- updatedAt: zInstant
136
- });
137
- var CONTROL_ERROR_CODES = [
138
- /** 401 — missing or invalid platform key / organization key. */
139
- "unauthorized",
140
- /** 403 — the organization key belongs to a different organization. */
141
- "organization_scope_mismatch",
142
- /**
143
- * 409 — this externalId exists with a materially different configuration.
144
- * Only fields whose change alters results are compared (see the docs); a
145
- * renamed brand is not a conflict.
146
- */
147
- "external_id_conflict",
148
- /** 409 — the externalId in a delete body does not match the project's. */
149
- "project_external_id_mismatch",
150
- /** 404 — no such organization or project, on an endpoint that is not idempotent. */
151
- "not_found",
152
- /** 400 — input failed the schema. */
153
- "validation_failed",
154
- /** 429 — per-credential rate limit. */
155
- "rate_limited",
156
- /** 500 — unexpected server failure. */
157
- "internal_error"
158
- ];
159
- var zControlErrorCode = z.enum(CONTROL_ERROR_CODES);
160
- var zControlError = z.object({
161
- status: z.number().int(),
162
- code: zControlErrorCode,
163
- error: z.string(),
164
- message: z.string().optional(),
165
- errors: z.unknown().optional(),
166
- requestId: z.string().optional()
167
- });
168
- var CONTROL_API_PATHS = {
169
- organizations: "/api/v1/organizations",
170
- organization: (id) => `/api/v1/organizations/${encodeURIComponent(id)}`,
171
- organizationKeys: (id) => `/api/v1/organizations/${encodeURIComponent(id)}/keys`,
172
- projects: "/api/v1/projects",
173
- project: (id) => `/api/v1/projects/${encodeURIComponent(id)}`,
174
- clientRotate: (projectId, clientId) => `/api/v1/projects/${encodeURIComponent(projectId)}/clients/${encodeURIComponent(clientId)}/rotate`,
175
- projectEvent: (projectId, name) => `/api/v1/projects/${encodeURIComponent(projectId)}/events/${encodeURIComponent(name)}`
176
- };
177
-
178
- export {
179
- PLATFORM_KEY_HEADER,
180
- ORG_KEY_HEADER,
181
- zExternalId,
182
- zCreateOrganizationInput,
183
- zOrganization,
184
- zCreateOrganizationResponse,
185
- zRotateOrganizationKeyResponse,
186
- zUpdateOrganizationQuotaInput,
187
- zUpdateOrganizationQuotaResponse,
188
- zCreateProjectInput,
189
- zProject,
190
- zCreateProjectResponse,
191
- zRotateClientSecretResponse,
192
- zDeleteProjectInput,
193
- zDeleteProjectResponse,
194
- EVENT_META_STATUSES,
195
- zEventMetaStatus,
196
- zEventName,
197
- zUpsertEventMetaInput,
198
- zEventMetaResponse,
199
- CONTROL_ERROR_CODES,
200
- zControlErrorCode,
201
- zControlError,
202
- CONTROL_API_PATHS
203
- };
204
- //# sourceMappingURL=chunk-L4KBQAOO.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/control.ts"],"sourcesContent":["/**\n * `POST /api/v1/organizations` · `/api/v1/projects` and friends — the\n * provisioning contract.\n *\n * A project-scoped client credential cannot authenticate the creation of the\n * project it is scoped to, and an organization key cannot authenticate the\n * creation of its own organization. So provisioning runs on two credentials\n * above the client one: a platform key that creates organizations and writes\n * quotas, and an organization key that creates and deletes projects inside one\n * organization.\n *\n * Quota writes sit with the platform key on purpose. Organization keys are held\n * one-per-workspace by the caller and so have a wide exposure surface; a leaked\n * one that could raise its own limits would turn a credential leak into an\n * unbounded bill.\n */\nimport { z } from \"zod\";\nimport { zInstant } from \"./index.js\";\nimport { zTimezone } from \"./query.js\";\n\nexport const PLATFORM_KEY_HEADER = \"reopt-platform-key\";\nexport const ORG_KEY_HEADER = \"reopt-org-key\";\n\n/**\n * The caller's own id for the thing being provisioned.\n *\n * Preferred over an `Idempotency-Key` header because this domain has a real\n * natural key — the caller already has a stable id for the workspace or brand —\n * and a header would make the caller store a second one just to retry safely.\n */\nexport const zExternalId = z.string().min(1).max(191);\n\n// ─── Organizations ──────────────────────────────────────────────────────────\n\nexport const zCreateOrganizationInput = z.object({\n externalId: zExternalId,\n name: z.string().min(1).max(200),\n slug: z\n .string()\n .min(1)\n .max(100)\n .regex(/^[a-z0-9-]+$/, \"lowercase letters, digits and hyphens only\")\n .optional(),\n monthlyEventQuota: z.number().int().nonnegative().optional(),\n monthlyCreditQuota: z.number().int().nonnegative().optional(),\n});\nexport type CreateOrganizationInput = z.input<typeof zCreateOrganizationInput>;\n\nexport const zOrganization = z.object({\n id: z.string(),\n externalId: zExternalId,\n name: z.string(),\n slug: z.string(),\n monthlyEventQuota: z.number().int(),\n monthlyCreditQuota: z.number().int(),\n});\n\nexport const zCreateOrganizationResponse = z.object({\n /** `false` when this externalId already had an organization. */\n created: z.boolean(),\n organization: zOrganization,\n /**\n * Plaintext, returned here and nowhere else.\n *\n * Absent when `created` is `false`: re-issuing on every retry would let a\n * retry quietly multiply live credentials. A caller that lost the key rotates\n * instead — see `POST /api/v1/organizations/{id}/keys`.\n */\n orgKey: z.string().optional(),\n});\nexport type CreateOrganizationResponse = z.infer<typeof zCreateOrganizationResponse>;\n\nexport const zRotateOrganizationKeyResponse = z.object({\n organizationId: z.string(),\n keyPrefix: z.string(),\n /** Plaintext, returned once. Every previously issued key is revoked immediately. */\n orgKey: z.string(),\n});\nexport type RotateOrganizationKeyResponse = z.infer<typeof zRotateOrganizationKeyResponse>;\n\nexport const zUpdateOrganizationQuotaInput = z\n .object({\n monthlyEventQuota: z.number().int().nonnegative().optional(),\n monthlyCreditQuota: z.number().int().nonnegative().optional(),\n })\n .refine((value) => value.monthlyEventQuota !== undefined || value.monthlyCreditQuota !== undefined, {\n message: \"at least one quota must be given\",\n });\nexport type UpdateOrganizationQuotaInput = z.input<typeof zUpdateOrganizationQuotaInput>;\n\nexport const zUpdateOrganizationQuotaResponse = z.object({ organization: zOrganization });\nexport type UpdateOrganizationQuotaResponse = z.infer<typeof zUpdateOrganizationQuotaResponse>;\n\n// ─── Projects ───────────────────────────────────────────────────────────────\n\nexport const zCreateProjectInput = z.object({\n externalId: zExternalId,\n name: z.string().min(1).max(200),\n domain: z.string().max(253).optional(),\n /** Becomes the project's default for every query that omits one. */\n timezone: zTimezone.optional(),\n retentionDays: z.number().int().min(1).max(3650).optional(),\n});\nexport type CreateProjectInput = z.input<typeof zCreateProjectInput>;\n\nexport const zProject = z.object({\n id: z.string(),\n externalId: zExternalId,\n organizationId: z.string(),\n name: z.string(),\n /** The applied value — `\"UTC\"` when none was given. */\n timezone: z.string(),\n retentionDays: z.number().int().nullable(),\n});\n\nexport const zCreateProjectResponse = z.object({\n created: z.boolean(),\n project: zProject,\n /**\n * Two credentials, not one. A key embedded in a browser bundle must not also\n * be able to read the project's analytics, so the browser write key and the\n * server secret are separate rows with different reach.\n */\n clients: z.object({\n browser: z.object({\n id: z.string(),\n /** Public by design, and recoverable — it ships in page source anyway. */\n writeKey: z.string(),\n scopes: z.array(z.string()),\n }),\n server: z.object({\n id: z.string(),\n /** Plaintext, returned once. Absent when `created` is `false`. */\n clientSecret: z.string().optional(),\n scopes: z.array(z.string()),\n }),\n }),\n});\nexport type CreateProjectResponse = z.infer<typeof zCreateProjectResponse>;\n\nexport const zRotateClientSecretResponse = z.object({\n projectId: z.string(),\n clientId: z.string(),\n /** Plaintext, returned once. The previous secret stops working immediately. */\n clientSecret: z.string(),\n});\nexport type RotateClientSecretResponse = z.infer<typeof zRotateClientSecretResponse>;\n\nexport const zDeleteProjectInput = z.object({\n /**\n * A field in the body rather than `?purge=true`. This deletes the project's\n * Postgres rows and every analytics row behind it, with no undo — and a query\n * parameter is far too easy to fire from shell history or a mis-copied URL.\n */\n purge: z.literal(true),\n /** Checked against the project on record; a mismatch is refused. */\n externalId: zExternalId,\n});\nexport type DeleteProjectInput = z.input<typeof zDeleteProjectInput>;\n\nexport const zDeleteProjectResponse = z.object({\n projectId: z.string(),\n /** Idempotent, but it still says which of the two happened. */\n purge: z.enum([\"queued\", \"already-absent\"]),\n /** When the purge was marked. Absent for `already-absent`. */\n purgingAt: zInstant.optional(),\n});\nexport type DeleteProjectResponse = z.infer<typeof zDeleteProjectResponse>;\n\n// ─── Errors ─────────────────────────────────────────────────────────────────\n\n// ─── Event metadata ─────────────────────────────────────────────────────────\n\n/**\n * `PUT /api/v1/projects/{projectId}/events/{name}` — catalogue settings for\n * one event name. Provisioning calls it to mark `form.submitted` a conversion\n * and an operational probe `internal`; the call is idempotent so a retried\n * provisioning run is harmless.\n */\nexport const EVENT_META_STATUSES = [\"active\", \"deprecated\", \"archived\", \"internal\"] as const;\nexport const zEventMetaStatus = z.enum(EVENT_META_STATUSES);\nexport type EventMetaStatus = z.infer<typeof zEventMetaStatus>;\n\nexport const zEventName = z.string().min(1).max(200);\n\nexport const zUpsertEventMetaInput = z\n .object({\n conversion: z.boolean().optional(),\n /** `internal` events are left out of every metric unless a query opts in. */\n status: zEventMetaStatus.optional(),\n /** Property keys the hourly rollups break this event down by. */\n rollupProperties: z.array(z.string().min(1).max(200)).max(20).optional(),\n })\n .strict();\nexport type UpsertEventMetaInput = z.infer<typeof zUpsertEventMetaInput>;\n\nexport const zEventMetaResponse = z.object({\n projectId: z.string(),\n name: z.string(),\n conversion: z.boolean(),\n status: zEventMetaStatus,\n rollupProperties: z.array(z.string()),\n /** Fields this call actually changed; empty on a repeat. */\n changed: z.array(z.string()),\n updatedAt: zInstant,\n});\nexport type EventMetaResponse = z.infer<typeof zEventMetaResponse>;\n\nexport const CONTROL_ERROR_CODES = [\n /** 401 — missing or invalid platform key / organization key. */\n \"unauthorized\",\n /** 403 — the organization key belongs to a different organization. */\n \"organization_scope_mismatch\",\n /**\n * 409 — this externalId exists with a materially different configuration.\n * Only fields whose change alters results are compared (see the docs); a\n * renamed brand is not a conflict.\n */\n \"external_id_conflict\",\n /** 409 — the externalId in a delete body does not match the project's. */\n \"project_external_id_mismatch\",\n /** 404 — no such organization or project, on an endpoint that is not idempotent. */\n \"not_found\",\n /** 400 — input failed the schema. */\n \"validation_failed\",\n /** 429 — per-credential rate limit. */\n \"rate_limited\",\n /** 500 — unexpected server failure. */\n \"internal_error\",\n] as const;\nexport const zControlErrorCode = z.enum(CONTROL_ERROR_CODES);\nexport type ControlErrorCode = z.infer<typeof zControlErrorCode>;\n\nexport const zControlError = z.object({\n status: z.number().int(),\n code: zControlErrorCode,\n error: z.string(),\n message: z.string().optional(),\n errors: z.unknown().optional(),\n requestId: z.string().optional(),\n});\nexport type ControlError = z.infer<typeof zControlError>;\n\n/** Paths, so the client and the route handlers cannot drift apart. */\nexport const CONTROL_API_PATHS = {\n organizations: \"/api/v1/organizations\",\n organization: (id: string) => `/api/v1/organizations/${encodeURIComponent(id)}`,\n organizationKeys: (id: string) => `/api/v1/organizations/${encodeURIComponent(id)}/keys`,\n projects: \"/api/v1/projects\",\n project: (id: string) => `/api/v1/projects/${encodeURIComponent(id)}`,\n clientRotate: (projectId: string, clientId: string) =>\n `/api/v1/projects/${encodeURIComponent(projectId)}/clients/${encodeURIComponent(clientId)}/rotate`,\n projectEvent: (projectId: string, name: string) =>\n `/api/v1/projects/${encodeURIComponent(projectId)}/events/${encodeURIComponent(name)}`,\n} as const;\n"],"mappings":"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","names":[]}