@cmssy/core 9.5.0 → 9.6.1
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 +163 -1
- package/dist/index.d.cts +49 -2
- package/dist/index.d.ts +49 -2
- package/dist/index.js +161 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -747,6 +747,156 @@ function localizeHtmlLinks(html, locale) {
|
|
|
747
747
|
);
|
|
748
748
|
}
|
|
749
749
|
|
|
750
|
+
// src/data/relation-resolver.ts
|
|
751
|
+
var RECORDS_BY_IDS_QUERY = `query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {
|
|
752
|
+
public {
|
|
753
|
+
model {
|
|
754
|
+
recordsByIds(workspaceId: $workspaceId, ids: $ids, locale: $locale) {
|
|
755
|
+
id modelId data status createdAt updatedAt
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}`;
|
|
760
|
+
var BY_IDS_CHUNK = 50;
|
|
761
|
+
var COLLECTION_DEFAULT_LIMIT = 50;
|
|
762
|
+
function relationModel(field) {
|
|
763
|
+
return field.relationTo?.startsWith("model:") ? field.relationTo.slice("model:".length) : void 0;
|
|
764
|
+
}
|
|
765
|
+
function storedIds(value) {
|
|
766
|
+
if (typeof value === "string" && value) return [value];
|
|
767
|
+
if (Array.isArray(value)) {
|
|
768
|
+
return value.filter((id) => typeof id === "string" && !!id);
|
|
769
|
+
}
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
772
|
+
function collectRefs(entries, schemas) {
|
|
773
|
+
const refs = [];
|
|
774
|
+
for (const entry of entries) {
|
|
775
|
+
const schema = schemas[entry.type];
|
|
776
|
+
if (!schema) continue;
|
|
777
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
778
|
+
if (field.type !== "relation") continue;
|
|
779
|
+
const model = relationModel(field);
|
|
780
|
+
if (!model) continue;
|
|
781
|
+
refs.push({ content: entry.content, key, field, model });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return refs;
|
|
785
|
+
}
|
|
786
|
+
function isResolvedRecord(value) {
|
|
787
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.id === "string" && typeof value.data === "object";
|
|
788
|
+
}
|
|
789
|
+
function isListShaped(field) {
|
|
790
|
+
return field.relationMode === "all" || field.relationType === "hasMany" || field.multiple === true;
|
|
791
|
+
}
|
|
792
|
+
function normalizeRelationContent(content, schema) {
|
|
793
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
794
|
+
if (field.type !== "relation") continue;
|
|
795
|
+
const value = content[key];
|
|
796
|
+
if (isListShaped(field)) {
|
|
797
|
+
content[key] = Array.isArray(value) ? value.filter(isResolvedRecord) : [];
|
|
798
|
+
} else if (!isResolvedRecord(value)) {
|
|
799
|
+
delete content[key];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function collectionKey(ref) {
|
|
804
|
+
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join("\0");
|
|
805
|
+
}
|
|
806
|
+
async function resolveRelationContent(config, entries, schemas, locale, requestOptions = {}) {
|
|
807
|
+
const refs = collectRefs(entries, schemas);
|
|
808
|
+
if (refs.length === 0) return;
|
|
809
|
+
const client = createCmssyClient(config);
|
|
810
|
+
const pickedIds = /* @__PURE__ */ new Set();
|
|
811
|
+
const collections = /* @__PURE__ */ new Map();
|
|
812
|
+
for (const ref of refs) {
|
|
813
|
+
if (ref.field.relationMode === "all") {
|
|
814
|
+
collections.set(collectionKey(ref), {
|
|
815
|
+
model: ref.model,
|
|
816
|
+
sort: ref.field.sort,
|
|
817
|
+
limit: ref.field.limit
|
|
818
|
+
});
|
|
819
|
+
} else {
|
|
820
|
+
for (const id of storedIds(ref.content[ref.key])) pickedIds.add(id);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
let recordsById;
|
|
824
|
+
let collectionItems;
|
|
825
|
+
try {
|
|
826
|
+
[recordsById, collectionItems] = await Promise.all([
|
|
827
|
+
fetchPickedRecords(client, [...pickedIds], locale, requestOptions),
|
|
828
|
+
fetchCollections(client, collections, locale, requestOptions)
|
|
829
|
+
]);
|
|
830
|
+
} catch (err) {
|
|
831
|
+
if (typeof console !== "undefined") {
|
|
832
|
+
console.error("[cmssy] relation resolution failed", err);
|
|
833
|
+
}
|
|
834
|
+
for (const ref of refs) {
|
|
835
|
+
if (ref.field.relationMode === "all" || Array.isArray(ref.content[ref.key])) {
|
|
836
|
+
ref.content[ref.key] = [];
|
|
837
|
+
} else {
|
|
838
|
+
delete ref.content[ref.key];
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
for (const ref of refs) {
|
|
844
|
+
if (ref.field.relationMode === "all") {
|
|
845
|
+
ref.content[ref.key] = collectionItems.get(collectionKey(ref)) ?? [];
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
const value = ref.content[ref.key];
|
|
849
|
+
if (Array.isArray(value)) {
|
|
850
|
+
ref.content[ref.key] = storedIds(value).map((id) => recordsById.get(id)).filter((record) => !!record);
|
|
851
|
+
} else if (typeof value === "string" && value) {
|
|
852
|
+
const record = recordsById.get(value);
|
|
853
|
+
if (record) ref.content[ref.key] = record;
|
|
854
|
+
else delete ref.content[ref.key];
|
|
855
|
+
} else {
|
|
856
|
+
delete ref.content[ref.key];
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
async function fetchPickedRecords(client, ids, locale, requestOptions = {}) {
|
|
861
|
+
const byId = /* @__PURE__ */ new Map();
|
|
862
|
+
const chunks = [];
|
|
863
|
+
for (let i = 0; i < ids.length; i += BY_IDS_CHUNK) {
|
|
864
|
+
chunks.push(ids.slice(i, i + BY_IDS_CHUNK));
|
|
865
|
+
}
|
|
866
|
+
await Promise.all(
|
|
867
|
+
chunks.map(async (chunk) => {
|
|
868
|
+
const result = await client.queryScoped(
|
|
869
|
+
RECORDS_BY_IDS_QUERY,
|
|
870
|
+
{ ids: chunk, locale: locale ?? null },
|
|
871
|
+
requestOptions
|
|
872
|
+
);
|
|
873
|
+
for (const record of result.public.model.recordsByIds) {
|
|
874
|
+
byId.set(record.id, record);
|
|
875
|
+
}
|
|
876
|
+
})
|
|
877
|
+
);
|
|
878
|
+
return byId;
|
|
879
|
+
}
|
|
880
|
+
async function fetchCollections(client, collections, locale, requestOptions = {}) {
|
|
881
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
882
|
+
await Promise.all(
|
|
883
|
+
[...collections.entries()].map(async ([key, { model, sort, limit }]) => {
|
|
884
|
+
const result = await client.queryScoped(
|
|
885
|
+
MODEL_RECORDS_QUERY,
|
|
886
|
+
{
|
|
887
|
+
modelSlug: model,
|
|
888
|
+
sort: sort ?? null,
|
|
889
|
+
limit: limit ?? COLLECTION_DEFAULT_LIMIT,
|
|
890
|
+
locale: locale ?? null
|
|
891
|
+
},
|
|
892
|
+
requestOptions
|
|
893
|
+
);
|
|
894
|
+
byKey.set(key, result.public.model.records.items);
|
|
895
|
+
})
|
|
896
|
+
);
|
|
897
|
+
return byKey;
|
|
898
|
+
}
|
|
899
|
+
|
|
750
900
|
// src/locale.ts
|
|
751
901
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
752
902
|
async function localeForPathname(config, pathname) {
|
|
@@ -810,7 +960,16 @@ var fields = {
|
|
|
810
960
|
radio: choice("radio"),
|
|
811
961
|
multiselect: (opts) => build("multiselect", opts),
|
|
812
962
|
media: (opts = {}) => build("media", opts),
|
|
813
|
-
repeater: (opts) => build("repeater", opts)
|
|
963
|
+
repeater: (opts) => build("repeater", opts),
|
|
964
|
+
relation: (opts) => {
|
|
965
|
+
const { model, mode, ...rest } = opts;
|
|
966
|
+
return build("relation", {
|
|
967
|
+
...rest,
|
|
968
|
+
relationTo: `model:${model}`,
|
|
969
|
+
relationType: mode === "all" || opts.multiple ? "hasMany" : "hasOne",
|
|
970
|
+
relationMode: mode ?? "picked"
|
|
971
|
+
});
|
|
972
|
+
}
|
|
814
973
|
};
|
|
815
974
|
|
|
816
975
|
// src/bridge/protocol.ts
|
|
@@ -1747,6 +1906,7 @@ exports.MODEL_DEFINITIONS_QUERY = MODEL_DEFINITIONS_QUERY;
|
|
|
1747
1906
|
exports.MODEL_RECORDS_QUERY = MODEL_RECORDS_QUERY;
|
|
1748
1907
|
exports.PRODUCTS_QUERY = PRODUCTS_QUERY;
|
|
1749
1908
|
exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
|
|
1909
|
+
exports.RECORDS_BY_IDS_QUERY = RECORDS_BY_IDS_QUERY;
|
|
1750
1910
|
exports.SESSION_MAX_AGE_SECONDS = SESSION_MAX_AGE_SECONDS;
|
|
1751
1911
|
exports.SITE_CONFIG_QUERY = SITE_CONFIG_QUERY;
|
|
1752
1912
|
exports.SUBMIT_FORM_MUTATION = SUBMIT_FORM_MUTATION;
|
|
@@ -1811,6 +1971,7 @@ exports.localizeHref = localizeHref;
|
|
|
1811
1971
|
exports.localizeHtmlLinks = localizeHtmlLinks;
|
|
1812
1972
|
exports.localizedPath = localizedPath;
|
|
1813
1973
|
exports.normalizeOrigin = normalizeOrigin;
|
|
1974
|
+
exports.normalizeRelationContent = normalizeRelationContent;
|
|
1814
1975
|
exports.normalizeSlug = normalizeSlug;
|
|
1815
1976
|
exports.openSession = openSession;
|
|
1816
1977
|
exports.parseEditorMessage = parseEditorMessage;
|
|
@@ -1820,6 +1981,7 @@ exports.resolveEditorOrigin = resolveEditorOrigin;
|
|
|
1820
1981
|
exports.resolveForms = resolveForms;
|
|
1821
1982
|
exports.resolveInitialTarget = resolveInitialTarget;
|
|
1822
1983
|
exports.resolvePublicUrl = resolvePublicUrl;
|
|
1984
|
+
exports.resolveRelationContent = resolveRelationContent;
|
|
1823
1985
|
exports.resolveSiteLocales = resolveSiteLocales;
|
|
1824
1986
|
exports.resolveWorkspaceId = resolveWorkspaceId;
|
|
1825
1987
|
exports.sealSession = sealSession;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _cmssy_types from '@cmssy/types';
|
|
2
|
-
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
2
|
+
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldDefinition, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
3
3
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyAddress, CmssyAuthConfig, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductPage, CmssyProductVariant, CmssyRecordList, CmssySessionPayload, CmssySessionUser, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyStockState, CmssyTaxSummaryLine, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, MyOrdersResult, RawBlock, RawLayoutBlock, SessionCookieOptions, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup } from '@cmssy/types';
|
|
4
4
|
import { F as FetchLike, R as RetryPolicy } from './content-client-D0EdiqbQ.cjs';
|
|
5
5
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, a as FetchLikeResponse, b as FetchPageOptions, f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-D0EdiqbQ.cjs';
|
|
@@ -158,6 +158,28 @@ declare function buildLocaleSwitchHref(target: string, pathname: string, locale:
|
|
|
158
158
|
*/
|
|
159
159
|
declare function localizeHtmlLinks(html: string, locale: CmssyLocaleContext): string;
|
|
160
160
|
|
|
161
|
+
declare const RECORDS_BY_IDS_QUERY = "query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {\n public {\n model {\n recordsByIds(workspaceId: $workspaceId, ids: $ids, locale: $locale) {\n id modelId data status createdAt updatedAt\n }\n }\n }\n}";
|
|
162
|
+
interface RelationContentEntry {
|
|
163
|
+
type: string;
|
|
164
|
+
content: Record<string, unknown>;
|
|
165
|
+
}
|
|
166
|
+
type BlockSchemaMap = Record<string, Record<string, FieldDefinition>>;
|
|
167
|
+
/**
|
|
168
|
+
* The editor canvas renders stored content without the server-side resolve, so
|
|
169
|
+
* a relation field there holds raw ids - or the "" a freshly inserted block is
|
|
170
|
+
* seeded with. A component is typed against resolved records, so anything that
|
|
171
|
+
* is not one is coerced to the safe empty shape instead of reaching it.
|
|
172
|
+
*/
|
|
173
|
+
declare function normalizeRelationContent(content: Record<string, unknown>, schema: Record<string, FieldDefinition>): void;
|
|
174
|
+
/**
|
|
175
|
+
* Resolves every `fields.relation` value on the given block contents in place:
|
|
176
|
+
* stored record ids become full records (one batched read for the whole page),
|
|
177
|
+
* and a `mode: "all"` field becomes the model's record list. A dangling id and
|
|
178
|
+
* a failed fetch both degrade to "no value" - a relation must never break a
|
|
179
|
+
* render.
|
|
180
|
+
*/
|
|
181
|
+
declare function resolveRelationContent(config: CmssyClientConfig, entries: RelationContentEntry[], schemas: BlockSchemaMap, locale?: string, requestOptions?: QueryScopedOptions): Promise<void>;
|
|
182
|
+
|
|
161
183
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
162
184
|
declare function localeForPathname(config: CmssyClientConfig, pathname: string): Promise<string>;
|
|
163
185
|
declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | undefined): Promise<{
|
|
@@ -182,6 +204,30 @@ type MediaValue<O> = O extends {
|
|
|
182
204
|
type RepeaterValue<O> = O extends {
|
|
183
205
|
itemSchema: infer Schema extends BlockPropsSchema;
|
|
184
206
|
} ? InferBlockContent<Schema>[] : Record<string, unknown>[];
|
|
207
|
+
/**
|
|
208
|
+
* The value a relation field holds AFTER server-side resolution: the SDK
|
|
209
|
+
* replaces the stored record id(s) with the records themselves before the
|
|
210
|
+
* component renders. `mode: "all"` and `multiple: true` yield a list; the
|
|
211
|
+
* default is a single record - `undefined` when the reference dangles, which
|
|
212
|
+
* no `required` flag can rule out.
|
|
213
|
+
*/
|
|
214
|
+
type RelationValue<O> = O extends {
|
|
215
|
+
mode: "all";
|
|
216
|
+
} | {
|
|
217
|
+
multiple: true;
|
|
218
|
+
} ? CmssyModelRecord[] : CmssyModelRecord | undefined;
|
|
219
|
+
interface RelationFieldOptions extends Omit<FieldOptions, "options" | "itemSchema"> {
|
|
220
|
+
/** Slug of the model the field points at. */
|
|
221
|
+
model: string;
|
|
222
|
+
/**
|
|
223
|
+
* "all" binds the field to every record of the model (no editor picking;
|
|
224
|
+
* `sort`/`limit` shape the list). Omit it for editor-picked record id(s).
|
|
225
|
+
*/
|
|
226
|
+
mode?: RelationMode;
|
|
227
|
+
multiple?: boolean;
|
|
228
|
+
sort?: string;
|
|
229
|
+
limit?: number;
|
|
230
|
+
}
|
|
185
231
|
declare const fields: {
|
|
186
232
|
text: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
187
233
|
textarea: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
@@ -204,6 +250,7 @@ declare const fields: {
|
|
|
204
250
|
multiselect: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>[], Declared<O>>;
|
|
205
251
|
media: <const O extends FieldOptions>(opts?: O) => TypedField<MediaValue<O>, Declared<O>>;
|
|
206
252
|
repeater: <const O extends FieldOptions>(opts: O) => TypedField<RepeaterValue<O>, Declared<O>>;
|
|
253
|
+
relation: <const O extends RelationFieldOptions>(opts: O) => TypedField<RelationValue<O>, Declared<O>>;
|
|
207
254
|
};
|
|
208
255
|
|
|
209
256
|
declare const PROTOCOL_VERSION = 2;
|
|
@@ -447,4 +494,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
447
494
|
*/
|
|
448
495
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
449
496
|
|
|
450
|
-
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, 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, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
|
497
|
+
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BlockSchemaMap, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, RECORDS_BY_IDS_QUERY, type ReadyMessage, type RelationContentEntry, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, 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, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, normalizeRelationContent, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveRelationContent, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _cmssy_types from '@cmssy/types';
|
|
2
|
-
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
2
|
+
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldDefinition, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, RelationMode, CmssyModelRecord, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
3
3
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyAddress, CmssyAuthConfig, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductPage, CmssyProductVariant, CmssyRecordList, CmssySessionPayload, CmssySessionUser, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyStockState, CmssyTaxSummaryLine, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, MyOrdersResult, RawBlock, RawLayoutBlock, SessionCookieOptions, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup } from '@cmssy/types';
|
|
4
4
|
import { F as FetchLike, R as RetryPolicy } from './content-client-D0EdiqbQ.js';
|
|
5
5
|
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, a as FetchLikeResponse, b as FetchPageOptions, f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-D0EdiqbQ.js';
|
|
@@ -158,6 +158,28 @@ declare function buildLocaleSwitchHref(target: string, pathname: string, locale:
|
|
|
158
158
|
*/
|
|
159
159
|
declare function localizeHtmlLinks(html: string, locale: CmssyLocaleContext): string;
|
|
160
160
|
|
|
161
|
+
declare const RECORDS_BY_IDS_QUERY = "query PublicRecordsByIds($workspaceId: String!, $ids: [String!]!, $locale: String) {\n public {\n model {\n recordsByIds(workspaceId: $workspaceId, ids: $ids, locale: $locale) {\n id modelId data status createdAt updatedAt\n }\n }\n }\n}";
|
|
162
|
+
interface RelationContentEntry {
|
|
163
|
+
type: string;
|
|
164
|
+
content: Record<string, unknown>;
|
|
165
|
+
}
|
|
166
|
+
type BlockSchemaMap = Record<string, Record<string, FieldDefinition>>;
|
|
167
|
+
/**
|
|
168
|
+
* The editor canvas renders stored content without the server-side resolve, so
|
|
169
|
+
* a relation field there holds raw ids - or the "" a freshly inserted block is
|
|
170
|
+
* seeded with. A component is typed against resolved records, so anything that
|
|
171
|
+
* is not one is coerced to the safe empty shape instead of reaching it.
|
|
172
|
+
*/
|
|
173
|
+
declare function normalizeRelationContent(content: Record<string, unknown>, schema: Record<string, FieldDefinition>): void;
|
|
174
|
+
/**
|
|
175
|
+
* Resolves every `fields.relation` value on the given block contents in place:
|
|
176
|
+
* stored record ids become full records (one batched read for the whole page),
|
|
177
|
+
* and a `mode: "all"` field becomes the model's record list. A dangling id and
|
|
178
|
+
* a failed fetch both degrade to "no value" - a relation must never break a
|
|
179
|
+
* render.
|
|
180
|
+
*/
|
|
181
|
+
declare function resolveRelationContent(config: CmssyClientConfig, entries: RelationContentEntry[], schemas: BlockSchemaMap, locale?: string, requestOptions?: QueryScopedOptions): Promise<void>;
|
|
182
|
+
|
|
161
183
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
162
184
|
declare function localeForPathname(config: CmssyClientConfig, pathname: string): Promise<string>;
|
|
163
185
|
declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | undefined): Promise<{
|
|
@@ -182,6 +204,30 @@ type MediaValue<O> = O extends {
|
|
|
182
204
|
type RepeaterValue<O> = O extends {
|
|
183
205
|
itemSchema: infer Schema extends BlockPropsSchema;
|
|
184
206
|
} ? InferBlockContent<Schema>[] : Record<string, unknown>[];
|
|
207
|
+
/**
|
|
208
|
+
* The value a relation field holds AFTER server-side resolution: the SDK
|
|
209
|
+
* replaces the stored record id(s) with the records themselves before the
|
|
210
|
+
* component renders. `mode: "all"` and `multiple: true` yield a list; the
|
|
211
|
+
* default is a single record - `undefined` when the reference dangles, which
|
|
212
|
+
* no `required` flag can rule out.
|
|
213
|
+
*/
|
|
214
|
+
type RelationValue<O> = O extends {
|
|
215
|
+
mode: "all";
|
|
216
|
+
} | {
|
|
217
|
+
multiple: true;
|
|
218
|
+
} ? CmssyModelRecord[] : CmssyModelRecord | undefined;
|
|
219
|
+
interface RelationFieldOptions extends Omit<FieldOptions, "options" | "itemSchema"> {
|
|
220
|
+
/** Slug of the model the field points at. */
|
|
221
|
+
model: string;
|
|
222
|
+
/**
|
|
223
|
+
* "all" binds the field to every record of the model (no editor picking;
|
|
224
|
+
* `sort`/`limit` shape the list). Omit it for editor-picked record id(s).
|
|
225
|
+
*/
|
|
226
|
+
mode?: RelationMode;
|
|
227
|
+
multiple?: boolean;
|
|
228
|
+
sort?: string;
|
|
229
|
+
limit?: number;
|
|
230
|
+
}
|
|
185
231
|
declare const fields: {
|
|
186
232
|
text: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
187
233
|
textarea: <const O extends FieldOptions>(opts?: O) => TypedField<string, Declared<O>>;
|
|
@@ -204,6 +250,7 @@ declare const fields: {
|
|
|
204
250
|
multiselect: <const O extends FieldOptions>(opts: O) => TypedField<OptionValue<O>[], Declared<O>>;
|
|
205
251
|
media: <const O extends FieldOptions>(opts?: O) => TypedField<MediaValue<O>, Declared<O>>;
|
|
206
252
|
repeater: <const O extends FieldOptions>(opts: O) => TypedField<RepeaterValue<O>, Declared<O>>;
|
|
253
|
+
relation: <const O extends RelationFieldOptions>(opts: O) => TypedField<RelationValue<O>, Declared<O>>;
|
|
207
254
|
};
|
|
208
255
|
|
|
209
256
|
declare const PROTOCOL_VERSION = 2;
|
|
@@ -447,4 +494,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
447
494
|
*/
|
|
448
495
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
449
496
|
|
|
450
|
-
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, 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, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
|
497
|
+
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BlockSchemaMap, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, RECORDS_BY_IDS_QUERY, type ReadyMessage, type RelationContentEntry, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, 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, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, normalizeRelationContent, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveRelationContent, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
package/dist/index.js
CHANGED
|
@@ -745,6 +745,156 @@ function localizeHtmlLinks(html, locale) {
|
|
|
745
745
|
);
|
|
746
746
|
}
|
|
747
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) {
|
|
791
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
792
|
+
if (field.type !== "relation") continue;
|
|
793
|
+
const value = content[key];
|
|
794
|
+
if (isListShaped(field)) {
|
|
795
|
+
content[key] = Array.isArray(value) ? value.filter(isResolvedRecord) : [];
|
|
796
|
+
} else if (!isResolvedRecord(value)) {
|
|
797
|
+
delete content[key];
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function collectionKey(ref) {
|
|
802
|
+
return [ref.model, ref.field.sort ?? "", ref.field.limit ?? ""].join("\0");
|
|
803
|
+
}
|
|
804
|
+
async function resolveRelationContent(config, entries, schemas, locale, requestOptions = {}) {
|
|
805
|
+
const refs = collectRefs(entries, schemas);
|
|
806
|
+
if (refs.length === 0) return;
|
|
807
|
+
const client = createCmssyClient(config);
|
|
808
|
+
const pickedIds = /* @__PURE__ */ new Set();
|
|
809
|
+
const collections = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const ref of refs) {
|
|
811
|
+
if (ref.field.relationMode === "all") {
|
|
812
|
+
collections.set(collectionKey(ref), {
|
|
813
|
+
model: ref.model,
|
|
814
|
+
sort: ref.field.sort,
|
|
815
|
+
limit: ref.field.limit
|
|
816
|
+
});
|
|
817
|
+
} else {
|
|
818
|
+
for (const id of storedIds(ref.content[ref.key])) pickedIds.add(id);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
let recordsById;
|
|
822
|
+
let collectionItems;
|
|
823
|
+
try {
|
|
824
|
+
[recordsById, collectionItems] = await Promise.all([
|
|
825
|
+
fetchPickedRecords(client, [...pickedIds], locale, requestOptions),
|
|
826
|
+
fetchCollections(client, collections, locale, requestOptions)
|
|
827
|
+
]);
|
|
828
|
+
} catch (err) {
|
|
829
|
+
if (typeof console !== "undefined") {
|
|
830
|
+
console.error("[cmssy] relation resolution failed", err);
|
|
831
|
+
}
|
|
832
|
+
for (const ref of refs) {
|
|
833
|
+
if (ref.field.relationMode === "all" || Array.isArray(ref.content[ref.key])) {
|
|
834
|
+
ref.content[ref.key] = [];
|
|
835
|
+
} else {
|
|
836
|
+
delete ref.content[ref.key];
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
for (const ref of refs) {
|
|
842
|
+
if (ref.field.relationMode === "all") {
|
|
843
|
+
ref.content[ref.key] = collectionItems.get(collectionKey(ref)) ?? [];
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
const value = ref.content[ref.key];
|
|
847
|
+
if (Array.isArray(value)) {
|
|
848
|
+
ref.content[ref.key] = storedIds(value).map((id) => recordsById.get(id)).filter((record) => !!record);
|
|
849
|
+
} else if (typeof value === "string" && value) {
|
|
850
|
+
const record = recordsById.get(value);
|
|
851
|
+
if (record) ref.content[ref.key] = record;
|
|
852
|
+
else delete ref.content[ref.key];
|
|
853
|
+
} else {
|
|
854
|
+
delete ref.content[ref.key];
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
async function fetchPickedRecords(client, ids, locale, requestOptions = {}) {
|
|
859
|
+
const byId = /* @__PURE__ */ new Map();
|
|
860
|
+
const chunks = [];
|
|
861
|
+
for (let i = 0; i < ids.length; i += BY_IDS_CHUNK) {
|
|
862
|
+
chunks.push(ids.slice(i, i + BY_IDS_CHUNK));
|
|
863
|
+
}
|
|
864
|
+
await Promise.all(
|
|
865
|
+
chunks.map(async (chunk) => {
|
|
866
|
+
const result = await client.queryScoped(
|
|
867
|
+
RECORDS_BY_IDS_QUERY,
|
|
868
|
+
{ ids: chunk, locale: locale ?? null },
|
|
869
|
+
requestOptions
|
|
870
|
+
);
|
|
871
|
+
for (const record of result.public.model.recordsByIds) {
|
|
872
|
+
byId.set(record.id, record);
|
|
873
|
+
}
|
|
874
|
+
})
|
|
875
|
+
);
|
|
876
|
+
return byId;
|
|
877
|
+
}
|
|
878
|
+
async function fetchCollections(client, collections, locale, requestOptions = {}) {
|
|
879
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
880
|
+
await Promise.all(
|
|
881
|
+
[...collections.entries()].map(async ([key, { model, sort, limit }]) => {
|
|
882
|
+
const result = await client.queryScoped(
|
|
883
|
+
MODEL_RECORDS_QUERY,
|
|
884
|
+
{
|
|
885
|
+
modelSlug: model,
|
|
886
|
+
sort: sort ?? null,
|
|
887
|
+
limit: limit ?? COLLECTION_DEFAULT_LIMIT,
|
|
888
|
+
locale: locale ?? null
|
|
889
|
+
},
|
|
890
|
+
requestOptions
|
|
891
|
+
);
|
|
892
|
+
byKey.set(key, result.public.model.records.items);
|
|
893
|
+
})
|
|
894
|
+
);
|
|
895
|
+
return byKey;
|
|
896
|
+
}
|
|
897
|
+
|
|
748
898
|
// src/locale.ts
|
|
749
899
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
750
900
|
async function localeForPathname(config, pathname) {
|
|
@@ -808,7 +958,16 @@ var fields = {
|
|
|
808
958
|
radio: choice("radio"),
|
|
809
959
|
multiselect: (opts) => build("multiselect", opts),
|
|
810
960
|
media: (opts = {}) => build("media", opts),
|
|
811
|
-
repeater: (opts) => build("repeater", opts)
|
|
961
|
+
repeater: (opts) => build("repeater", opts),
|
|
962
|
+
relation: (opts) => {
|
|
963
|
+
const { model, mode, ...rest } = opts;
|
|
964
|
+
return build("relation", {
|
|
965
|
+
...rest,
|
|
966
|
+
relationTo: `model:${model}`,
|
|
967
|
+
relationType: mode === "all" || opts.multiple ? "hasMany" : "hasOne",
|
|
968
|
+
relationMode: mode ?? "picked"
|
|
969
|
+
});
|
|
970
|
+
}
|
|
812
971
|
};
|
|
813
972
|
|
|
814
973
|
// src/bridge/protocol.ts
|
|
@@ -1726,4 +1885,4 @@ async function verifyCmssyWebhook(options) {
|
|
|
1726
1885
|
return parsed;
|
|
1727
1886
|
}
|
|
1728
1887
|
|
|
1729
|
-
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, 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, normalizeSlug, openSession, parseEditorMessage, postToEditor, resolveApiUrl, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolvePublicUrl, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
|
1888
|
+
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/core",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.6.1",
|
|
4
4
|
"description": "Framework-agnostic cmssy client: content, commerce, config, editor protocol. No React, no Next.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"vitest": "^2.1.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@cmssy/types": "0.
|
|
55
|
+
"@cmssy/types": "0.29.0",
|
|
56
56
|
"jose": "^6.2.3"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|