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