@cmssy/core 9.10.0 → 10.1.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 CHANGED
@@ -1,69 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var jose = require('jose');
4
3
  var types = require('@cmssy/types');
5
4
 
6
- // src/session.ts
7
- var CMSSY_SESSION_COOKIE = "cmssy_session";
8
- var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
9
- var MIN_SESSION_SECRET_LENGTH = 32;
10
- var ACCESS_EXPIRY_SKEW_MS = 3e4;
11
- async function deriveSessionKey(secret) {
12
- if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
13
- throw new Error(
14
- `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
15
- );
16
- }
17
- const digest = await crypto.subtle.digest(
18
- "SHA-256",
19
- new TextEncoder().encode(secret)
20
- );
21
- return new Uint8Array(digest);
22
- }
23
- async function sealSession(payload, secret, audience) {
24
- const key = await deriveSessionKey(secret);
25
- const jwt = new jose.EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
26
- if (audience) jwt.setAudience(audience);
27
- return jwt.encrypt(key);
28
- }
29
- async function openSession(token, secret, audience) {
30
- const key = await deriveSessionKey(secret);
31
- try {
32
- const { payload } = await jose.jwtDecrypt(token, key, {
33
- keyManagementAlgorithms: ["dir"],
34
- contentEncryptionAlgorithms: ["A256GCM"],
35
- ...audience ? { audience } : {}
36
- });
37
- const { accessToken, refreshToken, accessExpiresAt, user } = payload;
38
- if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
39
- return null;
40
- }
41
- return {
42
- accessToken,
43
- refreshToken,
44
- accessExpiresAt,
45
- user: {
46
- recordId: user.recordId,
47
- email: user.email
48
- }
49
- };
50
- } catch {
51
- return null;
52
- }
53
- }
54
- function isAccessExpired(payload, now = Date.now()) {
55
- return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
56
- }
57
- function sessionCookieOptions() {
58
- return {
59
- httpOnly: true,
60
- secure: process.env.NODE_ENV !== "development",
61
- sameSite: "lax",
62
- path: "/",
63
- maxAge: SESSION_MAX_AGE_SECONDS
64
- };
65
- }
66
-
67
5
  // src/config.ts
68
6
  var DEFAULT_CMSSY_EDITOR_ORIGINS = [
69
7
  "https://cmssy.io",
@@ -122,18 +60,6 @@ Set the listed environment variables (e.g. in .env.local) and restart the dev se
122
60
  }
123
61
  return resolved;
124
62
  }
125
- function assertAuthConfig(config) {
126
- const auth = config.auth;
127
- if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
128
- throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
129
- }
130
- if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
131
- throw new Error(
132
- `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
133
- );
134
- }
135
- return auth;
136
- }
137
63
 
138
64
  // src/data/http.ts
139
65
  var CmssyRequestError = class extends Error {
@@ -250,207 +176,6 @@ function resolvePublicUrl(config) {
250
176
  const base = resolveApiUrl(config.apiUrl).replace(/\/graphql\/?$/, "");
251
177
  return `${base}/public/${config.org}/${config.workspaceSlug}/graphql`;
252
178
  }
253
- var PUBLIC_PAGE_QUERY = `query PublicPage($workspaceSlug: String!, $slug: String!, $previewSecret: String) {
254
- public {
255
- page {
256
- get(workspaceSlug: $workspaceSlug, slug: $slug, previewSecret: $previewSecret) {
257
- id
258
- blocks { id type content style advanced }
259
- publishedBlocks { id type content style advanced }
260
- }
261
- }
262
- }
263
- }`;
264
- var PUBLIC_PAGE_DEV_QUERY = `query PublicPage($workspaceSlug: String!, $slug: String!, $previewSecret: String, $devPreview: Boolean) {
265
- public {
266
- page {
267
- get(workspaceSlug: $workspaceSlug, slug: $slug, previewSecret: $previewSecret, devPreview: $devPreview) {
268
- id
269
- blocks { id type content style advanced }
270
- publishedBlocks { id type content style advanced }
271
- }
272
- }
273
- }
274
- }`;
275
- var PUBLIC_PAGE_BY_ID_QUERY = `query PublicPageById($workspaceSlug: String!, $pageId: ID!) {
276
- public {
277
- page {
278
- getById(workspaceSlug: $workspaceSlug, pageId: $pageId) {
279
- id
280
- publishedBlocks { id type content style advanced }
281
- }
282
- }
283
- }
284
- }`;
285
- var PUBLIC_PAGES_QUERY = `query PublicPages($workspaceSlug: String!) {
286
- public {
287
- page {
288
- list(workspaceSlug: $workspaceSlug) {
289
- id
290
- slug
291
- updatedAt
292
- publishedAt
293
- }
294
- }
295
- }
296
- }`;
297
- var PUBLIC_PAGE_META_QUERY = `query PublicPageMeta($workspaceSlug: String!, $slug: String!) {
298
- public {
299
- page {
300
- get(workspaceSlug: $workspaceSlug, slug: $slug) {
301
- id
302
- seoTitle
303
- seoDescription
304
- seoKeywords
305
- displayName
306
- }
307
- }
308
- }
309
- }`;
310
- var PUBLIC_PAGE_LAYOUTS_QUERY = `query PublicPageLayouts($workspaceSlug: String!, $pageSlug: String!, $previewSecret: String) {
311
- public {
312
- page {
313
- layouts(workspaceSlug: $workspaceSlug, pageSlug: $pageSlug, previewSecret: $previewSecret) {
314
- position
315
- blocks { id type content style advanced order isActive }
316
- settings { desktopWidth mobileBehavior }
317
- }
318
- }
319
- }
320
- }`;
321
- function normalizeSlug(path) {
322
- if (Array.isArray(path)) {
323
- const joined = path.filter(Boolean).join("/");
324
- return joined ? `/${joined}` : "/";
325
- }
326
- if (!path || path === "/") return "/";
327
- return path.startsWith("/") ? path : `/${path}`;
328
- }
329
- async function fetchPage(config, path, options = {}) {
330
- const slug = normalizeSlug(path);
331
- const trimmedSecret = options.previewSecret?.trim();
332
- const previewSecret = trimmedSecret ? trimmedSecret : null;
333
- const devToken = options.devToken?.trim();
334
- const devPreview = Boolean(options.devPreview && devToken);
335
- const headers2 = {};
336
- if (devPreview && devToken) {
337
- headers2["authorization"] = `Bearer ${devToken}`;
338
- if (options.workspaceId) {
339
- headers2["x-workspace-id"] = options.workspaceId;
340
- }
341
- }
342
- const data = await postGraphql(
343
- resolvePublicUrl(config),
344
- devPreview ? PUBLIC_PAGE_DEV_QUERY : PUBLIC_PAGE_QUERY,
345
- {
346
- workspaceSlug: config.workspaceSlug,
347
- slug,
348
- previewSecret,
349
- ...devPreview ? { devPreview: true } : {}
350
- },
351
- {
352
- fetch: options.fetch,
353
- signal: options.signal,
354
- headers: headers2,
355
- retry: options.retry ?? {},
356
- label: "page fetch"
357
- }
358
- );
359
- const page = data?.public?.page?.get;
360
- if (!page) return null;
361
- const draft = previewSecret !== null || devPreview;
362
- const blocks = (draft ? page.blocks : page.publishedBlocks) ?? [];
363
- return { id: page.id, blocks };
364
- }
365
- async function fetchPageById(config, pageId, options = {}) {
366
- const data = await postGraphql(
367
- resolvePublicUrl(config),
368
- PUBLIC_PAGE_BY_ID_QUERY,
369
- { workspaceSlug: config.workspaceSlug, pageId },
370
- {
371
- fetch: options.fetch,
372
- signal: options.signal,
373
- retry: options.retry ?? {},
374
- label: "page-by-id fetch"
375
- }
376
- );
377
- const page = data?.public?.page?.getById;
378
- if (!page) return null;
379
- return { id: page.id, blocks: page.publishedBlocks ?? [] };
380
- }
381
- async function fetchPages(config, options = {}) {
382
- const data = await postGraphql(
383
- resolvePublicUrl(config),
384
- PUBLIC_PAGES_QUERY,
385
- { workspaceSlug: config.workspaceSlug },
386
- {
387
- fetch: options.fetch,
388
- signal: options.signal,
389
- retry: options.retry ?? {},
390
- label: "pages fetch"
391
- }
392
- );
393
- return data?.public?.page?.list ?? [];
394
- }
395
- async function fetchPageMeta(config, path, options = {}) {
396
- const slug = normalizeSlug(path);
397
- const data = await postGraphql(
398
- resolvePublicUrl(config),
399
- PUBLIC_PAGE_META_QUERY,
400
- { workspaceSlug: config.workspaceSlug, slug },
401
- {
402
- fetch: options.fetch,
403
- signal: options.signal,
404
- retry: options.retry ?? {},
405
- label: "page meta fetch"
406
- }
407
- );
408
- return data?.public?.page?.get ?? null;
409
- }
410
- async function fetchLayouts(config, path, options = {}) {
411
- const pageSlug = normalizeSlug(path);
412
- const trimmedSecret = options.previewSecret?.trim();
413
- const previewSecret = trimmedSecret ? trimmedSecret : null;
414
- const data = await postGraphql(
415
- resolvePublicUrl(config),
416
- PUBLIC_PAGE_LAYOUTS_QUERY,
417
- { workspaceSlug: config.workspaceSlug, pageSlug, previewSecret },
418
- {
419
- fetch: options.fetch,
420
- signal: options.signal,
421
- retry: options.retry ?? {},
422
- label: "layouts fetch"
423
- }
424
- );
425
- return data?.public?.page?.layouts ?? [];
426
- }
427
-
428
- // src/content/get-block-content.ts
429
- function isPlainObject(value) {
430
- return typeof value === "object" && value !== null && !Array.isArray(value);
431
- }
432
- function looksLikeLocaleKey(key) {
433
- return /^[a-z]{2}(-[A-Za-z]{2})?$/.test(key);
434
- }
435
- function getBlockContentForLanguage(content, locale, defaultLocale = "en", availableLocales) {
436
- if (!isPlainObject(content)) return {};
437
- const isLocale = availableLocales ? (key) => availableLocales.includes(key) : looksLikeLocaleKey;
438
- const localeEntries = Object.entries(content).filter(
439
- ([key, value]) => isLocale(key) && isPlainObject(value)
440
- );
441
- if (localeEntries.length === 0) return { ...content };
442
- const localeMap = Object.fromEntries(localeEntries);
443
- const nonTranslatable = {};
444
- for (const [key, value] of Object.entries(content)) {
445
- if (!(isLocale(key) && isPlainObject(value))) nonTranslatable[key] = value;
446
- }
447
- const fallbackKey = Object.keys(localeMap)[0];
448
- const chosen = localeMap[locale] ?? localeMap[defaultLocale] ?? localeMap[fallbackKey];
449
- return { ...nonTranslatable, ...chosen };
450
- }
451
- function asBucket(value) {
452
- return isPlainObject(value) ? value : {};
453
- }
454
179
 
455
180
  // src/data/graphql-request.ts
456
181
  async function graphqlRequest(config, query, variables, options = {}, label = "request") {
@@ -485,57 +210,6 @@ var SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
485
210
  }
486
211
  }
487
212
  }`;
488
- var MODEL_DEFINITIONS_QUERY = `query PublicModelDefinitions($workspaceId: String!) {
489
- public {
490
- model {
491
- definitions(workspaceId: $workspaceId) {
492
- id name slug description icon color displayField recordCount
493
- }
494
- }
495
- }
496
- }`;
497
- var MODEL_RECORDS_QUERY = `query PublicModelRecords($workspaceId: String!, $modelSlug: String!, $filter: JSON, $sort: String, $locale: String, $limit: Int, $offset: Int, $populate: [String!]) {
498
- public {
499
- model {
500
- records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, sort: $sort, locale: $locale, limit: $limit, offset: $offset, populate: $populate) {
501
- items { id modelId data status createdAt updatedAt }
502
- total
503
- hasMore
504
- }
505
- }
506
- }
507
- }`;
508
- var FORM_QUERY = `query PublicForm($formId: ID!) {
509
- public {
510
- form {
511
- get(formId: $formId) {
512
- id
513
- name
514
- slug
515
- description
516
- fields {
517
- id name fieldType label placeholder helpText
518
- defaultValue width order showWhen requiredWhen
519
- options { value label disabled }
520
- validation { required minLength maxLength minValue maxValue pattern customMessage }
521
- }
522
- settings {
523
- actionType submitButtonLabel successMessage errorMessage
524
- redirectUrl requireLogin enableCaptcha
525
- }
526
- }
527
- }
528
- }
529
- }`;
530
- var SUBMIT_FORM_MUTATION = `mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
531
- public {
532
- form {
533
- submit(formId: $formId, input: $input) {
534
- success message submissionId redirectUrl accessToken customer
535
- }
536
- }
537
- }
538
- }`;
539
213
 
540
214
  // src/data/settings-client.ts
541
215
  async function fetchSiteConfig(config, options = {}) {
@@ -557,21 +231,6 @@ async function resolveWorkspaceId(config, options = {}) {
557
231
  }
558
232
  return siteConfig.workspaceId;
559
233
  }
560
- var workspaceIdCache = /* @__PURE__ */ new Map();
561
- function cachedWorkspaceId(config) {
562
- const key = `${resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
563
- const existing = workspaceIdCache.get(key);
564
- if (existing) return existing;
565
- const fresh = resolveWorkspaceId(config).catch((err) => {
566
- workspaceIdCache.delete(key);
567
- throw err;
568
- });
569
- workspaceIdCache.set(key, fresh);
570
- return fresh;
571
- }
572
- function clearWorkspaceIdCache() {
573
- workspaceIdCache.clear();
574
- }
575
234
 
576
235
  // src/data/client.ts
577
236
  function createCmssyClient(input) {
@@ -579,13 +238,13 @@ function createCmssyClient(input) {
579
238
  ...input,
580
239
  apiUrl: resolveApiUrl(input.apiUrl)
581
240
  };
582
- let cachedWorkspaceId2;
241
+ let cachedWorkspaceId;
583
242
  let inFlight;
584
243
  function resolveWorkspaceId2(options) {
585
- if (cachedWorkspaceId2) return Promise.resolve(cachedWorkspaceId2);
244
+ if (cachedWorkspaceId) return Promise.resolve(cachedWorkspaceId);
586
245
  if (!inFlight) {
587
246
  inFlight = resolveWorkspaceId(config, options).then((id) => {
588
- cachedWorkspaceId2 = id;
247
+ cachedWorkspaceId = id;
589
248
  return id;
590
249
  }).finally(() => {
591
250
  inFlight = void 0;
@@ -606,341 +265,21 @@ function createCmssyClient(input) {
606
265
  );
607
266
  },
608
267
  async queryScoped(document2, variables = {}, options = {}) {
609
- const { workspaceId: provided, headers: headers2, ...rest } = options;
610
- const workspaceId = provided ?? await resolveWorkspaceId2({ ...rest, headers: headers2 });
268
+ const { workspaceId: provided, headers, ...rest } = options;
269
+ const workspaceId = provided ?? await resolveWorkspaceId2({ ...rest, headers });
611
270
  const hasWorkspaceId = variables.workspaceId !== void 0 && variables.workspaceId !== null;
612
271
  const scopedVariables = /\$workspaceId\b/.test(document2) && !hasWorkspaceId ? { ...variables, workspaceId } : variables;
613
272
  return graphqlRequest(
614
273
  config,
615
274
  document2,
616
275
  scopedVariables,
617
- { ...rest, headers: { ...headers2, "x-workspace-id": workspaceId } },
276
+ { ...rest, headers: { ...headers, "x-workspace-id": workspaceId } },
618
277
  "graphql operation"
619
278
  );
620
279
  }
621
280
  };
622
281
  }
623
282
 
624
- // src/data/resolve-forms.ts
625
- function collectFormIds(blocks, locale, defaultLocale) {
626
- const ids = /* @__PURE__ */ new Set();
627
- for (const block of blocks) {
628
- const content = getBlockContentForLanguage(
629
- block.content,
630
- locale,
631
- defaultLocale
632
- );
633
- const formId = content.formId;
634
- if (typeof formId === "string" && formId.trim()) ids.add(formId);
635
- }
636
- return [...ids];
637
- }
638
- async function resolveForms(config, blocks, locale, defaultLocale, options) {
639
- const ids = collectFormIds(blocks, locale, defaultLocale);
640
- if (ids.length === 0) return {};
641
- const client = createCmssyClient(config);
642
- const entries = await Promise.all(
643
- ids.map(async (id) => {
644
- try {
645
- const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
646
- return [id, data.public.form.get];
647
- } catch (err) {
648
- if (typeof console !== "undefined") {
649
- console.warn(`[cmssy] failed to resolve form ${id}`, err);
650
- }
651
- return [id, null];
652
- }
653
- })
654
- );
655
- const forms = {};
656
- for (const [id, def] of entries) {
657
- if (def) forms[id] = def;
658
- }
659
- return forms;
660
- }
661
-
662
- // src/data/site-locales.ts
663
- var TTL_MS = 6e4;
664
- var MAX_ENTRIES = 64;
665
- var cache = /* @__PURE__ */ new Map();
666
- function localesFromSiteConfig(siteConfig) {
667
- const defaultLocale = siteConfig?.defaultLanguage || "en";
668
- const enabled = siteConfig?.enabledLanguages ?? [];
669
- return {
670
- defaultLocale,
671
- locales: enabled.length > 0 ? enabled : [defaultLocale]
672
- };
673
- }
674
- async function resolveSiteLocales(config, options) {
675
- const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
676
- const cached = cache.get(key);
677
- if (cached && cached.expires > Date.now()) return cached.value;
678
- cache.delete(key);
679
- let value;
680
- try {
681
- const data = await graphqlRequest(
682
- config,
683
- SITE_CONFIG_QUERY,
684
- { workspaceSlug: config.workspaceSlug },
685
- { ...options, public: true, retry: options?.retry ?? {} },
686
- "site config"
687
- );
688
- value = localesFromSiteConfig(data.public?.siteConfig ?? null);
689
- } catch {
690
- value = { defaultLocale: "en", locales: ["en"] };
691
- }
692
- if (cache.size >= MAX_ENTRIES) cache.clear();
693
- cache.set(key, { value, expires: Date.now() + TTL_MS });
694
- return value;
695
- }
696
- function splitLocaleFromPath(path, siteLocales) {
697
- const first = path?.[0];
698
- if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
699
- return { locale: first, path: path.slice(1) };
700
- }
701
- return { locale: siteLocales.defaultLocale, path };
702
- }
703
-
704
- // src/data/localize-href.ts
705
- var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
706
- function isExternalHref(href) {
707
- const value = href.trim();
708
- if (!value) return true;
709
- if (value.startsWith("#")) return true;
710
- return PROTOCOL_OR_RELATIVE.test(value);
711
- }
712
- function stripLeadingLocale(path, locale) {
713
- const segments = path.split("/");
714
- const first = segments[1];
715
- if (first && first !== locale.default && locale.enabled.includes(first)) {
716
- segments.splice(1, 1);
717
- const rest = segments.join("/");
718
- return rest === "" ? "/" : rest;
719
- }
720
- return path;
721
- }
722
- function addLocalePrefix(path, target, locale) {
723
- if (target === locale.default) return path;
724
- if (path === "/") return `/${target}`;
725
- return `/${target}${path}`;
726
- }
727
- function localizeHref(href, locale) {
728
- const value = href.trim();
729
- if (isExternalHref(value)) return href;
730
- const boundary = value.search(/[?#]/);
731
- const path = boundary === -1 ? value : value.slice(0, boundary);
732
- const suffix = boundary === -1 ? "" : value.slice(boundary);
733
- if (!path.startsWith("/")) return href;
734
- const bare = stripLeadingLocale(path, locale);
735
- return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
736
- }
737
- function buildLocaleSwitchHref(target, pathname, locale) {
738
- const path = pathname && pathname.startsWith("/") ? pathname : "/";
739
- const bare = stripLeadingLocale(path, locale);
740
- return addLocalePrefix(bare, target, locale);
741
- }
742
- var ANCHOR_HREF = /(<a\b(?:"[^"]*"|'[^']*'|[^>])*?\shref=)(["'])(.*?)\2/gi;
743
- function localizeHtmlLinks(html, locale) {
744
- return html.replace(
745
- ANCHOR_HREF,
746
- (_match, prefix, quote, url) => `${prefix}${quote}${localizeHref(url, locale)}${quote}`
747
- );
748
- }
749
-
750
- // src/data/relation-resolver.ts
751
- var RECORDS_BY_IDS_QUERY = `query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {
752
- public {
753
- model {
754
- recordsByIds(workspaceId: $workspaceId, ids: $ids, locale: $locale) {
755
- id modelId data status createdAt updatedAt
756
- }
757
- }
758
- }
759
- }`;
760
- var BY_IDS_CHUNK = 50;
761
- var COLLECTION_DEFAULT_LIMIT = 50;
762
- function relationModel(field) {
763
- return field.relationTo?.startsWith("model:") ? field.relationTo.slice("model:".length) : void 0;
764
- }
765
- function storedIds(value) {
766
- if (typeof value === "string" && value) return [value];
767
- if (Array.isArray(value)) {
768
- return value.filter((id) => typeof id === "string" && !!id);
769
- }
770
- return [];
771
- }
772
- function collectRefs(entries, schemas) {
773
- const refs = [];
774
- for (const entry of entries) {
775
- const schema = schemas[entry.type];
776
- if (!schema) continue;
777
- for (const [key, field] of Object.entries(schema)) {
778
- if (field.type !== "relation") continue;
779
- const model = relationModel(field);
780
- if (!model) continue;
781
- refs.push({ content: entry.content, key, field, model });
782
- }
783
- }
784
- return refs;
785
- }
786
- function isResolvedRecord(value) {
787
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.id === "string" && typeof value.data === "object";
788
- }
789
- function isListShaped(field) {
790
- return field.relationMode === "all" || field.relationType === "hasMany" || field.multiple === true;
791
- }
792
- function normalizeRelationContent(content, schema, resolved) {
793
- for (const [key, field] of Object.entries(schema)) {
794
- if (field.type !== "relation") continue;
795
- const value = content[key];
796
- const fallback = resolved?.[key];
797
- if (isListShaped(field)) {
798
- if (Array.isArray(value)) {
799
- const records = value.filter(isResolvedRecord);
800
- if (records.length > 0 || value.length === 0) {
801
- content[key] = records;
802
- continue;
803
- }
804
- }
805
- content[key] = Array.isArray(fallback) ? fallback.filter(isResolvedRecord) : [];
806
- } else if (!isResolvedRecord(value)) {
807
- if (value != null && value !== "" && isResolvedRecord(fallback)) {
808
- content[key] = fallback;
809
- } else {
810
- delete content[key];
811
- }
812
- }
813
- }
814
- }
815
- function collectionKey(ref) {
816
- return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join("\0");
817
- }
818
- async function resolveRelationContent(config, entries, schemas, locale, requestOptions = {}) {
819
- const refs = collectRefs(entries, schemas);
820
- if (refs.length === 0) return;
821
- const client = createCmssyClient(config);
822
- const pickedIds = /* @__PURE__ */ new Set();
823
- const collections = /* @__PURE__ */ new Map();
824
- for (const ref of refs) {
825
- if (ref.field.relationMode === "all") {
826
- collections.set(collectionKey(ref), {
827
- model: ref.model,
828
- sort: ref.field.sort,
829
- limit: ref.field.limit
830
- });
831
- } else {
832
- for (const id of storedIds(ref.content[ref.key])) pickedIds.add(id);
833
- }
834
- }
835
- let recordsById;
836
- let collectionItems;
837
- try {
838
- [recordsById, collectionItems] = await Promise.all([
839
- fetchPickedRecords(client, [...pickedIds], locale, requestOptions),
840
- fetchCollections(client, collections, locale, requestOptions)
841
- ]);
842
- } catch (err) {
843
- if (typeof console !== "undefined") {
844
- console.error("[cmssy] relation resolution failed", err);
845
- }
846
- for (const ref of refs) {
847
- if (ref.field.relationMode === "all" || Array.isArray(ref.content[ref.key])) {
848
- ref.content[ref.key] = [];
849
- } else {
850
- delete ref.content[ref.key];
851
- }
852
- }
853
- return;
854
- }
855
- for (const ref of refs) {
856
- if (ref.field.relationMode === "all") {
857
- ref.content[ref.key] = collectionItems.get(collectionKey(ref)) ?? [];
858
- continue;
859
- }
860
- const value = ref.content[ref.key];
861
- if (Array.isArray(value)) {
862
- ref.content[ref.key] = storedIds(value).map((id) => recordsById.get(id)).filter((record) => !!record);
863
- } else if (typeof value === "string" && value) {
864
- const record = recordsById.get(value);
865
- if (record) ref.content[ref.key] = record;
866
- else delete ref.content[ref.key];
867
- } else {
868
- delete ref.content[ref.key];
869
- }
870
- }
871
- }
872
- async function fetchPickedRecords(client, ids, locale, requestOptions = {}) {
873
- const byId = /* @__PURE__ */ new Map();
874
- const chunks = [];
875
- for (let i = 0; i < ids.length; i += BY_IDS_CHUNK) {
876
- chunks.push(ids.slice(i, i + BY_IDS_CHUNK));
877
- }
878
- await Promise.all(
879
- chunks.map(async (chunk) => {
880
- const result = await client.queryScoped(
881
- RECORDS_BY_IDS_QUERY,
882
- { ids: chunk, locale: locale ?? null },
883
- requestOptions
884
- );
885
- for (const record of result.public.model.recordsByIds) {
886
- byId.set(record.id, record);
887
- }
888
- })
889
- );
890
- return byId;
891
- }
892
- async function fetchCollections(client, collections, locale, requestOptions = {}) {
893
- const byKey = /* @__PURE__ */ new Map();
894
- await Promise.all(
895
- [...collections.entries()].map(async ([key, { model, sort, limit }]) => {
896
- const result = await client.queryScoped(
897
- MODEL_RECORDS_QUERY,
898
- {
899
- modelSlug: model,
900
- sort: sort ?? null,
901
- limit: limit ?? COLLECTION_DEFAULT_LIMIT,
902
- locale: locale ?? null
903
- },
904
- requestOptions
905
- );
906
- byKey.set(key, result.public.model.records.items);
907
- })
908
- );
909
- return byKey;
910
- }
911
-
912
- // src/locale.ts
913
- var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
914
- async function localeForPathname(config, pathname) {
915
- const siteLocales = await resolveSiteLocales(config);
916
- const segments = pathname.split("/").filter(Boolean);
917
- return splitLocaleFromPath(segments, siteLocales).locale;
918
- }
919
- async function splitCmssyLocale(config, path) {
920
- const siteLocales = await resolveSiteLocales(config);
921
- return splitLocaleFromPath(path, siteLocales);
922
- }
923
- async function localeForPath(config, path) {
924
- const siteLocales = await resolveSiteLocales(config);
925
- const segments = Array.isArray(path) ? path.filter(Boolean) : path.split("/").filter(Boolean);
926
- return splitLocaleFromPath(segments, siteLocales).locale;
927
- }
928
-
929
- // src/block-context.ts
930
- function buildBlockContext(locale, defaultLocale, enabledLocales, isPreview, forms, extra) {
931
- return {
932
- locale: {
933
- current: locale,
934
- default: defaultLocale,
935
- enabled: enabledLocales && enabledLocales.length > 0 ? enabledLocales : Array.from(/* @__PURE__ */ new Set([defaultLocale, locale]))
936
- },
937
- isPreview: isPreview ?? false,
938
- forms,
939
- ...extra?.auth ? { auth: extra.auth } : {},
940
- ...extra?.workspace ? { workspace: extra.workspace } : {}
941
- };
942
- }
943
-
944
283
  // src/fields.ts
945
284
  function build(type, opts) {
946
285
  return { type, label: opts.label ?? "", ...opts };
@@ -984,6 +323,43 @@ var fields = {
984
323
  }
985
324
  };
986
325
 
326
+ // src/data/localize-href.ts
327
+ var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
328
+ function isExternalHref(href) {
329
+ const value = href.trim();
330
+ if (!value) return true;
331
+ if (value.startsWith("#")) return true;
332
+ return PROTOCOL_OR_RELATIVE.test(value);
333
+ }
334
+ function stripLeadingLocale(path, locale) {
335
+ const segments = path.split("/");
336
+ const first = segments[1];
337
+ if (first && first !== locale.default && locale.enabled.includes(first)) {
338
+ segments.splice(1, 1);
339
+ const rest = segments.join("/");
340
+ return rest === "" ? "/" : rest;
341
+ }
342
+ return path;
343
+ }
344
+ function addLocalePrefix(path, target, locale) {
345
+ if (target === locale.default) return path;
346
+ if (path === "/") return `/${target}`;
347
+ return `/${target}${path}`;
348
+ }
349
+ function localizeHref(href, locale) {
350
+ const value = href.trim();
351
+ if (isExternalHref(value)) return href;
352
+ const boundary = value.search(/[?#]/);
353
+ const path = boundary === -1 ? value : value.slice(0, boundary);
354
+ const suffix = boundary === -1 ? "" : value.slice(boundary);
355
+ if (!path.startsWith("/")) return href;
356
+ const bare = stripLeadingLocale(path, locale);
357
+ return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
358
+ }
359
+
360
+ // src/locale.ts
361
+ var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
362
+
987
363
  // src/bridge/protocol.ts
988
364
  var PROTOCOL_VERSION = 2;
989
365
  function isProtocolCompatible(version) {
@@ -1011,13 +387,6 @@ function isOriginAllowed(origin, allowed) {
1011
387
  return expected === "*" || expected === actual;
1012
388
  });
1013
389
  }
1014
- function resolveInitialTarget(editorOrigin) {
1015
- const list = (Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin]).map((origin) => normalizeOrigin(origin));
1016
- if (list.includes("*")) return "*";
1017
- if (list.length === 1) return list[0];
1018
- const referrerOrigin = typeof document !== "undefined" && document.referrer ? normalizeOrigin(document.referrer) : "";
1019
- return list.find((origin) => origin === referrerOrigin) ?? list[0];
1020
- }
1021
390
  function isObject(value) {
1022
391
  return typeof value === "object" && value !== null && !Array.isArray(value);
1023
392
  }
@@ -1137,11 +506,6 @@ function frameAncestors(editorOrigin) {
1137
506
  const normalized = origins.map((origin) => toCspOrigin(origin.trim()));
1138
507
  return `frame-ancestors ${normalized.join(" ")}`;
1139
508
  }
1140
- function cmssyCspHeaders(options) {
1141
- return {
1142
- "Content-Security-Policy": frameAncestors(options.editorOrigin)
1143
- };
1144
- }
1145
509
  function mergeFrameAncestors(existing, directive) {
1146
510
  if (!existing) return directive;
1147
511
  const kept = existing.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !/^frame-ancestors\b/i.test(part));
@@ -1158,657 +522,6 @@ function applyCmssyCsp(response, options) {
1158
522
  return response;
1159
523
  }
1160
524
 
1161
- // src/seo-paths.ts
1162
- function normalizeSlug2(slug) {
1163
- if (slug === "/" || slug === "") return "/";
1164
- return slug.startsWith("/") ? slug : `/${slug}`;
1165
- }
1166
- function localizedPath(slug, locale, defaultLocale) {
1167
- const normalized = normalizeSlug2(slug);
1168
- const base = normalized === "/" ? "" : normalized;
1169
- return locale === defaultLocale ? base || "/" : `/${locale}${base}`;
1170
- }
1171
-
1172
- // src/auth-client.ts
1173
- var LOGIN_MUTATION = `mutation SiteMemberLogin($input: SiteMemberLoginInput!) {
1174
- siteMember {
1175
- login(input: $input) {
1176
- success message accessToken refreshToken accessTokenExpiresIn
1177
- }
1178
- }
1179
- }`;
1180
- var REGISTER_MUTATION = `mutation SiteMemberRegister($input: SiteMemberRegisterInput!) {
1181
- siteMember {
1182
- register(input: $input) { success message }
1183
- }
1184
- }`;
1185
- var REFRESH_MUTATION = `mutation SiteMemberRefresh($refreshToken: String!) {
1186
- siteMember {
1187
- refresh(refreshToken: $refreshToken) {
1188
- success message accessToken refreshToken accessTokenExpiresIn
1189
- }
1190
- }
1191
- }`;
1192
- var LOGOUT_MUTATION = `mutation SiteMemberLogout($refreshToken: String!) {
1193
- siteMember {
1194
- logout(refreshToken: $refreshToken) { success message }
1195
- }
1196
- }`;
1197
- var LOGOUT_EVERYWHERE_MUTATION = `mutation SiteMemberLogoutEverywhere {
1198
- siteMember {
1199
- logoutEverywhere { success message }
1200
- }
1201
- }`;
1202
- var FORGOT_MUTATION = `mutation SiteMemberForgotPassword($modelSlug: String!, $identity: String!) {
1203
- siteMember {
1204
- forgotPassword(modelSlug: $modelSlug, identity: $identity) { success message }
1205
- }
1206
- }`;
1207
- var RESET_MUTATION = `mutation SiteMemberResetPassword($token: String!, $newPassword: String!) {
1208
- siteMember {
1209
- resetPassword(token: $token, newPassword: $newPassword) { success message }
1210
- }
1211
- }`;
1212
- var VERIFY_MUTATION = `mutation SiteMemberVerifyEmail($token: String!) {
1213
- siteMember {
1214
- verifyEmail(token: $token) { success message }
1215
- }
1216
- }`;
1217
- async function authRequest(config, query, variables, label, accessToken) {
1218
- const workspaceId = await cachedWorkspaceId(config);
1219
- return graphqlRequest(
1220
- config,
1221
- query,
1222
- variables,
1223
- {
1224
- headers: {
1225
- "x-workspace-id": workspaceId,
1226
- ...accessToken ? { authorization: `Bearer ${accessToken}` } : {}
1227
- }
1228
- },
1229
- label
1230
- );
1231
- }
1232
- function decodeAccessClaims(accessToken) {
1233
- const parts = accessToken.split(".");
1234
- if (parts.length !== 3) return null;
1235
- try {
1236
- const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
1237
- const bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
1238
- const json = JSON.parse(new TextDecoder().decode(bytes));
1239
- if (typeof json.recordId !== "string" || typeof json.email !== "string" || json.type !== "site_member") {
1240
- return null;
1241
- }
1242
- return { recordId: json.recordId, email: json.email };
1243
- } catch {
1244
- return null;
1245
- }
1246
- }
1247
- function toSessionPayload(result) {
1248
- if (!result.success || !result.accessToken || !result.refreshToken || !result.accessTokenExpiresIn) {
1249
- return null;
1250
- }
1251
- const user = decodeAccessClaims(result.accessToken);
1252
- if (!user) return null;
1253
- return {
1254
- accessToken: result.accessToken,
1255
- refreshToken: result.refreshToken,
1256
- accessExpiresAt: Date.now() + result.accessTokenExpiresIn * 1e3,
1257
- user
1258
- };
1259
- }
1260
- async function backendSignIn(config, modelSlug, identity, password) {
1261
- const data = await authRequest(
1262
- config,
1263
- LOGIN_MUTATION,
1264
- {
1265
- input: { modelSlug, identity, password }
1266
- },
1267
- "site member login"
1268
- );
1269
- return data.siteMember.login;
1270
- }
1271
- async function backendRegister(config, modelSlug, identity, password, fields2) {
1272
- const data = await authRequest(
1273
- config,
1274
- REGISTER_MUTATION,
1275
- {
1276
- input: { modelSlug, identity, password, fields: fields2 }
1277
- },
1278
- "site member register"
1279
- );
1280
- return data.siteMember.register;
1281
- }
1282
- async function backendRefresh(config, refreshToken) {
1283
- const data = await authRequest(
1284
- config,
1285
- REFRESH_MUTATION,
1286
- { refreshToken },
1287
- "site member refresh"
1288
- );
1289
- return data.siteMember.refresh;
1290
- }
1291
- async function backendSignOut(config, refreshToken) {
1292
- try {
1293
- await authRequest(
1294
- config,
1295
- LOGOUT_MUTATION,
1296
- { refreshToken },
1297
- "site member logout"
1298
- );
1299
- } catch {
1300
- return;
1301
- }
1302
- }
1303
- async function backendSignOutEverywhere(config, accessToken) {
1304
- try {
1305
- await authRequest(
1306
- config,
1307
- LOGOUT_EVERYWHERE_MUTATION,
1308
- {},
1309
- "site member logout everywhere",
1310
- accessToken
1311
- );
1312
- } catch {
1313
- return;
1314
- }
1315
- }
1316
- async function backendForgotPassword(config, modelSlug, identity) {
1317
- const data = await authRequest(
1318
- config,
1319
- FORGOT_MUTATION,
1320
- { modelSlug, identity },
1321
- "site member forgot password"
1322
- );
1323
- return data.siteMember.forgotPassword;
1324
- }
1325
- async function backendResetPassword(config, token, newPassword) {
1326
- const data = await authRequest(
1327
- config,
1328
- RESET_MUTATION,
1329
- { token, newPassword },
1330
- "site member reset password"
1331
- );
1332
- return data.siteMember.resetPassword;
1333
- }
1334
- async function backendVerifyEmail(config, token) {
1335
- const data = await authRequest(
1336
- config,
1337
- VERIFY_MUTATION,
1338
- { token },
1339
- "site member verify email"
1340
- );
1341
- return data.siteMember.verifyEmail;
1342
- }
1343
-
1344
- // src/cart-client.ts
1345
- var CART_FIELDS = `
1346
- id
1347
- status
1348
- itemCount
1349
- subtotal
1350
- currency
1351
- discountedTotal
1352
- tax
1353
- totalGross
1354
- pricesIncludeTax
1355
- shippingTotal
1356
- taxSummary { rateId name rate base amount }
1357
- shippingMethod { id label price etaLabel }
1358
- availableShippingMethods { id label price etaLabel }
1359
- appliedDiscount { code type value computedAmount }
1360
- items {
1361
- id
1362
- recordId
1363
- quantity
1364
- variantSelections
1365
- unitPrice
1366
- currentPrice
1367
- priceMismatch
1368
- snapshot { name price currency imageUrl sku tiers { minQty price } }
1369
- }
1370
- `;
1371
- var CART_QUERY = `query Cart($workspaceId: ID!) { cart { get(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1372
- var ADD_TO_CART = `mutation AddToCart($input: AddToCartInput!) { cart { addItem(input: $input) { ${CART_FIELDS} } } }`;
1373
- var UPDATE_ITEM = `mutation UpdateCartItem($input: UpdateCartItemInput!) { cart { updateItem(input: $input) { ${CART_FIELDS} } } }`;
1374
- var REMOVE_ITEM = `mutation RemoveCartItem($workspaceId: ID!, $itemId: ID!) { cart { removeItem(workspaceId: $workspaceId, itemId: $itemId) { ${CART_FIELDS} } } }`;
1375
- var CLEAR_CART = `mutation ClearCart($workspaceId: ID!) { cart { clear(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1376
- var APPLY_DISCOUNT = `mutation ApplyDiscount($workspaceId: ID!, $code: String!) { cart { applyDiscount(workspaceId: $workspaceId, code: $code) { ${CART_FIELDS} } } }`;
1377
- var REMOVE_DISCOUNT = `mutation RemoveDiscount($workspaceId: ID!) { cart { removeDiscount(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1378
- var SET_SHIPPING_METHOD = `mutation SetShippingMethod($workspaceId: ID!, $shippingMethodId: String) {
1379
- cart { setShippingMethod(workspaceId: $workspaceId, shippingMethodId: $shippingMethodId) { ${CART_FIELDS} } }
1380
- }`;
1381
- var MERGE_CART = `mutation MergeCart($workspaceId: ID!) { cart { merge(workspaceId: $workspaceId) { ${CART_FIELDS} } } }`;
1382
- var CHECKOUT = `mutation Checkout($input: CheckoutInput!) {
1383
- cart {
1384
- checkout(input: $input) {
1385
- id
1386
- orderNumber
1387
- status
1388
- subtotal
1389
- discount
1390
- appliedDiscount { code type value amount }
1391
- tax
1392
- total
1393
- currency
1394
- customerEmail
1395
- accessToken
1396
- poNumber
1397
- customerNote
1398
- shippingTotal
1399
- pricesIncludeTax
1400
- shippingMethod { id label price }
1401
- shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1402
- taxSummary { rateId name rate base amount }
1403
- items { name sku quantity price listPrice tierMinQty currency taxRate taxAmount }
1404
- }
1405
- }
1406
- }`;
1407
- var PRODUCT = `query Product($workspaceId: String!, $modelSlug: String!, $filter: JSON) {
1408
- public {
1409
- model {
1410
- records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, limit: 1) {
1411
- items {
1412
- id
1413
- data
1414
- priceTiers { minQty price }
1415
- variants { id sku price inventory tiers { minQty price } selectedOptions { name value } }
1416
- }
1417
- }
1418
- }
1419
- }
1420
- }`;
1421
- async function request(config, ctx, workspaceId, query, variables, label) {
1422
- return graphqlRequest(
1423
- config,
1424
- query,
1425
- variables,
1426
- {
1427
- headers: {
1428
- "x-workspace-id": workspaceId,
1429
- "x-cart-session": ctx.cartToken,
1430
- ...ctx.accessToken ? { authorization: `Bearer ${ctx.accessToken}` } : {}
1431
- }
1432
- },
1433
- label
1434
- );
1435
- }
1436
- async function backendGetCart(config, ctx) {
1437
- const workspaceId = await cachedWorkspaceId(config);
1438
- const data = await request(
1439
- config,
1440
- ctx,
1441
- workspaceId,
1442
- CART_QUERY,
1443
- { workspaceId },
1444
- "cart query"
1445
- );
1446
- return data.cart.get;
1447
- }
1448
- async function backendAddToCart(config, ctx, input) {
1449
- const workspaceId = await cachedWorkspaceId(config);
1450
- const data = await request(
1451
- config,
1452
- ctx,
1453
- workspaceId,
1454
- ADD_TO_CART,
1455
- { input: { workspaceId, ...input } },
1456
- "add to cart"
1457
- );
1458
- return data.cart.addItem;
1459
- }
1460
- async function backendUpdateItem(config, ctx, input) {
1461
- const workspaceId = await cachedWorkspaceId(config);
1462
- const data = await request(
1463
- config,
1464
- ctx,
1465
- workspaceId,
1466
- UPDATE_ITEM,
1467
- { input: { workspaceId, ...input } },
1468
- "update cart item"
1469
- );
1470
- return data.cart.updateItem;
1471
- }
1472
- async function backendRemoveItem(config, ctx, itemId) {
1473
- const workspaceId = await cachedWorkspaceId(config);
1474
- const data = await request(
1475
- config,
1476
- ctx,
1477
- workspaceId,
1478
- REMOVE_ITEM,
1479
- { workspaceId, itemId },
1480
- "remove cart item"
1481
- );
1482
- return data.cart.removeItem;
1483
- }
1484
- async function backendClearCart(config, ctx) {
1485
- const workspaceId = await cachedWorkspaceId(config);
1486
- const data = await request(
1487
- config,
1488
- ctx,
1489
- workspaceId,
1490
- CLEAR_CART,
1491
- { workspaceId },
1492
- "clear cart"
1493
- );
1494
- return data.cart.clear;
1495
- }
1496
- async function backendApplyDiscount(config, ctx, code) {
1497
- const workspaceId = await cachedWorkspaceId(config);
1498
- const data = await request(
1499
- config,
1500
- ctx,
1501
- workspaceId,
1502
- APPLY_DISCOUNT,
1503
- { workspaceId, code },
1504
- "apply discount"
1505
- );
1506
- return data.cart.applyDiscount;
1507
- }
1508
- async function backendRemoveDiscount(config, ctx) {
1509
- const workspaceId = await cachedWorkspaceId(config);
1510
- const data = await request(
1511
- config,
1512
- ctx,
1513
- workspaceId,
1514
- REMOVE_DISCOUNT,
1515
- { workspaceId },
1516
- "remove discount"
1517
- );
1518
- return data.cart.removeDiscount;
1519
- }
1520
- async function backendSetShippingMethod(config, ctx, shippingMethodId) {
1521
- const workspaceId = await cachedWorkspaceId(config);
1522
- const data = await request(
1523
- config,
1524
- ctx,
1525
- workspaceId,
1526
- SET_SHIPPING_METHOD,
1527
- { workspaceId, shippingMethodId },
1528
- "set shipping method"
1529
- );
1530
- return data.cart.setShippingMethod;
1531
- }
1532
- async function backendMergeCart(config, ctx) {
1533
- const workspaceId = await cachedWorkspaceId(config);
1534
- const data = await request(
1535
- config,
1536
- ctx,
1537
- workspaceId,
1538
- MERGE_CART,
1539
- { workspaceId },
1540
- "merge cart"
1541
- );
1542
- return data.cart.merge;
1543
- }
1544
- async function backendCheckout(config, ctx, input) {
1545
- const workspaceId = await cachedWorkspaceId(config);
1546
- const data = await request(
1547
- config,
1548
- ctx,
1549
- workspaceId,
1550
- CHECKOUT,
1551
- { input: { workspaceId, ...input } },
1552
- "checkout"
1553
- );
1554
- return data.cart.checkout;
1555
- }
1556
- async function backendProduct(config, ctx, modelSlug, filter) {
1557
- const workspaceId = await cachedWorkspaceId(config);
1558
- const data = await request(
1559
- config,
1560
- ctx,
1561
- workspaceId,
1562
- PRODUCT,
1563
- { workspaceId, modelSlug, filter },
1564
- "product query"
1565
- );
1566
- return data.public.model.records.items[0] ?? null;
1567
- }
1568
-
1569
- // src/orders-client.ts
1570
- var ORDER_FIELDS = `
1571
- id
1572
- status
1573
- subtotal
1574
- discount
1575
- appliedDiscount { code type value amount }
1576
- tax
1577
- total
1578
- pricesIncludeTax
1579
- taxSummary { rateId name rate base amount }
1580
- currency
1581
- customerEmail
1582
- refundedAmount
1583
- paymentProvider
1584
- paymentStatus
1585
- fulfillmentStatus
1586
- amountPaid
1587
- balanceDue
1588
- paymentReference
1589
- trackingNumber
1590
- trackingCarrier
1591
- invoiceNumber
1592
- invoiceUrl
1593
- invoiceProvider
1594
- paidAt
1595
- fulfilledAt
1596
- createdAt
1597
- orderNumber
1598
- poNumber
1599
- customerNote
1600
- shippingTotal
1601
- shippingMethod { id label price }
1602
- shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1603
- items { name price listPrice tierMinQty currency quantity sku }
1604
- payments { amount reference provider at }
1605
- `;
1606
- var PUBLIC_ORDER_FIELDS = `
1607
- id
1608
- orderNumber
1609
- status
1610
- paymentStatus
1611
- fulfillmentStatus
1612
- subtotal
1613
- discount
1614
- appliedDiscount { code type value amount }
1615
- tax
1616
- total
1617
- pricesIncludeTax
1618
- taxSummary { rateId name rate base amount }
1619
- currency
1620
- customerEmail
1621
- poNumber
1622
- customerNote
1623
- shippingTotal
1624
- shippingMethod { id label price }
1625
- shippingAddress { name company line1 line2 postalCode city region country phone vatId }
1626
- amountPaid
1627
- balanceDue
1628
- refundedAmount
1629
- trackingNumber
1630
- trackingCarrier
1631
- invoiceNumber
1632
- invoiceUrl
1633
- paidAt
1634
- fulfilledAt
1635
- createdAt
1636
- items { name price listPrice tierMinQty currency quantity sku taxRate taxAmount }
1637
- `;
1638
- var MY_ORDERS = `query MyOrders($workspaceId: ID!, $skip: Int, $limit: Int) {
1639
- account {
1640
- orders(workspaceId: $workspaceId, skip: $skip, limit: $limit) {
1641
- total
1642
- hasMore
1643
- items { ${ORDER_FIELDS} }
1644
- }
1645
- }
1646
- }`;
1647
- var MY_ORDER = `query MyOrder($workspaceId: ID!, $id: ID!) {
1648
- account {
1649
- order(workspaceId: $workspaceId, id: $id) { ${ORDER_FIELDS} }
1650
- }
1651
- }`;
1652
- var PUBLIC_ORDER_BY_TOKEN = `query PublicOrder($workspaceId: ID!, $orderId: ID!, $accessToken: String!) {
1653
- public {
1654
- order {
1655
- byToken(workspaceId: $workspaceId, orderId: $orderId, accessToken: $accessToken) {
1656
- ${PUBLIC_ORDER_FIELDS}
1657
- }
1658
- }
1659
- }
1660
- }`;
1661
- function headers(workspaceId, accessToken) {
1662
- return {
1663
- "x-workspace-id": workspaceId,
1664
- authorization: `Bearer ${accessToken}`
1665
- };
1666
- }
1667
- async function backendMyOrders(config, accessToken, options) {
1668
- const workspaceId = await cachedWorkspaceId(config);
1669
- const data = await graphqlRequest(
1670
- config,
1671
- MY_ORDERS,
1672
- { workspaceId, skip: options.skip, limit: options.limit },
1673
- { headers: headers(workspaceId, accessToken) },
1674
- "my orders"
1675
- );
1676
- return data.account.orders;
1677
- }
1678
- async function backendMyOrder(config, accessToken, id) {
1679
- const workspaceId = await cachedWorkspaceId(config);
1680
- const data = await graphqlRequest(
1681
- config,
1682
- MY_ORDER,
1683
- { workspaceId, id },
1684
- { headers: headers(workspaceId, accessToken) },
1685
- "my order"
1686
- );
1687
- return data.account.order;
1688
- }
1689
- async function backendOrderByToken(config, options) {
1690
- const workspaceId = await cachedWorkspaceId(config);
1691
- const data = await graphqlRequest(
1692
- config,
1693
- PUBLIC_ORDER_BY_TOKEN,
1694
- {
1695
- workspaceId,
1696
- orderId: options.orderId,
1697
- accessToken: options.accessToken
1698
- },
1699
- { headers: { "x-workspace-id": workspaceId } },
1700
- "public order lookup"
1701
- );
1702
- return data.public.order.byToken;
1703
- }
1704
-
1705
- // src/commerce/product-client.ts
1706
- var PRODUCTS_QUERY = `query Products($workspaceId: String!, $modelSlug: String!, $filter: JSON, $stockState: String, $locale: String, $limit: Int, $offset: Int, $sort: String) {
1707
- public {
1708
- model {
1709
- records(workspaceId: $workspaceId, modelSlug: $modelSlug, filter: $filter, stockState: $stockState, locale: $locale, limit: $limit, offset: $offset, sort: $sort) {
1710
- total
1711
- hasMore
1712
- items {
1713
- id
1714
- data
1715
- priceTiers { minQty price }
1716
- variants { id sku price inventory tiers { minQty price } selectedOptions { name value } }
1717
- }
1718
- }
1719
- }
1720
- }
1721
- }`;
1722
- async function fetchProducts(config, options) {
1723
- const workspaceId = await resolveWorkspaceId(config);
1724
- const locale = options.locale ?? null;
1725
- const data = await graphqlRequest(
1726
- config,
1727
- PRODUCTS_QUERY,
1728
- {
1729
- workspaceId,
1730
- modelSlug: options.modelSlug,
1731
- filter: options.filter ?? {},
1732
- stockState: options.stockState ?? null,
1733
- locale,
1734
- limit: options.limit ?? 50,
1735
- offset: options.offset ?? 0,
1736
- sort: options.sort ?? null
1737
- },
1738
- { headers: { "x-workspace-id": workspaceId } },
1739
- "products query"
1740
- );
1741
- return data.public.model.records;
1742
- }
1743
- async function fetchProduct(config, options) {
1744
- const page = await fetchProducts(config, {
1745
- modelSlug: options.modelSlug,
1746
- filter: { [options.slugField ?? "slug"]: options.slug },
1747
- locale: options.locale,
1748
- limit: 1
1749
- });
1750
- return page.items[0] ?? null;
1751
- }
1752
-
1753
- // src/commerce/order-client.ts
1754
- async function fetchOrderByToken(config, options) {
1755
- return backendOrderByToken(config, options);
1756
- }
1757
-
1758
- // src/commerce/money.ts
1759
- var ZERO_DECIMAL = /* @__PURE__ */ new Set([
1760
- "BIF",
1761
- "CLP",
1762
- "DJF",
1763
- "GNF",
1764
- "JPY",
1765
- "KMF",
1766
- "KRW",
1767
- "MGA",
1768
- "PYG",
1769
- "RWF",
1770
- "UGX",
1771
- "VND",
1772
- "VUV",
1773
- "XAF",
1774
- "XOF",
1775
- "XPF"
1776
- ]);
1777
- var THREE_DECIMAL = /* @__PURE__ */ new Set([
1778
- "BHD",
1779
- "IQD",
1780
- "JOD",
1781
- "KWD",
1782
- "LYD",
1783
- "OMR",
1784
- "TND"
1785
- ]);
1786
- function fractionDigits(currency) {
1787
- const code = currency.toUpperCase();
1788
- if (ZERO_DECIMAL.has(code)) return 0;
1789
- if (THREE_DECIMAL.has(code)) return 3;
1790
- return 2;
1791
- }
1792
- function fromMinorUnits(minor, currency) {
1793
- return minor / 10 ** fractionDigits(currency);
1794
- }
1795
- function toMinorUnits(amount, currency) {
1796
- return Math.round(amount * 10 ** fractionDigits(currency));
1797
- }
1798
- function formatPrice(minor, currency) {
1799
- if (!Number.isFinite(minor)) return "";
1800
- const code = currency ?? "USD";
1801
- const amount = fromMinorUnits(minor, code);
1802
- try {
1803
- return new Intl.NumberFormat(void 0, {
1804
- style: "currency",
1805
- currency: code
1806
- }).format(amount);
1807
- } catch {
1808
- return `${amount.toFixed(fractionDigits(code))} ${code}`;
1809
- }
1810
- }
1811
-
1812
525
  // src/verify-webhook.ts
1813
526
  var CmssyWebhookError = class extends Error {
1814
527
  constructor(message) {
@@ -1907,100 +620,21 @@ exports.CMSSY_EDIT_HEADER = CMSSY_EDIT_HEADER;
1907
620
  exports.CMSSY_EDIT_QUERY_PARAM = CMSSY_EDIT_QUERY_PARAM;
1908
621
  exports.CMSSY_LOCALE_HEADER = CMSSY_LOCALE_HEADER;
1909
622
  exports.CMSSY_SECRET_QUERY_PARAM = CMSSY_SECRET_QUERY_PARAM;
1910
- exports.CMSSY_SESSION_COOKIE = CMSSY_SESSION_COOKIE;
1911
623
  exports.CmssyRequestError = CmssyRequestError;
1912
624
  exports.CmssyWebhookError = CmssyWebhookError;
1913
625
  exports.DEFAULT_CMSSY_API_URL = DEFAULT_CMSSY_API_URL;
1914
626
  exports.DEFAULT_CMSSY_EDITOR_ORIGINS = DEFAULT_CMSSY_EDITOR_ORIGINS;
1915
- exports.FORM_QUERY = FORM_QUERY;
1916
- exports.MIN_SESSION_SECRET_LENGTH = MIN_SESSION_SECRET_LENGTH;
1917
- exports.MODEL_DEFINITIONS_QUERY = MODEL_DEFINITIONS_QUERY;
1918
- exports.MODEL_RECORDS_QUERY = MODEL_RECORDS_QUERY;
1919
- exports.PRODUCTS_QUERY = PRODUCTS_QUERY;
1920
627
  exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
1921
- exports.RECORDS_BY_IDS_QUERY = RECORDS_BY_IDS_QUERY;
1922
- exports.SESSION_MAX_AGE_SECONDS = SESSION_MAX_AGE_SECONDS;
1923
- exports.SITE_CONFIG_QUERY = SITE_CONFIG_QUERY;
1924
- exports.SUBMIT_FORM_MUTATION = SUBMIT_FORM_MUTATION;
1925
628
  exports.applyCmssyCsp = applyCmssyCsp;
1926
- exports.asBucket = asBucket;
1927
- exports.assertAuthConfig = assertAuthConfig;
1928
- exports.backendAddToCart = backendAddToCart;
1929
- exports.backendApplyDiscount = backendApplyDiscount;
1930
- exports.backendCheckout = backendCheckout;
1931
- exports.backendClearCart = backendClearCart;
1932
- exports.backendForgotPassword = backendForgotPassword;
1933
- exports.backendGetCart = backendGetCart;
1934
- exports.backendMergeCart = backendMergeCart;
1935
- exports.backendMyOrder = backendMyOrder;
1936
- exports.backendMyOrders = backendMyOrders;
1937
- exports.backendOrderByToken = backendOrderByToken;
1938
- exports.backendProduct = backendProduct;
1939
- exports.backendRefresh = backendRefresh;
1940
- exports.backendRegister = backendRegister;
1941
- exports.backendRemoveDiscount = backendRemoveDiscount;
1942
- exports.backendRemoveItem = backendRemoveItem;
1943
- exports.backendResetPassword = backendResetPassword;
1944
- exports.backendSetShippingMethod = backendSetShippingMethod;
1945
- exports.backendSignIn = backendSignIn;
1946
- exports.backendSignOut = backendSignOut;
1947
- exports.backendSignOutEverywhere = backendSignOutEverywhere;
1948
- exports.backendUpdateItem = backendUpdateItem;
1949
- exports.backendVerifyEmail = backendVerifyEmail;
1950
- exports.buildBlockContext = buildBlockContext;
1951
- exports.buildLocaleSwitchHref = buildLocaleSwitchHref;
1952
- exports.cachedWorkspaceId = cachedWorkspaceId;
1953
- exports.clearWorkspaceIdCache = clearWorkspaceIdCache;
1954
- exports.cmssyCspHeaders = cmssyCspHeaders;
1955
- exports.cmssySecretsMatch = cmssySecretsMatch;
1956
- exports.collectFormIds = collectFormIds;
1957
629
  exports.createCmssyClient = createCmssyClient;
1958
- exports.decodeAccessClaims = decodeAccessClaims;
1959
630
  exports.defineCmssyConfig = defineCmssyConfig;
1960
- exports.fetchLayouts = fetchLayouts;
1961
- exports.fetchOrderByToken = fetchOrderByToken;
1962
- exports.fetchPage = fetchPage;
1963
- exports.fetchPageById = fetchPageById;
1964
- exports.fetchPageMeta = fetchPageMeta;
1965
- exports.fetchPages = fetchPages;
1966
- exports.fetchProduct = fetchProduct;
1967
- exports.fetchProducts = fetchProducts;
1968
- exports.fetchSiteConfig = fetchSiteConfig;
1969
631
  exports.fields = fields;
1970
- exports.formatPrice = formatPrice;
1971
- exports.fractionDigits = fractionDigits;
1972
- exports.fromMinorUnits = fromMinorUnits;
1973
- exports.getBlockContentForLanguage = getBlockContentForLanguage;
1974
632
  exports.graphqlRequest = graphqlRequest;
1975
- exports.isAccessExpired = isAccessExpired;
1976
- exports.isDevelopment = isDevelopment;
1977
633
  exports.isProtocolCompatible = isProtocolCompatible;
1978
634
  exports.isVerifiedEditUrl = isVerifiedEditUrl;
1979
- exports.localeForPath = localeForPath;
1980
- exports.localeForPathname = localeForPathname;
1981
- exports.localesFromSiteConfig = localesFromSiteConfig;
1982
635
  exports.localizeHref = localizeHref;
1983
- exports.localizeHtmlLinks = localizeHtmlLinks;
1984
- exports.localizedPath = localizedPath;
1985
636
  exports.normalizeOrigin = normalizeOrigin;
1986
- exports.normalizeRelationContent = normalizeRelationContent;
1987
- exports.normalizeSlug = normalizeSlug;
1988
- exports.openSession = openSession;
1989
637
  exports.parseEditorMessage = parseEditorMessage;
1990
638
  exports.postToEditor = postToEditor;
1991
- exports.resolveApiUrl = resolveApiUrl;
1992
639
  exports.resolveEditorOrigin = resolveEditorOrigin;
1993
- exports.resolveForms = resolveForms;
1994
- exports.resolveInitialTarget = resolveInitialTarget;
1995
- exports.resolvePublicUrl = resolvePublicUrl;
1996
- exports.resolveRelationContent = resolveRelationContent;
1997
- exports.resolveSiteLocales = resolveSiteLocales;
1998
- exports.resolveWorkspaceId = resolveWorkspaceId;
1999
- exports.sealSession = sealSession;
2000
- exports.sessionCookieOptions = sessionCookieOptions;
2001
- exports.splitCmssyLocale = splitCmssyLocale;
2002
- exports.splitLocaleFromPath = splitLocaleFromPath;
2003
- exports.toCspOrigin = toCspOrigin;
2004
- exports.toMinorUnits = toMinorUnits;
2005
- exports.toSessionPayload = toSessionPayload;
2006
640
  exports.verifyCmssyWebhook = verifyCmssyWebhook;