@cmssy/core 5.0.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/index.cjs ADDED
@@ -0,0 +1,1799 @@
1
+ 'use strict';
2
+
3
+ var jose = require('jose');
4
+ var types = require('@cmssy/types');
5
+ var crypto$1 = require('crypto');
6
+
7
+ // src/session.ts
8
+ var CMSSY_SESSION_COOKIE = "cmssy_session";
9
+ var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
10
+ var MIN_SESSION_SECRET_LENGTH = 32;
11
+ var ACCESS_EXPIRY_SKEW_MS = 3e4;
12
+ async function deriveSessionKey(secret) {
13
+ if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
14
+ throw new Error(
15
+ `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
16
+ );
17
+ }
18
+ const digest = await crypto.subtle.digest(
19
+ "SHA-256",
20
+ new TextEncoder().encode(secret)
21
+ );
22
+ return new Uint8Array(digest);
23
+ }
24
+ async function sealSession(payload, secret, audience) {
25
+ const key = await deriveSessionKey(secret);
26
+ const jwt = new jose.EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
27
+ if (audience) jwt.setAudience(audience);
28
+ return jwt.encrypt(key);
29
+ }
30
+ async function openSession(token, secret, audience) {
31
+ const key = await deriveSessionKey(secret);
32
+ try {
33
+ const { payload } = await jose.jwtDecrypt(token, key, {
34
+ keyManagementAlgorithms: ["dir"],
35
+ contentEncryptionAlgorithms: ["A256GCM"],
36
+ ...audience ? { audience } : {}
37
+ });
38
+ const { accessToken, refreshToken, accessExpiresAt, user } = payload;
39
+ if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
40
+ return null;
41
+ }
42
+ return {
43
+ accessToken,
44
+ refreshToken,
45
+ accessExpiresAt,
46
+ user: {
47
+ recordId: user.recordId,
48
+ email: user.email
49
+ }
50
+ };
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function isAccessExpired(payload, now = Date.now()) {
56
+ return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
57
+ }
58
+ function sessionCookieOptions() {
59
+ return {
60
+ httpOnly: true,
61
+ secure: process.env.NODE_ENV !== "development",
62
+ sameSite: "lax",
63
+ path: "/",
64
+ maxAge: SESSION_MAX_AGE_SECONDS
65
+ };
66
+ }
67
+
68
+ // src/config.ts
69
+ var DEFAULT_CMSSY_EDITOR_ORIGINS = [
70
+ "https://cmssy.io",
71
+ "https://www.cmssy.io"
72
+ ];
73
+ function parseEditorOriginEnv(raw) {
74
+ if (!raw) return void 0;
75
+ const parts = raw.split(",").map((o) => o.trim()).filter((o) => o.length > 0);
76
+ if (parts.length === 0) return void 0;
77
+ return parts.length === 1 ? parts[0] : parts;
78
+ }
79
+ function isDevelopment() {
80
+ return typeof process !== "undefined" && process.env.NODE_ENV === "development";
81
+ }
82
+ function resolveEditorOrigin(editorOrigin) {
83
+ const value = editorOrigin ?? (typeof process !== "undefined" ? parseEditorOriginEnv(process.env.CMSSY_EDITOR_ORIGIN) : void 0);
84
+ if (value === void 0) {
85
+ return isDevelopment() ? "*" : DEFAULT_CMSSY_EDITOR_ORIGINS;
86
+ }
87
+ if (Array.isArray(value)) {
88
+ const cleaned = value.filter((o) => o && o.trim().length > 0);
89
+ return cleaned.length > 0 ? cleaned : DEFAULT_CMSSY_EDITOR_ORIGINS;
90
+ }
91
+ return value.trim().length > 0 ? value : DEFAULT_CMSSY_EDITOR_ORIGINS;
92
+ }
93
+ var REQUIRED_CONFIG_ENV = [
94
+ ["org", "CMSSY_ORG_SLUG"],
95
+ ["workspaceSlug", "CMSSY_WORKSPACE_SLUG"],
96
+ ["draftSecret", "CMSSY_DRAFT_SECRET"]
97
+ ];
98
+ function defineCmssyConfig(config) {
99
+ const resolved = { ...config };
100
+ const missing = [];
101
+ for (const [key, env] of REQUIRED_CONFIG_ENV) {
102
+ const value = config[key];
103
+ const trimmed = typeof value === "string" ? value.trim() : "";
104
+ if (trimmed) {
105
+ resolved[key] = trimmed;
106
+ } else {
107
+ missing.push(`${env} (config.${key})`);
108
+ }
109
+ }
110
+ if (missing.length > 0) {
111
+ if (typeof window !== "undefined") {
112
+ throw new Error(
113
+ "cmssy: the config was evaluated in the browser, so it cannot see the server's environment variables.\n\nThis is an import problem, not a config problem: client-side code imported a VALUE from a module that reads the cmssy config - directly, or through a helper sitting next to one.\n\nFix it by importing types only (they are erased at build time), or by moving the value into a module that does not touch the config."
114
+ );
115
+ }
116
+ throw new Error(
117
+ `cmssy: missing required configuration:
118
+ - ${missing.join(
119
+ "\n - "
120
+ )}
121
+ Set the listed environment variables (e.g. in .env.local) and restart the dev server.`
122
+ );
123
+ }
124
+ return resolved;
125
+ }
126
+ function assertAuthConfig(config) {
127
+ const auth = config.auth;
128
+ if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
129
+ throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
130
+ }
131
+ if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
132
+ throw new Error(
133
+ `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
134
+ );
135
+ }
136
+ return auth;
137
+ }
138
+
139
+ // src/data/http.ts
140
+ var CmssyRequestError = class extends Error {
141
+ status;
142
+ constructor(message, status) {
143
+ super(message);
144
+ this.name = "CmssyRequestError";
145
+ this.status = status;
146
+ }
147
+ };
148
+ var DEFAULT_RETRY_STATUSES = [429, 503];
149
+ function retryAfterMs(response) {
150
+ const raw = response.headers?.get("retry-after");
151
+ if (!raw) return null;
152
+ const seconds = Number(raw);
153
+ if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
154
+ const date = Date.parse(raw);
155
+ if (Number.isFinite(date)) return Math.max(0, date - Date.now());
156
+ return null;
157
+ }
158
+ function sleep(ms, signal) {
159
+ return new Promise((resolve, reject) => {
160
+ if (signal?.aborted) {
161
+ reject(new Error("cmssy: request aborted"));
162
+ return;
163
+ }
164
+ const timer = setTimeout(() => {
165
+ signal?.removeEventListener("abort", onAbort);
166
+ resolve();
167
+ }, ms);
168
+ function onAbort() {
169
+ clearTimeout(timer);
170
+ reject(new Error("cmssy: request aborted"));
171
+ }
172
+ signal?.addEventListener("abort", onAbort, { once: true });
173
+ });
174
+ }
175
+ async function fetchWithRetry(doFetch, url, init, retry) {
176
+ if (retry === false || retry === void 0) {
177
+ return doFetch(url, init);
178
+ }
179
+ const maxRetries = retry.maxRetries ?? 3;
180
+ const baseDelayMs = retry.baseDelayMs ?? 300;
181
+ const maxDelayMs = retry.maxDelayMs ?? 3e3;
182
+ const retryStatuses = retry.retryStatuses ?? DEFAULT_RETRY_STATUSES;
183
+ let response = await doFetch(url, init);
184
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
185
+ if (response.ok || !retryStatuses.includes(response.status)) {
186
+ return response;
187
+ }
188
+ const backoff = baseDelayMs * 2 ** attempt;
189
+ const wait = Math.min(retryAfterMs(response) ?? backoff, maxDelayMs);
190
+ await sleep(wait, init.signal);
191
+ response = await doFetch(url, init);
192
+ }
193
+ return response;
194
+ }
195
+ async function postGraphql(url, query, variables, options) {
196
+ const doFetch = options.fetch ?? globalThis.fetch;
197
+ if (typeof doFetch !== "function") {
198
+ throw new Error(
199
+ "cmssy: no fetch implementation available - pass options.fetch"
200
+ );
201
+ }
202
+ const response = await fetchWithRetry(
203
+ doFetch,
204
+ url,
205
+ {
206
+ method: "POST",
207
+ headers: { "content-type": "application/json", ...options.headers },
208
+ body: JSON.stringify({ query, variables }),
209
+ signal: options.signal
210
+ },
211
+ options.retry
212
+ );
213
+ if (!response.ok) {
214
+ let detail = "";
215
+ try {
216
+ const body = await response.json();
217
+ if (body.errors && body.errors.length > 0) {
218
+ detail = ` - ${body.errors.map((error) => error.message ?? "GraphQL error").join("; ")}`;
219
+ }
220
+ } catch {
221
+ detail = "";
222
+ }
223
+ throw new CmssyRequestError(
224
+ `cmssy: ${options.label} failed (${response.status})${detail}`,
225
+ response.status
226
+ );
227
+ }
228
+ let json;
229
+ try {
230
+ json = await response.json();
231
+ } catch {
232
+ throw new Error(`cmssy: invalid JSON response from the ${options.label}`);
233
+ }
234
+ if (json.errors && json.errors.length > 0) {
235
+ const message = json.errors.map((error) => error.message ?? "GraphQL error").join("; ");
236
+ throw new Error(`cmssy: ${options.label} error - ${message}`);
237
+ }
238
+ return json.data;
239
+ }
240
+
241
+ // src/content/content-client.ts
242
+ var DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
243
+ function resolveApiUrl(apiUrl) {
244
+ const explicit = apiUrl?.trim();
245
+ if (explicit) return explicit;
246
+ const env = globalThis.process?.env;
247
+ const fromEnv = env?.CMSSY_API_URL?.trim() ?? "";
248
+ return fromEnv.length > 0 ? fromEnv : DEFAULT_CMSSY_API_URL;
249
+ }
250
+ function resolvePublicUrl(config) {
251
+ const base = resolveApiUrl(config.apiUrl).replace(/\/graphql\/?$/, "");
252
+ return `${base}/public/${config.org}/${config.workspaceSlug}/graphql`;
253
+ }
254
+ var PUBLIC_PAGE_QUERY = `query PublicPage($workspaceSlug: String!, $slug: String!, $previewSecret: String) {
255
+ public {
256
+ page {
257
+ get(workspaceSlug: $workspaceSlug, slug: $slug, previewSecret: $previewSecret) {
258
+ id
259
+ blocks { id type content style advanced }
260
+ publishedBlocks { id type content style advanced }
261
+ }
262
+ }
263
+ }
264
+ }`;
265
+ var PUBLIC_PAGE_DEV_QUERY = `query PublicPage($workspaceSlug: String!, $slug: String!, $previewSecret: String, $devPreview: Boolean) {
266
+ public {
267
+ page {
268
+ get(workspaceSlug: $workspaceSlug, slug: $slug, previewSecret: $previewSecret, devPreview: $devPreview) {
269
+ id
270
+ blocks { id type content style advanced }
271
+ publishedBlocks { id type content style advanced }
272
+ }
273
+ }
274
+ }
275
+ }`;
276
+ var PUBLIC_PAGE_BY_ID_QUERY = `query PublicPageById($workspaceSlug: String!, $pageId: ID!) {
277
+ public {
278
+ page {
279
+ getById(workspaceSlug: $workspaceSlug, pageId: $pageId) {
280
+ id
281
+ publishedBlocks { id type content style advanced }
282
+ }
283
+ }
284
+ }
285
+ }`;
286
+ var PUBLIC_PAGES_QUERY = `query PublicPages($workspaceSlug: String!) {
287
+ public {
288
+ page {
289
+ list(workspaceSlug: $workspaceSlug) {
290
+ id
291
+ slug
292
+ updatedAt
293
+ publishedAt
294
+ }
295
+ }
296
+ }
297
+ }`;
298
+ var PUBLIC_PAGE_META_QUERY = `query PublicPageMeta($workspaceSlug: String!, $slug: String!) {
299
+ public {
300
+ page {
301
+ get(workspaceSlug: $workspaceSlug, slug: $slug) {
302
+ id
303
+ seoTitle
304
+ seoDescription
305
+ seoKeywords
306
+ displayName
307
+ }
308
+ }
309
+ }
310
+ }`;
311
+ var PUBLIC_PAGE_LAYOUTS_QUERY = `query PublicPageLayouts($workspaceSlug: String!, $pageSlug: String!, $previewSecret: String) {
312
+ public {
313
+ page {
314
+ layouts(workspaceSlug: $workspaceSlug, pageSlug: $pageSlug, previewSecret: $previewSecret) {
315
+ position
316
+ blocks { id type content style advanced order isActive }
317
+ settings { desktopWidth mobileBehavior }
318
+ }
319
+ }
320
+ }
321
+ }`;
322
+ function normalizeSlug(path) {
323
+ if (Array.isArray(path)) {
324
+ const joined = path.filter(Boolean).join("/");
325
+ return joined ? `/${joined}` : "/";
326
+ }
327
+ if (!path || path === "/") return "/";
328
+ return path.startsWith("/") ? path : `/${path}`;
329
+ }
330
+ async function fetchPage(config, path, options = {}) {
331
+ const slug = normalizeSlug(path);
332
+ const trimmedSecret = options.previewSecret?.trim();
333
+ const previewSecret = trimmedSecret ? trimmedSecret : null;
334
+ const devToken = options.devToken?.trim();
335
+ const devPreview = Boolean(options.devPreview && devToken);
336
+ const headers2 = {};
337
+ if (devPreview && devToken) {
338
+ headers2["authorization"] = `Bearer ${devToken}`;
339
+ if (options.workspaceId) {
340
+ headers2["x-workspace-id"] = options.workspaceId;
341
+ }
342
+ }
343
+ const data = await postGraphql(
344
+ resolvePublicUrl(config),
345
+ devPreview ? PUBLIC_PAGE_DEV_QUERY : PUBLIC_PAGE_QUERY,
346
+ {
347
+ workspaceSlug: config.workspaceSlug,
348
+ slug,
349
+ previewSecret,
350
+ ...devPreview ? { devPreview: true } : {}
351
+ },
352
+ {
353
+ fetch: options.fetch,
354
+ signal: options.signal,
355
+ headers: headers2,
356
+ retry: options.retry ?? {},
357
+ label: "page fetch"
358
+ }
359
+ );
360
+ const page = data?.public?.page?.get;
361
+ if (!page) return null;
362
+ const draft = previewSecret !== null || devPreview;
363
+ const blocks = (draft ? page.blocks : page.publishedBlocks) ?? [];
364
+ return { id: page.id, blocks };
365
+ }
366
+ async function fetchPageById(config, pageId, options = {}) {
367
+ const data = await postGraphql(
368
+ resolvePublicUrl(config),
369
+ PUBLIC_PAGE_BY_ID_QUERY,
370
+ { workspaceSlug: config.workspaceSlug, pageId },
371
+ {
372
+ fetch: options.fetch,
373
+ signal: options.signal,
374
+ retry: options.retry ?? {},
375
+ label: "page-by-id fetch"
376
+ }
377
+ );
378
+ const page = data?.public?.page?.getById;
379
+ if (!page) return null;
380
+ return { id: page.id, blocks: page.publishedBlocks ?? [] };
381
+ }
382
+ async function fetchPages(config, options = {}) {
383
+ const data = await postGraphql(
384
+ resolvePublicUrl(config),
385
+ PUBLIC_PAGES_QUERY,
386
+ { workspaceSlug: config.workspaceSlug },
387
+ {
388
+ fetch: options.fetch,
389
+ signal: options.signal,
390
+ retry: options.retry ?? {},
391
+ label: "pages fetch"
392
+ }
393
+ );
394
+ return data?.public?.page?.list ?? [];
395
+ }
396
+ async function fetchPageMeta(config, path, options = {}) {
397
+ const slug = normalizeSlug(path);
398
+ const data = await postGraphql(
399
+ resolvePublicUrl(config),
400
+ PUBLIC_PAGE_META_QUERY,
401
+ { workspaceSlug: config.workspaceSlug, slug },
402
+ {
403
+ fetch: options.fetch,
404
+ signal: options.signal,
405
+ retry: options.retry ?? {},
406
+ label: "page meta fetch"
407
+ }
408
+ );
409
+ return data?.public?.page?.get ?? null;
410
+ }
411
+ async function fetchLayouts(config, path, options = {}) {
412
+ const pageSlug = normalizeSlug(path);
413
+ const trimmedSecret = options.previewSecret?.trim();
414
+ const previewSecret = trimmedSecret ? trimmedSecret : null;
415
+ const data = await postGraphql(
416
+ resolvePublicUrl(config),
417
+ PUBLIC_PAGE_LAYOUTS_QUERY,
418
+ { workspaceSlug: config.workspaceSlug, pageSlug, previewSecret },
419
+ {
420
+ fetch: options.fetch,
421
+ signal: options.signal,
422
+ retry: options.retry ?? {},
423
+ label: "layouts fetch"
424
+ }
425
+ );
426
+ return data?.public?.page?.layouts ?? [];
427
+ }
428
+
429
+ // src/content/get-block-content.ts
430
+ function isPlainObject(value) {
431
+ return typeof value === "object" && value !== null && !Array.isArray(value);
432
+ }
433
+ function looksLikeLocaleKey(key) {
434
+ return /^[a-z]{2}(-[A-Za-z]{2})?$/.test(key);
435
+ }
436
+ function getBlockContentForLanguage(content, locale, defaultLocale = "en", availableLocales) {
437
+ if (!isPlainObject(content)) return {};
438
+ const isLocale = availableLocales ? (key) => availableLocales.includes(key) : looksLikeLocaleKey;
439
+ const localeEntries = Object.entries(content).filter(
440
+ ([key, value]) => isLocale(key) && isPlainObject(value)
441
+ );
442
+ if (localeEntries.length === 0) return { ...content };
443
+ const localeMap = Object.fromEntries(localeEntries);
444
+ const nonTranslatable = {};
445
+ for (const [key, value] of Object.entries(content)) {
446
+ if (!(isLocale(key) && isPlainObject(value))) nonTranslatable[key] = value;
447
+ }
448
+ const fallbackKey = Object.keys(localeMap)[0];
449
+ const chosen = localeMap[locale] ?? localeMap[defaultLocale] ?? localeMap[fallbackKey];
450
+ return { ...nonTranslatable, ...chosen };
451
+ }
452
+ function asBucket(value) {
453
+ return isPlainObject(value) ? value : {};
454
+ }
455
+
456
+ // src/data/graphql-request.ts
457
+ async function graphqlRequest(config, query, variables, options = {}, label = "request") {
458
+ const url = options.public ? resolvePublicUrl(config) : resolveApiUrl(config.apiUrl);
459
+ return postGraphql(url, query, variables, {
460
+ fetch: options.fetch,
461
+ signal: options.signal,
462
+ headers: options.headers,
463
+ retry: options.retry,
464
+ label
465
+ });
466
+ }
467
+
468
+ // src/data/queries.ts
469
+ var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
470
+ public {
471
+ siteConfig(workspaceSlug: $workspaceSlug) {
472
+ id
473
+ workspaceId
474
+ siteName
475
+ defaultLanguage
476
+ enabledLanguages
477
+ enabledFeatures
478
+ notFoundPageId
479
+ previewUrl
480
+ branding {
481
+ brandName
482
+ logoUrl
483
+ faviconUrl
484
+ ogImageUrl
485
+ }
486
+ }
487
+ }
488
+ }`;
489
+ var MODEL_DEFINITIONS_QUERY = `query PublicModelDefinitions($workspaceId: String!) {
490
+ public {
491
+ model {
492
+ definitions(workspaceId: $workspaceId) {
493
+ id name slug description icon color displayField recordCount
494
+ }
495
+ }
496
+ }
497
+ }`;
498
+ var MODEL_RECORDS_QUERY = `query PublicModelRecords($workspaceId: String!, $modelSlug: String!, $filter: JSON, $sort: String, $locale: String, $limit: Int, $offset: Int, $populate: [String!]) {
499
+ public {
500
+ model {
501
+ records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, sort: $sort, locale: $locale, limit: $limit, offset: $offset, populate: $populate) {
502
+ items { id modelId data status createdAt updatedAt }
503
+ total
504
+ hasMore
505
+ }
506
+ }
507
+ }
508
+ }`;
509
+ var FORM_QUERY = `query PublicForm($formId: ID!) {
510
+ public {
511
+ form {
512
+ get(formId: $formId) {
513
+ id
514
+ name
515
+ slug
516
+ description
517
+ fields {
518
+ id name fieldType label placeholder helpText
519
+ defaultValue width order showWhen requiredWhen
520
+ options { value label disabled }
521
+ validation { required minLength maxLength minValue maxValue pattern customMessage }
522
+ }
523
+ settings {
524
+ actionType submitButtonLabel successMessage errorMessage
525
+ redirectUrl requireLogin enableCaptcha
526
+ }
527
+ }
528
+ }
529
+ }
530
+ }`;
531
+ var SUBMIT_FORM_MUTATION = `mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
532
+ public {
533
+ form {
534
+ submit(formId: $formId, input: $input) {
535
+ success message submissionId redirectUrl accessToken customer
536
+ }
537
+ }
538
+ }
539
+ }`;
540
+
541
+ // src/data/settings-client.ts
542
+ async function fetchSiteConfig(config, options = {}) {
543
+ const data = await graphqlRequest(
544
+ config,
545
+ SITE_CONFIG_QUERY,
546
+ { workspaceSlug: config.workspaceSlug },
547
+ { ...options, public: true, retry: options.retry ?? {} },
548
+ "site config query"
549
+ );
550
+ return data.public?.siteConfig ?? null;
551
+ }
552
+ async function resolveWorkspaceId(config, options = {}) {
553
+ const siteConfig = await fetchSiteConfig(config, options);
554
+ if (!siteConfig?.workspaceId) {
555
+ throw new Error(
556
+ `cmssy: could not resolve workspaceId for "${config.workspaceSlug}"`
557
+ );
558
+ }
559
+ return siteConfig.workspaceId;
560
+ }
561
+ var workspaceIdCache = /* @__PURE__ */ new Map();
562
+ function cachedWorkspaceId(config) {
563
+ const key = `${resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
564
+ const existing = workspaceIdCache.get(key);
565
+ if (existing) return existing;
566
+ const fresh = resolveWorkspaceId(config).catch((err) => {
567
+ workspaceIdCache.delete(key);
568
+ throw err;
569
+ });
570
+ workspaceIdCache.set(key, fresh);
571
+ return fresh;
572
+ }
573
+ function clearWorkspaceIdCache() {
574
+ workspaceIdCache.clear();
575
+ }
576
+
577
+ // src/data/client.ts
578
+ function createCmssyClient(input) {
579
+ const config = {
580
+ ...input,
581
+ apiUrl: resolveApiUrl(input.apiUrl)
582
+ };
583
+ let cachedWorkspaceId2;
584
+ let inFlight;
585
+ function resolveWorkspaceId2(options) {
586
+ if (cachedWorkspaceId2) return Promise.resolve(cachedWorkspaceId2);
587
+ if (!inFlight) {
588
+ inFlight = resolveWorkspaceId(config, options).then((id) => {
589
+ cachedWorkspaceId2 = id;
590
+ return id;
591
+ }).finally(() => {
592
+ inFlight = void 0;
593
+ });
594
+ }
595
+ return inFlight;
596
+ }
597
+ return {
598
+ config,
599
+ resolveWorkspaceId: resolveWorkspaceId2,
600
+ query(document2, variables = {}, options) {
601
+ return graphqlRequest(
602
+ config,
603
+ document2,
604
+ variables,
605
+ options,
606
+ "graphql operation"
607
+ );
608
+ },
609
+ async queryScoped(document2, variables = {}, options = {}) {
610
+ const { workspaceId: provided, headers: headers2, ...rest } = options;
611
+ const workspaceId = provided ?? await resolveWorkspaceId2({ ...rest, headers: headers2 });
612
+ const hasWorkspaceId = variables.workspaceId !== void 0 && variables.workspaceId !== null;
613
+ const scopedVariables = /\$workspaceId\b/.test(document2) && !hasWorkspaceId ? { ...variables, workspaceId } : variables;
614
+ return graphqlRequest(
615
+ config,
616
+ document2,
617
+ scopedVariables,
618
+ { ...rest, headers: { ...headers2, "x-workspace-id": workspaceId } },
619
+ "graphql operation"
620
+ );
621
+ }
622
+ };
623
+ }
624
+
625
+ // src/data/resolve-forms.ts
626
+ function collectFormIds(blocks, locale, defaultLocale) {
627
+ const ids = /* @__PURE__ */ new Set();
628
+ for (const block of blocks) {
629
+ const content = getBlockContentForLanguage(
630
+ block.content,
631
+ locale,
632
+ defaultLocale
633
+ );
634
+ const formId = content.formId;
635
+ if (typeof formId === "string" && formId.trim()) ids.add(formId);
636
+ }
637
+ return [...ids];
638
+ }
639
+ async function resolveForms(config, blocks, locale, defaultLocale, options) {
640
+ const ids = collectFormIds(blocks, locale, defaultLocale);
641
+ if (ids.length === 0) return {};
642
+ const client = createCmssyClient(config);
643
+ const entries = await Promise.all(
644
+ ids.map(async (id) => {
645
+ try {
646
+ const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
647
+ return [id, data.public.form.get];
648
+ } catch (err) {
649
+ if (typeof console !== "undefined") {
650
+ console.warn(`[cmssy] failed to resolve form ${id}`, err);
651
+ }
652
+ return [id, null];
653
+ }
654
+ })
655
+ );
656
+ const forms = {};
657
+ for (const [id, def] of entries) {
658
+ if (def) forms[id] = def;
659
+ }
660
+ return forms;
661
+ }
662
+
663
+ // src/data/site-locales.ts
664
+ var TTL_MS = 6e4;
665
+ var MAX_ENTRIES = 64;
666
+ var cache = /* @__PURE__ */ new Map();
667
+ async function resolveSiteLocales(config, options) {
668
+ const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
669
+ const cached = cache.get(key);
670
+ if (cached && cached.expires > Date.now()) return cached.value;
671
+ cache.delete(key);
672
+ let value;
673
+ try {
674
+ const data = await graphqlRequest(
675
+ config,
676
+ SITE_CONFIG_QUERY,
677
+ { workspaceSlug: config.workspaceSlug },
678
+ { ...options, public: true, retry: options?.retry ?? {} },
679
+ "site config"
680
+ );
681
+ const siteConfig = data.public?.siteConfig ?? null;
682
+ const defaultLocale = siteConfig?.defaultLanguage || "en";
683
+ const enabled = siteConfig?.enabledLanguages ?? [];
684
+ value = {
685
+ defaultLocale,
686
+ locales: enabled.length > 0 ? enabled : [defaultLocale]
687
+ };
688
+ } catch {
689
+ value = { defaultLocale: "en", locales: ["en"] };
690
+ }
691
+ if (cache.size >= MAX_ENTRIES) cache.clear();
692
+ cache.set(key, { value, expires: Date.now() + TTL_MS });
693
+ return value;
694
+ }
695
+ function splitLocaleFromPath(path, siteLocales) {
696
+ const first = path?.[0];
697
+ if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
698
+ return { locale: first, path: path.slice(1) };
699
+ }
700
+ return { locale: siteLocales.defaultLocale, path };
701
+ }
702
+
703
+ // src/data/localize-href.ts
704
+ var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
705
+ function isExternalHref(href) {
706
+ const value = href.trim();
707
+ if (!value) return true;
708
+ if (value.startsWith("#")) return true;
709
+ return PROTOCOL_OR_RELATIVE.test(value);
710
+ }
711
+ function stripLeadingLocale(path, locale) {
712
+ const segments = path.split("/");
713
+ const first = segments[1];
714
+ if (first && first !== locale.default && locale.enabled.includes(first)) {
715
+ segments.splice(1, 1);
716
+ const rest = segments.join("/");
717
+ return rest === "" ? "/" : rest;
718
+ }
719
+ return path;
720
+ }
721
+ function addLocalePrefix(path, target, locale) {
722
+ if (target === locale.default) return path;
723
+ if (path === "/") return `/${target}`;
724
+ return `/${target}${path}`;
725
+ }
726
+ function localizeHref(href, locale) {
727
+ const value = href.trim();
728
+ if (isExternalHref(value)) return href;
729
+ const boundary = value.search(/[?#]/);
730
+ const path = boundary === -1 ? value : value.slice(0, boundary);
731
+ const suffix = boundary === -1 ? "" : value.slice(boundary);
732
+ if (!path.startsWith("/")) return href;
733
+ const bare = stripLeadingLocale(path, locale);
734
+ return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
735
+ }
736
+ function buildLocaleSwitchHref(target, pathname, locale) {
737
+ const path = pathname && pathname.startsWith("/") ? pathname : "/";
738
+ const bare = stripLeadingLocale(path, locale);
739
+ return addLocalePrefix(bare, target, locale);
740
+ }
741
+ var ANCHOR_HREF = /(<a\b(?:"[^"]*"|'[^']*'|[^>])*?\shref=)(["'])(.*?)\2/gi;
742
+ function localizeHtmlLinks(html, locale) {
743
+ return html.replace(
744
+ ANCHOR_HREF,
745
+ (_match, prefix, quote, url) => `${prefix}${quote}${localizeHref(url, locale)}${quote}`
746
+ );
747
+ }
748
+
749
+ // src/locale.ts
750
+ var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
751
+ async function localeForPathname(config, pathname) {
752
+ const siteLocales = await resolveSiteLocales(config);
753
+ const segments = pathname.split("/").filter(Boolean);
754
+ return splitLocaleFromPath(segments, siteLocales).locale;
755
+ }
756
+ async function splitCmssyLocale(config, path) {
757
+ const siteLocales = await resolveSiteLocales(config);
758
+ return splitLocaleFromPath(path, siteLocales);
759
+ }
760
+ async function localeForPath(config, path) {
761
+ const siteLocales = await resolveSiteLocales(config);
762
+ const segments = Array.isArray(path) ? path.filter(Boolean) : path.split("/").filter(Boolean);
763
+ return splitLocaleFromPath(segments, siteLocales).locale;
764
+ }
765
+
766
+ // src/block-context.ts
767
+ function buildBlockContext(locale, defaultLocale, enabledLocales, isPreview, forms, extra) {
768
+ return {
769
+ locale: {
770
+ current: locale,
771
+ default: defaultLocale,
772
+ enabled: enabledLocales && enabledLocales.length > 0 ? enabledLocales : Array.from(/* @__PURE__ */ new Set([defaultLocale, locale]))
773
+ },
774
+ isPreview: isPreview ?? false,
775
+ forms,
776
+ ...extra?.auth ? { auth: extra.auth } : {},
777
+ ...extra?.workspace ? { workspace: extra.workspace } : {}
778
+ };
779
+ }
780
+
781
+ // src/fields.ts
782
+ function control(type) {
783
+ return (opts = {}) => ({
784
+ type,
785
+ label: opts.label ?? "",
786
+ ...opts
787
+ });
788
+ }
789
+ var fields = {
790
+ text: control("text"),
791
+ textarea: control("textarea"),
792
+ richText: control("richText"),
793
+ markdown: control("markdown"),
794
+ number: control("number"),
795
+ date: control("date"),
796
+ datetime: control("datetime"),
797
+ boolean: control("boolean"),
798
+ color: control("color"),
799
+ media: control("media"),
800
+ link: control("link"),
801
+ url: control("url"),
802
+ email: control("email"),
803
+ select: control("select"),
804
+ multiselect: control("multiselect"),
805
+ radio: control("radio"),
806
+ repeater: control("repeater"),
807
+ table: control("table"),
808
+ json: control("json"),
809
+ form: control("form"),
810
+ pageSelector: control("pageSelector")
811
+ };
812
+
813
+ // src/bridge/protocol.ts
814
+ var PROTOCOL_VERSION = 2;
815
+ function isProtocolCompatible(version) {
816
+ return version === PROTOCOL_VERSION;
817
+ }
818
+
819
+ // src/bridge/messages.ts
820
+ function normalizeOrigin(origin) {
821
+ const trimmed = origin.trim();
822
+ if (trimmed === "*") return "*";
823
+ try {
824
+ return new URL(trimmed).origin;
825
+ } catch {
826
+ return trimmed;
827
+ }
828
+ }
829
+ function postToEditor(target, editorOrigin, message) {
830
+ target.postMessage(message, normalizeOrigin(editorOrigin));
831
+ }
832
+ function isOriginAllowed(origin, allowed) {
833
+ const list = Array.isArray(allowed) ? allowed : [allowed];
834
+ const actual = normalizeOrigin(origin);
835
+ return list.some((candidate) => {
836
+ const expected = normalizeOrigin(candidate);
837
+ return expected === "*" || expected === actual;
838
+ });
839
+ }
840
+ function resolveInitialTarget(editorOrigin) {
841
+ const list = (Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin]).map((origin) => normalizeOrigin(origin));
842
+ if (list.includes("*")) return "*";
843
+ if (list.length === 1) return list[0];
844
+ const referrerOrigin = typeof document !== "undefined" && document.referrer ? normalizeOrigin(document.referrer) : "";
845
+ return list.find((origin) => origin === referrerOrigin) ?? list[0];
846
+ }
847
+ function isObject(value) {
848
+ return typeof value === "object" && value !== null && !Array.isArray(value);
849
+ }
850
+ function parseEditorMessage(data, origin, expectedOrigin) {
851
+ if (!isOriginAllowed(origin, expectedOrigin)) return null;
852
+ if (!isObject(data)) return null;
853
+ switch (data.type) {
854
+ case "cmssy:select":
855
+ return typeof data.blockId === "string" && data.protocolVersion === PROTOCOL_VERSION ? {
856
+ type: "cmssy:select",
857
+ protocolVersion: PROTOCOL_VERSION,
858
+ blockId: data.blockId
859
+ } : null;
860
+ case "cmssy:patch":
861
+ return typeof data.blockId === "string" && isObject(data.content) && data.protocolVersion === PROTOCOL_VERSION ? {
862
+ type: "cmssy:patch",
863
+ blockId: data.blockId,
864
+ content: data.content,
865
+ protocolVersion: PROTOCOL_VERSION,
866
+ ...isObject(data.style) ? { style: data.style } : {},
867
+ ...isObject(data.advanced) ? { advanced: data.advanced } : {},
868
+ ...typeof data.layoutPosition === "string" ? { layoutPosition: data.layoutPosition } : {}
869
+ } : null;
870
+ case "cmssy:parent-ready":
871
+ return data.protocolVersion === PROTOCOL_VERSION ? { type: "cmssy:parent-ready", protocolVersion: PROTOCOL_VERSION } : null;
872
+ case "cmssy:insert":
873
+ return typeof data.blockId === "string" && typeof data.blockType === "string" && isObject(data.content) && typeof data.index === "number" && data.protocolVersion === PROTOCOL_VERSION ? {
874
+ type: "cmssy:insert",
875
+ protocolVersion: PROTOCOL_VERSION,
876
+ blockId: data.blockId,
877
+ blockType: data.blockType,
878
+ content: data.content,
879
+ ...isObject(data.style) ? { style: data.style } : {},
880
+ ...isObject(data.advanced) ? { advanced: data.advanced } : {},
881
+ index: data.index
882
+ } : null;
883
+ case "cmssy:reorder":
884
+ return Array.isArray(data.blockIds) && data.blockIds.every((id) => typeof id === "string") && data.protocolVersion === PROTOCOL_VERSION ? {
885
+ type: "cmssy:reorder",
886
+ protocolVersion: PROTOCOL_VERSION,
887
+ blockIds: data.blockIds
888
+ } : null;
889
+ case "cmssy:remove":
890
+ return typeof data.blockId === "string" && data.protocolVersion === PROTOCOL_VERSION ? {
891
+ type: "cmssy:remove",
892
+ protocolVersion: PROTOCOL_VERSION,
893
+ blockId: data.blockId
894
+ } : null;
895
+ case "cmssy:drag-over":
896
+ return typeof data.y === "number" && data.protocolVersion === PROTOCOL_VERSION ? {
897
+ type: "cmssy:drag-over",
898
+ protocolVersion: PROTOCOL_VERSION,
899
+ y: data.y
900
+ } : null;
901
+ case "cmssy:drag-end":
902
+ return data.protocolVersion === PROTOCOL_VERSION ? { type: "cmssy:drag-end", protocolVersion: PROTOCOL_VERSION } : null;
903
+ default:
904
+ return null;
905
+ }
906
+ }
907
+
908
+ // src/secret-match.ts
909
+ var MAX_SECRET_LENGTH = 256;
910
+ async function cmssySecretsMatch(a, b) {
911
+ if (a.length > MAX_SECRET_LENGTH || b.length > MAX_SECRET_LENGTH) {
912
+ return false;
913
+ }
914
+ const encoder = new TextEncoder();
915
+ const [ha, hb] = await Promise.all([
916
+ crypto.subtle.digest("SHA-256", encoder.encode(a)),
917
+ crypto.subtle.digest("SHA-256", encoder.encode(b))
918
+ ]);
919
+ const va = new Uint8Array(ha);
920
+ const vb = new Uint8Array(hb);
921
+ let diff = 0;
922
+ for (let i = 0; i < va.length; i += 1) {
923
+ diff |= (va[i] ?? 0) ^ (vb[i] ?? 0);
924
+ }
925
+ return diff === 0;
926
+ }
927
+
928
+ // src/edit-request.ts
929
+ var CMSSY_EDIT_HEADER = "x-cmssy-edit";
930
+ var CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
931
+ var CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
932
+ async function isVerifiedEditUrl(url, config) {
933
+ if (!url.searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) {
934
+ return false;
935
+ }
936
+ const provided = url.searchParams.get(CMSSY_SECRET_QUERY_PARAM);
937
+ if (!provided || !config.draftSecret) return false;
938
+ return cmssySecretsMatch(provided, config.draftSecret);
939
+ }
940
+
941
+ // src/csp.ts
942
+ function toCspOrigin(origin) {
943
+ if (origin === "*") return "*";
944
+ let parsed;
945
+ try {
946
+ parsed = new URL(origin);
947
+ } catch {
948
+ throw new Error(`cmssy: invalid editorOrigin "${origin}"`);
949
+ }
950
+ if (parsed.origin === "null") {
951
+ throw new Error(`cmssy: editorOrigin "${origin}" has no usable origin`);
952
+ }
953
+ return parsed.origin;
954
+ }
955
+ function frameAncestors(editorOrigin) {
956
+ const resolved = resolveEditorOrigin(editorOrigin);
957
+ const origins = Array.isArray(resolved) ? resolved : [resolved];
958
+ if (origins.length === 0) {
959
+ throw new Error(
960
+ "cmssy: editorOrigin must contain at least one valid origin"
961
+ );
962
+ }
963
+ const normalized = origins.map((origin) => toCspOrigin(origin.trim()));
964
+ return `frame-ancestors ${normalized.join(" ")}`;
965
+ }
966
+ function cmssyCspHeaders(options) {
967
+ return {
968
+ "Content-Security-Policy": frameAncestors(options.editorOrigin)
969
+ };
970
+ }
971
+ function mergeFrameAncestors(existing, directive) {
972
+ if (!existing) return directive;
973
+ const kept = existing.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !/^frame-ancestors\b/i.test(part));
974
+ kept.push(directive);
975
+ return kept.join("; ");
976
+ }
977
+ function applyCmssyCsp(response, options) {
978
+ const merged = mergeFrameAncestors(
979
+ response.headers.get("Content-Security-Policy"),
980
+ frameAncestors(options.editorOrigin)
981
+ );
982
+ response.headers.set("Content-Security-Policy", merged);
983
+ response.headers.delete("X-Frame-Options");
984
+ return response;
985
+ }
986
+
987
+ // src/seo-paths.ts
988
+ function resolveSeoLocales(config, siteConfig) {
989
+ const defaultLocale = config.defaultLocale ?? siteConfig?.defaultLanguage ?? "en";
990
+ const locales = config.enabledLocales && config.enabledLocales.length > 0 ? config.enabledLocales : siteConfig?.enabledLanguages && siteConfig.enabledLanguages.length > 0 ? siteConfig.enabledLanguages : [defaultLocale];
991
+ return { defaultLocale, locales };
992
+ }
993
+ function normalizeSlug2(slug) {
994
+ if (slug === "/" || slug === "") return "/";
995
+ return slug.startsWith("/") ? slug : `/${slug}`;
996
+ }
997
+ function localizedPath(slug, locale, defaultLocale) {
998
+ const normalized = normalizeSlug2(slug);
999
+ const base = normalized === "/" ? "" : normalized;
1000
+ return locale === defaultLocale ? base || "/" : `/${locale}${base}`;
1001
+ }
1002
+
1003
+ // src/auth-client.ts
1004
+ var LOGIN_MUTATION = `mutation SiteMemberLogin($input: SiteMemberLoginInput!) {
1005
+ siteMember {
1006
+ login(input: $input) {
1007
+ success message accessToken refreshToken accessTokenExpiresIn
1008
+ }
1009
+ }
1010
+ }`;
1011
+ var REGISTER_MUTATION = `mutation SiteMemberRegister($input: SiteMemberRegisterInput!) {
1012
+ siteMember {
1013
+ register(input: $input) { success message }
1014
+ }
1015
+ }`;
1016
+ var REFRESH_MUTATION = `mutation SiteMemberRefresh($refreshToken: String!) {
1017
+ siteMember {
1018
+ refresh(refreshToken: $refreshToken) {
1019
+ success message accessToken refreshToken accessTokenExpiresIn
1020
+ }
1021
+ }
1022
+ }`;
1023
+ var LOGOUT_MUTATION = `mutation SiteMemberLogout($refreshToken: String!) {
1024
+ siteMember {
1025
+ logout(refreshToken: $refreshToken) { success message }
1026
+ }
1027
+ }`;
1028
+ var LOGOUT_EVERYWHERE_MUTATION = `mutation SiteMemberLogoutEverywhere {
1029
+ siteMember {
1030
+ logoutEverywhere { success message }
1031
+ }
1032
+ }`;
1033
+ var FORGOT_MUTATION = `mutation SiteMemberForgotPassword($modelSlug: String!, $identity: String!) {
1034
+ siteMember {
1035
+ forgotPassword(modelSlug: $modelSlug, identity: $identity) { success message }
1036
+ }
1037
+ }`;
1038
+ var RESET_MUTATION = `mutation SiteMemberResetPassword($token: String!, $newPassword: String!) {
1039
+ siteMember {
1040
+ resetPassword(token: $token, newPassword: $newPassword) { success message }
1041
+ }
1042
+ }`;
1043
+ var VERIFY_MUTATION = `mutation SiteMemberVerifyEmail($token: String!) {
1044
+ siteMember {
1045
+ verifyEmail(token: $token) { success message }
1046
+ }
1047
+ }`;
1048
+ async function authRequest(config, query, variables, label, accessToken) {
1049
+ const workspaceId = await cachedWorkspaceId(config);
1050
+ return graphqlRequest(
1051
+ config,
1052
+ query,
1053
+ variables,
1054
+ {
1055
+ headers: {
1056
+ "x-workspace-id": workspaceId,
1057
+ ...accessToken ? { authorization: `Bearer ${accessToken}` } : {}
1058
+ }
1059
+ },
1060
+ label
1061
+ );
1062
+ }
1063
+ function decodeAccessClaims(accessToken) {
1064
+ const parts = accessToken.split(".");
1065
+ if (parts.length !== 3) return null;
1066
+ try {
1067
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
1068
+ const bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
1069
+ const json = JSON.parse(new TextDecoder().decode(bytes));
1070
+ if (typeof json.recordId !== "string" || typeof json.email !== "string" || json.type !== "site_member") {
1071
+ return null;
1072
+ }
1073
+ return { recordId: json.recordId, email: json.email };
1074
+ } catch {
1075
+ return null;
1076
+ }
1077
+ }
1078
+ function toSessionPayload(result) {
1079
+ if (!result.success || !result.accessToken || !result.refreshToken || !result.accessTokenExpiresIn) {
1080
+ return null;
1081
+ }
1082
+ const user = decodeAccessClaims(result.accessToken);
1083
+ if (!user) return null;
1084
+ return {
1085
+ accessToken: result.accessToken,
1086
+ refreshToken: result.refreshToken,
1087
+ accessExpiresAt: Date.now() + result.accessTokenExpiresIn * 1e3,
1088
+ user
1089
+ };
1090
+ }
1091
+ async function backendSignIn(config, modelSlug, identity, password) {
1092
+ const data = await authRequest(
1093
+ config,
1094
+ LOGIN_MUTATION,
1095
+ {
1096
+ input: { modelSlug, identity, password }
1097
+ },
1098
+ "site member login"
1099
+ );
1100
+ return data.siteMember.login;
1101
+ }
1102
+ async function backendRegister(config, modelSlug, identity, password, fields2) {
1103
+ const data = await authRequest(
1104
+ config,
1105
+ REGISTER_MUTATION,
1106
+ {
1107
+ input: { modelSlug, identity, password, fields: fields2 }
1108
+ },
1109
+ "site member register"
1110
+ );
1111
+ return data.siteMember.register;
1112
+ }
1113
+ async function backendRefresh(config, refreshToken) {
1114
+ const data = await authRequest(
1115
+ config,
1116
+ REFRESH_MUTATION,
1117
+ { refreshToken },
1118
+ "site member refresh"
1119
+ );
1120
+ return data.siteMember.refresh;
1121
+ }
1122
+ async function backendSignOut(config, refreshToken) {
1123
+ try {
1124
+ await authRequest(
1125
+ config,
1126
+ LOGOUT_MUTATION,
1127
+ { refreshToken },
1128
+ "site member logout"
1129
+ );
1130
+ } catch {
1131
+ return;
1132
+ }
1133
+ }
1134
+ async function backendSignOutEverywhere(config, accessToken) {
1135
+ try {
1136
+ await authRequest(
1137
+ config,
1138
+ LOGOUT_EVERYWHERE_MUTATION,
1139
+ {},
1140
+ "site member logout everywhere",
1141
+ accessToken
1142
+ );
1143
+ } catch {
1144
+ return;
1145
+ }
1146
+ }
1147
+ async function backendForgotPassword(config, modelSlug, identity) {
1148
+ const data = await authRequest(
1149
+ config,
1150
+ FORGOT_MUTATION,
1151
+ { modelSlug, identity },
1152
+ "site member forgot password"
1153
+ );
1154
+ return data.siteMember.forgotPassword;
1155
+ }
1156
+ async function backendResetPassword(config, token, newPassword) {
1157
+ const data = await authRequest(
1158
+ config,
1159
+ RESET_MUTATION,
1160
+ { token, newPassword },
1161
+ "site member reset password"
1162
+ );
1163
+ return data.siteMember.resetPassword;
1164
+ }
1165
+ async function backendVerifyEmail(config, token) {
1166
+ const data = await authRequest(
1167
+ config,
1168
+ VERIFY_MUTATION,
1169
+ { token },
1170
+ "site member verify email"
1171
+ );
1172
+ return data.siteMember.verifyEmail;
1173
+ }
1174
+
1175
+ // src/cart-client.ts
1176
+ var CART_FIELDS = `
1177
+ id
1178
+ status
1179
+ itemCount
1180
+ subtotal
1181
+ currency
1182
+ discountedTotal
1183
+ tax
1184
+ totalGross
1185
+ pricesIncludeTax
1186
+ shippingTotal
1187
+ taxSummary { rateId name rate base amount }
1188
+ shippingMethod { id label price etaLabel }
1189
+ availableShippingMethods { id label price etaLabel }
1190
+ appliedDiscount { code type value computedAmount }
1191
+ items {
1192
+ id
1193
+ recordId
1194
+ quantity
1195
+ variantSelections
1196
+ unitPrice
1197
+ currentPrice
1198
+ priceMismatch
1199
+ snapshot { name price currency imageUrl sku tiers { minQty price } }
1200
+ }
1201
+ `;
1202
+ var CART_QUERY = `query Cart($workspaceId: ID!) { cart { get(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1203
+ var ADD_TO_CART = `mutation AddToCart($input: AddToCartInput!) { cart { addItem(input: $input) { ${CART_FIELDS} } } }`;
1204
+ var UPDATE_ITEM = `mutation UpdateCartItem($input: UpdateCartItemInput!) { cart { updateItem(input: $input) { ${CART_FIELDS} } } }`;
1205
+ var REMOVE_ITEM = `mutation RemoveCartItem($workspaceId: ID!, $itemId: ID!) { cart { removeItem(workspaceId: $workspaceId, itemId: $itemId) { ${CART_FIELDS} } } }`;
1206
+ var CLEAR_CART = `mutation ClearCart($workspaceId: ID!) { cart { clear(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1207
+ var APPLY_DISCOUNT = `mutation ApplyDiscount($workspaceId: ID!, $code: String!) { cart { applyDiscount(workspaceId: $workspaceId, code: $code) { ${CART_FIELDS} } } }`;
1208
+ var REMOVE_DISCOUNT = `mutation RemoveDiscount($workspaceId: ID!) { cart { removeDiscount(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1209
+ var SET_SHIPPING_METHOD = `mutation SetShippingMethod($workspaceId: ID!, $shippingMethodId: String) {
1210
+ cart { setShippingMethod(workspaceId: $workspaceId, shippingMethodId: $shippingMethodId) { ${CART_FIELDS} } }
1211
+ }`;
1212
+ var MERGE_CART = `mutation MergeCart($workspaceId: ID!) { cart { merge(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1213
+ var CHECKOUT = `mutation Checkout($input: CheckoutInput!) {
1214
+ cart {
1215
+ checkout(input: $input) {
1216
+ id
1217
+ orderNumber
1218
+ status
1219
+ subtotal
1220
+ discount
1221
+ appliedDiscount { code type value amount }
1222
+ tax
1223
+ total
1224
+ currency
1225
+ customerEmail
1226
+ accessToken
1227
+ poNumber
1228
+ customerNote
1229
+ shippingTotal
1230
+ pricesIncludeTax
1231
+ shippingMethod { id label price }
1232
+ shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1233
+ taxSummary { rateId name rate base amount }
1234
+ items { name sku quantity price listPrice tierMinQty currency taxRate taxAmount }
1235
+ }
1236
+ }
1237
+ }`;
1238
+ var PRODUCT = `query Product($workspaceId: String!, $modelSlug: String!, $filter: JSON) {
1239
+ public {
1240
+ model {
1241
+ records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, limit: 1) {
1242
+ items {
1243
+ id
1244
+ data
1245
+ priceTiers { minQty price }
1246
+ variants { id sku price inventory tiers { minQty price } selectedOptions { name value } }
1247
+ }
1248
+ }
1249
+ }
1250
+ }
1251
+ }`;
1252
+ async function request(config, ctx, workspaceId, query, variables, label) {
1253
+ return graphqlRequest(
1254
+ config,
1255
+ query,
1256
+ variables,
1257
+ {
1258
+ headers: {
1259
+ "x-workspace-id": workspaceId,
1260
+ "x-cart-session": ctx.cartToken,
1261
+ ...ctx.accessToken ? { authorization: `Bearer ${ctx.accessToken}` } : {}
1262
+ }
1263
+ },
1264
+ label
1265
+ );
1266
+ }
1267
+ async function backendGetCart(config, ctx) {
1268
+ const workspaceId = await cachedWorkspaceId(config);
1269
+ const data = await request(
1270
+ config,
1271
+ ctx,
1272
+ workspaceId,
1273
+ CART_QUERY,
1274
+ { workspaceId },
1275
+ "cart query"
1276
+ );
1277
+ return data.cart.get;
1278
+ }
1279
+ async function backendAddToCart(config, ctx, input) {
1280
+ const workspaceId = await cachedWorkspaceId(config);
1281
+ const data = await request(
1282
+ config,
1283
+ ctx,
1284
+ workspaceId,
1285
+ ADD_TO_CART,
1286
+ { input: { workspaceId, ...input } },
1287
+ "add to cart"
1288
+ );
1289
+ return data.cart.addItem;
1290
+ }
1291
+ async function backendUpdateItem(config, ctx, input) {
1292
+ const workspaceId = await cachedWorkspaceId(config);
1293
+ const data = await request(
1294
+ config,
1295
+ ctx,
1296
+ workspaceId,
1297
+ UPDATE_ITEM,
1298
+ { input: { workspaceId, ...input } },
1299
+ "update cart item"
1300
+ );
1301
+ return data.cart.updateItem;
1302
+ }
1303
+ async function backendRemoveItem(config, ctx, itemId) {
1304
+ const workspaceId = await cachedWorkspaceId(config);
1305
+ const data = await request(
1306
+ config,
1307
+ ctx,
1308
+ workspaceId,
1309
+ REMOVE_ITEM,
1310
+ { workspaceId, itemId },
1311
+ "remove cart item"
1312
+ );
1313
+ return data.cart.removeItem;
1314
+ }
1315
+ async function backendClearCart(config, ctx) {
1316
+ const workspaceId = await cachedWorkspaceId(config);
1317
+ const data = await request(
1318
+ config,
1319
+ ctx,
1320
+ workspaceId,
1321
+ CLEAR_CART,
1322
+ { workspaceId },
1323
+ "clear cart"
1324
+ );
1325
+ return data.cart.clear;
1326
+ }
1327
+ async function backendApplyDiscount(config, ctx, code) {
1328
+ const workspaceId = await cachedWorkspaceId(config);
1329
+ const data = await request(
1330
+ config,
1331
+ ctx,
1332
+ workspaceId,
1333
+ APPLY_DISCOUNT,
1334
+ { workspaceId, code },
1335
+ "apply discount"
1336
+ );
1337
+ return data.cart.applyDiscount;
1338
+ }
1339
+ async function backendRemoveDiscount(config, ctx) {
1340
+ const workspaceId = await cachedWorkspaceId(config);
1341
+ const data = await request(
1342
+ config,
1343
+ ctx,
1344
+ workspaceId,
1345
+ REMOVE_DISCOUNT,
1346
+ { workspaceId },
1347
+ "remove discount"
1348
+ );
1349
+ return data.cart.removeDiscount;
1350
+ }
1351
+ async function backendSetShippingMethod(config, ctx, shippingMethodId) {
1352
+ const workspaceId = await cachedWorkspaceId(config);
1353
+ const data = await request(
1354
+ config,
1355
+ ctx,
1356
+ workspaceId,
1357
+ SET_SHIPPING_METHOD,
1358
+ { workspaceId, shippingMethodId },
1359
+ "set shipping method"
1360
+ );
1361
+ return data.cart.setShippingMethod;
1362
+ }
1363
+ async function backendMergeCart(config, ctx) {
1364
+ const workspaceId = await cachedWorkspaceId(config);
1365
+ const data = await request(
1366
+ config,
1367
+ ctx,
1368
+ workspaceId,
1369
+ MERGE_CART,
1370
+ { workspaceId },
1371
+ "merge cart"
1372
+ );
1373
+ return data.cart.merge;
1374
+ }
1375
+ async function backendCheckout(config, ctx, input) {
1376
+ const workspaceId = await cachedWorkspaceId(config);
1377
+ const data = await request(
1378
+ config,
1379
+ ctx,
1380
+ workspaceId,
1381
+ CHECKOUT,
1382
+ { input: { workspaceId, ...input } },
1383
+ "checkout"
1384
+ );
1385
+ return data.cart.checkout;
1386
+ }
1387
+ async function backendProduct(config, ctx, modelSlug, filter) {
1388
+ const workspaceId = await cachedWorkspaceId(config);
1389
+ const data = await request(
1390
+ config,
1391
+ ctx,
1392
+ workspaceId,
1393
+ PRODUCT,
1394
+ { workspaceId, modelSlug, filter },
1395
+ "product query"
1396
+ );
1397
+ return data.public.model.records.items[0] ?? null;
1398
+ }
1399
+
1400
+ // src/orders-client.ts
1401
+ var ORDER_FIELDS = `
1402
+ id
1403
+ status
1404
+ subtotal
1405
+ discount
1406
+ appliedDiscount { code type value amount }
1407
+ tax
1408
+ total
1409
+ pricesIncludeTax
1410
+ taxSummary { rateId name rate base amount }
1411
+ currency
1412
+ customerEmail
1413
+ refundedAmount
1414
+ paymentProvider
1415
+ paymentStatus
1416
+ fulfillmentStatus
1417
+ amountPaid
1418
+ balanceDue
1419
+ paymentReference
1420
+ trackingNumber
1421
+ trackingCarrier
1422
+ invoiceNumber
1423
+ invoiceUrl
1424
+ invoiceProvider
1425
+ paidAt
1426
+ fulfilledAt
1427
+ createdAt
1428
+ orderNumber
1429
+ poNumber
1430
+ customerNote
1431
+ shippingTotal
1432
+ shippingMethod { id label price }
1433
+ shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1434
+ items { name price listPrice tierMinQty currency quantity sku }
1435
+ payments { amount reference provider at }
1436
+ `;
1437
+ var PUBLIC_ORDER_FIELDS = `
1438
+ id
1439
+ orderNumber
1440
+ status
1441
+ paymentStatus
1442
+ fulfillmentStatus
1443
+ subtotal
1444
+ discount
1445
+ appliedDiscount { code type value amount }
1446
+ tax
1447
+ total
1448
+ pricesIncludeTax
1449
+ taxSummary { rateId name rate base amount }
1450
+ currency
1451
+ customerEmail
1452
+ poNumber
1453
+ customerNote
1454
+ shippingTotal
1455
+ shippingMethod { id label price }
1456
+ shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1457
+ amountPaid
1458
+ balanceDue
1459
+ refundedAmount
1460
+ trackingNumber
1461
+ trackingCarrier
1462
+ invoiceNumber
1463
+ invoiceUrl
1464
+ paidAt
1465
+ fulfilledAt
1466
+ createdAt
1467
+ items { name price listPrice tierMinQty currency quantity sku taxRate taxAmount }
1468
+ `;
1469
+ var MY_ORDERS = `query MyOrders($workspaceId: ID!, $skip: Int, $limit: Int) {
1470
+ account {
1471
+ orders(workspaceId: $workspaceId, skip: $skip, limit: $limit) {
1472
+ total
1473
+ hasMore
1474
+ items { ${ORDER_FIELDS} }
1475
+ }
1476
+ }
1477
+ }`;
1478
+ var MY_ORDER = `query MyOrder($workspaceId: ID!, $id: ID!) {
1479
+ account {
1480
+ order(workspaceId: $workspaceId, id: $id) { ${ORDER_FIELDS} }
1481
+ }
1482
+ }`;
1483
+ var PUBLIC_ORDER_BY_TOKEN = `query PublicOrder($workspaceId: ID!, $orderId: ID!, $accessToken: String!) {
1484
+ public {
1485
+ order {
1486
+ byToken(workspaceId: $workspaceId, orderId: $orderId, accessToken: $accessToken) {
1487
+ ${PUBLIC_ORDER_FIELDS}
1488
+ }
1489
+ }
1490
+ }
1491
+ }`;
1492
+ function headers(workspaceId, accessToken) {
1493
+ return {
1494
+ "x-workspace-id": workspaceId,
1495
+ authorization: `Bearer ${accessToken}`
1496
+ };
1497
+ }
1498
+ async function backendMyOrders(config, accessToken, options) {
1499
+ const workspaceId = await cachedWorkspaceId(config);
1500
+ const data = await graphqlRequest(
1501
+ config,
1502
+ MY_ORDERS,
1503
+ { workspaceId, skip: options.skip, limit: options.limit },
1504
+ { headers: headers(workspaceId, accessToken) },
1505
+ "my orders"
1506
+ );
1507
+ return data.account.orders;
1508
+ }
1509
+ async function backendMyOrder(config, accessToken, id) {
1510
+ const workspaceId = await cachedWorkspaceId(config);
1511
+ const data = await graphqlRequest(
1512
+ config,
1513
+ MY_ORDER,
1514
+ { workspaceId, id },
1515
+ { headers: headers(workspaceId, accessToken) },
1516
+ "my order"
1517
+ );
1518
+ return data.account.order;
1519
+ }
1520
+ async function backendOrderByToken(config, options) {
1521
+ const workspaceId = await cachedWorkspaceId(config);
1522
+ const data = await graphqlRequest(
1523
+ config,
1524
+ PUBLIC_ORDER_BY_TOKEN,
1525
+ {
1526
+ workspaceId,
1527
+ orderId: options.orderId,
1528
+ accessToken: options.accessToken
1529
+ },
1530
+ { headers: { "x-workspace-id": workspaceId } },
1531
+ "public order lookup"
1532
+ );
1533
+ return data.public.order.byToken;
1534
+ }
1535
+
1536
+ // src/commerce/product-client.ts
1537
+ var PRODUCTS_QUERY = `query Products($workspaceId: String!, $modelSlug: String!, $filter: JSON, $stockState: String, $locale: String, $limit: Int, $offset: Int, $sort: String) {
1538
+ public {
1539
+ model {
1540
+ records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, stockState: $stockState, locale: $locale, limit: $limit, offset: $offset, sort: $sort) {
1541
+ total
1542
+ hasMore
1543
+ items {
1544
+ id
1545
+ data
1546
+ priceTiers { minQty price }
1547
+ variants { id sku price inventory tiers { minQty price } selectedOptions { name value } }
1548
+ }
1549
+ }
1550
+ }
1551
+ }
1552
+ }`;
1553
+ async function fetchProducts(config, options) {
1554
+ const workspaceId = await resolveWorkspaceId(config);
1555
+ const locale = options.locale ?? null;
1556
+ const data = await graphqlRequest(
1557
+ config,
1558
+ PRODUCTS_QUERY,
1559
+ {
1560
+ workspaceId,
1561
+ modelSlug: options.modelSlug,
1562
+ filter: options.filter ?? {},
1563
+ stockState: options.stockState ?? null,
1564
+ locale,
1565
+ limit: options.limit ?? 50,
1566
+ offset: options.offset ?? 0,
1567
+ sort: options.sort ?? null
1568
+ },
1569
+ { headers: { "x-workspace-id": workspaceId } },
1570
+ "products query"
1571
+ );
1572
+ return data.public.model.records;
1573
+ }
1574
+ async function fetchProduct(config, options) {
1575
+ const page = await fetchProducts(config, {
1576
+ modelSlug: options.modelSlug,
1577
+ filter: { [options.slugField ?? "slug"]: options.slug },
1578
+ locale: options.locale,
1579
+ limit: 1
1580
+ });
1581
+ return page.items[0] ?? null;
1582
+ }
1583
+
1584
+ // src/commerce/order-client.ts
1585
+ async function fetchOrderByToken(config, options) {
1586
+ return backendOrderByToken(config, options);
1587
+ }
1588
+
1589
+ // src/commerce/money.ts
1590
+ var ZERO_DECIMAL = /* @__PURE__ */ new Set([
1591
+ "BIF",
1592
+ "CLP",
1593
+ "DJF",
1594
+ "GNF",
1595
+ "JPY",
1596
+ "KMF",
1597
+ "KRW",
1598
+ "MGA",
1599
+ "PYG",
1600
+ "RWF",
1601
+ "UGX",
1602
+ "VND",
1603
+ "VUV",
1604
+ "XAF",
1605
+ "XOF",
1606
+ "XPF"
1607
+ ]);
1608
+ var THREE_DECIMAL = /* @__PURE__ */ new Set([
1609
+ "BHD",
1610
+ "IQD",
1611
+ "JOD",
1612
+ "KWD",
1613
+ "LYD",
1614
+ "OMR",
1615
+ "TND"
1616
+ ]);
1617
+ function fractionDigits(currency) {
1618
+ const code = currency.toUpperCase();
1619
+ if (ZERO_DECIMAL.has(code)) return 0;
1620
+ if (THREE_DECIMAL.has(code)) return 3;
1621
+ return 2;
1622
+ }
1623
+ function fromMinorUnits(minor, currency) {
1624
+ return minor / 10 ** fractionDigits(currency);
1625
+ }
1626
+ function toMinorUnits(amount, currency) {
1627
+ return Math.round(amount * 10 ** fractionDigits(currency));
1628
+ }
1629
+ function formatPrice(minor, currency) {
1630
+ if (!Number.isFinite(minor)) return "";
1631
+ const code = currency ?? "USD";
1632
+ const amount = fromMinorUnits(minor, code);
1633
+ try {
1634
+ return new Intl.NumberFormat(void 0, {
1635
+ style: "currency",
1636
+ currency: code
1637
+ }).format(amount);
1638
+ } catch {
1639
+ return `${amount.toFixed(fractionDigits(code))} ${code}`;
1640
+ }
1641
+ }
1642
+ var CmssyWebhookError = class extends Error {
1643
+ constructor(message) {
1644
+ super(message);
1645
+ this.name = "CmssyWebhookError";
1646
+ }
1647
+ };
1648
+ var DEFAULT_TOLERANCE_SECONDS = 300;
1649
+ function parseSignatureHeader(header) {
1650
+ let timestamp = null;
1651
+ let signature = null;
1652
+ for (const part of header.split(",")) {
1653
+ const idx = part.indexOf("=");
1654
+ if (idx === -1) continue;
1655
+ const key = part.slice(0, idx).trim();
1656
+ const value = part.slice(idx + 1).trim();
1657
+ if (key === "t") timestamp = Number(value);
1658
+ else if (key === "v1") signature = value;
1659
+ }
1660
+ if (timestamp === null || !Number.isFinite(timestamp) || !signature) {
1661
+ throw new CmssyWebhookError("Malformed X-Cmssy-Signature header");
1662
+ }
1663
+ return { timestamp, signature };
1664
+ }
1665
+ function timingSafeHexEqual(expectedHex, providedHex) {
1666
+ const expected = Buffer.from(expectedHex, "hex");
1667
+ const provided = Buffer.from(providedHex, "hex");
1668
+ if (expected.length !== provided.length) return false;
1669
+ return crypto$1.timingSafeEqual(expected, provided);
1670
+ }
1671
+ function verifyCmssyWebhook(options) {
1672
+ const { body, signatureHeader, secret } = options;
1673
+ if (!signatureHeader) {
1674
+ throw new CmssyWebhookError("Missing X-Cmssy-Signature header");
1675
+ }
1676
+ if (!secret) {
1677
+ throw new CmssyWebhookError("Missing webhook secret");
1678
+ }
1679
+ const { timestamp, signature } = parseSignatureHeader(signatureHeader);
1680
+ const toleranceMs = (options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS) * 1e3;
1681
+ const now = options.now ?? Date.now();
1682
+ if (Math.abs(now - timestamp) > toleranceMs) {
1683
+ throw new CmssyWebhookError("Webhook timestamp outside tolerance");
1684
+ }
1685
+ const expected = crypto$1.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
1686
+ if (!timingSafeHexEqual(expected, signature)) {
1687
+ throw new CmssyWebhookError("Webhook signature mismatch");
1688
+ }
1689
+ let parsed;
1690
+ try {
1691
+ parsed = JSON.parse(body);
1692
+ } catch {
1693
+ throw new CmssyWebhookError("Webhook body is not valid JSON");
1694
+ }
1695
+ return parsed;
1696
+ }
1697
+
1698
+ Object.defineProperty(exports, "evaluateFieldConditionGroup", {
1699
+ enumerable: true,
1700
+ get: function () { return types.evaluateFieldConditionGroup; }
1701
+ });
1702
+ exports.CMSSY_EDIT_HEADER = CMSSY_EDIT_HEADER;
1703
+ exports.CMSSY_EDIT_QUERY_PARAM = CMSSY_EDIT_QUERY_PARAM;
1704
+ exports.CMSSY_LOCALE_HEADER = CMSSY_LOCALE_HEADER;
1705
+ exports.CMSSY_SECRET_QUERY_PARAM = CMSSY_SECRET_QUERY_PARAM;
1706
+ exports.CMSSY_SESSION_COOKIE = CMSSY_SESSION_COOKIE;
1707
+ exports.CmssyRequestError = CmssyRequestError;
1708
+ exports.CmssyWebhookError = CmssyWebhookError;
1709
+ exports.DEFAULT_CMSSY_API_URL = DEFAULT_CMSSY_API_URL;
1710
+ exports.DEFAULT_CMSSY_EDITOR_ORIGINS = DEFAULT_CMSSY_EDITOR_ORIGINS;
1711
+ exports.FORM_QUERY = FORM_QUERY;
1712
+ exports.MIN_SESSION_SECRET_LENGTH = MIN_SESSION_SECRET_LENGTH;
1713
+ exports.MODEL_DEFINITIONS_QUERY = MODEL_DEFINITIONS_QUERY;
1714
+ exports.MODEL_RECORDS_QUERY = MODEL_RECORDS_QUERY;
1715
+ exports.PRODUCTS_QUERY = PRODUCTS_QUERY;
1716
+ exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
1717
+ exports.SESSION_MAX_AGE_SECONDS = SESSION_MAX_AGE_SECONDS;
1718
+ exports.SITE_CONFIG_QUERY = SITE_CONFIG_QUERY;
1719
+ exports.SUBMIT_FORM_MUTATION = SUBMIT_FORM_MUTATION;
1720
+ exports.applyCmssyCsp = applyCmssyCsp;
1721
+ exports.asBucket = asBucket;
1722
+ exports.assertAuthConfig = assertAuthConfig;
1723
+ exports.backendAddToCart = backendAddToCart;
1724
+ exports.backendApplyDiscount = backendApplyDiscount;
1725
+ exports.backendCheckout = backendCheckout;
1726
+ exports.backendClearCart = backendClearCart;
1727
+ exports.backendForgotPassword = backendForgotPassword;
1728
+ exports.backendGetCart = backendGetCart;
1729
+ exports.backendMergeCart = backendMergeCart;
1730
+ exports.backendMyOrder = backendMyOrder;
1731
+ exports.backendMyOrders = backendMyOrders;
1732
+ exports.backendOrderByToken = backendOrderByToken;
1733
+ exports.backendProduct = backendProduct;
1734
+ exports.backendRefresh = backendRefresh;
1735
+ exports.backendRegister = backendRegister;
1736
+ exports.backendRemoveDiscount = backendRemoveDiscount;
1737
+ exports.backendRemoveItem = backendRemoveItem;
1738
+ exports.backendResetPassword = backendResetPassword;
1739
+ exports.backendSetShippingMethod = backendSetShippingMethod;
1740
+ exports.backendSignIn = backendSignIn;
1741
+ exports.backendSignOut = backendSignOut;
1742
+ exports.backendSignOutEverywhere = backendSignOutEverywhere;
1743
+ exports.backendUpdateItem = backendUpdateItem;
1744
+ exports.backendVerifyEmail = backendVerifyEmail;
1745
+ exports.buildBlockContext = buildBlockContext;
1746
+ exports.buildLocaleSwitchHref = buildLocaleSwitchHref;
1747
+ exports.cachedWorkspaceId = cachedWorkspaceId;
1748
+ exports.clearWorkspaceIdCache = clearWorkspaceIdCache;
1749
+ exports.cmssyCspHeaders = cmssyCspHeaders;
1750
+ exports.cmssySecretsMatch = cmssySecretsMatch;
1751
+ exports.collectFormIds = collectFormIds;
1752
+ exports.createCmssyClient = createCmssyClient;
1753
+ exports.decodeAccessClaims = decodeAccessClaims;
1754
+ exports.defineCmssyConfig = defineCmssyConfig;
1755
+ exports.fetchLayouts = fetchLayouts;
1756
+ exports.fetchOrderByToken = fetchOrderByToken;
1757
+ exports.fetchPage = fetchPage;
1758
+ exports.fetchPageById = fetchPageById;
1759
+ exports.fetchPageMeta = fetchPageMeta;
1760
+ exports.fetchPages = fetchPages;
1761
+ exports.fetchProduct = fetchProduct;
1762
+ exports.fetchProducts = fetchProducts;
1763
+ exports.fetchSiteConfig = fetchSiteConfig;
1764
+ exports.fields = fields;
1765
+ exports.formatPrice = formatPrice;
1766
+ exports.fractionDigits = fractionDigits;
1767
+ exports.fromMinorUnits = fromMinorUnits;
1768
+ exports.getBlockContentForLanguage = getBlockContentForLanguage;
1769
+ exports.graphqlRequest = graphqlRequest;
1770
+ exports.isAccessExpired = isAccessExpired;
1771
+ exports.isDevelopment = isDevelopment;
1772
+ exports.isProtocolCompatible = isProtocolCompatible;
1773
+ exports.isVerifiedEditUrl = isVerifiedEditUrl;
1774
+ exports.localeForPath = localeForPath;
1775
+ exports.localeForPathname = localeForPathname;
1776
+ exports.localizeHref = localizeHref;
1777
+ exports.localizeHtmlLinks = localizeHtmlLinks;
1778
+ exports.localizedPath = localizedPath;
1779
+ exports.normalizeOrigin = normalizeOrigin;
1780
+ exports.normalizeSlug = normalizeSlug;
1781
+ exports.openSession = openSession;
1782
+ exports.parseEditorMessage = parseEditorMessage;
1783
+ exports.postToEditor = postToEditor;
1784
+ exports.resolveApiUrl = resolveApiUrl;
1785
+ exports.resolveEditorOrigin = resolveEditorOrigin;
1786
+ exports.resolveForms = resolveForms;
1787
+ exports.resolveInitialTarget = resolveInitialTarget;
1788
+ exports.resolvePublicUrl = resolvePublicUrl;
1789
+ exports.resolveSeoLocales = resolveSeoLocales;
1790
+ exports.resolveSiteLocales = resolveSiteLocales;
1791
+ exports.resolveWorkspaceId = resolveWorkspaceId;
1792
+ exports.sealSession = sealSession;
1793
+ exports.sessionCookieOptions = sessionCookieOptions;
1794
+ exports.splitCmssyLocale = splitCmssyLocale;
1795
+ exports.splitLocaleFromPath = splitLocaleFromPath;
1796
+ exports.toCspOrigin = toCspOrigin;
1797
+ exports.toMinorUnits = toMinorUnits;
1798
+ exports.toSessionPayload = toSessionPayload;
1799
+ exports.verifyCmssyWebhook = verifyCmssyWebhook;