@cmssy/core 12.5.0 → 12.6.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 +1 -1
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +1 -1
- package/dist/internal.cjs +240 -152
- package/dist/internal.d.cts +19 -6
- package/dist/internal.d.ts +19 -6
- package/dist/internal.js +240 -152
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -486,11 +486,11 @@ var fields = {
|
|
|
486
486
|
table: control("table"),
|
|
487
487
|
json: control("json"),
|
|
488
488
|
form: control("form"),
|
|
489
|
-
pageSelector: control("pageSelector"),
|
|
490
489
|
select: choice("select"),
|
|
491
490
|
radio: choice("radio"),
|
|
492
491
|
multiselect: (opts) => build("multiselect", opts),
|
|
493
492
|
media: (opts = {}) => build("media", opts),
|
|
493
|
+
pageSelector: (opts = {}) => build("pageSelector", opts),
|
|
494
494
|
repeater: (opts) => build("repeater", opts),
|
|
495
495
|
relation: (opts) => {
|
|
496
496
|
const { model, mode, ...rest } = opts;
|
package/dist/index.d.cts
CHANGED
|
@@ -2,9 +2,14 @@ export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as Cm
|
|
|
2
2
|
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref, r as resolveCmssyLocale } from './locale-BS61R-fF.cjs';
|
|
3
3
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, F as FetchLike, a as FetchLikeResponse, b as FetchPageOptions, R as RetryPolicy } from './content-client-BPpRfq0h.cjs';
|
|
4
4
|
import * as _cmssy_types from '@cmssy/types';
|
|
5
|
-
import { FieldOptions, TypedField, ResolvedMediaValue, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, ResolvedMedia, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
5
|
+
import { FieldOptions, TypedField, ResolvedMediaValue, PageRef, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, ResolvedMedia, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
6
6
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockPage, CmssyBlockWorkspace, CmssyBranding, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyRecordList, CmssySiteConfig, CmssyWebhookEvent, CmssyWebhookOrder, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, LayoutPosition, RawBlock, RawLayoutBlock, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup, layoutPositionValues } from '@cmssy/types';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Whether the content is guaranteed to carry the field. Only `required` says
|
|
10
|
+
* so: a declared `defaultValue` seeds the editor and is not put in place at
|
|
11
|
+
* read time, so it promises nothing about the stored content (CMS-1220).
|
|
12
|
+
*/
|
|
8
13
|
type Declared<O> = O extends {
|
|
9
14
|
required: true;
|
|
10
15
|
} ? true : false;
|
|
@@ -22,6 +27,14 @@ type RelationValue<O> = O extends {
|
|
|
22
27
|
} | {
|
|
23
28
|
multiple: true;
|
|
24
29
|
} ? CmssyModelRecord[] : CmssyModelRecord | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* A page selector holds a list unless it is explicitly single. The default is
|
|
32
|
+
* the multiple one, which is the reading the editor already takes: it treats
|
|
33
|
+
* only `multiple: false` as single.
|
|
34
|
+
*/
|
|
35
|
+
type PageSelectorValue<O> = O extends {
|
|
36
|
+
multiple: false;
|
|
37
|
+
} ? PageRef | undefined : PageRef[];
|
|
25
38
|
interface RelationFieldOptions extends Omit<FieldOptions, "options" | "itemSchema"> {
|
|
26
39
|
model: string;
|
|
27
40
|
mode?: RelationMode;
|
|
@@ -45,11 +58,11 @@ declare const fields: {
|
|
|
45
58
|
table: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.TableValue, Declared<O>>;
|
|
46
59
|
json: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.JsonValue, Declared<O>>;
|
|
47
60
|
form: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
48
|
-
pageSelector: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.PageRef[], Declared<O>>;
|
|
49
61
|
select: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>, Declared<O>>;
|
|
50
62
|
radio: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>, Declared<O>>;
|
|
51
63
|
multiselect: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>[], Declared<O>>;
|
|
52
64
|
media: <const O extends FieldOptions>(opts?: O) => TypedField<MediaValue<O>, Declared<O>>;
|
|
65
|
+
pageSelector: <const O extends FieldOptions>(opts?: O) => TypedField<PageSelectorValue<O>, Declared<O>>;
|
|
53
66
|
repeater: <const O extends FieldOptions>(opts: O) => TypedField<RepeaterValue<O>, Declared<O>>;
|
|
54
67
|
relation: <const O extends RelationFieldOptions>(opts: O) => TypedField<RelationValue<O>, Declared<O>>;
|
|
55
68
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,14 @@ export { A as AppToEditorMessage, B as BoundsMessage, C as ClickMessage, a as Cm
|
|
|
2
2
|
export { C as CMSSY_LOCALE_HEADER, G as GraphqlRequestOptions, g as graphqlRequest, l as localizeHref, r as resolveCmssyLocale } from './locale-DQZ80cew.js';
|
|
3
3
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, F as FetchLike, a as FetchLikeResponse, b as FetchPageOptions, R as RetryPolicy } from './content-client-BPpRfq0h.js';
|
|
4
4
|
import * as _cmssy_types from '@cmssy/types';
|
|
5
|
-
import { FieldOptions, TypedField, ResolvedMediaValue, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, ResolvedMedia, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
5
|
+
import { FieldOptions, TypedField, ResolvedMediaValue, PageRef, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, ResolvedMedia, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
6
6
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockPage, CmssyBlockWorkspace, CmssyBranding, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyRecordList, CmssySiteConfig, CmssyWebhookEvent, CmssyWebhookOrder, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, LayoutPosition, RawBlock, RawLayoutBlock, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup, layoutPositionValues } from '@cmssy/types';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Whether the content is guaranteed to carry the field. Only `required` says
|
|
10
|
+
* so: a declared `defaultValue` seeds the editor and is not put in place at
|
|
11
|
+
* read time, so it promises nothing about the stored content (CMS-1220).
|
|
12
|
+
*/
|
|
8
13
|
type Declared<O> = O extends {
|
|
9
14
|
required: true;
|
|
10
15
|
} ? true : false;
|
|
@@ -22,6 +27,14 @@ type RelationValue<O> = O extends {
|
|
|
22
27
|
} | {
|
|
23
28
|
multiple: true;
|
|
24
29
|
} ? CmssyModelRecord[] : CmssyModelRecord | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* A page selector holds a list unless it is explicitly single. The default is
|
|
32
|
+
* the multiple one, which is the reading the editor already takes: it treats
|
|
33
|
+
* only `multiple: false` as single.
|
|
34
|
+
*/
|
|
35
|
+
type PageSelectorValue<O> = O extends {
|
|
36
|
+
multiple: false;
|
|
37
|
+
} ? PageRef | undefined : PageRef[];
|
|
25
38
|
interface RelationFieldOptions extends Omit<FieldOptions, "options" | "itemSchema"> {
|
|
26
39
|
model: string;
|
|
27
40
|
mode?: RelationMode;
|
|
@@ -45,11 +58,11 @@ declare const fields: {
|
|
|
45
58
|
table: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.TableValue, Declared<O>>;
|
|
46
59
|
json: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.JsonValue, Declared<O>>;
|
|
47
60
|
form: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
48
|
-
pageSelector: <const O extends FieldOptions>(opts?: O) => TypedField<_cmssy_types.PageRef[], Declared<O>>;
|
|
49
61
|
select: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>, Declared<O>>;
|
|
50
62
|
radio: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>, Declared<O>>;
|
|
51
63
|
multiselect: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>[], Declared<O>>;
|
|
52
64
|
media: <const O extends FieldOptions>(opts?: O) => TypedField<MediaValue<O>, Declared<O>>;
|
|
65
|
+
pageSelector: <const O extends FieldOptions>(opts?: O) => TypedField<PageSelectorValue<O>, Declared<O>>;
|
|
53
66
|
repeater: <const O extends FieldOptions>(opts: O) => TypedField<RepeaterValue<O>, Declared<O>>;
|
|
54
67
|
relation: <const O extends RelationFieldOptions>(opts: O) => TypedField<RelationValue<O>, Declared<O>>;
|
|
55
68
|
};
|
package/dist/index.js
CHANGED
|
@@ -484,11 +484,11 @@ var fields = {
|
|
|
484
484
|
table: control("table"),
|
|
485
485
|
json: control("json"),
|
|
486
486
|
form: control("form"),
|
|
487
|
-
pageSelector: control("pageSelector"),
|
|
488
487
|
select: choice("select"),
|
|
489
488
|
radio: choice("radio"),
|
|
490
489
|
multiselect: (opts) => build("multiselect", opts),
|
|
491
490
|
media: (opts = {}) => build("media", opts),
|
|
491
|
+
pageSelector: (opts = {}) => build("pageSelector", opts),
|
|
492
492
|
repeater: (opts) => build("repeater", opts),
|
|
493
493
|
relation: (opts) => {
|
|
494
494
|
const { model, mode, ...rest } = opts;
|
package/dist/internal.cjs
CHANGED
|
@@ -659,131 +659,7 @@ function createCmssyClient(input) {
|
|
|
659
659
|
return client;
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
-
// src/data/
|
|
663
|
-
function collectFormIds(blocks, locale, defaultLocale) {
|
|
664
|
-
const ids = /* @__PURE__ */ new Set();
|
|
665
|
-
for (const block of blocks) {
|
|
666
|
-
const content = getBlockContentForLanguage(
|
|
667
|
-
block.content,
|
|
668
|
-
locale,
|
|
669
|
-
defaultLocale
|
|
670
|
-
);
|
|
671
|
-
const formId = content.formId;
|
|
672
|
-
if (typeof formId === "string" && formId.trim()) ids.add(formId);
|
|
673
|
-
}
|
|
674
|
-
return [...ids];
|
|
675
|
-
}
|
|
676
|
-
async function resolveForms(config, blocks, locale, defaultLocale, options) {
|
|
677
|
-
const ids = collectFormIds(blocks, locale, defaultLocale);
|
|
678
|
-
if (ids.length === 0) return {};
|
|
679
|
-
const client = createCmssyClient(config);
|
|
680
|
-
const entries = await Promise.all(
|
|
681
|
-
ids.map(async (id) => {
|
|
682
|
-
try {
|
|
683
|
-
const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
|
|
684
|
-
return [id, data.public.form.get];
|
|
685
|
-
} catch (err) {
|
|
686
|
-
if (typeof console !== "undefined") {
|
|
687
|
-
console.warn(`[cmssy] failed to resolve form ${id}`, err);
|
|
688
|
-
}
|
|
689
|
-
return [id, null];
|
|
690
|
-
}
|
|
691
|
-
})
|
|
692
|
-
);
|
|
693
|
-
const forms = {};
|
|
694
|
-
for (const [id, def] of entries) {
|
|
695
|
-
if (def) forms[id] = def;
|
|
696
|
-
}
|
|
697
|
-
return forms;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// src/data/site-locales.ts
|
|
701
|
-
var TTL_MS = 6e4;
|
|
702
|
-
var FAILURE_TTL_MS = 5e3;
|
|
703
|
-
var MAX_ENTRIES = 64;
|
|
704
|
-
var cache = /* @__PURE__ */ new Map();
|
|
705
|
-
function localesFromSiteConfig(siteConfig) {
|
|
706
|
-
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
707
|
-
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
708
|
-
return {
|
|
709
|
-
defaultLocale,
|
|
710
|
-
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
async function loadSiteLocales(config, options) {
|
|
714
|
-
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
715
|
-
const cached = cache.get(key);
|
|
716
|
-
if (cached && cached.expires > Date.now()) return cached.value;
|
|
717
|
-
cache.delete(key);
|
|
718
|
-
let value;
|
|
719
|
-
try {
|
|
720
|
-
const data = await graphqlRequest(
|
|
721
|
-
config,
|
|
722
|
-
SITE_CONFIG_QUERY,
|
|
723
|
-
{ workspaceSlug: config.workspaceSlug },
|
|
724
|
-
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
725
|
-
"site config"
|
|
726
|
-
);
|
|
727
|
-
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
728
|
-
} catch {
|
|
729
|
-
value = null;
|
|
730
|
-
}
|
|
731
|
-
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
732
|
-
cache.set(key, {
|
|
733
|
-
value,
|
|
734
|
-
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
735
|
-
});
|
|
736
|
-
return value;
|
|
737
|
-
}
|
|
738
|
-
async function resolveSiteLocales(config, options) {
|
|
739
|
-
return await loadSiteLocales(config, options) ?? {
|
|
740
|
-
defaultLocale: "en",
|
|
741
|
-
locales: ["en"]
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
function splitLocaleFromPath(path, siteLocales) {
|
|
745
|
-
const first = path?.[0];
|
|
746
|
-
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
747
|
-
return { locale: first, path: path.slice(1) };
|
|
748
|
-
}
|
|
749
|
-
return { locale: siteLocales.defaultLocale, path };
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// src/data/localize-href.ts
|
|
753
|
-
var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
754
|
-
function isExternalHref(href) {
|
|
755
|
-
const value = href.trim();
|
|
756
|
-
if (!value) return true;
|
|
757
|
-
if (value.startsWith("#")) return true;
|
|
758
|
-
return PROTOCOL_OR_RELATIVE.test(value);
|
|
759
|
-
}
|
|
760
|
-
function stripLeadingLocale(path, locale) {
|
|
761
|
-
const segments = path.split("/");
|
|
762
|
-
const first = segments[1];
|
|
763
|
-
if (first && first !== locale.default && locale.enabled.includes(first)) {
|
|
764
|
-
segments.splice(1, 1);
|
|
765
|
-
const rest = segments.join("/");
|
|
766
|
-
return rest === "" ? "/" : rest;
|
|
767
|
-
}
|
|
768
|
-
return path;
|
|
769
|
-
}
|
|
770
|
-
function addLocalePrefix(path, target, locale) {
|
|
771
|
-
if (target === locale.default) return path;
|
|
772
|
-
if (path === "/") return `/${target}`;
|
|
773
|
-
return `/${target}${path}`;
|
|
774
|
-
}
|
|
775
|
-
function localizeHref(href, locale) {
|
|
776
|
-
const value = href.trim();
|
|
777
|
-
if (isExternalHref(value)) return href;
|
|
778
|
-
const boundary = value.search(/[?#]/);
|
|
779
|
-
const path = boundary === -1 ? value : value.slice(0, boundary);
|
|
780
|
-
const suffix = boundary === -1 ? "" : value.slice(boundary);
|
|
781
|
-
if (!path.startsWith("/")) return href;
|
|
782
|
-
const bare = stripLeadingLocale(path, locale);
|
|
783
|
-
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// src/data/relation-resolver.ts
|
|
662
|
+
// src/data/block-content.ts
|
|
787
663
|
var RECORDS_BY_IDS_QUERY = `query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {
|
|
788
664
|
public {
|
|
789
665
|
model {
|
|
@@ -805,17 +681,54 @@ function storedIds(value) {
|
|
|
805
681
|
}
|
|
806
682
|
return [];
|
|
807
683
|
}
|
|
684
|
+
function isRecord(value) {
|
|
685
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
686
|
+
}
|
|
687
|
+
var BLOCK_BUCKETS = ["content", "style", "advanced"];
|
|
688
|
+
function bucketOf(field) {
|
|
689
|
+
return field.tab === "style" || field.tab === "advanced" ? field.tab : "content";
|
|
690
|
+
}
|
|
691
|
+
function walkBlockFields(values, schema, visit, options = {}, path = []) {
|
|
692
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
693
|
+
if (options.bucket && path.length === 0 && bucketOf(field) !== options.bucket) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (field.type === "repeater") {
|
|
697
|
+
const itemSchema = field.itemSchema;
|
|
698
|
+
const stored = values[key];
|
|
699
|
+
if (!itemSchema || !Array.isArray(stored)) continue;
|
|
700
|
+
const rows = options.copyRows ? stored.map((row) => isRecord(row) ? { ...row } : row) : stored;
|
|
701
|
+
if (options.copyRows) values[key] = rows;
|
|
702
|
+
rows.forEach((row, index) => {
|
|
703
|
+
if (isRecord(row)) {
|
|
704
|
+
walkBlockFields(row, itemSchema, visit, options, [
|
|
705
|
+
...path,
|
|
706
|
+
key,
|
|
707
|
+
index
|
|
708
|
+
]);
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
visit(values, key, field, path);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
808
716
|
function collectRefs(entries, schemas) {
|
|
809
717
|
const refs = [];
|
|
810
718
|
for (const entry of entries) {
|
|
811
719
|
const schema = schemas[entry.type];
|
|
812
720
|
if (!schema) continue;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
721
|
+
walkBlockFields(
|
|
722
|
+
entry.content,
|
|
723
|
+
schema,
|
|
724
|
+
(holder, key, field) => {
|
|
725
|
+
if (field.type !== "relation") return;
|
|
726
|
+
const model = relationModel(field);
|
|
727
|
+
if (!model) return;
|
|
728
|
+
refs.push({ content: holder, key, field, model });
|
|
729
|
+
},
|
|
730
|
+
{ copyRows: true, bucket: "content" }
|
|
731
|
+
);
|
|
819
732
|
}
|
|
820
733
|
return refs;
|
|
821
734
|
}
|
|
@@ -825,31 +738,70 @@ function isResolvedRecord(value) {
|
|
|
825
738
|
function isListShaped(field) {
|
|
826
739
|
return field.relationMode === "all" || field.relationType === "hasMany" || field.multiple === true;
|
|
827
740
|
}
|
|
828
|
-
function
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
741
|
+
function toPageRef(value) {
|
|
742
|
+
if (typeof value === "string") {
|
|
743
|
+
return value ? { slug: value, displayName: {} } : null;
|
|
744
|
+
}
|
|
745
|
+
if (isRecord(value) && typeof value.slug === "string" && value.slug) {
|
|
746
|
+
return value;
|
|
747
|
+
}
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
function toPageRefs(value) {
|
|
751
|
+
const items = Array.isArray(value) ? value : [value];
|
|
752
|
+
return items.map(toPageRef).filter((ref) => ref !== null);
|
|
753
|
+
}
|
|
754
|
+
function fallbackHolder(resolved, path) {
|
|
755
|
+
let holder = resolved;
|
|
756
|
+
for (const step of path) {
|
|
757
|
+
if (holder === void 0 || holder === null) return void 0;
|
|
758
|
+
holder = holder[step];
|
|
759
|
+
}
|
|
760
|
+
return isRecord(holder) ? holder : void 0;
|
|
761
|
+
}
|
|
762
|
+
function normalizeBlockContent(content, schema, resolved) {
|
|
763
|
+
walkBlockFields(
|
|
764
|
+
content,
|
|
765
|
+
schema,
|
|
766
|
+
(holder, key, field, path) => {
|
|
767
|
+
if (field.type === "pageSelector") {
|
|
768
|
+
if (!(key in holder)) return;
|
|
769
|
+
const refs = toPageRefs(holder[key]);
|
|
770
|
+
if (field.multiple === false) {
|
|
771
|
+
if (refs[0]) holder[key] = refs[0];
|
|
772
|
+
else delete holder[key];
|
|
773
|
+
} else {
|
|
774
|
+
holder[key] = refs;
|
|
839
775
|
}
|
|
776
|
+
return;
|
|
840
777
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
778
|
+
if (field.type !== "relation") return;
|
|
779
|
+
const value = holder[key];
|
|
780
|
+
const fallback = fallbackHolder(resolved, path)?.[key];
|
|
781
|
+
if (isListShaped(field)) {
|
|
782
|
+
if (Array.isArray(value)) {
|
|
783
|
+
const records = value.filter(isResolvedRecord);
|
|
784
|
+
if (records.length > 0 || value.length === 0) {
|
|
785
|
+
holder[key] = records;
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
holder[key] = Array.isArray(fallback) ? fallback.filter(isResolvedRecord) : [];
|
|
790
|
+
} else if (!isResolvedRecord(value)) {
|
|
791
|
+
if (value != null && value !== "" && isResolvedRecord(fallback)) {
|
|
792
|
+
holder[key] = fallback;
|
|
793
|
+
} else {
|
|
794
|
+
delete holder[key];
|
|
795
|
+
}
|
|
847
796
|
}
|
|
848
|
-
}
|
|
849
|
-
|
|
797
|
+
},
|
|
798
|
+
{ copyRows: true, bucket: "content" }
|
|
799
|
+
);
|
|
850
800
|
}
|
|
851
801
|
function collectionKey(ref) {
|
|
852
|
-
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join(
|
|
802
|
+
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join(
|
|
803
|
+
"\0"
|
|
804
|
+
);
|
|
853
805
|
}
|
|
854
806
|
async function resolveRelationContent(config, entries, schemas, locale, requestOptions = {}) {
|
|
855
807
|
const refs = collectRefs(entries, schemas);
|
|
@@ -945,6 +897,142 @@ async function fetchCollections(client, collections, locale, requestOptions = {}
|
|
|
945
897
|
return byKey;
|
|
946
898
|
}
|
|
947
899
|
|
|
900
|
+
// src/data/resolve-forms.ts
|
|
901
|
+
function collectFormIds(blocks, schemas, locale, defaultLocale) {
|
|
902
|
+
const ids = /* @__PURE__ */ new Set();
|
|
903
|
+
for (const block of blocks) {
|
|
904
|
+
const schema = schemas[block.type];
|
|
905
|
+
if (!schema) continue;
|
|
906
|
+
const values = {
|
|
907
|
+
content: getBlockContentForLanguage(block.content, locale, defaultLocale),
|
|
908
|
+
style: asBucket(block.style),
|
|
909
|
+
advanced: asBucket(block.advanced)
|
|
910
|
+
};
|
|
911
|
+
for (const bucket of BLOCK_BUCKETS) {
|
|
912
|
+
walkBlockFields(
|
|
913
|
+
values[bucket],
|
|
914
|
+
schema,
|
|
915
|
+
(holder, key, field) => {
|
|
916
|
+
if (field.type !== "form") return;
|
|
917
|
+
const id = holder[key];
|
|
918
|
+
if (typeof id === "string" && id.trim()) ids.add(id);
|
|
919
|
+
},
|
|
920
|
+
{ bucket }
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return [...ids];
|
|
925
|
+
}
|
|
926
|
+
async function resolveForms(config, blocks, schemas, locale, defaultLocale, options) {
|
|
927
|
+
const ids = collectFormIds(blocks, schemas, locale, defaultLocale);
|
|
928
|
+
if (ids.length === 0) return {};
|
|
929
|
+
const client = createCmssyClient(config);
|
|
930
|
+
const entries = await Promise.all(
|
|
931
|
+
ids.map(async (id) => {
|
|
932
|
+
try {
|
|
933
|
+
const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
|
|
934
|
+
return [id, data.public.form.get];
|
|
935
|
+
} catch (err) {
|
|
936
|
+
if (typeof console !== "undefined") {
|
|
937
|
+
console.warn(`[cmssy] failed to resolve form ${id}`, err);
|
|
938
|
+
}
|
|
939
|
+
return [id, null];
|
|
940
|
+
}
|
|
941
|
+
})
|
|
942
|
+
);
|
|
943
|
+
const forms = {};
|
|
944
|
+
for (const [id, def] of entries) {
|
|
945
|
+
if (def) forms[id] = def;
|
|
946
|
+
}
|
|
947
|
+
return forms;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/data/site-locales.ts
|
|
951
|
+
var TTL_MS = 6e4;
|
|
952
|
+
var FAILURE_TTL_MS = 5e3;
|
|
953
|
+
var MAX_ENTRIES = 64;
|
|
954
|
+
var cache = /* @__PURE__ */ new Map();
|
|
955
|
+
function localesFromSiteConfig(siteConfig) {
|
|
956
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
957
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
958
|
+
return {
|
|
959
|
+
defaultLocale,
|
|
960
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
async function loadSiteLocales(config, options) {
|
|
964
|
+
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
965
|
+
const cached = cache.get(key);
|
|
966
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
967
|
+
cache.delete(key);
|
|
968
|
+
let value;
|
|
969
|
+
try {
|
|
970
|
+
const data = await graphqlRequest(
|
|
971
|
+
config,
|
|
972
|
+
SITE_CONFIG_QUERY,
|
|
973
|
+
{ workspaceSlug: config.workspaceSlug },
|
|
974
|
+
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
975
|
+
"site config"
|
|
976
|
+
);
|
|
977
|
+
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
978
|
+
} catch {
|
|
979
|
+
value = null;
|
|
980
|
+
}
|
|
981
|
+
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
982
|
+
cache.set(key, {
|
|
983
|
+
value,
|
|
984
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
985
|
+
});
|
|
986
|
+
return value;
|
|
987
|
+
}
|
|
988
|
+
async function resolveSiteLocales(config, options) {
|
|
989
|
+
return await loadSiteLocales(config, options) ?? {
|
|
990
|
+
defaultLocale: "en",
|
|
991
|
+
locales: ["en"]
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
function splitLocaleFromPath(path, siteLocales) {
|
|
995
|
+
const first = path?.[0];
|
|
996
|
+
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
997
|
+
return { locale: first, path: path.slice(1) };
|
|
998
|
+
}
|
|
999
|
+
return { locale: siteLocales.defaultLocale, path };
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/data/localize-href.ts
|
|
1003
|
+
var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
1004
|
+
function isExternalHref(href) {
|
|
1005
|
+
const value = href.trim();
|
|
1006
|
+
if (!value) return true;
|
|
1007
|
+
if (value.startsWith("#")) return true;
|
|
1008
|
+
return PROTOCOL_OR_RELATIVE.test(value);
|
|
1009
|
+
}
|
|
1010
|
+
function stripLeadingLocale(path, locale) {
|
|
1011
|
+
const segments = path.split("/");
|
|
1012
|
+
const first = segments[1];
|
|
1013
|
+
if (first && first !== locale.default && locale.enabled.includes(first)) {
|
|
1014
|
+
segments.splice(1, 1);
|
|
1015
|
+
const rest = segments.join("/");
|
|
1016
|
+
return rest === "" ? "/" : rest;
|
|
1017
|
+
}
|
|
1018
|
+
return path;
|
|
1019
|
+
}
|
|
1020
|
+
function addLocalePrefix(path, target, locale) {
|
|
1021
|
+
if (target === locale.default) return path;
|
|
1022
|
+
if (path === "/") return `/${target}`;
|
|
1023
|
+
return `/${target}${path}`;
|
|
1024
|
+
}
|
|
1025
|
+
function localizeHref(href, locale) {
|
|
1026
|
+
const value = href.trim();
|
|
1027
|
+
if (isExternalHref(value)) return href;
|
|
1028
|
+
const boundary = value.search(/[?#]/);
|
|
1029
|
+
const path = boundary === -1 ? value : value.slice(0, boundary);
|
|
1030
|
+
const suffix = boundary === -1 ? "" : value.slice(boundary);
|
|
1031
|
+
if (!path.startsWith("/")) return href;
|
|
1032
|
+
const bare = stripLeadingLocale(path, locale);
|
|
1033
|
+
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
948
1036
|
// src/locale.ts
|
|
949
1037
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
950
1038
|
async function localeForPathname(config, pathname) {
|
|
@@ -1172,7 +1260,7 @@ exports.localeForPath = localeForPath;
|
|
|
1172
1260
|
exports.localeForPathname = localeForPathname;
|
|
1173
1261
|
exports.localizeHref = localizeHref;
|
|
1174
1262
|
exports.localizedPath = localizedPath;
|
|
1175
|
-
exports.
|
|
1263
|
+
exports.normalizeBlockContent = normalizeBlockContent;
|
|
1176
1264
|
exports.resolveApiUrl = resolveApiUrl;
|
|
1177
1265
|
exports.resolveForms = resolveForms;
|
|
1178
1266
|
exports.resolveInitialTarget = resolveInitialTarget;
|
package/dist/internal.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, r as resolveApiUrl } from './content-client-BPpRfq0h.cjs';
|
|
2
|
-
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig,
|
|
2
|
+
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig, FieldDefinition, RawBlock } from '@cmssy/types';
|
|
3
3
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
4
4
|
import { G as GraphqlRequestOptions } from './locale-BS61R-fF.cjs';
|
|
5
5
|
export { C as CMSSY_LOCALE_HEADER, a as localeForPath, b as localeForPathname, l as localizeHref, c as resolveSiteLocales, s as splitCmssyLocale, d as splitLocaleFromPath } from './locale-BS61R-fF.cjs';
|
|
@@ -22,17 +22,30 @@ declare function fetchSiteConfig(config: CmssyClientConfig, options?: GraphqlReq
|
|
|
22
22
|
declare function resolveWorkspaceId(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<string>;
|
|
23
23
|
declare function clearWorkspaceIdCache(): void;
|
|
24
24
|
|
|
25
|
-
declare function collectFormIds(blocks: RawBlock[], locale: string, defaultLocale: string): string[];
|
|
26
|
-
declare function resolveForms(config: CmssyClientConfig, blocks: RawBlock[], locale: string, defaultLocale: string, options?: QueryScopedOptions): Promise<Record<string, CmssyFormDefinition>>;
|
|
27
|
-
|
|
28
25
|
interface RelationContentEntry {
|
|
29
26
|
type: string;
|
|
30
27
|
content: Record<string, unknown>;
|
|
31
28
|
}
|
|
32
29
|
type BlockSchemaMap = Record<string, Record<string, FieldDefinition>>;
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Brings stored content in line with what the block's schema declares, for the
|
|
32
|
+
* field types whose stored shape differs from the authored one: a relation
|
|
33
|
+
* carries ids, a page selector always carries a list even when it holds one
|
|
34
|
+
* page.
|
|
35
|
+
*/
|
|
36
|
+
declare function normalizeBlockContent(content: Record<string, unknown>, schema: Record<string, FieldDefinition>, resolved?: Record<string, unknown>): void;
|
|
34
37
|
declare function resolveRelationContent(config: CmssyClientConfig, entries: RelationContentEntry[], schemas: BlockSchemaMap, locale?: string, requestOptions?: QueryScopedOptions): Promise<void>;
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Finds the forms a page needs by reading the blocks' schemas, so a form field
|
|
41
|
+
* is found under whatever key it was declared with. Reading a fixed `formId`
|
|
42
|
+
* key instead meant `fields.form()` under any other name silently resolved to
|
|
43
|
+
* nothing, and a form field on the style or advanced tab was invisible either
|
|
44
|
+
* way: the editor writes those into their own bucket, not into content.
|
|
45
|
+
*/
|
|
46
|
+
declare function collectFormIds(blocks: RawBlock[], schemas: BlockSchemaMap, locale: string, defaultLocale: string): string[];
|
|
47
|
+
declare function resolveForms(config: CmssyClientConfig, blocks: RawBlock[], schemas: BlockSchemaMap, locale: string, defaultLocale: string, options?: QueryScopedOptions): Promise<Record<string, CmssyFormDefinition>>;
|
|
48
|
+
|
|
36
49
|
declare function cmssySecretsMatch(a: string, b: string): Promise<boolean>;
|
|
37
50
|
|
|
38
51
|
interface CmssyDeliveryOperation {
|
|
@@ -41,4 +54,4 @@ interface CmssyDeliveryOperation {
|
|
|
41
54
|
}
|
|
42
55
|
declare const CMSSY_DELIVERY_OPERATIONS: readonly CmssyDeliveryOperation[];
|
|
43
56
|
|
|
44
|
-
export { type BlockSchemaMap, CMSSY_DELIVERY_OPERATIONS, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, type RelationContentEntry, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssySecretsMatch, collectFormIds, fetchSiteConfig, getBlockContentForLanguage,
|
|
57
|
+
export { type BlockSchemaMap, CMSSY_DELIVERY_OPERATIONS, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, type RelationContentEntry, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssySecretsMatch, collectFormIds, fetchSiteConfig, getBlockContentForLanguage, normalizeBlockContent, resolveForms, resolveRelationContent, resolveWorkspaceId };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, r as resolveApiUrl } from './content-client-BPpRfq0h.js';
|
|
2
|
-
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig,
|
|
2
|
+
import { CmssyFormDefinition, BuildBlockContextExtra, CmssyBlockContext, CmssyClientConfig, CmssySiteConfig, FieldDefinition, RawBlock } from '@cmssy/types';
|
|
3
3
|
export { CmssySiteLocales } from '@cmssy/types';
|
|
4
4
|
import { G as GraphqlRequestOptions } from './locale-DQZ80cew.js';
|
|
5
5
|
export { C as CMSSY_LOCALE_HEADER, a as localeForPath, b as localeForPathname, l as localizeHref, c as resolveSiteLocales, s as splitCmssyLocale, d as splitLocaleFromPath } from './locale-DQZ80cew.js';
|
|
@@ -22,17 +22,30 @@ declare function fetchSiteConfig(config: CmssyClientConfig, options?: GraphqlReq
|
|
|
22
22
|
declare function resolveWorkspaceId(config: CmssyClientConfig, options?: GraphqlRequestOptions): Promise<string>;
|
|
23
23
|
declare function clearWorkspaceIdCache(): void;
|
|
24
24
|
|
|
25
|
-
declare function collectFormIds(blocks: RawBlock[], locale: string, defaultLocale: string): string[];
|
|
26
|
-
declare function resolveForms(config: CmssyClientConfig, blocks: RawBlock[], locale: string, defaultLocale: string, options?: QueryScopedOptions): Promise<Record<string, CmssyFormDefinition>>;
|
|
27
|
-
|
|
28
25
|
interface RelationContentEntry {
|
|
29
26
|
type: string;
|
|
30
27
|
content: Record<string, unknown>;
|
|
31
28
|
}
|
|
32
29
|
type BlockSchemaMap = Record<string, Record<string, FieldDefinition>>;
|
|
33
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Brings stored content in line with what the block's schema declares, for the
|
|
32
|
+
* field types whose stored shape differs from the authored one: a relation
|
|
33
|
+
* carries ids, a page selector always carries a list even when it holds one
|
|
34
|
+
* page.
|
|
35
|
+
*/
|
|
36
|
+
declare function normalizeBlockContent(content: Record<string, unknown>, schema: Record<string, FieldDefinition>, resolved?: Record<string, unknown>): void;
|
|
34
37
|
declare function resolveRelationContent(config: CmssyClientConfig, entries: RelationContentEntry[], schemas: BlockSchemaMap, locale?: string, requestOptions?: QueryScopedOptions): Promise<void>;
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Finds the forms a page needs by reading the blocks' schemas, so a form field
|
|
41
|
+
* is found under whatever key it was declared with. Reading a fixed `formId`
|
|
42
|
+
* key instead meant `fields.form()` under any other name silently resolved to
|
|
43
|
+
* nothing, and a form field on the style or advanced tab was invisible either
|
|
44
|
+
* way: the editor writes those into their own bucket, not into content.
|
|
45
|
+
*/
|
|
46
|
+
declare function collectFormIds(blocks: RawBlock[], schemas: BlockSchemaMap, locale: string, defaultLocale: string): string[];
|
|
47
|
+
declare function resolveForms(config: CmssyClientConfig, blocks: RawBlock[], schemas: BlockSchemaMap, locale: string, defaultLocale: string, options?: QueryScopedOptions): Promise<Record<string, CmssyFormDefinition>>;
|
|
48
|
+
|
|
36
49
|
declare function cmssySecretsMatch(a: string, b: string): Promise<boolean>;
|
|
37
50
|
|
|
38
51
|
interface CmssyDeliveryOperation {
|
|
@@ -41,4 +54,4 @@ interface CmssyDeliveryOperation {
|
|
|
41
54
|
}
|
|
42
55
|
declare const CMSSY_DELIVERY_OPERATIONS: readonly CmssyDeliveryOperation[];
|
|
43
56
|
|
|
44
|
-
export { type BlockSchemaMap, CMSSY_DELIVERY_OPERATIONS, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, type RelationContentEntry, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssySecretsMatch, collectFormIds, fetchSiteConfig, getBlockContentForLanguage,
|
|
57
|
+
export { type BlockSchemaMap, CMSSY_DELIVERY_OPERATIONS, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, type RelationContentEntry, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssySecretsMatch, collectFormIds, fetchSiteConfig, getBlockContentForLanguage, normalizeBlockContent, resolveForms, resolveRelationContent, resolveWorkspaceId };
|
package/dist/internal.js
CHANGED
|
@@ -657,131 +657,7 @@ function createCmssyClient(input) {
|
|
|
657
657
|
return client;
|
|
658
658
|
}
|
|
659
659
|
|
|
660
|
-
// src/data/
|
|
661
|
-
function collectFormIds(blocks, locale, defaultLocale) {
|
|
662
|
-
const ids = /* @__PURE__ */ new Set();
|
|
663
|
-
for (const block of blocks) {
|
|
664
|
-
const content = getBlockContentForLanguage(
|
|
665
|
-
block.content,
|
|
666
|
-
locale,
|
|
667
|
-
defaultLocale
|
|
668
|
-
);
|
|
669
|
-
const formId = content.formId;
|
|
670
|
-
if (typeof formId === "string" && formId.trim()) ids.add(formId);
|
|
671
|
-
}
|
|
672
|
-
return [...ids];
|
|
673
|
-
}
|
|
674
|
-
async function resolveForms(config, blocks, locale, defaultLocale, options) {
|
|
675
|
-
const ids = collectFormIds(blocks, locale, defaultLocale);
|
|
676
|
-
if (ids.length === 0) return {};
|
|
677
|
-
const client = createCmssyClient(config);
|
|
678
|
-
const entries = await Promise.all(
|
|
679
|
-
ids.map(async (id) => {
|
|
680
|
-
try {
|
|
681
|
-
const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
|
|
682
|
-
return [id, data.public.form.get];
|
|
683
|
-
} catch (err) {
|
|
684
|
-
if (typeof console !== "undefined") {
|
|
685
|
-
console.warn(`[cmssy] failed to resolve form ${id}`, err);
|
|
686
|
-
}
|
|
687
|
-
return [id, null];
|
|
688
|
-
}
|
|
689
|
-
})
|
|
690
|
-
);
|
|
691
|
-
const forms = {};
|
|
692
|
-
for (const [id, def] of entries) {
|
|
693
|
-
if (def) forms[id] = def;
|
|
694
|
-
}
|
|
695
|
-
return forms;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// src/data/site-locales.ts
|
|
699
|
-
var TTL_MS = 6e4;
|
|
700
|
-
var FAILURE_TTL_MS = 5e3;
|
|
701
|
-
var MAX_ENTRIES = 64;
|
|
702
|
-
var cache = /* @__PURE__ */ new Map();
|
|
703
|
-
function localesFromSiteConfig(siteConfig) {
|
|
704
|
-
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
705
|
-
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
706
|
-
return {
|
|
707
|
-
defaultLocale,
|
|
708
|
-
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
async function loadSiteLocales(config, options) {
|
|
712
|
-
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
713
|
-
const cached = cache.get(key);
|
|
714
|
-
if (cached && cached.expires > Date.now()) return cached.value;
|
|
715
|
-
cache.delete(key);
|
|
716
|
-
let value;
|
|
717
|
-
try {
|
|
718
|
-
const data = await graphqlRequest(
|
|
719
|
-
config,
|
|
720
|
-
SITE_CONFIG_QUERY,
|
|
721
|
-
{ workspaceSlug: config.workspaceSlug },
|
|
722
|
-
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
723
|
-
"site config"
|
|
724
|
-
);
|
|
725
|
-
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
726
|
-
} catch {
|
|
727
|
-
value = null;
|
|
728
|
-
}
|
|
729
|
-
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
730
|
-
cache.set(key, {
|
|
731
|
-
value,
|
|
732
|
-
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
733
|
-
});
|
|
734
|
-
return value;
|
|
735
|
-
}
|
|
736
|
-
async function resolveSiteLocales(config, options) {
|
|
737
|
-
return await loadSiteLocales(config, options) ?? {
|
|
738
|
-
defaultLocale: "en",
|
|
739
|
-
locales: ["en"]
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function splitLocaleFromPath(path, siteLocales) {
|
|
743
|
-
const first = path?.[0];
|
|
744
|
-
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
745
|
-
return { locale: first, path: path.slice(1) };
|
|
746
|
-
}
|
|
747
|
-
return { locale: siteLocales.defaultLocale, path };
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// src/data/localize-href.ts
|
|
751
|
-
var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
752
|
-
function isExternalHref(href) {
|
|
753
|
-
const value = href.trim();
|
|
754
|
-
if (!value) return true;
|
|
755
|
-
if (value.startsWith("#")) return true;
|
|
756
|
-
return PROTOCOL_OR_RELATIVE.test(value);
|
|
757
|
-
}
|
|
758
|
-
function stripLeadingLocale(path, locale) {
|
|
759
|
-
const segments = path.split("/");
|
|
760
|
-
const first = segments[1];
|
|
761
|
-
if (first && first !== locale.default && locale.enabled.includes(first)) {
|
|
762
|
-
segments.splice(1, 1);
|
|
763
|
-
const rest = segments.join("/");
|
|
764
|
-
return rest === "" ? "/" : rest;
|
|
765
|
-
}
|
|
766
|
-
return path;
|
|
767
|
-
}
|
|
768
|
-
function addLocalePrefix(path, target, locale) {
|
|
769
|
-
if (target === locale.default) return path;
|
|
770
|
-
if (path === "/") return `/${target}`;
|
|
771
|
-
return `/${target}${path}`;
|
|
772
|
-
}
|
|
773
|
-
function localizeHref(href, locale) {
|
|
774
|
-
const value = href.trim();
|
|
775
|
-
if (isExternalHref(value)) return href;
|
|
776
|
-
const boundary = value.search(/[?#]/);
|
|
777
|
-
const path = boundary === -1 ? value : value.slice(0, boundary);
|
|
778
|
-
const suffix = boundary === -1 ? "" : value.slice(boundary);
|
|
779
|
-
if (!path.startsWith("/")) return href;
|
|
780
|
-
const bare = stripLeadingLocale(path, locale);
|
|
781
|
-
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// src/data/relation-resolver.ts
|
|
660
|
+
// src/data/block-content.ts
|
|
785
661
|
var RECORDS_BY_IDS_QUERY = `query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {
|
|
786
662
|
public {
|
|
787
663
|
model {
|
|
@@ -803,17 +679,54 @@ function storedIds(value) {
|
|
|
803
679
|
}
|
|
804
680
|
return [];
|
|
805
681
|
}
|
|
682
|
+
function isRecord(value) {
|
|
683
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
684
|
+
}
|
|
685
|
+
var BLOCK_BUCKETS = ["content", "style", "advanced"];
|
|
686
|
+
function bucketOf(field) {
|
|
687
|
+
return field.tab === "style" || field.tab === "advanced" ? field.tab : "content";
|
|
688
|
+
}
|
|
689
|
+
function walkBlockFields(values, schema, visit, options = {}, path = []) {
|
|
690
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
691
|
+
if (options.bucket && path.length === 0 && bucketOf(field) !== options.bucket) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (field.type === "repeater") {
|
|
695
|
+
const itemSchema = field.itemSchema;
|
|
696
|
+
const stored = values[key];
|
|
697
|
+
if (!itemSchema || !Array.isArray(stored)) continue;
|
|
698
|
+
const rows = options.copyRows ? stored.map((row) => isRecord(row) ? { ...row } : row) : stored;
|
|
699
|
+
if (options.copyRows) values[key] = rows;
|
|
700
|
+
rows.forEach((row, index) => {
|
|
701
|
+
if (isRecord(row)) {
|
|
702
|
+
walkBlockFields(row, itemSchema, visit, options, [
|
|
703
|
+
...path,
|
|
704
|
+
key,
|
|
705
|
+
index
|
|
706
|
+
]);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
visit(values, key, field, path);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
806
714
|
function collectRefs(entries, schemas) {
|
|
807
715
|
const refs = [];
|
|
808
716
|
for (const entry of entries) {
|
|
809
717
|
const schema = schemas[entry.type];
|
|
810
718
|
if (!schema) continue;
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
719
|
+
walkBlockFields(
|
|
720
|
+
entry.content,
|
|
721
|
+
schema,
|
|
722
|
+
(holder, key, field) => {
|
|
723
|
+
if (field.type !== "relation") return;
|
|
724
|
+
const model = relationModel(field);
|
|
725
|
+
if (!model) return;
|
|
726
|
+
refs.push({ content: holder, key, field, model });
|
|
727
|
+
},
|
|
728
|
+
{ copyRows: true, bucket: "content" }
|
|
729
|
+
);
|
|
817
730
|
}
|
|
818
731
|
return refs;
|
|
819
732
|
}
|
|
@@ -823,31 +736,70 @@ function isResolvedRecord(value) {
|
|
|
823
736
|
function isListShaped(field) {
|
|
824
737
|
return field.relationMode === "all" || field.relationType === "hasMany" || field.multiple === true;
|
|
825
738
|
}
|
|
826
|
-
function
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
739
|
+
function toPageRef(value) {
|
|
740
|
+
if (typeof value === "string") {
|
|
741
|
+
return value ? { slug: value, displayName: {} } : null;
|
|
742
|
+
}
|
|
743
|
+
if (isRecord(value) && typeof value.slug === "string" && value.slug) {
|
|
744
|
+
return value;
|
|
745
|
+
}
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
function toPageRefs(value) {
|
|
749
|
+
const items = Array.isArray(value) ? value : [value];
|
|
750
|
+
return items.map(toPageRef).filter((ref) => ref !== null);
|
|
751
|
+
}
|
|
752
|
+
function fallbackHolder(resolved, path) {
|
|
753
|
+
let holder = resolved;
|
|
754
|
+
for (const step of path) {
|
|
755
|
+
if (holder === void 0 || holder === null) return void 0;
|
|
756
|
+
holder = holder[step];
|
|
757
|
+
}
|
|
758
|
+
return isRecord(holder) ? holder : void 0;
|
|
759
|
+
}
|
|
760
|
+
function normalizeBlockContent(content, schema, resolved) {
|
|
761
|
+
walkBlockFields(
|
|
762
|
+
content,
|
|
763
|
+
schema,
|
|
764
|
+
(holder, key, field, path) => {
|
|
765
|
+
if (field.type === "pageSelector") {
|
|
766
|
+
if (!(key in holder)) return;
|
|
767
|
+
const refs = toPageRefs(holder[key]);
|
|
768
|
+
if (field.multiple === false) {
|
|
769
|
+
if (refs[0]) holder[key] = refs[0];
|
|
770
|
+
else delete holder[key];
|
|
771
|
+
} else {
|
|
772
|
+
holder[key] = refs;
|
|
837
773
|
}
|
|
774
|
+
return;
|
|
838
775
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
776
|
+
if (field.type !== "relation") return;
|
|
777
|
+
const value = holder[key];
|
|
778
|
+
const fallback = fallbackHolder(resolved, path)?.[key];
|
|
779
|
+
if (isListShaped(field)) {
|
|
780
|
+
if (Array.isArray(value)) {
|
|
781
|
+
const records = value.filter(isResolvedRecord);
|
|
782
|
+
if (records.length > 0 || value.length === 0) {
|
|
783
|
+
holder[key] = records;
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
holder[key] = Array.isArray(fallback) ? fallback.filter(isResolvedRecord) : [];
|
|
788
|
+
} else if (!isResolvedRecord(value)) {
|
|
789
|
+
if (value != null && value !== "" && isResolvedRecord(fallback)) {
|
|
790
|
+
holder[key] = fallback;
|
|
791
|
+
} else {
|
|
792
|
+
delete holder[key];
|
|
793
|
+
}
|
|
845
794
|
}
|
|
846
|
-
}
|
|
847
|
-
|
|
795
|
+
},
|
|
796
|
+
{ copyRows: true, bucket: "content" }
|
|
797
|
+
);
|
|
848
798
|
}
|
|
849
799
|
function collectionKey(ref) {
|
|
850
|
-
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join(
|
|
800
|
+
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join(
|
|
801
|
+
"\0"
|
|
802
|
+
);
|
|
851
803
|
}
|
|
852
804
|
async function resolveRelationContent(config, entries, schemas, locale, requestOptions = {}) {
|
|
853
805
|
const refs = collectRefs(entries, schemas);
|
|
@@ -943,6 +895,142 @@ async function fetchCollections(client, collections, locale, requestOptions = {}
|
|
|
943
895
|
return byKey;
|
|
944
896
|
}
|
|
945
897
|
|
|
898
|
+
// src/data/resolve-forms.ts
|
|
899
|
+
function collectFormIds(blocks, schemas, locale, defaultLocale) {
|
|
900
|
+
const ids = /* @__PURE__ */ new Set();
|
|
901
|
+
for (const block of blocks) {
|
|
902
|
+
const schema = schemas[block.type];
|
|
903
|
+
if (!schema) continue;
|
|
904
|
+
const values = {
|
|
905
|
+
content: getBlockContentForLanguage(block.content, locale, defaultLocale),
|
|
906
|
+
style: asBucket(block.style),
|
|
907
|
+
advanced: asBucket(block.advanced)
|
|
908
|
+
};
|
|
909
|
+
for (const bucket of BLOCK_BUCKETS) {
|
|
910
|
+
walkBlockFields(
|
|
911
|
+
values[bucket],
|
|
912
|
+
schema,
|
|
913
|
+
(holder, key, field) => {
|
|
914
|
+
if (field.type !== "form") return;
|
|
915
|
+
const id = holder[key];
|
|
916
|
+
if (typeof id === "string" && id.trim()) ids.add(id);
|
|
917
|
+
},
|
|
918
|
+
{ bucket }
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return [...ids];
|
|
923
|
+
}
|
|
924
|
+
async function resolveForms(config, blocks, schemas, locale, defaultLocale, options) {
|
|
925
|
+
const ids = collectFormIds(blocks, schemas, locale, defaultLocale);
|
|
926
|
+
if (ids.length === 0) return {};
|
|
927
|
+
const client = createCmssyClient(config);
|
|
928
|
+
const entries = await Promise.all(
|
|
929
|
+
ids.map(async (id) => {
|
|
930
|
+
try {
|
|
931
|
+
const data = await client.queryScoped(FORM_QUERY, { formId: id }, options);
|
|
932
|
+
return [id, data.public.form.get];
|
|
933
|
+
} catch (err) {
|
|
934
|
+
if (typeof console !== "undefined") {
|
|
935
|
+
console.warn(`[cmssy] failed to resolve form ${id}`, err);
|
|
936
|
+
}
|
|
937
|
+
return [id, null];
|
|
938
|
+
}
|
|
939
|
+
})
|
|
940
|
+
);
|
|
941
|
+
const forms = {};
|
|
942
|
+
for (const [id, def] of entries) {
|
|
943
|
+
if (def) forms[id] = def;
|
|
944
|
+
}
|
|
945
|
+
return forms;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/data/site-locales.ts
|
|
949
|
+
var TTL_MS = 6e4;
|
|
950
|
+
var FAILURE_TTL_MS = 5e3;
|
|
951
|
+
var MAX_ENTRIES = 64;
|
|
952
|
+
var cache = /* @__PURE__ */ new Map();
|
|
953
|
+
function localesFromSiteConfig(siteConfig) {
|
|
954
|
+
const defaultLocale = siteConfig?.defaultLanguage || "en";
|
|
955
|
+
const enabled = siteConfig?.enabledLanguages ?? [];
|
|
956
|
+
return {
|
|
957
|
+
defaultLocale,
|
|
958
|
+
locales: enabled.length > 0 ? enabled : [defaultLocale]
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
async function loadSiteLocales(config, options) {
|
|
962
|
+
const key = `${resolveApiUrl(config.apiUrl)}::${config.org}::${config.workspaceSlug}`;
|
|
963
|
+
const cached = cache.get(key);
|
|
964
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
965
|
+
cache.delete(key);
|
|
966
|
+
let value;
|
|
967
|
+
try {
|
|
968
|
+
const data = await graphqlRequest(
|
|
969
|
+
config,
|
|
970
|
+
SITE_CONFIG_QUERY,
|
|
971
|
+
{ workspaceSlug: config.workspaceSlug },
|
|
972
|
+
{ ...options, public: true, retry: options?.retry ?? {} },
|
|
973
|
+
"site config"
|
|
974
|
+
);
|
|
975
|
+
value = localesFromSiteConfig(data.public?.siteConfig ?? null);
|
|
976
|
+
} catch {
|
|
977
|
+
value = null;
|
|
978
|
+
}
|
|
979
|
+
if (cache.size >= MAX_ENTRIES) cache.clear();
|
|
980
|
+
cache.set(key, {
|
|
981
|
+
value,
|
|
982
|
+
expires: Date.now() + (value ? TTL_MS : FAILURE_TTL_MS)
|
|
983
|
+
});
|
|
984
|
+
return value;
|
|
985
|
+
}
|
|
986
|
+
async function resolveSiteLocales(config, options) {
|
|
987
|
+
return await loadSiteLocales(config, options) ?? {
|
|
988
|
+
defaultLocale: "en",
|
|
989
|
+
locales: ["en"]
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function splitLocaleFromPath(path, siteLocales) {
|
|
993
|
+
const first = path?.[0];
|
|
994
|
+
if (first && first !== siteLocales.defaultLocale && siteLocales.locales.includes(first)) {
|
|
995
|
+
return { locale: first, path: path.slice(1) };
|
|
996
|
+
}
|
|
997
|
+
return { locale: siteLocales.defaultLocale, path };
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// src/data/localize-href.ts
|
|
1001
|
+
var PROTOCOL_OR_RELATIVE = /^([a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
1002
|
+
function isExternalHref(href) {
|
|
1003
|
+
const value = href.trim();
|
|
1004
|
+
if (!value) return true;
|
|
1005
|
+
if (value.startsWith("#")) return true;
|
|
1006
|
+
return PROTOCOL_OR_RELATIVE.test(value);
|
|
1007
|
+
}
|
|
1008
|
+
function stripLeadingLocale(path, locale) {
|
|
1009
|
+
const segments = path.split("/");
|
|
1010
|
+
const first = segments[1];
|
|
1011
|
+
if (first && first !== locale.default && locale.enabled.includes(first)) {
|
|
1012
|
+
segments.splice(1, 1);
|
|
1013
|
+
const rest = segments.join("/");
|
|
1014
|
+
return rest === "" ? "/" : rest;
|
|
1015
|
+
}
|
|
1016
|
+
return path;
|
|
1017
|
+
}
|
|
1018
|
+
function addLocalePrefix(path, target, locale) {
|
|
1019
|
+
if (target === locale.default) return path;
|
|
1020
|
+
if (path === "/") return `/${target}`;
|
|
1021
|
+
return `/${target}${path}`;
|
|
1022
|
+
}
|
|
1023
|
+
function localizeHref(href, locale) {
|
|
1024
|
+
const value = href.trim();
|
|
1025
|
+
if (isExternalHref(value)) return href;
|
|
1026
|
+
const boundary = value.search(/[?#]/);
|
|
1027
|
+
const path = boundary === -1 ? value : value.slice(0, boundary);
|
|
1028
|
+
const suffix = boundary === -1 ? "" : value.slice(boundary);
|
|
1029
|
+
if (!path.startsWith("/")) return href;
|
|
1030
|
+
const bare = stripLeadingLocale(path, locale);
|
|
1031
|
+
return `${addLocalePrefix(bare, locale.current, locale)}${suffix}`;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
946
1034
|
// src/locale.ts
|
|
947
1035
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
948
1036
|
async function localeForPathname(config, pathname) {
|
|
@@ -1145,4 +1233,4 @@ var CMSSY_DELIVERY_OPERATIONS = [
|
|
|
1145
1233
|
}
|
|
1146
1234
|
];
|
|
1147
1235
|
|
|
1148
|
-
export { CMSSY_DELIVERY_OPERATIONS, CMSSY_LOCALE_HEADER, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, getBlockContentForLanguage, isDevelopment, localeForPath, localeForPathname, localizeHref, localizedPath,
|
|
1236
|
+
export { CMSSY_DELIVERY_OPERATIONS, CMSSY_LOCALE_HEADER, FORM_QUERY, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, asBucket, buildBlockContext, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, fetchLayouts, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchSiteConfig, getBlockContentForLanguage, isDevelopment, localeForPath, localeForPathname, localizeHref, localizedPath, normalizeBlockContent, resolveApiUrl, resolveForms, resolveInitialTarget, resolveRelationContent, resolveSiteLocales, resolveWorkspaceId, splitCmssyLocale, splitLocaleFromPath, toCspOrigin };
|