@carlonicora/nextjs-jsonapi 5.1.3 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BlockNoteEditor-66FJUDL4.mjs → BlockNoteEditor-2QJVK2HH.mjs} +4 -4
- package/dist/{BlockNoteEditor-KO6YFNIU.js → BlockNoteEditor-EGY4ZAAM.js} +19 -19
- package/dist/{BlockNoteEditor-KO6YFNIU.js.map → BlockNoteEditor-EGY4ZAAM.js.map} +1 -1
- package/dist/{BlockNoteViewer-SUK7OMWH.mjs → BlockNoteViewer-6BQXJ4TB.mjs} +2 -2
- package/dist/{BlockNoteViewer-LK22AE7Z.js → BlockNoteViewer-I2MMFH3S.js} +3 -3
- package/dist/{BlockNoteViewer-LK22AE7Z.js.map → BlockNoteViewer-I2MMFH3S.js.map} +1 -1
- package/dist/auth.interface-CSzJ1oP1.d.ts +64 -0
- package/dist/auth.interface-DD4uQv2K.d.mts +64 -0
- package/dist/billing/index.js +441 -441
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-5JADF7ZA.mjs → chunk-2Z3ZCYXX.mjs} +171 -95
- package/dist/chunk-2Z3ZCYXX.mjs.map +1 -0
- package/dist/{chunk-SZQNUGDG.js → chunk-3VEQ5JZJ.js} +1005 -929
- package/dist/chunk-3VEQ5JZJ.js.map +1 -0
- package/dist/{chunk-TO6IYGML.js → chunk-75MLT74B.js} +126 -8
- package/dist/chunk-75MLT74B.js.map +1 -0
- package/dist/{chunk-67NJY7SM.js → chunk-EXICQC4A.js} +3 -3
- package/dist/{chunk-67NJY7SM.js.map → chunk-EXICQC4A.js.map} +1 -1
- package/dist/{chunk-OCJEXAGE.mjs → chunk-HNOWSFM5.mjs} +55 -4
- package/dist/chunk-HNOWSFM5.mjs.map +1 -0
- package/dist/{chunk-OKUGTXDN.mjs → chunk-JEZQTZQ4.mjs} +121 -3
- package/dist/chunk-JEZQTZQ4.mjs.map +1 -0
- package/dist/{chunk-KL7CAQP3.mjs → chunk-NPKFMRH5.mjs} +2 -2
- package/dist/{chunk-VVJDWQEI.js → chunk-S5MQDWWC.js} +55 -4
- package/dist/chunk-S5MQDWWC.js.map +1 -0
- package/dist/{chunk-XCHXOCEJ.js → chunk-THKYH65N.js} +4 -4
- package/dist/{chunk-XCHXOCEJ.js.map → chunk-THKYH65N.js.map} +1 -1
- package/dist/{chunk-XLVRAHUK.mjs → chunk-YMUPTXOT.mjs} +2 -2
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +17 -3
- package/dist/components/index.d.ts +17 -3
- package/dist/components/index.js +6 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +5 -3
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +27 -7
- package/dist/core/index.d.ts +27 -7
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/features/analytics/index.js +122 -122
- package/dist/features/analytics/index.mjs +4 -4
- package/dist/features/analytics/server.js +4 -4
- package/dist/features/analytics/server.mjs +2 -2
- package/dist/features/help/index.js +37 -37
- package/dist/features/help/index.mjs +3 -3
- package/dist/features/tokenusage/index.js +125 -125
- package/dist/features/tokenusage/index.mjs +4 -4
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -3
- package/dist/{s3.service-C-4YpEMu.d.ts → s3.service-CY-jgOAM.d.ts} +1 -1
- package/dist/{s3.service-dVj-nxMl.d.mts → s3.service-Sri0QP7Z.d.mts} +1 -1
- package/dist/server/index.d.mts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +2 -2
- package/dist/{waitlist.config-kPfjImle.d.mts → waitlist.config-Cagmgm7-.d.mts} +7 -0
- package/dist/{waitlist.config-kPfjImle.d.ts → waitlist.config-Cagmgm7-.d.ts} +7 -0
- package/messages/en.json +15 -1
- package/messages/it.json +15 -1
- package/package.json +1 -1
- package/scripts/validate-messages.mjs +1 -0
- package/src/components/index.ts +1 -0
- package/src/features/auth/components/buttons/GoogleSignInButton.tsx +8 -4
- package/src/features/auth/components/forms/Register.tsx +20 -8
- package/src/features/auth/data/auth.interface.ts +2 -0
- package/src/features/auth/data/auth.ts +10 -0
- package/src/features/signup-source/SignupSourceCapture.tsx +27 -0
- package/src/features/signup-source/__tests__/signup-source.spec.ts +161 -0
- package/src/features/signup-source/index.ts +9 -0
- package/src/features/signup-source/signup-source.ts +135 -0
- package/src/features/waitlist/components/forms/WaitlistForm.tsx +2 -0
- package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +15 -13
- package/src/features/waitlist/components/lists/WaitlistList.tsx +60 -22
- package/src/features/waitlist/components/sections/WaitlistHeroSection.tsx +6 -5
- package/src/features/waitlist/config/waitlist.config.ts +23 -0
- package/src/features/waitlist/data/Waitlist.ts +51 -3
- package/src/features/waitlist/data/WaitlistInterface.ts +9 -1
- package/src/features/waitlist/data/WaitlistService.ts +3 -2
- package/src/features/waitlist/data/__tests__/Waitlist.spec.ts +82 -0
- package/src/features/waitlist/data/__tests__/WaitlistService.spec.ts +48 -0
- package/src/features/waitlist/data/waitlist.fields.ts +1 -0
- package/src/features/waitlist/hooks/__tests__/formatSignupSource.spec.ts +24 -0
- package/src/features/waitlist/hooks/formatSignupSource.ts +14 -0
- package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +26 -14
- package/src/hooks/__tests__/usePageTracker.spec.tsx +16 -10
- package/src/index.ts +3 -0
- package/dist/auth.interface-CeFCz9NU.d.mts +0 -34
- package/dist/auth.interface-D1R7VVky.d.ts +0 -34
- package/dist/chunk-5JADF7ZA.mjs.map +0 -1
- package/dist/chunk-OCJEXAGE.mjs.map +0 -1
- package/dist/chunk-OKUGTXDN.mjs.map +0 -1
- package/dist/chunk-SZQNUGDG.js.map +0 -1
- package/dist/chunk-TO6IYGML.js.map +0 -1
- package/dist/chunk-VVJDWQEI.js.map +0 -1
- /package/dist/{BlockNoteEditor-66FJUDL4.mjs.map → BlockNoteEditor-2QJVK2HH.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-SUK7OMWH.mjs.map → BlockNoteViewer-6BQXJ4TB.mjs.map} +0 -0
- /package/dist/{chunk-KL7CAQP3.mjs.map → chunk-NPKFMRH5.mjs.map} +0 -0
- /package/dist/{chunk-XLVRAHUK.mjs.map → chunk-YMUPTXOT.mjs.map} +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
var _chunkS5MQDWWCjs = require('./chunk-S5MQDWWC.js');
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -122,6 +123,15 @@ function getWaitlistConfig() {
|
|
|
122
123
|
return _waitlistConfig;
|
|
123
124
|
}
|
|
124
125
|
_chunk7QVYU63Ejs.__name.call(void 0, getWaitlistConfig, "getWaitlistConfig");
|
|
126
|
+
function useWaitlistText() {
|
|
127
|
+
const t = _chunkS5MQDWWCjs.useI18nTranslations.call(void 0, );
|
|
128
|
+
const translate = getWaitlistConfig().translate === true;
|
|
129
|
+
return (value) => {
|
|
130
|
+
if (!value) return void 0;
|
|
131
|
+
return translate ? t(value) : value;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
_chunk7QVYU63Ejs.__name.call(void 0, useWaitlistText, "useWaitlistText");
|
|
125
135
|
|
|
126
136
|
// src/features/referral/config.ts
|
|
127
137
|
var DEFAULT_REFERRAL_CONFIG = {
|
|
@@ -146,12 +156,113 @@ function isReferralEnabled() {
|
|
|
146
156
|
}
|
|
147
157
|
_chunk7QVYU63Ejs.__name.call(void 0, isReferralEnabled, "isReferralEnabled");
|
|
148
158
|
|
|
159
|
+
// src/features/signup-source/signup-source.ts
|
|
160
|
+
var SIGNUP_SOURCE_COOKIE = "signup_source";
|
|
161
|
+
var SIGNUP_SOURCE_COOKIE_MAX_AGE = 2592e3;
|
|
162
|
+
var SIGNUP_SOURCE_FIELDS = [
|
|
163
|
+
"sourceRef",
|
|
164
|
+
"sourceUtmSource",
|
|
165
|
+
"sourceUtmMedium",
|
|
166
|
+
"sourceUtmCampaign",
|
|
167
|
+
"sourceReferrer",
|
|
168
|
+
"sourceLandingPath"
|
|
169
|
+
];
|
|
170
|
+
var SIGNUP_SOURCE_MAX_LENGTH = 200;
|
|
171
|
+
var memorySource;
|
|
172
|
+
function clean(value) {
|
|
173
|
+
if (typeof value !== "string") return void 0;
|
|
174
|
+
const trimmed = value.trim();
|
|
175
|
+
if (!trimmed) return void 0;
|
|
176
|
+
return trimmed.slice(0, SIGNUP_SOURCE_MAX_LENGTH);
|
|
177
|
+
}
|
|
178
|
+
_chunk7QVYU63Ejs.__name.call(void 0, clean, "clean");
|
|
179
|
+
function sanitise(input) {
|
|
180
|
+
if (!input || typeof input !== "object") return void 0;
|
|
181
|
+
const result = {};
|
|
182
|
+
for (const field of SIGNUP_SOURCE_FIELDS) {
|
|
183
|
+
const value = clean(input[field]);
|
|
184
|
+
if (value !== void 0) result[field] = value;
|
|
185
|
+
}
|
|
186
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
187
|
+
}
|
|
188
|
+
_chunk7QVYU63Ejs.__name.call(void 0, sanitise, "sanitise");
|
|
189
|
+
function readSignupSourceFromLocation(params) {
|
|
190
|
+
let url;
|
|
191
|
+
try {
|
|
192
|
+
url = new URL(params.url);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
const sourceRef = clean(url.searchParams.get("ref"));
|
|
197
|
+
const sourceUtmSource = clean(url.searchParams.get("utm_source"));
|
|
198
|
+
const sourceUtmMedium = clean(url.searchParams.get("utm_medium"));
|
|
199
|
+
const sourceUtmCampaign = clean(url.searchParams.get("utm_campaign"));
|
|
200
|
+
let sourceReferrer;
|
|
201
|
+
if (params.referrer) {
|
|
202
|
+
try {
|
|
203
|
+
const referrerHost = new URL(params.referrer).host;
|
|
204
|
+
if (referrerHost && referrerHost !== params.ownHost) sourceReferrer = clean(referrerHost);
|
|
205
|
+
} catch (e2) {
|
|
206
|
+
sourceReferrer = void 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (!sourceRef && !sourceUtmSource && !sourceUtmMedium && !sourceUtmCampaign && !sourceReferrer) return void 0;
|
|
210
|
+
return sanitise({
|
|
211
|
+
sourceRef,
|
|
212
|
+
sourceUtmSource,
|
|
213
|
+
sourceUtmMedium,
|
|
214
|
+
sourceUtmCampaign,
|
|
215
|
+
sourceReferrer,
|
|
216
|
+
sourceLandingPath: url.pathname
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
_chunk7QVYU63Ejs.__name.call(void 0, readSignupSourceFromLocation, "readSignupSourceFromLocation");
|
|
220
|
+
function readSignupSourceCookie() {
|
|
221
|
+
if (typeof document === "undefined") return void 0;
|
|
222
|
+
const cookies = document.cookie ? document.cookie.split("; ") : [];
|
|
223
|
+
for (const cookie of cookies) {
|
|
224
|
+
const separator = cookie.indexOf("=");
|
|
225
|
+
if (separator === -1) continue;
|
|
226
|
+
if (cookie.slice(0, separator) !== SIGNUP_SOURCE_COOKIE) continue;
|
|
227
|
+
const value = cookie.slice(separator + 1);
|
|
228
|
+
if (!value) return void 0;
|
|
229
|
+
try {
|
|
230
|
+
return sanitise(JSON.parse(decodeURIComponent(value)));
|
|
231
|
+
} catch (e3) {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return void 0;
|
|
236
|
+
}
|
|
237
|
+
_chunk7QVYU63Ejs.__name.call(void 0, readSignupSourceCookie, "readSignupSourceCookie");
|
|
238
|
+
function writeSignupSourceCookie(source) {
|
|
239
|
+
document.cookie = `${SIGNUP_SOURCE_COOKIE}=${encodeURIComponent(JSON.stringify(source))}; path=/; max-age=${SIGNUP_SOURCE_COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
240
|
+
}
|
|
241
|
+
_chunk7QVYU63Ejs.__name.call(void 0, writeSignupSourceCookie, "writeSignupSourceCookie");
|
|
242
|
+
function captureSignupSource(params) {
|
|
243
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
244
|
+
const existingCookie = readSignupSourceCookie();
|
|
245
|
+
if (!memorySource) {
|
|
246
|
+
memorySource = _nullishCoalesce(existingCookie, () => ( readSignupSourceFromLocation({
|
|
247
|
+
url: window.location.href,
|
|
248
|
+
referrer: document.referrer,
|
|
249
|
+
ownHost: window.location.host
|
|
250
|
+
})));
|
|
251
|
+
}
|
|
252
|
+
if (params.persist && !existingCookie && memorySource) writeSignupSourceCookie(memorySource);
|
|
253
|
+
}
|
|
254
|
+
_chunk7QVYU63Ejs.__name.call(void 0, captureSignupSource, "captureSignupSource");
|
|
255
|
+
function getSignupSource() {
|
|
256
|
+
return _nullishCoalesce(memorySource, () => ( readSignupSourceCookie()));
|
|
257
|
+
}
|
|
258
|
+
_chunk7QVYU63Ejs.__name.call(void 0, getSignupSource, "getSignupSource");
|
|
259
|
+
|
|
149
260
|
// src/features/rbac/data/RbacTypes.ts
|
|
150
261
|
var COMPANY_ADMINISTRATOR_ROLE_ID = "2e1eee00-6cba-4506-9059-ccd24e4ea5b0";
|
|
151
262
|
var ACTION_TYPES = ["read", "create", "update", "delete"];
|
|
152
263
|
|
|
153
264
|
// src/features/rbac/data/RbacService.ts
|
|
154
|
-
var RbacService = class extends
|
|
265
|
+
var RbacService = class extends _chunkS5MQDWWCjs.AbstractService {
|
|
155
266
|
static {
|
|
156
267
|
_chunk7QVYU63Ejs.__name.call(void 0, this, "RbacService");
|
|
157
268
|
}
|
|
@@ -162,9 +273,9 @@ var RbacService = class extends _chunkVVJDWQEIjs.AbstractService {
|
|
|
162
273
|
* Dev-only endpoint — see class header.
|
|
163
274
|
*/
|
|
164
275
|
static async fetchMatrix() {
|
|
165
|
-
const endpoint = new (0,
|
|
276
|
+
const endpoint = new (0, _chunkS5MQDWWCjs.EndpointCreator)({ endpoint: _chunkS5MQDWWCjs.Modules.RbacMatrix }).generate();
|
|
166
277
|
const model = await this.callApi({
|
|
167
|
-
type:
|
|
278
|
+
type: _chunkS5MQDWWCjs.Modules.RbacMatrix,
|
|
168
279
|
method: "GET" /* GET */,
|
|
169
280
|
endpoint
|
|
170
281
|
});
|
|
@@ -184,9 +295,9 @@ var RbacService = class extends _chunkVVJDWQEIjs.AbstractService {
|
|
|
184
295
|
* Dev-only endpoint — see class header.
|
|
185
296
|
*/
|
|
186
297
|
static async saveMatrix(args) {
|
|
187
|
-
const endpoint = new (0,
|
|
298
|
+
const endpoint = new (0, _chunkS5MQDWWCjs.EndpointCreator)({ endpoint: _chunkS5MQDWWCjs.Modules.RbacMatrix }).generate();
|
|
188
299
|
const model = await this.callApi({
|
|
189
|
-
type:
|
|
300
|
+
type: _chunkS5MQDWWCjs.Modules.RbacMatrix,
|
|
190
301
|
method: "PUT" /* PUT */,
|
|
191
302
|
endpoint,
|
|
192
303
|
input: args
|
|
@@ -221,5 +332,12 @@ var RbacService = class extends _chunkVVJDWQEIjs.AbstractService {
|
|
|
221
332
|
|
|
222
333
|
|
|
223
334
|
|
|
224
|
-
|
|
225
|
-
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
exports._getStaticHelpContent = _getStaticHelpContent; exports.configureJsonApi = configureJsonApi; exports.configureClientConfig = configureClientConfig; exports.getApiUrl = getApiUrl; exports.getPublicApiUrl = getPublicApiUrl; exports.getAppUrl = getAppUrl; exports.getTrackablePages = getTrackablePages; exports.getStripePublishableKey = getStripePublishableKey; exports.configureLogin = configureLogin; exports.isDiscordAuthEnabled = isDiscordAuthEnabled; exports.isGoogleAuthEnabled = isGoogleAuthEnabled; exports.isInternalAuthEnabled = isInternalAuthEnabled; exports.isRegistrationAllowed = isRegistrationAllowed; exports.getRegistrationMode = getRegistrationMode; exports.configureWaitlist = configureWaitlist; exports.getWaitlistConfig = getWaitlistConfig; exports.useWaitlistText = useWaitlistText; exports.configureReferral = configureReferral; exports.getReferralConfig = getReferralConfig; exports.isReferralEnabled = isReferralEnabled; exports.SIGNUP_SOURCE_COOKIE = SIGNUP_SOURCE_COOKIE; exports.SIGNUP_SOURCE_COOKIE_MAX_AGE = SIGNUP_SOURCE_COOKIE_MAX_AGE; exports.SIGNUP_SOURCE_FIELDS = SIGNUP_SOURCE_FIELDS; exports.readSignupSourceFromLocation = readSignupSourceFromLocation; exports.captureSignupSource = captureSignupSource; exports.getSignupSource = getSignupSource; exports.COMPANY_ADMINISTRATOR_ROLE_ID = COMPANY_ADMINISTRATOR_ROLE_ID; exports.ACTION_TYPES = ACTION_TYPES; exports.RbacService = RbacService;
|
|
343
|
+
//# sourceMappingURL=chunk-75MLT74B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-75MLT74B.js","../src/core/registry/helpStore.ts","../src/client/config.ts","../src/login/config.ts","../src/features/waitlist/config/waitlist.config.ts","../src/features/referral/config.ts","../src/features/signup-source/signup-source.ts","../src/features/rbac/data/RbacTypes.ts","../src/features/rbac/data/RbacService.ts"],"names":[],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-75MLT74B.js","sourcesContent":[null,"/**\n * Centralized help-content store accessible from client and server contexts.\n * Uses a globalThis Symbol key to persist across HMR/Turbopack reloads and to\n * bridge between the client-side `configureJsonApi` (in `client/config.ts`)\n * and the HelpProvider context. Holds the brand-only HelpContentConfig.\n *\n * NO external dependencies to avoid circular imports.\n *\n * Pattern mirrors `bootstrapStore.ts`.\n */\n\nconst HELP_CONTENT_KEY = Symbol.for(\"nextjs-jsonapi:helpContent\");\n\nconst globalStore = globalThis as unknown as {\n [HELP_CONTENT_KEY]?: unknown | null;\n};\n\nif (globalStore[HELP_CONTENT_KEY] === undefined) {\n globalStore[HELP_CONTENT_KEY] = null;\n}\n\nexport function _setStaticHelpContent(cfg: unknown | null): void {\n globalStore[HELP_CONTENT_KEY] = cfg;\n}\n\nexport function _getStaticHelpContent<T = unknown>(): T | null {\n return (globalStore[HELP_CONTENT_KEY] as T | null) ?? null;\n}\n","\"use client\";\n\nimport { ModuleWithPermissions } from \"../permissions/types\";\nimport { ENV } from \"../core/env\";\nimport { setBootstrapper } from \"../core/registry/bootstrapStore\";\nimport { _setStaticHelpContent } from \"../core/registry/helpStore\";\nimport type { HelpContentConfig } from \"../features/help/interfaces/help-content-config.interface\";\n\n// Config storage for client-side contexts\nlet _clientConfig: {\n apiUrl: string;\n appUrl?: string;\n trackablePages?: ModuleWithPermissions[];\n bootstrapper?: () => void;\n additionalHeaders?: Record<string, string>;\n stripePublishableKey?: string;\n} | null = null;\n\n/**\n * Configure the JSON:API client. This is the main configuration function.\n * This is typically called during app initialization.\n * @param config.helpContent - Optional help-content config (brand only). Forwarded to the help feature's globalThis-backed store; not stored on the client config.\n */\nexport function configureJsonApi(config: {\n apiUrl: string;\n appUrl?: string;\n trackablePages?: ModuleWithPermissions[];\n bootstrapper?: () => void;\n additionalHeaders?: Record<string, string>;\n stripePublishableKey?: string;\n helpContent?: HelpContentConfig;\n}): void {\n const { helpContent, ...rest } = config;\n _clientConfig = rest;\n if (helpContent) {\n _setStaticHelpContent(helpContent);\n }\n // Register and call bootstrapper to register all modules\n if (config.bootstrapper) {\n setBootstrapper(config.bootstrapper);\n config.bootstrapper();\n }\n}\n\n/**\n * Configure the client config. This is typically called during app initialization.\n * @deprecated Use configureJsonApi instead\n */\nexport function configureClientConfig(config: {\n apiUrl: string;\n appUrl?: string;\n trackablePages?: ModuleWithPermissions[];\n}): void {\n _clientConfig = config;\n}\n\n/**\n * Get the configured API URL.\n *\n * This may resolve to an internal/private host (e.g. a docker-network\n * hostname) when configured to do so for SSR fetches. Do NOT use this for\n * URLs that are rendered into HTML and followed by the browser — use\n * `getPublicApiUrl()` instead.\n */\nexport function getApiUrl(): string {\n if (_clientConfig?.apiUrl) {\n return _clientConfig.apiUrl;\n }\n return ENV.API_URL;\n}\n\n/**\n * Get the public-facing API URL.\n *\n * Always sourced from `NEXT_PUBLIC_API_URL` so it is identical on server\n * and client, and reachable from the user's browser. Use for any URL that\n * gets rendered into HTML the browser will navigate to (links, redirects,\n * OAuth hrefs).\n */\nexport function getPublicApiUrl(): string {\n return ENV.API_URL;\n}\n\n/**\n * Get the configured app URL.\n */\nexport function getAppUrl(): string {\n if (_clientConfig?.appUrl) {\n return _clientConfig.appUrl;\n }\n if (ENV.APP_URL_ALTERNATE) {\n return ENV.APP_URL_ALTERNATE;\n }\n if (typeof window !== \"undefined\") {\n return window.location.origin;\n }\n return \"\";\n}\n\n/**\n * Get the configured trackable pages.\n */\nexport function getTrackablePages(): ModuleWithPermissions[] {\n return _clientConfig?.trackablePages ?? [];\n}\n\n/**\n * Get the configured Stripe publishable key.\n */\nexport function getStripePublishableKey(): string | undefined {\n return _clientConfig?.stripePublishableKey;\n}\n","let _useDiscordAuth: boolean = false;\nlet _useGoogleAuth: boolean = false;\nlet _useInternalAuth: boolean = true;\nlet _allowRegistration: boolean = true;\nlet _registrationMode: \"open\" | \"closed\" | \"waitlist\" = \"open\";\n\nexport type RegistrationMode = \"open\" | \"closed\" | \"waitlist\";\n\nexport interface LoginConfig {\n discordClientId?: string;\n googleClientId?: string;\n useInternalAuth?: boolean;\n allowRegistration?: boolean;\n registrationMode?: RegistrationMode;\n}\n\nexport function configureLogin(params: LoginConfig): void {\n _useDiscordAuth = !!params.discordClientId;\n _useGoogleAuth = !!params.googleClientId;\n _useInternalAuth = params.useInternalAuth ?? true;\n _allowRegistration = params.allowRegistration ?? true;\n _registrationMode = params.registrationMode ?? \"open\";\n}\n\nexport function isDiscordAuthEnabled(): boolean {\n return _useDiscordAuth;\n}\n\nexport function isGoogleAuthEnabled(): boolean {\n return _useGoogleAuth;\n}\n\nexport function isInternalAuthEnabled(): boolean {\n return _useInternalAuth;\n}\n\nexport function isRegistrationAllowed(): boolean {\n return _allowRegistration;\n}\n\nexport function getRegistrationMode(): RegistrationMode {\n return _registrationMode;\n}\n","import { useI18nTranslations } from \"../../../i18n\";\n\nexport type QuestionnaireFieldType = \"text\" | \"textarea\" | \"select\" | \"checkbox\";\n\nexport interface QuestionnaireOption {\n value: string;\n label: string;\n description?: string;\n}\n\nexport interface QuestionnaireField {\n id: string;\n type: QuestionnaireFieldType;\n label: string;\n description?: string;\n placeholder?: string;\n required?: boolean;\n options?: QuestionnaireOption[];\n}\n\nexport interface WaitlistConfig {\n questionnaire?: QuestionnaireField[];\n heroTitle?: string;\n heroSubtitle?: string;\n heroDescription?: string;\n benefits?: string[];\n /**\n * When true, every text above (hero title, subtitle, description, benefits and\n * the questionnaire's labels, descriptions, placeholders and option labels) is\n * a message key and renders through `t()`. Defaults to false: the strings\n * render as given.\n */\n translate?: boolean;\n}\n\nlet _waitlistConfig: WaitlistConfig = {};\n\nexport function configureWaitlist(config: WaitlistConfig): void {\n _waitlistConfig = config;\n}\n\nexport function getWaitlistConfig(): WaitlistConfig {\n return _waitlistConfig;\n}\n\n/**\n * Resolves a waitlist config text: `t(value)` when the config sets\n * `translate`, the value itself otherwise. Empty values stay undefined.\n */\nexport function useWaitlistText(): (value?: string) => string | undefined {\n const t = useI18nTranslations();\n const translate = getWaitlistConfig().translate === true;\n\n return (value?: string) => {\n if (!value) return undefined;\n return translate ? t(value) : value;\n };\n}\n","/**\n * Configuration interface for frontend referral feature.\n */\nexport interface ReferralConfig {\n /**\n * Whether the referral feature is enabled.\n * When false, components render nothing and hooks return null.\n * @default false\n */\n enabled?: boolean;\n\n /**\n * Name of the cookie used to store referral codes.\n * @default \"referral_code\"\n */\n cookieName?: string;\n\n /**\n * Number of days the referral cookie is valid.\n * @default 30\n */\n cookieDays?: number;\n\n /**\n * Query parameter name for referral code in URL.\n * @default \"ref\"\n */\n urlParamName?: string;\n\n /**\n * Base URL for referral links.\n * @default window.location.origin (client-side only)\n */\n referralUrlBase?: string;\n\n /**\n * Path to append to base URL for referral links.\n * @default \"/\"\n */\n referralPath?: string;\n}\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_REFERRAL_CONFIG: Required<ReferralConfig> = {\n enabled: false,\n cookieName: \"referral_code\",\n cookieDays: 30,\n urlParamName: \"ref\",\n referralUrlBase: \"\",\n referralPath: \"/\",\n};\n\n// Private storage for configuration\nlet _referralConfig: Required<ReferralConfig> = { ...DEFAULT_REFERRAL_CONFIG };\n\n/**\n * Configure referral feature settings.\n * Call this at app startup to enable and configure referral functionality.\n *\n * @example\n * ```typescript\n * import { configureReferral } from \"@carlonicora/nextjs-jsonapi\";\n *\n * configureReferral({\n * enabled: process.env.NEXT_PUBLIC_REFERRAL_ENABLED === 'true',\n * cookieDays: 30,\n * });\n * ```\n */\nexport function configureReferral(config: ReferralConfig): void {\n _referralConfig = { ...DEFAULT_REFERRAL_CONFIG, ...config };\n}\n\n/**\n * Get the current referral configuration.\n * @internal\n */\nexport function getReferralConfig(): Required<ReferralConfig> {\n return _referralConfig;\n}\n\n/**\n * Check if referral feature is enabled.\n */\nexport function isReferralEnabled(): boolean {\n return _referralConfig.enabled;\n}\n","export type SignupSource = {\n sourceRef?: string;\n sourceUtmSource?: string;\n sourceUtmMedium?: string;\n sourceUtmCampaign?: string;\n sourceReferrer?: string;\n sourceLandingPath?: string;\n};\n\nexport const SIGNUP_SOURCE_COOKIE = \"signup_source\";\nexport const SIGNUP_SOURCE_COOKIE_MAX_AGE = 2592000;\n\n/** The six source fields, in display order. */\nexport const SIGNUP_SOURCE_FIELDS: readonly (keyof SignupSource)[] = [\n \"sourceRef\",\n \"sourceUtmSource\",\n \"sourceUtmMedium\",\n \"sourceUtmCampaign\",\n \"sourceReferrer\",\n \"sourceLandingPath\",\n];\n\nconst SIGNUP_SOURCE_MAX_LENGTH = 200;\n\n// First touch for the current page session. Survives client navigations, lost on a full reload.\nlet memorySource: SignupSource | undefined;\n\nfunction clean(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n return trimmed.slice(0, SIGNUP_SOURCE_MAX_LENGTH);\n}\n\nfunction sanitise(input: unknown): SignupSource | undefined {\n if (!input || typeof input !== \"object\") return undefined;\n const result: SignupSource = {};\n for (const field of SIGNUP_SOURCE_FIELDS) {\n const value = clean((input as Record<string, unknown>)[field]);\n if (value !== undefined) result[field] = value;\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Pure. Reads ref and utm_source/medium/campaign from the URL, the referrer host when it is not ownHost,\n * and the landing path. Returns undefined for a direct visit (no ref, no UTM, no external referrer).\n */\nexport function readSignupSourceFromLocation(params: {\n url: string;\n referrer: string;\n ownHost: string;\n}): SignupSource | undefined {\n let url: URL;\n try {\n url = new URL(params.url);\n } catch {\n return undefined;\n }\n\n const sourceRef = clean(url.searchParams.get(\"ref\"));\n const sourceUtmSource = clean(url.searchParams.get(\"utm_source\"));\n const sourceUtmMedium = clean(url.searchParams.get(\"utm_medium\"));\n const sourceUtmCampaign = clean(url.searchParams.get(\"utm_campaign\"));\n\n let sourceReferrer: string | undefined;\n if (params.referrer) {\n try {\n const referrerHost = new URL(params.referrer).host;\n if (referrerHost && referrerHost !== params.ownHost) sourceReferrer = clean(referrerHost);\n } catch {\n sourceReferrer = undefined;\n }\n }\n\n if (!sourceRef && !sourceUtmSource && !sourceUtmMedium && !sourceUtmCampaign && !sourceReferrer) return undefined;\n\n return sanitise({\n sourceRef,\n sourceUtmSource,\n sourceUtmMedium,\n sourceUtmCampaign,\n sourceReferrer,\n sourceLandingPath: url.pathname,\n });\n}\n\nfunction readSignupSourceCookie(): SignupSource | undefined {\n if (typeof document === \"undefined\") return undefined;\n const cookies = document.cookie ? document.cookie.split(\"; \") : [];\n for (const cookie of cookies) {\n const separator = cookie.indexOf(\"=\");\n if (separator === -1) continue;\n if (cookie.slice(0, separator) !== SIGNUP_SOURCE_COOKIE) continue;\n const value = cookie.slice(separator + 1);\n if (!value) return undefined;\n try {\n return sanitise(JSON.parse(decodeURIComponent(value)));\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nfunction writeSignupSourceCookie(source: SignupSource): void {\n document.cookie = `${SIGNUP_SOURCE_COOKIE}=${encodeURIComponent(JSON.stringify(source))}; path=/; max-age=${SIGNUP_SOURCE_COOKIE_MAX_AGE}; SameSite=Lax`;\n}\n\n/**\n * First touch: an existing in-memory value or cookie is never overwritten.\n * Writes the cookie only when persist is true and no cookie exists yet.\n */\nexport function captureSignupSource(params: { persist: boolean }): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n const existingCookie = readSignupSourceCookie();\n\n if (!memorySource) {\n memorySource =\n existingCookie ??\n readSignupSourceFromLocation({\n url: window.location.href,\n referrer: document.referrer,\n ownHost: window.location.host,\n });\n }\n\n if (params.persist && !existingCookie && memorySource) writeSignupSourceCookie(memorySource);\n}\n\n/** The in-memory value, else the cookie value, else undefined. */\nexport function getSignupSource(): SignupSource | undefined {\n return memorySource ?? readSignupSourceCookie();\n}\n","export const COMPANY_ADMINISTRATOR_ROLE_ID = \"2e1eee00-6cba-4506-9059-ccd24e4ea5b0\";\n\nexport type PermissionValue = boolean | string;\n\nexport type ActionType = \"read\" | \"create\" | \"update\" | \"delete\";\n\nexport const ACTION_TYPES: ActionType[] = [\"read\", \"create\", \"update\", \"delete\"];\n\n/** The permissions object shape used by both Module and PermissionMapping entities */\nexport type PermissionsMap = {\n create?: PermissionValue;\n read?: PermissionValue;\n update?: PermissionValue;\n delete?: PermissionValue;\n};\n\n/**\n * Declarative-RBAC matrix types.\n *\n * Mirror of the library types defined in\n * `packages/nestjs-neo4jsonapi/src/foundations/rbac/dsl/types.ts`.\n * Frontend does not import from backend, so the shape is redefined here.\n *\n * A `PermToken` represents a single permission entry:\n * - `scope: true` → unconditional (e.g. full read of the module)\n * - `scope: false` → nothing (rarely used, mostly a placeholder)\n * - `scope: \"path\"` → scoped by relationship path (e.g. \"orders.account\")\n */\nexport type PermToken = { action: string; scope: boolean | string };\n\n/**\n * A per-module block of the matrix. Always has a `default` row (permissions\n * granted to every role). Additional keys are role IDs → role-specific\n * permission tokens that are unioned with `default` to produce the effective\n * permissions for that role in that module.\n */\nexport type RbacModuleBlock = { default: PermToken[] } & Record<string, PermToken[]>;\n\n/**\n * The full RBAC matrix as served by the dev endpoint `GET /_dev/rbac/matrix`.\n * Keys are module IDs; values are module blocks.\n */\nexport type RbacMatrix = Record<string, RbacModuleBlock>;\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport type { RbacMatrixModel } from \"./RbacMatrixModel\";\nimport type { RbacMatrix } from \"./RbacTypes\";\n\n/**\n * RbacService — fetches RBAC configuration for the admin UI.\n *\n * Declarative-matrix methods (`fetchMatrix`, `saveMatrix`) talk to the\n * dev-only endpoints added in\n * `packages/nestjs-neo4jsonapi/.../rbac-dev.controller.ts`. The controller\n * speaks JSON:API (singleton resource with `type: \"rbac-matrix\"`, `id:\n * \"singleton\"`), so these methods go through the standard `callApi()`\n * pipeline like every other service in the codebase.\n *\n * The backend only registers these routes when `devMode` is enabled on\n * `RbacModule.register` (see `apps/api/src/features/features.modules.ts`).\n * In production the routes return 404; callers should guard with a dev-mode\n * check.\n */\nexport class RbacService extends AbstractService {\n /**\n * Fetch the current RBAC matrix plus each module's known BFS relationship\n * paths (used by the permission picker as scope suggestions).\n *\n * Dev-only endpoint — see class header.\n */\n static async fetchMatrix(): Promise<{\n matrix: RbacMatrix;\n modulePaths: Record<string, readonly string[]>;\n }> {\n const endpoint = new EndpointCreator({ endpoint: Modules.RbacMatrix }).generate();\n\n const model = await this.callApi<RbacMatrixModel>({\n type: Modules.RbacMatrix,\n method: HttpMethod.GET,\n endpoint,\n });\n\n return {\n matrix: model.matrix ?? {},\n modulePaths: model.modulePaths ?? {},\n };\n }\n\n /**\n * Persist a matrix back to the declarative `permissions.ts` file.\n *\n * The backend serializes the matrix to formatted TypeScript using the\n * provided `roleNames` / `moduleNames` lookup tables (so the emitted file\n * references `RoleId.X` / `ModuleId.X` rather than raw UUIDs) and writes\n * it to `outputPath` (absolute, or relative to the repo root).\n *\n * Dev-only endpoint — see class header.\n */\n static async saveMatrix(args: {\n matrix: RbacMatrix;\n roleNames: Record<string, string>;\n moduleNames: Record<string, string>;\n outputPath: string;\n }): Promise<{ bytesWritten: number; path: string }> {\n const endpoint = new EndpointCreator({ endpoint: Modules.RbacMatrix }).generate();\n\n const model = await this.callApi<RbacMatrixModel>({\n type: Modules.RbacMatrix,\n method: HttpMethod.PUT,\n endpoint,\n input: args,\n });\n\n return {\n bytesWritten: model.bytesWritten ?? 0,\n path: model.path ?? \"\",\n };\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } 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
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkS5MQDWWCjs = require('./chunk-S5MQDWWC.js');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var _chunk7QVYU63Ejs = require('./chunk-7QVYU63E.js');
|
|
@@ -43,7 +43,7 @@ function createUsageFormatters(locale, currency) {
|
|
|
43
43
|
_chunk7QVYU63Ejs.__name.call(void 0, createUsageFormatters, "createUsageFormatters");
|
|
44
44
|
function useUsageFormatters() {
|
|
45
45
|
const locale = _nextintl.useLocale.call(void 0, );
|
|
46
|
-
const currency =
|
|
46
|
+
const currency = _chunkS5MQDWWCjs.getTokenUsageCurrency.call(void 0, );
|
|
47
47
|
return _react.useMemo.call(void 0, () => createUsageFormatters(locale, currency), [locale, currency]);
|
|
48
48
|
}
|
|
49
49
|
_chunk7QVYU63Ejs.__name.call(void 0, useUsageFormatters, "useUsageFormatters");
|
|
@@ -107,4 +107,4 @@ _chunk7QVYU63Ejs.__name.call(void 0, seriesColor, "seriesColor");
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
exports.createUsageFormatters = createUsageFormatters; exports.useUsageFormatters = useUsageFormatters; exports.metricValue = metricValue; exports.cacheHitPercentage = cacheHitPercentage; exports.percentageDelta = percentageDelta; exports.CATEGORICAL_CEILING = CATEGORICAL_CEILING; exports.OTHER_COLOR = OTHER_COLOR; exports.SEQUENTIAL_RAMP = SEQUENTIAL_RAMP; exports.seriesColor = seriesColor;
|
|
110
|
-
//# sourceMappingURL=chunk-
|
|
110
|
+
//# sourceMappingURL=chunk-EXICQC4A.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-67NJY7SM.js","../src/features/tokenusage/lib/formatters.ts","../src/features/tokenusage/lib/metrics.ts","../src/features/tokenusage/lib/palette.ts"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACLA,qCAA0B;AAC1B,8BAAwB;AAsCjB,SAAS,qBAAA,CAAsB,MAAA,EAAgB,QAAA,EAAmC;AACvF,EAAA,MAAM,OAAA,mCACJ,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,eAAA,EAAiB,eAAe,CAAC,CAAA,mBAC3F,aAAA,mBAAc,CAAC,CAAA,qBACf,IAAA,mBAAK,CAAC,IAAA,EAAA,GAAS,IAAA,CAAK,KAAA,IAAS,UAAU,CAAA,6BAAG,OAAA,UAAS,UAAA;AAExD,EAAA,MAAM,QAAA,kBAAU,qCAAA,CAAC,KAAA,EAAe,QAAA,EAAA,GAC9B,KAAA,CAAM,cAAA,CAAe,MAAA,EAAQ,EAAE,qBAAA,EAAuB,QAAA,EAAU,qBAAA,EAAuB,SAAS,CAAC,CAAA,EADnF,SAAA,CAAA;AAGhB,EAAA,MAAM,UAAA,EAAY,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,GAAA,EAAK,SAAA,EAAW,KAAA,EAAO,OAAA,EAAS,QAAA,EAAU,MAAM,CAAC,CAAA;AACrG,EAAA,MAAM,YAAA,EAAc,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,QAAA,EAAU,MAAM,CAAC,CAAA;AAKxG,EAAA,MAAM,cAAA,EAAgB,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,QAAA,EAAU,SAAA,EAAW,wBAAA,EAA0B,EAAE,CAAC,CAAA;AACxG,EAAA,MAAM,SAAA,EAAW,IAAI,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAEzC,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IAEA,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ;AACzB,MAAA,GAAA,CAAI,OAAA,IAAW,MAAA,EAAQ,OAAO,CAAA,EAAA;AAIJ,MAAA;AACH,MAAA;AACzB,IAAA;AAE0B,IAAA;AACI,MAAA;AAC9B,IAAA;AAE6B,IAAA;AACF,MAAA;AAC3B,IAAA;AAEe,IAAA;AACe,MAAA;AAC9B,IAAA;AAE6B,IAAA;AACd,MAAA;AACU,MAAA;AACzB,IAAA;AAEc,IAAA;AACgB,MAAA;AAC9B,IAAA;AACF,EAAA;AACF;AAnDgB;AA0DsC;AAC3B,EAAA;AACR,EAAA;AACI,EAAA;AACvB;AAJgB;ADlDqB;AACA;AEzBuC;AACxC,EAAA;AACD,EAAA;AACP,EAAA;AAC5B;AAJgB;AAUmC;AAC3B,EAAA;AACO,EAAA;AAC/B;AAHgB;AAYiC;AACzB,EAAA;AACQ,EAAA;AAChC;AAHgB;AFoBqB;AACA;AGwDQ;AAC3C,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAG4C;AAC1C,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAcmC;AAOR;AAcuB;AAUA;AAE3B;AAYsC;AACjC,EAAA;AACO,EAAA;AACnC;AAHgB;AHzGqB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-67NJY7SM.js","sourcesContent":[null,"\"use client\";\n\nimport { useLocale } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { getTokenUsageCurrency } from \"./config\";\n\nexport type UsageFormatters = {\n /** Locale-aware fixed-decimal number, e.g. 9.46 → \"9,46\" in it-IT. */\n decimal(value: number, decimals: number): string;\n /** Currency for cost, 2 decimals for credits, whole numbers for tokens. */\n metricValue(value: number, metric: Metric): string;\n /** Currency with an explicit decimal count — the per-call tile needs 4. */\n currency(value: number, decimals: number): string;\n /** Percentage with one decimal by default, e.g. 60 → \"60,0 %\" in it-IT. */\n percent(value: number, decimals?: number): string;\n /** Compact notation for axis ticks, e.g. 1500000 → \"1,5 Mln\" in it-IT. */\n compact(value: number): string;\n /** A \"YYYY-MM-DD\" bucket key rendered as an axis or tooltip label, in UTC. */\n bucketDate(iso: string, granularity: \"day\" | \"week\" | \"month\"): string;\n /** Locale collation for client-side table sorting. */\n compare(a: string, b: string): number;\n};\n\n/**\n * Builds every formatter the token-usage surfaces need, for one locale and one\n * currency.\n *\n * A pure factory on purpose: it takes no React context, so the arithmetic and\n * the formatting are unit-testable without rendering, and a server component or\n * a test can construct a set for an arbitrary locale.\n *\n * The currency symbol is placed with a space rather than through\n * `style: \"currency\"`, which is what the previous hard-coded implementation\n * rendered (\"€ 9,46\"). Keeping that shape means a consuming app on EUR sees\n * byte-identical output after this refactor.\n *\n * `bucketDate` formats in UTC, always. The wire value is a `type: \"date\"` — a\n * calendar day — parsed to UTC midnight; reading it back with local getters\n * would shift the label a day early west of UTC.\n */\nexport function createUsageFormatters(locale: string, currency: string): UsageFormatters {\n const symbol =\n new Intl.NumberFormat(locale, { style: \"currency\", currency, currencyDisplay: \"narrowSymbol\" })\n .formatToParts(0)\n .find((part) => part.type === \"currency\")?.value ?? currency;\n\n const decimal = (value: number, decimals: number): string =>\n value.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });\n\n const dayFormat = new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", timeZone: \"UTC\" });\n const monthFormat = new Intl.DateTimeFormat(locale, { month: \"short\", year: \"numeric\", timeZone: \"UTC\" });\n // Axis ticks step by 1, 2, 2.5 or 5 times a power of ten, so a tick never needs\n // more than three significant digits. A fixed one-decimal cap rounded a 0.05\n // step into repeated labels (0.1, 0.1, 0.2, 0.2); significant digits keep every\n // tick distinct at any scale (0.05, 0.15, 1.25K).\n const compactFormat = new Intl.NumberFormat(locale, { notation: \"compact\", maximumSignificantDigits: 3 });\n const collator = new Intl.Collator(locale);\n\n return {\n decimal,\n\n metricValue(value, metric) {\n if (metric === \"cost\") return `${symbol} ${decimal(value, 2)}`;\n // Credits are stored to 4 decimals (round4(cost / creditCost)), but that\n // precision is noise to a reader comparing rows: one decimal is enough to\n // separate two values and keeps the columns narrow enough to scan.\n if (metric === \"credits\") return decimal(value, 1);\n return decimal(value, 0);\n },\n\n currency(value, decimals) {\n return `${symbol} ${decimal(value, decimals)}`;\n },\n\n percent(value, decimals = 1) {\n return `${decimal(value, decimals)} %`;\n },\n\n compact(value) {\n return compactFormat.format(value);\n },\n\n bucketDate(iso, granularity) {\n const date = new Date(`${iso}T00:00:00.000Z`);\n return granularity === \"month\" ? monthFormat.format(date) : dayFormat.format(date);\n },\n\n compare(a, b) {\n return collator.compare(a, b);\n },\n };\n}\n\n/**\n * The formatter set for the current request's locale and the app's configured\n * currency. Memoised on both, so the Intl objects are built once per locale\n * rather than on every render.\n */\nexport function useUsageFormatters(): UsageFormatters {\n const locale = useLocale();\n const currency = getTokenUsageCurrency();\n return useMemo(() => createUsageFormatters(locale, currency), [locale, currency]);\n}\n","import type { Metric } from \"../data/tokenusage-admin.types\";\n\n/**\n * Locale-free arithmetic for the token-usage surfaces.\n *\n * Everything that turns a number into a STRING lives in `./formatters` instead,\n * because it depends on the request's locale and the app's configured currency.\n * This file stays pure arithmetic so it needs neither.\n */\n\n/**\n * The metric field set every admin token-usage resource carries. Declared\n * structurally so it accepts the summary, timeline and breakdown interfaces\n * alike — all three expose exactly these getters.\n */\nexport type TokenUsageMetrics = {\n cost: number;\n credits: number;\n tokensIn: number;\n tokensOut: number;\n cached: number;\n calls: number;\n};\n\n/** Reads the single number a row contributes for the currently selected metric. */\nexport function metricValue(row: TokenUsageMetrics, metric: Metric): number {\n if (metric === \"cost\") return row.cost;\n if (metric === \"credits\") return row.credits;\n return row.tokensIn + row.tokensOut;\n}\n\n/**\n * Share of cached input tokens. Returns 0 rather than NaN when the window holds\n * no input tokens at all, so the tile renders \"0,0 %\" instead of \"NaN %\".\n */\nexport function cacheHitPercentage(cached: number, tokensIn: number): number {\n if (!tokensIn) return 0;\n return (cached / tokensIn) * 100;\n}\n\n/**\n * Percentage change of `current` against `previous`, rounded to a whole number.\n *\n * Returns `undefined` when the previous window is zero — there is no meaningful\n * percentage change from nothing, and rendering Infinity would be worse than\n * rendering nothing. Callers show an em dash in that slot.\n */\nexport function percentageDelta(current: number, previous: number): number | undefined {\n if (!previous) return undefined;\n return Math.round(((current - previous) / previous) * 100);\n}\n","/**\n * Chart palette for the administrative token-usage dashboard.\n *\n * Every value here is a documented step of the dataviz skill's reference ramps —\n * nothing was eyeballed, nothing was hand-mixed. The palette was run through the\n * skill's validator against THIS application's real chart surfaces in both modes.\n * The verbatim output is below.\n *\n * The surface is `--card`, NOT `--background`. Every chart that uses this palette\n * is painted inside a `<Card>`, so the card is the surface the marks actually sit\n * on — and in dark mode it is the *lighter* of the two (`oklch(0.205 0 0)` vs the\n * background's `oklch(0.145 0 0)`), which is the harder case for contrast. Taken\n * from `apps/web/src/app/globals.css` (narr8 declares two `:root`/`.dark` pairs;\n * the LATER pair wins, and it is the one read here) — light `oklch(1 0 0)`, dark\n * `oklch(0.205 0 0)`. a360ai declares the same two values.\n *\n * oklch→hex conversion: the algebraic inverse of the Björn Ottosson OKLab\n * matrices — the same matrices the validator applies in the forward direction —\n * giving light `#ffffff` and dark `#171717`. Cross-checked by feeding it the\n * previously recorded background value, which it reproduces exactly:\n * `oklch(0.145 0 0)` → `#0a0a0a`. ✓\n *\n * Light and dark are two SELECTED sets of steps, chosen for their own surface —\n * never one set with its lightness flipped at runtime. Consumers pick a set from\n * the resolved theme (`seriesColor(i, mode)` / `sequentialColor(i, mode)`).\n *\n * ---------------------------------------------------------------------------\n * VALIDATOR OUTPUT — dataviz `scripts/validate_palette.js`, run 2026-08-15\n * ---------------------------------------------------------------------------\n *\n * $ node scripts/validate_palette.js \"#2a78d6,#eb6834,#1baf7a,#eda100,#e87ba4,#008300,#4a3aa7\" --mode light --surface \"#ffffff\"\n *\n * Palette (light, surface #ffffff, categorical): 7 slots\n * [PASS] Lightness band all 7 inside L 0.43–0.77\n * [PASS] Chroma floor all 7 >= 0.1\n * [PASS] CVD separation worst adjacent #eda100↔#1baf7a ΔE 9.1 (protan) · tritan 5.8\n * [PASS] Normal-vision floor worst adjacent #e87ba4↔#eda100 ΔE 19.6 (normal)\n * [WARN] Contrast vs surface below 3:1 — relief required (visible labels or table view): [[\"#1baf7a\",2.82],[\"#eda100\",2.17],[\"#e87ba4\",2.69]]\n *\n * → ALL CHECKS PASS (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n * scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#3987e5,#d95926,#199e70,#c98500,#d55181,#008300,#9085e9\" --mode dark --surface \"#171717\"\n *\n * Palette (dark, surface #171717, categorical): 7 slots\n * [PASS] Lightness band all 7 inside L 0.48–0.67\n * [PASS] Chroma floor all 7 >= 0.1\n * [PASS] CVD separation worst adjacent #c98500↔#199e70 ΔE 8.4 (protan) · tritan 8.7\n * [PASS] Normal-vision floor worst adjacent #d55181↔#c98500 ΔE 19.3 (normal)\n * [PASS] Contrast vs surface all 7 >= 3:1\n *\n * → ALL CHECKS PASS (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n * scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#104281,#1c5cab,#2a78d6,#5598e7,#86b6ef\" --mode light --surface \"#ffffff\" --ordinal\n *\n * Palette (light, surface #ffffff, ordinal ramp): 5 slots\n * [PASS] Lightness monotone steps read light→dark\n * [PASS] Adjacent ΔL all gaps >= 0.06\n * [PASS] Light-end contrast #86b6ef at 2.11:1 vs surface\n * [PASS] Single hue hue spread 3°\n *\n * → ALL CHECKS PASS (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * $ node scripts/validate_palette.js \"#9ec5f4,#6da7ec,#3987e5,#256abf,#184f95\" --mode dark --surface \"#171717\" --ordinal\n *\n * Palette (dark, surface #171717, ordinal ramp): 5 slots\n * [PASS] Lightness monotone steps read light→dark\n * [PASS] Adjacent ΔL all gaps >= 0.06\n * [PASS] Light-end contrast #184f95 at 2.21:1 vs surface\n * [PASS] Single hue hue spread 3°\n *\n * → ALL CHECKS PASS (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * OTHER_COLOR is achromatic, so the categorical checks (which gate hue identity)\n * do not apply to it; it was gated on contrast alone with the validator's own\n * `contrast()` export: 3.59:1 on `#ffffff`, 4.99:1 on `#171717` — both clear 3:1.\n *\n * ---------------------------------------------------------------------------\n * WHAT THE RESULTS OBLIGE US TO DO\n * ---------------------------------------------------------------------------\n *\n * - NO COLOUR VALUE CHANGED in the move from `--background` to `--card`. Every\n * gate that passed on the background still passes on the card, so re-stepping\n * a hue would have been churn, not a fix. Only the recorded surface, the\n * recorded numbers and this prose changed.\n * - The dark set still clears 3:1 on the LIGHTER card surface: the worst slot is\n * green `#008300` at 3.63:1, and the ordinal ramp's surface-nearest step lands\n * at 2.21:1 (down from 2.44:1 on the background) — still above the 2:1 gate.\n * That headroom is thin, so a future card-surface lightening MUST re-run these\n * four commands rather than assume the set still holds.\n * - The light-mode contrast WARN is NOT dismissable. Three slots (aqua, yellow,\n * magenta) sit below 3:1 on white, so every surface that paints with this\n * palette MUST ship the relief channel: a visible legend, direct labels and a\n * tooltip carrying the value in text. The timeline chart and the breakdown\n * table both do. The light card is `#ffffff`, identical to the light\n * background, so this WARN is unchanged rather than newly incurred.\n * - CVD separation is measured on ADJACENT pairs, which is the correct pairlist\n * for stacked bars, grouped bars and lines — the only forms this palette paints.\n * A scatter / bubble / small-multiples chart would need `--pairs all`, which\n * caps the usable slot count at three; do not reuse this array there without\n * re-running the validator with that flag.\n */\n\n/** Which surface the colours are being painted on. */\nexport type ChartMode = \"light\" | \"dark\";\n\n/**\n * The chart surfaces the palette was validated against (globals.css `--card`).\n *\n * `--card`, never `--background`: the charts are painted inside a `<Card>`.\n */\nexport const CHART_SURFACE: Readonly<Record<ChartMode, string>> = {\n light: \"#ffffff\",\n dark: \"#171717\",\n};\n\n/**\n * Categorical slots, in FIXED order. Slot n is series n — the order is the\n * CVD-safety mechanism, not decoration, so it is never re-ordered and never\n * cycled. The two-series default (customer / platform) takes slots 0 and 1.\n *\n * Hues, in order: blue, orange, aqua, yellow, magenta, green, violet.\n */\nconst CATEGORICAL_LIGHT: readonly string[] = [\n \"#2a78d6\",\n \"#eb6834\",\n \"#1baf7a\",\n \"#eda100\",\n \"#e87ba4\",\n \"#008300\",\n \"#4a3aa7\",\n];\n\n/** The same seven hues, re-stepped for the dark surface. Not a lightness flip. */\nconst CATEGORICAL_DARK: readonly string[] = [\n \"#3987e5\",\n \"#d95926\",\n \"#199e70\",\n \"#c98500\",\n \"#d55181\",\n \"#008300\",\n \"#9085e9\",\n];\n\n/**\n * The default (light-surface) categorical set.\n *\n * Prefer `seriesColor(index, mode)` — this array exists for consumers that only\n * need the documented default order.\n */\nexport const CATEGORICAL: readonly string[] = CATEGORICAL_LIGHT;\n\n/**\n * How many series may carry their own identity hue. An 8th series is NEVER a\n * generated hue: it folds into `other`.\n */\nexport const CATEGORICAL_CEILING = CATEGORICAL_LIGHT.length;\n\n/**\n * The \"other\" rollup colour: deliberately achromatic so it reads as \"not an\n * identity\" beside the seven hues. Same step in both modes — it clears 3:1 on\n * both card surfaces (3.59:1 on `#ffffff`, 4.99:1 on `#171717`).\n */\nexport const OTHER_COLOR = \"#898781\";\n\n/**\n * Sequential (single-hue, blue) ramp for MAGNITUDE, indexed by rank position:\n * index 0 is rank 1 — the largest value — so the strongest step leads.\n *\n * On the light surface \"strongest\" is the darkest step; on the dark surface it\n * is the lightest. The anchor flips with the surface, which is why there are two\n * selected sets rather than one array reversed at runtime.\n *\n * Five steps, not eight: with the adjacent-ΔL ≥ 0.06 gate and the ≥ 2:1\n * surface-contrast gate on the step nearest the surface, the blue ramp fits\n * exactly five distinguishable steps. Consumers clamp past the end.\n */\nconst SEQUENTIAL_RAMP_LIGHT: readonly string[] = [\"#104281\", \"#1c5cab\", \"#2a78d6\", \"#5598e7\", \"#86b6ef\"];\n\nconst SEQUENTIAL_RAMP_DARK: readonly string[] = [\"#9ec5f4\", \"#6da7ec\", \"#3987e5\", \"#256abf\", \"#184f95\"];\n\n/**\n * The default (light-surface) sequential ramp.\n *\n * Prefer `sequentialColor(index, mode)` — this array exists for consumers that\n * only need the documented default.\n */\nexport const SEQUENTIAL_RAMP: readonly string[] = SEQUENTIAL_RAMP_LIGHT;\n\nconst categoricalSet = (mode: ChartMode): readonly string[] => (mode === \"dark\" ? CATEGORICAL_DARK : CATEGORICAL_LIGHT);\n\nconst sequentialSet = (mode: ChartMode): readonly string[] =>\n mode === \"dark\" ? SEQUENTIAL_RAMP_DARK : SEQUENTIAL_RAMP_LIGHT;\n\n/**\n * The identity colour for series `index`.\n *\n * Never generates a hue and never cycles: anything at or past the ceiling gets\n * `OTHER_COLOR`, because a series past the ceiling should already have been\n * folded into the \"other\" bucket by the caller.\n */\nexport function seriesColor(index: number, mode: ChartMode = \"light\"): string {\n if (index < 0 || index >= CATEGORICAL_CEILING) return OTHER_COLOR;\n return categoricalSet(mode)[index];\n}\n\n/**\n * Both mode steps for series `index`, shaped for a shadcn `ChartConfig` entry's\n * `theme` field.\n */\nexport function seriesTheme(index: number): { light: string; dark: string } {\n return { light: seriesColor(index, \"light\"), dark: seriesColor(index, \"dark\") };\n}\n\n/**\n * The magnitude colour for rank position `index`, clamped to the last step so a\n * long list keeps its weakest step rather than falling off the ramp.\n */\nexport function sequentialColor(index: number, mode: ChartMode = \"light\"): string {\n const ramp = sequentialSet(mode);\n const clamped = Math.min(Math.max(index, 0), ramp.length - 1);\n return ramp[clamped];\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-EXICQC4A.js","../src/features/tokenusage/lib/formatters.ts","../src/features/tokenusage/lib/metrics.ts","../src/features/tokenusage/lib/palette.ts"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACLA,qCAA0B;AAC1B,8BAAwB;AAsCjB,SAAS,qBAAA,CAAsB,MAAA,EAAgB,QAAA,EAAmC;AACvF,EAAA,MAAM,OAAA,mCACJ,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,KAAA,EAAO,UAAA,EAAY,QAAA,EAAU,eAAA,EAAiB,eAAe,CAAC,CAAA,mBAC3F,aAAA,mBAAc,CAAC,CAAA,qBACf,IAAA,mBAAK,CAAC,IAAA,EAAA,GAAS,IAAA,CAAK,KAAA,IAAS,UAAU,CAAA,6BAAG,OAAA,UAAS,UAAA;AAExD,EAAA,MAAM,QAAA,kBAAU,qCAAA,CAAC,KAAA,EAAe,QAAA,EAAA,GAC9B,KAAA,CAAM,cAAA,CAAe,MAAA,EAAQ,EAAE,qBAAA,EAAuB,QAAA,EAAU,qBAAA,EAAuB,SAAS,CAAC,CAAA,EADnF,SAAA,CAAA;AAGhB,EAAA,MAAM,UAAA,EAAY,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,GAAA,EAAK,SAAA,EAAW,KAAA,EAAO,OAAA,EAAS,QAAA,EAAU,MAAM,CAAC,CAAA;AACrG,EAAA,MAAM,YAAA,EAAc,IAAI,IAAA,CAAK,cAAA,CAAe,MAAA,EAAQ,EAAE,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,QAAA,EAAU,MAAM,CAAC,CAAA;AAKxG,EAAA,MAAM,cAAA,EAAgB,IAAI,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,QAAA,EAAU,SAAA,EAAW,wBAAA,EAA0B,EAAE,CAAC,CAAA;AACxG,EAAA,MAAM,SAAA,EAAW,IAAI,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AAEzC,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IAEA,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ;AACzB,MAAA,GAAA,CAAI,OAAA,IAAW,MAAA,EAAQ,OAAO,CAAA,EAAA;AAIJ,MAAA;AACH,MAAA;AACzB,IAAA;AAE0B,IAAA;AACI,MAAA;AAC9B,IAAA;AAE6B,IAAA;AACF,MAAA;AAC3B,IAAA;AAEe,IAAA;AACe,MAAA;AAC9B,IAAA;AAE6B,IAAA;AACd,MAAA;AACU,MAAA;AACzB,IAAA;AAEc,IAAA;AACgB,MAAA;AAC9B,IAAA;AACF,EAAA;AACF;AAnDgB;AA0DsC;AAC3B,EAAA;AACR,EAAA;AACI,EAAA;AACvB;AAJgB;ADlDqB;AACA;AEzBuC;AACxC,EAAA;AACD,EAAA;AACP,EAAA;AAC5B;AAJgB;AAUmC;AAC3B,EAAA;AACO,EAAA;AAC/B;AAHgB;AAYiC;AACzB,EAAA;AACQ,EAAA;AAChC;AAHgB;AFoBqB;AACA;AGwDQ;AAC3C,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAG4C;AAC1C,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAcmC;AAOR;AAcuB;AAUA;AAE3B;AAYsC;AACjC,EAAA;AACO,EAAA;AACnC;AAHgB;AHzGqB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-EXICQC4A.js","sourcesContent":[null,"\"use client\";\n\nimport { useLocale } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport type { Metric } from \"../data/tokenusage-admin.types\";\nimport { getTokenUsageCurrency } from \"./config\";\n\nexport type UsageFormatters = {\n /** Locale-aware fixed-decimal number, e.g. 9.46 → \"9,46\" in it-IT. */\n decimal(value: number, decimals: number): string;\n /** Currency for cost, 2 decimals for credits, whole numbers for tokens. */\n metricValue(value: number, metric: Metric): string;\n /** Currency with an explicit decimal count — the per-call tile needs 4. */\n currency(value: number, decimals: number): string;\n /** Percentage with one decimal by default, e.g. 60 → \"60,0 %\" in it-IT. */\n percent(value: number, decimals?: number): string;\n /** Compact notation for axis ticks, e.g. 1500000 → \"1,5 Mln\" in it-IT. */\n compact(value: number): string;\n /** A \"YYYY-MM-DD\" bucket key rendered as an axis or tooltip label, in UTC. */\n bucketDate(iso: string, granularity: \"day\" | \"week\" | \"month\"): string;\n /** Locale collation for client-side table sorting. */\n compare(a: string, b: string): number;\n};\n\n/**\n * Builds every formatter the token-usage surfaces need, for one locale and one\n * currency.\n *\n * A pure factory on purpose: it takes no React context, so the arithmetic and\n * the formatting are unit-testable without rendering, and a server component or\n * a test can construct a set for an arbitrary locale.\n *\n * The currency symbol is placed with a space rather than through\n * `style: \"currency\"`, which is what the previous hard-coded implementation\n * rendered (\"€ 9,46\"). Keeping that shape means a consuming app on EUR sees\n * byte-identical output after this refactor.\n *\n * `bucketDate` formats in UTC, always. The wire value is a `type: \"date\"` — a\n * calendar day — parsed to UTC midnight; reading it back with local getters\n * would shift the label a day early west of UTC.\n */\nexport function createUsageFormatters(locale: string, currency: string): UsageFormatters {\n const symbol =\n new Intl.NumberFormat(locale, { style: \"currency\", currency, currencyDisplay: \"narrowSymbol\" })\n .formatToParts(0)\n .find((part) => part.type === \"currency\")?.value ?? currency;\n\n const decimal = (value: number, decimals: number): string =>\n value.toLocaleString(locale, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });\n\n const dayFormat = new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", timeZone: \"UTC\" });\n const monthFormat = new Intl.DateTimeFormat(locale, { month: \"short\", year: \"numeric\", timeZone: \"UTC\" });\n // Axis ticks step by 1, 2, 2.5 or 5 times a power of ten, so a tick never needs\n // more than three significant digits. A fixed one-decimal cap rounded a 0.05\n // step into repeated labels (0.1, 0.1, 0.2, 0.2); significant digits keep every\n // tick distinct at any scale (0.05, 0.15, 1.25K).\n const compactFormat = new Intl.NumberFormat(locale, { notation: \"compact\", maximumSignificantDigits: 3 });\n const collator = new Intl.Collator(locale);\n\n return {\n decimal,\n\n metricValue(value, metric) {\n if (metric === \"cost\") return `${symbol} ${decimal(value, 2)}`;\n // Credits are stored to 4 decimals (round4(cost / creditCost)), but that\n // precision is noise to a reader comparing rows: one decimal is enough to\n // separate two values and keeps the columns narrow enough to scan.\n if (metric === \"credits\") return decimal(value, 1);\n return decimal(value, 0);\n },\n\n currency(value, decimals) {\n return `${symbol} ${decimal(value, decimals)}`;\n },\n\n percent(value, decimals = 1) {\n return `${decimal(value, decimals)} %`;\n },\n\n compact(value) {\n return compactFormat.format(value);\n },\n\n bucketDate(iso, granularity) {\n const date = new Date(`${iso}T00:00:00.000Z`);\n return granularity === \"month\" ? monthFormat.format(date) : dayFormat.format(date);\n },\n\n compare(a, b) {\n return collator.compare(a, b);\n },\n };\n}\n\n/**\n * The formatter set for the current request's locale and the app's configured\n * currency. Memoised on both, so the Intl objects are built once per locale\n * rather than on every render.\n */\nexport function useUsageFormatters(): UsageFormatters {\n const locale = useLocale();\n const currency = getTokenUsageCurrency();\n return useMemo(() => createUsageFormatters(locale, currency), [locale, currency]);\n}\n","import type { Metric } from \"../data/tokenusage-admin.types\";\n\n/**\n * Locale-free arithmetic for the token-usage surfaces.\n *\n * Everything that turns a number into a STRING lives in `./formatters` instead,\n * because it depends on the request's locale and the app's configured currency.\n * This file stays pure arithmetic so it needs neither.\n */\n\n/**\n * The metric field set every admin token-usage resource carries. Declared\n * structurally so it accepts the summary, timeline and breakdown interfaces\n * alike — all three expose exactly these getters.\n */\nexport type TokenUsageMetrics = {\n cost: number;\n credits: number;\n tokensIn: number;\n tokensOut: number;\n cached: number;\n calls: number;\n};\n\n/** Reads the single number a row contributes for the currently selected metric. */\nexport function metricValue(row: TokenUsageMetrics, metric: Metric): number {\n if (metric === \"cost\") return row.cost;\n if (metric === \"credits\") return row.credits;\n return row.tokensIn + row.tokensOut;\n}\n\n/**\n * Share of cached input tokens. Returns 0 rather than NaN when the window holds\n * no input tokens at all, so the tile renders \"0,0 %\" instead of \"NaN %\".\n */\nexport function cacheHitPercentage(cached: number, tokensIn: number): number {\n if (!tokensIn) return 0;\n return (cached / tokensIn) * 100;\n}\n\n/**\n * Percentage change of `current` against `previous`, rounded to a whole number.\n *\n * Returns `undefined` when the previous window is zero — there is no meaningful\n * percentage change from nothing, and rendering Infinity would be worse than\n * rendering nothing. Callers show an em dash in that slot.\n */\nexport function percentageDelta(current: number, previous: number): number | undefined {\n if (!previous) return undefined;\n return Math.round(((current - previous) / previous) * 100);\n}\n","/**\n * Chart palette for the administrative token-usage dashboard.\n *\n * Every value here is a documented step of the dataviz skill's reference ramps —\n * nothing was eyeballed, nothing was hand-mixed. The palette was run through the\n * skill's validator against THIS application's real chart surfaces in both modes.\n * The verbatim output is below.\n *\n * The surface is `--card`, NOT `--background`. Every chart that uses this palette\n * is painted inside a `<Card>`, so the card is the surface the marks actually sit\n * on — and in dark mode it is the *lighter* of the two (`oklch(0.205 0 0)` vs the\n * background's `oklch(0.145 0 0)`), which is the harder case for contrast. Taken\n * from `apps/web/src/app/globals.css` (narr8 declares two `:root`/`.dark` pairs;\n * the LATER pair wins, and it is the one read here) — light `oklch(1 0 0)`, dark\n * `oklch(0.205 0 0)`. a360ai declares the same two values.\n *\n * oklch→hex conversion: the algebraic inverse of the Björn Ottosson OKLab\n * matrices — the same matrices the validator applies in the forward direction —\n * giving light `#ffffff` and dark `#171717`. Cross-checked by feeding it the\n * previously recorded background value, which it reproduces exactly:\n * `oklch(0.145 0 0)` → `#0a0a0a`. ✓\n *\n * Light and dark are two SELECTED sets of steps, chosen for their own surface —\n * never one set with its lightness flipped at runtime. Consumers pick a set from\n * the resolved theme (`seriesColor(i, mode)` / `sequentialColor(i, mode)`).\n *\n * ---------------------------------------------------------------------------\n * VALIDATOR OUTPUT — dataviz `scripts/validate_palette.js`, run 2026-08-15\n * ---------------------------------------------------------------------------\n *\n * $ node scripts/validate_palette.js \"#2a78d6,#eb6834,#1baf7a,#eda100,#e87ba4,#008300,#4a3aa7\" --mode light --surface \"#ffffff\"\n *\n * Palette (light, surface #ffffff, categorical): 7 slots\n * [PASS] Lightness band all 7 inside L 0.43–0.77\n * [PASS] Chroma floor all 7 >= 0.1\n * [PASS] CVD separation worst adjacent #eda100↔#1baf7a ΔE 9.1 (protan) · tritan 5.8\n * [PASS] Normal-vision floor worst adjacent #e87ba4↔#eda100 ΔE 19.6 (normal)\n * [WARN] Contrast vs surface below 3:1 — relief required (visible labels or table view): [[\"#1baf7a\",2.82],[\"#eda100\",2.17],[\"#e87ba4\",2.69]]\n *\n * → ALL CHECKS PASS (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n * scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#3987e5,#d95926,#199e70,#c98500,#d55181,#008300,#9085e9\" --mode dark --surface \"#171717\"\n *\n * Palette (dark, surface #171717, categorical): 7 slots\n * [PASS] Lightness band all 7 inside L 0.48–0.67\n * [PASS] Chroma floor all 7 >= 0.1\n * [PASS] CVD separation worst adjacent #c98500↔#199e70 ΔE 8.4 (protan) · tritan 8.7\n * [PASS] Normal-vision floor worst adjacent #d55181↔#c98500 ΔE 19.3 (normal)\n * [PASS] Contrast vs surface all 7 >= 3:1\n *\n * → ALL CHECKS PASS (CVD in the 6–8 floor band is legal ONLY with secondary encoding: direct labels, gaps, or texture)\n * scope: categorical palettes only. For a lone status/text color check WCAG text contrast; for a sequential ramp, lightness monotonicity.\n *\n * $ node scripts/validate_palette.js \"#104281,#1c5cab,#2a78d6,#5598e7,#86b6ef\" --mode light --surface \"#ffffff\" --ordinal\n *\n * Palette (light, surface #ffffff, ordinal ramp): 5 slots\n * [PASS] Lightness monotone steps read light→dark\n * [PASS] Adjacent ΔL all gaps >= 0.06\n * [PASS] Light-end contrast #86b6ef at 2.11:1 vs surface\n * [PASS] Single hue hue spread 3°\n *\n * → ALL CHECKS PASS (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * $ node scripts/validate_palette.js \"#9ec5f4,#6da7ec,#3987e5,#256abf,#184f95\" --mode dark --surface \"#171717\" --ordinal\n *\n * Palette (dark, surface #171717, ordinal ramp): 5 slots\n * [PASS] Lightness monotone steps read light→dark\n * [PASS] Adjacent ΔL all gaps >= 0.06\n * [PASS] Light-end contrast #184f95 at 2.21:1 vs surface\n * [PASS] Single hue hue spread 3°\n *\n * → ALL CHECKS PASS (ordinal: one hue, monotone L, visible step gaps, light end clears surface)\n *\n * OTHER_COLOR is achromatic, so the categorical checks (which gate hue identity)\n * do not apply to it; it was gated on contrast alone with the validator's own\n * `contrast()` export: 3.59:1 on `#ffffff`, 4.99:1 on `#171717` — both clear 3:1.\n *\n * ---------------------------------------------------------------------------\n * WHAT THE RESULTS OBLIGE US TO DO\n * ---------------------------------------------------------------------------\n *\n * - NO COLOUR VALUE CHANGED in the move from `--background` to `--card`. Every\n * gate that passed on the background still passes on the card, so re-stepping\n * a hue would have been churn, not a fix. Only the recorded surface, the\n * recorded numbers and this prose changed.\n * - The dark set still clears 3:1 on the LIGHTER card surface: the worst slot is\n * green `#008300` at 3.63:1, and the ordinal ramp's surface-nearest step lands\n * at 2.21:1 (down from 2.44:1 on the background) — still above the 2:1 gate.\n * That headroom is thin, so a future card-surface lightening MUST re-run these\n * four commands rather than assume the set still holds.\n * - The light-mode contrast WARN is NOT dismissable. Three slots (aqua, yellow,\n * magenta) sit below 3:1 on white, so every surface that paints with this\n * palette MUST ship the relief channel: a visible legend, direct labels and a\n * tooltip carrying the value in text. The timeline chart and the breakdown\n * table both do. The light card is `#ffffff`, identical to the light\n * background, so this WARN is unchanged rather than newly incurred.\n * - CVD separation is measured on ADJACENT pairs, which is the correct pairlist\n * for stacked bars, grouped bars and lines — the only forms this palette paints.\n * A scatter / bubble / small-multiples chart would need `--pairs all`, which\n * caps the usable slot count at three; do not reuse this array there without\n * re-running the validator with that flag.\n */\n\n/** Which surface the colours are being painted on. */\nexport type ChartMode = \"light\" | \"dark\";\n\n/**\n * The chart surfaces the palette was validated against (globals.css `--card`).\n *\n * `--card`, never `--background`: the charts are painted inside a `<Card>`.\n */\nexport const CHART_SURFACE: Readonly<Record<ChartMode, string>> = {\n light: \"#ffffff\",\n dark: \"#171717\",\n};\n\n/**\n * Categorical slots, in FIXED order. Slot n is series n — the order is the\n * CVD-safety mechanism, not decoration, so it is never re-ordered and never\n * cycled. The two-series default (customer / platform) takes slots 0 and 1.\n *\n * Hues, in order: blue, orange, aqua, yellow, magenta, green, violet.\n */\nconst CATEGORICAL_LIGHT: readonly string[] = [\n \"#2a78d6\",\n \"#eb6834\",\n \"#1baf7a\",\n \"#eda100\",\n \"#e87ba4\",\n \"#008300\",\n \"#4a3aa7\",\n];\n\n/** The same seven hues, re-stepped for the dark surface. Not a lightness flip. */\nconst CATEGORICAL_DARK: readonly string[] = [\n \"#3987e5\",\n \"#d95926\",\n \"#199e70\",\n \"#c98500\",\n \"#d55181\",\n \"#008300\",\n \"#9085e9\",\n];\n\n/**\n * The default (light-surface) categorical set.\n *\n * Prefer `seriesColor(index, mode)` — this array exists for consumers that only\n * need the documented default order.\n */\nexport const CATEGORICAL: readonly string[] = CATEGORICAL_LIGHT;\n\n/**\n * How many series may carry their own identity hue. An 8th series is NEVER a\n * generated hue: it folds into `other`.\n */\nexport const CATEGORICAL_CEILING = CATEGORICAL_LIGHT.length;\n\n/**\n * The \"other\" rollup colour: deliberately achromatic so it reads as \"not an\n * identity\" beside the seven hues. Same step in both modes — it clears 3:1 on\n * both card surfaces (3.59:1 on `#ffffff`, 4.99:1 on `#171717`).\n */\nexport const OTHER_COLOR = \"#898781\";\n\n/**\n * Sequential (single-hue, blue) ramp for MAGNITUDE, indexed by rank position:\n * index 0 is rank 1 — the largest value — so the strongest step leads.\n *\n * On the light surface \"strongest\" is the darkest step; on the dark surface it\n * is the lightest. The anchor flips with the surface, which is why there are two\n * selected sets rather than one array reversed at runtime.\n *\n * Five steps, not eight: with the adjacent-ΔL ≥ 0.06 gate and the ≥ 2:1\n * surface-contrast gate on the step nearest the surface, the blue ramp fits\n * exactly five distinguishable steps. Consumers clamp past the end.\n */\nconst SEQUENTIAL_RAMP_LIGHT: readonly string[] = [\"#104281\", \"#1c5cab\", \"#2a78d6\", \"#5598e7\", \"#86b6ef\"];\n\nconst SEQUENTIAL_RAMP_DARK: readonly string[] = [\"#9ec5f4\", \"#6da7ec\", \"#3987e5\", \"#256abf\", \"#184f95\"];\n\n/**\n * The default (light-surface) sequential ramp.\n *\n * Prefer `sequentialColor(index, mode)` — this array exists for consumers that\n * only need the documented default.\n */\nexport const SEQUENTIAL_RAMP: readonly string[] = SEQUENTIAL_RAMP_LIGHT;\n\nconst categoricalSet = (mode: ChartMode): readonly string[] => (mode === \"dark\" ? CATEGORICAL_DARK : CATEGORICAL_LIGHT);\n\nconst sequentialSet = (mode: ChartMode): readonly string[] =>\n mode === \"dark\" ? SEQUENTIAL_RAMP_DARK : SEQUENTIAL_RAMP_LIGHT;\n\n/**\n * The identity colour for series `index`.\n *\n * Never generates a hue and never cycles: anything at or past the ceiling gets\n * `OTHER_COLOR`, because a series past the ceiling should already have been\n * folded into the \"other\" bucket by the caller.\n */\nexport function seriesColor(index: number, mode: ChartMode = \"light\"): string {\n if (index < 0 || index >= CATEGORICAL_CEILING) return OTHER_COLOR;\n return categoricalSet(mode)[index];\n}\n\n/**\n * Both mode steps for series `index`, shaped for a shadcn `ChartConfig` entry's\n * `theme` field.\n */\nexport function seriesTheme(index: number): { light: string; dark: string } {\n return { light: seriesColor(index, \"light\"), dark: seriesColor(index, \"dark\") };\n}\n\n/**\n * The magnitude colour for rank position `index`, clamped to the last step so a\n * long list keeps its weakest step rather than falling off the ramp.\n */\nexport function sequentialColor(index: number, mode: ChartMode = \"light\"): string {\n const ramp = sequentialSet(mode);\n const clamped = Math.min(Math.max(index, 0), ramp.length - 1);\n return ramp[clamped];\n}\n"]}
|
|
@@ -3077,6 +3077,15 @@ var Auth = class extends AbstractApiData {
|
|
|
3077
3077
|
if (data.marketingConsentAt !== void 0) response.data.attributes.marketingConsentAt = data.marketingConsentAt;
|
|
3078
3078
|
if (data.inviteCode !== void 0) response.data.attributes.inviteCode = data.inviteCode;
|
|
3079
3079
|
if (data.referralCode !== void 0) response.data.attributes.referralCode = data.referralCode;
|
|
3080
|
+
if (data.source !== void 0) {
|
|
3081
|
+
const attributes = response.data.attributes;
|
|
3082
|
+
if (data.source.sourceRef !== void 0) attributes.sourceRef = data.source.sourceRef;
|
|
3083
|
+
if (data.source.sourceUtmSource !== void 0) attributes.sourceUtmSource = data.source.sourceUtmSource;
|
|
3084
|
+
if (data.source.sourceUtmMedium !== void 0) attributes.sourceUtmMedium = data.source.sourceUtmMedium;
|
|
3085
|
+
if (data.source.sourceUtmCampaign !== void 0) attributes.sourceUtmCampaign = data.source.sourceUtmCampaign;
|
|
3086
|
+
if (data.source.sourceReferrer !== void 0) attributes.sourceReferrer = data.source.sourceReferrer;
|
|
3087
|
+
if (data.source.sourceLandingPath !== void 0) attributes.sourceLandingPath = data.source.sourceLandingPath;
|
|
3088
|
+
}
|
|
3080
3089
|
return response;
|
|
3081
3090
|
}
|
|
3082
3091
|
};
|
|
@@ -8630,6 +8639,12 @@ var Waitlist = class extends AbstractApiData {
|
|
|
8630
8639
|
_confirmedAt;
|
|
8631
8640
|
_invitedAt;
|
|
8632
8641
|
_registeredAt;
|
|
8642
|
+
_sourceRef;
|
|
8643
|
+
_sourceUtmSource;
|
|
8644
|
+
_sourceUtmMedium;
|
|
8645
|
+
_sourceUtmCampaign;
|
|
8646
|
+
_sourceReferrer;
|
|
8647
|
+
_sourceLandingPath;
|
|
8633
8648
|
get email() {
|
|
8634
8649
|
return this._email ?? "";
|
|
8635
8650
|
}
|
|
@@ -8660,6 +8675,24 @@ var Waitlist = class extends AbstractApiData {
|
|
|
8660
8675
|
get registeredAt() {
|
|
8661
8676
|
return this._registeredAt;
|
|
8662
8677
|
}
|
|
8678
|
+
get sourceRef() {
|
|
8679
|
+
return this._sourceRef;
|
|
8680
|
+
}
|
|
8681
|
+
get sourceUtmSource() {
|
|
8682
|
+
return this._sourceUtmSource;
|
|
8683
|
+
}
|
|
8684
|
+
get sourceUtmMedium() {
|
|
8685
|
+
return this._sourceUtmMedium;
|
|
8686
|
+
}
|
|
8687
|
+
get sourceUtmCampaign() {
|
|
8688
|
+
return this._sourceUtmCampaign;
|
|
8689
|
+
}
|
|
8690
|
+
get sourceReferrer() {
|
|
8691
|
+
return this._sourceReferrer;
|
|
8692
|
+
}
|
|
8693
|
+
get sourceLandingPath() {
|
|
8694
|
+
return this._sourceLandingPath;
|
|
8695
|
+
}
|
|
8663
8696
|
rehydrate(data) {
|
|
8664
8697
|
super.rehydrate(data);
|
|
8665
8698
|
this._email = data.jsonApi.attributes.email;
|
|
@@ -8667,11 +8700,18 @@ var Waitlist = class extends AbstractApiData {
|
|
|
8667
8700
|
this._gdprConsentAt = data.jsonApi.attributes.gdprConsentAt;
|
|
8668
8701
|
this._marketingConsent = data.jsonApi.attributes.marketingConsent;
|
|
8669
8702
|
this._marketingConsentAt = data.jsonApi.attributes.marketingConsentAt;
|
|
8670
|
-
|
|
8703
|
+
const questionnaire = data.jsonApi.attributes.questionnaire;
|
|
8704
|
+
this._questionnaire = questionnaire && typeof questionnaire === "object" ? questionnaire : void 0;
|
|
8671
8705
|
this._status = data.jsonApi.attributes.status;
|
|
8672
8706
|
this._confirmedAt = data.jsonApi.attributes.confirmedAt;
|
|
8673
8707
|
this._invitedAt = data.jsonApi.attributes.invitedAt;
|
|
8674
8708
|
this._registeredAt = data.jsonApi.attributes.registeredAt;
|
|
8709
|
+
this._sourceRef = data.jsonApi.attributes.sourceRef;
|
|
8710
|
+
this._sourceUtmSource = data.jsonApi.attributes.sourceUtmSource;
|
|
8711
|
+
this._sourceUtmMedium = data.jsonApi.attributes.sourceUtmMedium;
|
|
8712
|
+
this._sourceUtmCampaign = data.jsonApi.attributes.sourceUtmCampaign;
|
|
8713
|
+
this._sourceReferrer = data.jsonApi.attributes.sourceReferrer;
|
|
8714
|
+
this._sourceLandingPath = data.jsonApi.attributes.sourceLandingPath;
|
|
8675
8715
|
return this;
|
|
8676
8716
|
}
|
|
8677
8717
|
createJsonApi(data) {
|
|
@@ -8698,6 +8738,15 @@ var Waitlist = class extends AbstractApiData {
|
|
|
8698
8738
|
if (data.questionnaire !== void 0) {
|
|
8699
8739
|
response.data.attributes.questionnaire = JSON.stringify(data.questionnaire);
|
|
8700
8740
|
}
|
|
8741
|
+
if (data.source !== void 0) {
|
|
8742
|
+
const attributes = response.data.attributes;
|
|
8743
|
+
if (data.source.sourceRef !== void 0) attributes.sourceRef = data.source.sourceRef;
|
|
8744
|
+
if (data.source.sourceUtmSource !== void 0) attributes.sourceUtmSource = data.source.sourceUtmSource;
|
|
8745
|
+
if (data.source.sourceUtmMedium !== void 0) attributes.sourceUtmMedium = data.source.sourceUtmMedium;
|
|
8746
|
+
if (data.source.sourceUtmCampaign !== void 0) attributes.sourceUtmCampaign = data.source.sourceUtmCampaign;
|
|
8747
|
+
if (data.source.sourceReferrer !== void 0) attributes.sourceReferrer = data.source.sourceReferrer;
|
|
8748
|
+
if (data.source.sourceLandingPath !== void 0) attributes.sourceLandingPath = data.source.sourceLandingPath;
|
|
8749
|
+
}
|
|
8701
8750
|
return response;
|
|
8702
8751
|
}
|
|
8703
8752
|
};
|
|
@@ -8789,11 +8838,12 @@ var WaitlistService = class extends AbstractService {
|
|
|
8789
8838
|
});
|
|
8790
8839
|
}
|
|
8791
8840
|
/**
|
|
8792
|
-
* Get statistics (admin)
|
|
8841
|
+
* Get statistics (admin): GET /waitlists/stats, rehydrated as waitlist-stats
|
|
8793
8842
|
*/
|
|
8794
8843
|
static async getStats() {
|
|
8795
8844
|
const endpoint = new EndpointCreator({
|
|
8796
|
-
endpoint: Modules.
|
|
8845
|
+
endpoint: Modules.Waitlist,
|
|
8846
|
+
id: "stats"
|
|
8797
8847
|
});
|
|
8798
8848
|
return this.callApi({
|
|
8799
8849
|
type: Modules.WaitlistStats,
|
|
@@ -8873,6 +8923,7 @@ var WaitlistFields = /* @__PURE__ */ ((WaitlistFields2) => {
|
|
|
8873
8923
|
WaitlistFields2["email"] = "email";
|
|
8874
8924
|
WaitlistFields2["status"] = "status";
|
|
8875
8925
|
WaitlistFields2["createdAt"] = "createdAt";
|
|
8926
|
+
WaitlistFields2["source"] = "source";
|
|
8876
8927
|
WaitlistFields2["questionnaire"] = "questionnaire";
|
|
8877
8928
|
WaitlistFields2["actions"] = "actions";
|
|
8878
8929
|
return WaitlistFields2;
|
|
@@ -10301,4 +10352,4 @@ export {
|
|
|
10301
10352
|
AuditLogService,
|
|
10302
10353
|
AuditLogModule
|
|
10303
10354
|
};
|
|
10304
|
-
//# sourceMappingURL=chunk-
|
|
10355
|
+
//# sourceMappingURL=chunk-HNOWSFM5.mjs.map
|