@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 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.5.0",
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.28.0",
55
+ "@cmssy/types": "0.29.0",
56
56
  "jose": "^6.2.3"
57
57
  },
58
58
  "scripts": {