@webiny/website-builder-sdk 0.0.0-unstable.7be00a75a9 → 0.0.0-unstable.7c2def32a7
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/BindingsResolver.d.ts +4 -1
- package/BindingsResolver.js.map +1 -1
- package/ComponentInputTraverser.js +4 -0
- package/ComponentInputTraverser.js.map +1 -1
- package/ComponentResolver.d.ts +6 -3
- package/ComponentResolver.js +9 -4
- package/ComponentResolver.js.map +1 -1
- package/ContentSdk.d.ts +67 -1
- package/ContentSdk.js +90 -8
- package/ContentSdk.js.map +1 -1
- package/DocumentStoreManager.d.ts +9 -2
- package/DocumentStoreManager.js +24 -5
- package/DocumentStoreManager.js.map +1 -1
- package/EditingSdk.d.ts +7 -1
- package/EditingSdk.js +5 -3
- package/EditingSdk.js.map +1 -1
- package/ElementFactory.js +7 -2
- package/ElementFactory.js.map +1 -1
- package/ElementFactory.test.js +102 -0
- package/ElementFactory.test.js.map +1 -1
- package/LiveSdk.d.ts +3 -1
- package/LiveSdk.js +3 -2
- package/LiveSdk.js.map +1 -1
- package/PreviewDocument.d.ts +3 -1
- package/PreviewDocument.js +5 -2
- package/PreviewDocument.js.map +1 -1
- package/PreviewSdk.d.ts +4 -1
- package/PreviewSdk.js +6 -2
- package/PreviewSdk.js.map +1 -1
- package/ResizeObserver.js +1 -1
- package/ResizeObserver.js.map +1 -1
- package/StylesBindingsProcessor.js +3 -2
- package/StylesBindingsProcessor.js.map +1 -1
- package/Theme.js +2 -1
- package/Theme.js.map +1 -1
- package/asset/deliveryUrl.d.ts +41 -0
- package/asset/deliveryUrl.js +34 -0
- package/asset/deliveryUrl.js.map +1 -0
- package/asset/deliveryUrl.test.d.ts +1 -0
- package/asset/deliveryUrl.test.js +127 -0
- package/asset/deliveryUrl.test.js.map +1 -0
- package/asset/geometry.d.ts +7 -0
- package/asset/geometry.js +10 -0
- package/asset/geometry.js.map +1 -0
- package/asset/imageSrcSet.d.ts +63 -0
- package/asset/imageSrcSet.js +98 -0
- package/asset/imageSrcSet.js.map +1 -0
- package/asset/imageSrcSet.test.d.ts +1 -0
- package/asset/imageSrcSet.test.js +103 -0
- package/asset/imageSrcSet.test.js.map +1 -0
- package/asset/index.d.ts +5 -0
- package/asset/index.js +5 -0
- package/asset/normalize.d.ts +24 -0
- package/asset/normalize.js +106 -0
- package/asset/normalize.js.map +1 -0
- package/asset/normalize.test.d.ts +1 -0
- package/asset/normalize.test.js +263 -0
- package/asset/normalize.test.js.map +1 -0
- package/asset/types.d.ts +2 -0
- package/asset/types.js +0 -0
- package/componentLoader/ComponentLoader.d.ts +16 -0
- package/componentLoader/ComponentLoader.js +0 -0
- package/componentLoader/RegistryOnlyComponentLoader.d.ts +9 -0
- package/componentLoader/RegistryOnlyComponentLoader.js +8 -0
- package/componentLoader/RegistryOnlyComponentLoader.js.map +1 -0
- package/componentLoader/createComponentLoader.d.ts +7 -0
- package/componentLoader/createComponentLoader.js +10 -0
- package/componentLoader/createComponentLoader.js.map +1 -0
- package/componentLoader/defaultComponentLoader.d.ts +7 -0
- package/componentLoader/defaultComponentLoader.js +8 -0
- package/componentLoader/defaultComponentLoader.js.map +1 -0
- package/componentLoader/index.d.ts +3 -0
- package/componentLoader/index.js +3 -0
- package/contentClientState.test.d.ts +1 -0
- package/contentClientState.test.js +384 -0
- package/contentClientState.test.js.map +1 -0
- package/contentEntry/ContentEntryBindingsResolver.d.ts +33 -0
- package/contentEntry/ContentEntryBindingsResolver.js +52 -0
- package/contentEntry/ContentEntryBindingsResolver.js.map +1 -0
- package/contentEntry/ContentEntryCache.d.ts +49 -0
- package/contentEntry/ContentEntryCache.js +51 -0
- package/contentEntry/ContentEntryCache.js.map +1 -0
- package/contentEntry/resolveContentEntry.d.ts +74 -0
- package/contentEntry/resolveContentEntry.js +53 -0
- package/contentEntry/resolveContentEntry.js.map +1 -0
- package/contentEntry/resolveContentEntry.test.d.ts +1 -0
- package/contentEntry/resolveContentEntry.test.js +213 -0
- package/contentEntry/resolveContentEntry.test.js.map +1 -0
- package/createComponent.d.ts +5 -0
- package/createComponent.js +32 -0
- package/createComponent.js.map +1 -0
- package/createContentClient.d.ts +17 -0
- package/createContentClient.js +16 -0
- package/createContentClient.js.map +1 -0
- package/createContentClient.test.d.ts +1 -0
- package/createContentClient.test.js +165 -0
- package/createContentClient.test.js.map +1 -0
- package/createInput.d.ts +10 -2
- package/createInput.js +8 -1
- package/createInput.js.map +1 -1
- package/dataProviders/ApiClient.d.ts +10 -5
- package/dataProviders/ApiClient.js +10 -10
- package/dataProviders/ApiClient.js.map +1 -1
- package/dataProviders/DefaultDataProvider.d.ts +4 -0
- package/dataProviders/DefaultDataProvider.js +35 -0
- package/dataProviders/DefaultDataProvider.js.map +1 -1
- package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
- package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
- package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
- package/dataProviders/GET_PAGE_BY_ID.d.ts +1 -1
- package/dataProviders/GET_PAGE_BY_ID.js +2 -0
- package/dataProviders/GET_PAGE_BY_ID.js.map +1 -1
- package/dataProviders/GET_PAGE_BY_PATH.d.ts +1 -1
- package/dataProviders/GET_PAGE_BY_PATH.js +2 -0
- package/dataProviders/GET_PAGE_BY_PATH.js.map +1 -1
- package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
- package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
- package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
- package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
- package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
- package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
- package/dataProviders/NullDataProvider.d.ts +4 -0
- package/dataProviders/NullDataProvider.js +9 -0
- package/dataProviders/NullDataProvider.js.map +1 -1
- package/dataProviders/queries.test.d.ts +1 -0
- package/dataProviders/queries.test.js +45 -0
- package/dataProviders/queries.test.js.map +1 -0
- package/defaultBreakpoints.js +23 -3
- package/defaultBreakpoints.js.map +1 -1
- package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
- package/experiments/PostHogAnalyticsProvider.js +28 -0
- package/experiments/PostHogAnalyticsProvider.js.map +1 -0
- package/experiments/analyticsProvider.d.ts +4 -0
- package/experiments/analyticsProvider.js +11 -0
- package/experiments/analyticsProvider.js.map +1 -0
- package/experiments/bucketing.d.ts +27 -0
- package/experiments/bucketing.js +91 -0
- package/experiments/bucketing.js.map +1 -0
- package/experiments/bucketing.test.d.ts +1 -0
- package/experiments/bucketing.test.js +183 -0
- package/experiments/bucketing.test.js.map +1 -0
- package/experiments/index.d.ts +5 -0
- package/experiments/index.js +5 -0
- package/experiments/render.d.ts +82 -0
- package/experiments/render.js +149 -0
- package/experiments/render.js.map +1 -0
- package/experiments/types.d.ts +108 -0
- package/experiments/types.js +4 -0
- package/experiments/types.js.map +1 -0
- package/headersProvider.d.ts +1 -2
- package/headersProvider.js.map +1 -1
- package/image/geometry.d.ts +54 -0
- package/image/geometry.js +112 -0
- package/image/geometry.js.map +1 -0
- package/image/geometry.test.d.ts +1 -0
- package/image/geometry.test.js +190 -0
- package/image/geometry.test.js.map +1 -0
- package/image/index.d.ts +3 -0
- package/image/index.js +3 -0
- package/image/presets.d.ts +6 -0
- package/image/presets.js +20 -0
- package/image/presets.js.map +1 -0
- package/image/types.d.ts +10 -0
- package/image/types.js +0 -0
- package/index.d.ts +12 -0
- package/index.js +12 -0
- package/jsonPatch.js +3 -3
- package/package.json +7 -6
- package/types/WebsiteBuilderTheme.d.ts +5 -0
- package/types.d.ts +75 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ContentEntryReference, ContentEntryQueryValue } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal CMS data loader the content-entry resolver depends on. Injected by the
|
|
4
|
+
* caller (the render layer passes the CMS `contentSdk`) so this module stays
|
|
5
|
+
* decoupled from `@webiny/cms-sdk`.
|
|
6
|
+
*/
|
|
7
|
+
export interface ContentEntryLoader {
|
|
8
|
+
getEntry(params: {
|
|
9
|
+
modelId: string;
|
|
10
|
+
entryId: string;
|
|
11
|
+
}): Promise<unknown | null>;
|
|
12
|
+
listEntries(params: {
|
|
13
|
+
modelId: string;
|
|
14
|
+
sort?: Record<string, "asc" | "desc">;
|
|
15
|
+
limit?: number;
|
|
16
|
+
search?: string;
|
|
17
|
+
after?: string;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
data: unknown[];
|
|
20
|
+
meta: {
|
|
21
|
+
cursor: string | null;
|
|
22
|
+
hasMoreItems: boolean;
|
|
23
|
+
totalCount: number;
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The query params used to produce a page. Embedded in the query result so the
|
|
29
|
+
* client can fetch further pages (loadMore) from `pageInfo.cursor`.
|
|
30
|
+
*/
|
|
31
|
+
export interface ContentEntryQuerySpec {
|
|
32
|
+
modelId: string;
|
|
33
|
+
sort?: Record<string, "asc" | "desc">;
|
|
34
|
+
limit?: number;
|
|
35
|
+
search?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ResolvedContentEntryQuery<T = unknown> {
|
|
38
|
+
items: T[];
|
|
39
|
+
pageInfo: {
|
|
40
|
+
cursor: string | null;
|
|
41
|
+
hasMore: boolean;
|
|
42
|
+
totalCount: number;
|
|
43
|
+
};
|
|
44
|
+
/** Continuation params for loadMore. */
|
|
45
|
+
query?: ContentEntryQuerySpec;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The shape passed to a component for a content-entry input:
|
|
49
|
+
* - manual, single -> the resolved entry (or null)
|
|
50
|
+
* - manual, list -> an array of resolved entries
|
|
51
|
+
* - query -> `{ items, pageInfo, query? }`
|
|
52
|
+
*/
|
|
53
|
+
export type ResolvedContentEntry = unknown | unknown[] | ResolvedContentEntryQuery | null;
|
|
54
|
+
/**
|
|
55
|
+
* Detect whether a raw binding value is a `ContentEntryQueryValue` (query mode)
|
|
56
|
+
* rather than a manual `ContentEntryReference`.
|
|
57
|
+
*
|
|
58
|
+
* Query values carry `modelId` but NOT `id`; manual references carry both.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isQueryValue(value: unknown): value is ContentEntryQueryValue;
|
|
61
|
+
/**
|
|
62
|
+
* Detect whether a content-entry input's value is already a resolved CMS entry
|
|
63
|
+
* rather than a bare reference that needs fetching.
|
|
64
|
+
*/
|
|
65
|
+
export declare function isAlreadyResolved(value: unknown, list: boolean): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve a content-entry binding value into CMS entries. Works entirely from
|
|
68
|
+
* the value shape — no component manifest needed.
|
|
69
|
+
*
|
|
70
|
+
* - `{ id, modelId }` → fetch single entry
|
|
71
|
+
* - `[{ id, modelId }, ...]` → fetch multiple entries
|
|
72
|
+
* - `{ modelId, sort?, limit?, search? }` → list query
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveContentEntryValue(value: ContentEntryReference | ContentEntryReference[] | ContentEntryQueryValue | null | undefined, list: boolean, loader: ContentEntryLoader): Promise<ResolvedContentEntry>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function isQueryValue(value) {
|
|
2
|
+
return "object" == typeof value && null !== value && "modelId" in value && !("id" in value) && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function isAlreadyResolved(value, list) {
|
|
5
|
+
if (null == value) return false;
|
|
6
|
+
if ("object" == typeof value && "items" in value) return true;
|
|
7
|
+
if (list && Array.isArray(value) && value.length > 0) {
|
|
8
|
+
const first = value[0];
|
|
9
|
+
return "object" == typeof first && null !== first && "values" in first;
|
|
10
|
+
}
|
|
11
|
+
if (!list && "object" == typeof value && "values" in value) return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
async function resolveContentEntryValue(value, list, loader) {
|
|
15
|
+
if (null == value) return list ? [] : null;
|
|
16
|
+
if (isQueryValue(value)) {
|
|
17
|
+
const query = value;
|
|
18
|
+
const listParams = {
|
|
19
|
+
modelId: query.modelId,
|
|
20
|
+
sort: query.sort ? {
|
|
21
|
+
[query.sort.field]: query.sort.order
|
|
22
|
+
} : void 0,
|
|
23
|
+
limit: query.limit,
|
|
24
|
+
search: query.search
|
|
25
|
+
};
|
|
26
|
+
const result = await loader.listEntries(listParams);
|
|
27
|
+
return {
|
|
28
|
+
items: result.data,
|
|
29
|
+
pageInfo: {
|
|
30
|
+
cursor: result.meta.cursor,
|
|
31
|
+
hasMore: result.meta.hasMoreItems,
|
|
32
|
+
totalCount: result.meta.totalCount
|
|
33
|
+
},
|
|
34
|
+
query: listParams
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
const refs = value;
|
|
39
|
+
const entries = await Promise.all(refs.map((ref)=>loader.getEntry({
|
|
40
|
+
modelId: ref.modelId,
|
|
41
|
+
entryId: ref.id
|
|
42
|
+
})));
|
|
43
|
+
return entries.filter((entry)=>null !== entry);
|
|
44
|
+
}
|
|
45
|
+
const ref = value;
|
|
46
|
+
return loader.getEntry({
|
|
47
|
+
modelId: ref.modelId,
|
|
48
|
+
entryId: ref.id
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export { isAlreadyResolved, isQueryValue, resolveContentEntryValue };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=resolveContentEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentEntry/resolveContentEntry.js","sources":["../../src/contentEntry/resolveContentEntry.ts"],"sourcesContent":["import type { ContentEntryReference, ContentEntryQueryValue } from \"~/types.js\";\n\n/**\n * Minimal CMS data loader the content-entry resolver depends on. Injected by the\n * caller (the render layer passes the CMS `contentSdk`) so this module stays\n * decoupled from `@webiny/cms-sdk`.\n */\nexport interface ContentEntryLoader {\n getEntry(params: { modelId: string; entryId: string }): Promise<unknown | null>;\n listEntries(params: {\n modelId: string;\n sort?: Record<string, \"asc\" | \"desc\">;\n limit?: number;\n search?: string;\n after?: string;\n }): Promise<{\n data: unknown[];\n meta: { cursor: string | null; hasMoreItems: boolean; totalCount: number };\n }>;\n}\n\n/**\n * The query params used to produce a page. Embedded in the query result so the\n * client can fetch further pages (loadMore) from `pageInfo.cursor`.\n */\nexport interface ContentEntryQuerySpec {\n modelId: string;\n sort?: Record<string, \"asc\" | \"desc\">;\n limit?: number;\n search?: string;\n}\n\nexport interface ResolvedContentEntryQuery<T = unknown> {\n items: T[];\n pageInfo: { cursor: string | null; hasMore: boolean; totalCount: number };\n /** Continuation params for loadMore. */\n query?: ContentEntryQuerySpec;\n}\n\n/**\n * The shape passed to a component for a content-entry input:\n * - manual, single -> the resolved entry (or null)\n * - manual, list -> an array of resolved entries\n * - query -> `{ items, pageInfo, query? }`\n */\nexport type ResolvedContentEntry = unknown | unknown[] | ResolvedContentEntryQuery | null;\n\n/**\n * Detect whether a raw binding value is a `ContentEntryQueryValue` (query mode)\n * rather than a manual `ContentEntryReference`.\n *\n * Query values carry `modelId` but NOT `id`; manual references carry both.\n */\nexport function isQueryValue(value: unknown): value is ContentEntryQueryValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"modelId\" in value &&\n !(\"id\" in value) &&\n !Array.isArray(value)\n );\n}\n\n/**\n * Detect whether a content-entry input's value is already a resolved CMS entry\n * rather than a bare reference that needs fetching.\n */\nexport function isAlreadyResolved(value: unknown, list: boolean): boolean {\n if (value == null) {\n return false;\n }\n // Query result — already resolved if it has `items`.\n if (typeof value === \"object\" && \"items\" in (value as any)) {\n return true;\n }\n // List of resolved entries — each has `values` (CMS entry shape).\n if (list && Array.isArray(value) && value.length > 0) {\n const first = value[0];\n return typeof first === \"object\" && first !== null && \"values\" in first;\n }\n // Single resolved entry.\n if (!list && typeof value === \"object\" && \"values\" in (value as any)) {\n return true;\n }\n return false;\n}\n\n/**\n * Resolve a content-entry binding value into CMS entries. Works entirely from\n * the value shape — no component manifest needed.\n *\n * - `{ id, modelId }` → fetch single entry\n * - `[{ id, modelId }, ...]` → fetch multiple entries\n * - `{ modelId, sort?, limit?, search? }` → list query\n */\nexport async function resolveContentEntryValue(\n value:\n | ContentEntryReference\n | ContentEntryReference[]\n | ContentEntryQueryValue\n | null\n | undefined,\n list: boolean,\n loader: ContentEntryLoader\n): Promise<ResolvedContentEntry> {\n if (value == null) {\n return list ? [] : null;\n }\n\n // Query mode: value is `{ modelId, sort?, limit?, search? }`.\n if (isQueryValue(value)) {\n const query = value;\n const listParams: ContentEntryQuerySpec = {\n modelId: query.modelId,\n sort: query.sort ? { [query.sort.field]: query.sort.order } : undefined,\n limit: query.limit,\n search: query.search\n };\n const result = await loader.listEntries(listParams);\n return {\n items: result.data,\n pageInfo: {\n cursor: result.meta.cursor,\n hasMore: result.meta.hasMoreItems,\n totalCount: result.meta.totalCount\n },\n query: listParams\n };\n }\n\n // Manual list: value is `[{ id, modelId }, ...]`.\n if (Array.isArray(value)) {\n const refs = value as ContentEntryReference[];\n const entries = await Promise.all(\n refs.map(ref => loader.getEntry({ modelId: ref.modelId, entryId: ref.id }))\n );\n return entries.filter(entry => entry !== null);\n }\n\n // Manual single: value is `{ id, modelId }`.\n const ref = value as ContentEntryReference;\n return loader.getEntry({ modelId: ref.modelId, entryId: ref.id });\n}\n"],"names":["isQueryValue","value","Array","isAlreadyResolved","list","first","resolveContentEntryValue","loader","query","listParams","undefined","result","refs","entries","Promise","ref","entry"],"mappings":"AAqDO,SAASA,aAAaC,KAAc;IACvC,OACI,AAAiB,YAAjB,OAAOA,SACPA,AAAU,SAAVA,SACA,aAAaA,SACb,CAAE,SAAQA,KAAI,KACd,CAACC,MAAM,OAAO,CAACD;AAEvB;AAMO,SAASE,kBAAkBF,KAAc,EAAEG,IAAa;IAC3D,IAAIH,AAAS,QAATA,OACA,OAAO;IAGX,IAAI,AAAiB,YAAjB,OAAOA,SAAsB,WAAYA,OACzC,OAAO;IAGX,IAAIG,QAAQF,MAAM,OAAO,CAACD,UAAUA,MAAM,MAAM,GAAG,GAAG;QAClD,MAAMI,QAAQJ,KAAK,CAAC,EAAE;QACtB,OAAO,AAAiB,YAAjB,OAAOI,SAAsBA,AAAU,SAAVA,SAAkB,YAAYA;IACtE;IAEA,IAAI,CAACD,QAAQ,AAAiB,YAAjB,OAAOH,SAAsB,YAAaA,OACnD,OAAO;IAEX,OAAO;AACX;AAUO,eAAeK,yBAClBL,KAKe,EACfG,IAAa,EACbG,MAA0B;IAE1B,IAAIN,AAAS,QAATA,OACA,OAAOG,OAAO,EAAE,GAAG;IAIvB,IAAIJ,aAAaC,QAAQ;QACrB,MAAMO,QAAQP;QACd,MAAMQ,aAAoC;YACtC,SAASD,MAAM,OAAO;YACtB,MAAMA,MAAM,IAAI,GAAG;gBAAE,CAACA,MAAM,IAAI,CAAC,KAAK,CAAC,EAAEA,MAAM,IAAI,CAAC,KAAK;YAAC,IAAIE;YAC9D,OAAOF,MAAM,KAAK;YAClB,QAAQA,MAAM,MAAM;QACxB;QACA,MAAMG,SAAS,MAAMJ,OAAO,WAAW,CAACE;QACxC,OAAO;YACH,OAAOE,OAAO,IAAI;YAClB,UAAU;gBACN,QAAQA,OAAO,IAAI,CAAC,MAAM;gBAC1B,SAASA,OAAO,IAAI,CAAC,YAAY;gBACjC,YAAYA,OAAO,IAAI,CAAC,UAAU;YACtC;YACA,OAAOF;QACX;IACJ;IAGA,IAAIP,MAAM,OAAO,CAACD,QAAQ;QACtB,MAAMW,OAAOX;QACb,MAAMY,UAAU,MAAMC,QAAQ,GAAG,CAC7BF,KAAK,GAAG,CAACG,CAAAA,MAAOR,OAAO,QAAQ,CAAC;gBAAE,SAASQ,IAAI,OAAO;gBAAE,SAASA,IAAI,EAAE;YAAC;QAE5E,OAAOF,QAAQ,MAAM,CAACG,CAAAA,QAASA,AAAU,SAAVA;IACnC;IAGA,MAAMD,MAAMd;IACZ,OAAOM,OAAO,QAAQ,CAAC;QAAE,SAASQ,IAAI,OAAO;QAAE,SAASA,IAAI,EAAE;IAAC;AACnE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { isAlreadyResolved, isQueryValue, resolveContentEntryValue } from "./resolveContentEntry.js";
|
|
3
|
+
const entry = (id)=>({
|
|
4
|
+
id,
|
|
5
|
+
entryId: id,
|
|
6
|
+
values: {
|
|
7
|
+
title: `T${id}`
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
function makeLoader(over = {}) {
|
|
11
|
+
return {
|
|
12
|
+
getEntry: vi.fn(async ({ entryId })=>entry(entryId)),
|
|
13
|
+
listEntries: vi.fn(async ()=>({
|
|
14
|
+
data: [
|
|
15
|
+
entry("1"),
|
|
16
|
+
entry("2")
|
|
17
|
+
],
|
|
18
|
+
meta: {
|
|
19
|
+
cursor: "c1",
|
|
20
|
+
hasMoreItems: true,
|
|
21
|
+
totalCount: 5
|
|
22
|
+
}
|
|
23
|
+
})),
|
|
24
|
+
...over
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe("resolveContentEntryValue — manual mode", ()=>{
|
|
28
|
+
it("resolves a single reference", async ()=>{
|
|
29
|
+
const loader = makeLoader();
|
|
30
|
+
const result = await resolveContentEntryValue({
|
|
31
|
+
id: "42",
|
|
32
|
+
modelId: "blog"
|
|
33
|
+
}, false, loader);
|
|
34
|
+
expect(loader.getEntry).toHaveBeenCalledWith({
|
|
35
|
+
modelId: "blog",
|
|
36
|
+
entryId: "42"
|
|
37
|
+
});
|
|
38
|
+
expect(result).toEqual(entry("42"));
|
|
39
|
+
});
|
|
40
|
+
it("returns null for a null single value", async ()=>{
|
|
41
|
+
const loader = makeLoader();
|
|
42
|
+
const result = await resolveContentEntryValue(null, false, loader);
|
|
43
|
+
expect(result).toBeNull();
|
|
44
|
+
expect(loader.getEntry).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
it("resolves a list of references, preserving order", async ()=>{
|
|
47
|
+
const loader = makeLoader();
|
|
48
|
+
const result = await resolveContentEntryValue([
|
|
49
|
+
{
|
|
50
|
+
id: "1",
|
|
51
|
+
modelId: "blog"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "2",
|
|
55
|
+
modelId: "blog"
|
|
56
|
+
}
|
|
57
|
+
], true, loader);
|
|
58
|
+
expect(loader.getEntry).toHaveBeenCalledTimes(2);
|
|
59
|
+
expect(result).toEqual([
|
|
60
|
+
entry("1"),
|
|
61
|
+
entry("2")
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
it("filters out unresolved (deleted) references in a list", async ()=>{
|
|
65
|
+
const loader = makeLoader({
|
|
66
|
+
getEntry: vi.fn(async ({ entryId })=>"2" === entryId ? null : entry(entryId))
|
|
67
|
+
});
|
|
68
|
+
const result = await resolveContentEntryValue([
|
|
69
|
+
{
|
|
70
|
+
id: "1",
|
|
71
|
+
modelId: "blog"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "2",
|
|
75
|
+
modelId: "blog"
|
|
76
|
+
}
|
|
77
|
+
], true, loader);
|
|
78
|
+
expect(result).toEqual([
|
|
79
|
+
entry("1")
|
|
80
|
+
]);
|
|
81
|
+
});
|
|
82
|
+
it("returns an empty array for a null list value", async ()=>{
|
|
83
|
+
const loader = makeLoader();
|
|
84
|
+
const result = await resolveContentEntryValue(null, true, loader);
|
|
85
|
+
expect(result).toEqual([]);
|
|
86
|
+
expect(loader.getEntry).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("resolveContentEntryValue — query mode", ()=>{
|
|
90
|
+
it("runs the query and returns items + pageInfo", async ()=>{
|
|
91
|
+
const loader = makeLoader();
|
|
92
|
+
const result = await resolveContentEntryValue({
|
|
93
|
+
modelId: "blog",
|
|
94
|
+
sort: {
|
|
95
|
+
field: "values_title",
|
|
96
|
+
order: "asc"
|
|
97
|
+
},
|
|
98
|
+
limit: 5,
|
|
99
|
+
search: "cat"
|
|
100
|
+
}, false, loader);
|
|
101
|
+
expect(loader.listEntries).toHaveBeenCalledWith({
|
|
102
|
+
modelId: "blog",
|
|
103
|
+
sort: {
|
|
104
|
+
values_title: "asc"
|
|
105
|
+
},
|
|
106
|
+
limit: 5,
|
|
107
|
+
search: "cat"
|
|
108
|
+
});
|
|
109
|
+
expect(result).toMatchObject({
|
|
110
|
+
items: [
|
|
111
|
+
entry("1"),
|
|
112
|
+
entry("2")
|
|
113
|
+
],
|
|
114
|
+
pageInfo: {
|
|
115
|
+
cursor: "c1",
|
|
116
|
+
hasMore: true,
|
|
117
|
+
totalCount: 5
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
it("always embeds the continuation query", async ()=>{
|
|
122
|
+
const loader = makeLoader();
|
|
123
|
+
const result = await resolveContentEntryValue({
|
|
124
|
+
modelId: "blog",
|
|
125
|
+
limit: 4
|
|
126
|
+
}, false, loader);
|
|
127
|
+
expect(result.query).toEqual({
|
|
128
|
+
modelId: "blog",
|
|
129
|
+
sort: void 0,
|
|
130
|
+
limit: 4,
|
|
131
|
+
search: void 0
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
it("runs a minimal query (modelId only)", async ()=>{
|
|
135
|
+
const loader = makeLoader();
|
|
136
|
+
await resolveContentEntryValue({
|
|
137
|
+
modelId: "blog"
|
|
138
|
+
}, false, loader);
|
|
139
|
+
expect(loader.listEntries).toHaveBeenCalledWith({
|
|
140
|
+
modelId: "blog",
|
|
141
|
+
sort: void 0,
|
|
142
|
+
limit: void 0,
|
|
143
|
+
search: void 0
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe("isQueryValue", ()=>{
|
|
148
|
+
it("returns true for { modelId } without id", ()=>{
|
|
149
|
+
expect(isQueryValue({
|
|
150
|
+
modelId: "blog"
|
|
151
|
+
})).toBe(true);
|
|
152
|
+
expect(isQueryValue({
|
|
153
|
+
modelId: "blog",
|
|
154
|
+
sort: {
|
|
155
|
+
field: "a",
|
|
156
|
+
order: "asc"
|
|
157
|
+
}
|
|
158
|
+
})).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
it("returns false for a manual reference { id, modelId }", ()=>{
|
|
161
|
+
expect(isQueryValue({
|
|
162
|
+
id: "42",
|
|
163
|
+
modelId: "blog"
|
|
164
|
+
})).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
it("returns false for arrays and non-objects", ()=>{
|
|
167
|
+
expect(isQueryValue([
|
|
168
|
+
{
|
|
169
|
+
modelId: "blog"
|
|
170
|
+
}
|
|
171
|
+
])).toBe(false);
|
|
172
|
+
expect(isQueryValue(null)).toBe(false);
|
|
173
|
+
expect(isQueryValue("string")).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe("isAlreadyResolved", ()=>{
|
|
177
|
+
it("returns true for a query result with items", ()=>{
|
|
178
|
+
expect(isAlreadyResolved({
|
|
179
|
+
items: [],
|
|
180
|
+
pageInfo: {}
|
|
181
|
+
}, false)).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
it("returns true for a list of resolved entries", ()=>{
|
|
184
|
+
expect(isAlreadyResolved([
|
|
185
|
+
{
|
|
186
|
+
values: {
|
|
187
|
+
title: "x"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
], true)).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
it("returns false for a list of bare references", ()=>{
|
|
193
|
+
expect(isAlreadyResolved([
|
|
194
|
+
{
|
|
195
|
+
id: "1",
|
|
196
|
+
modelId: "blog"
|
|
197
|
+
}
|
|
198
|
+
], true)).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
it("returns true for a single resolved entry", ()=>{
|
|
201
|
+
expect(isAlreadyResolved({
|
|
202
|
+
values: {
|
|
203
|
+
title: "x"
|
|
204
|
+
}
|
|
205
|
+
}, false)).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
it("returns false for null", ()=>{
|
|
208
|
+
expect(isAlreadyResolved(null, false)).toBe(false);
|
|
209
|
+
expect(isAlreadyResolved(null, true)).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
//# sourceMappingURL=resolveContentEntry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentEntry/resolveContentEntry.test.js","sources":["../../src/contentEntry/resolveContentEntry.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from \"vitest\";\nimport {\n resolveContentEntryValue,\n isQueryValue,\n isAlreadyResolved,\n type ContentEntryLoader\n} from \"./resolveContentEntry.js\";\n\nconst entry = (id: string) => ({ id, entryId: id, values: { title: `T${id}` } });\n\nfunction makeLoader(over: Partial<ContentEntryLoader> = {}): ContentEntryLoader {\n return {\n getEntry: vi.fn(async ({ entryId }: { entryId: string }) => entry(entryId)),\n listEntries: vi.fn(async () => ({\n data: [entry(\"1\"), entry(\"2\")],\n meta: { cursor: \"c1\", hasMoreItems: true, totalCount: 5 }\n })),\n ...over\n };\n}\n\ndescribe(\"resolveContentEntryValue — manual mode\", () => {\n it(\"resolves a single reference\", async () => {\n const loader = makeLoader();\n const result = await resolveContentEntryValue({ id: \"42\", modelId: \"blog\" }, false, loader);\n expect(loader.getEntry).toHaveBeenCalledWith({ modelId: \"blog\", entryId: \"42\" });\n expect(result).toEqual(entry(\"42\"));\n });\n\n it(\"returns null for a null single value\", async () => {\n const loader = makeLoader();\n const result = await resolveContentEntryValue(null, false, loader);\n expect(result).toBeNull();\n expect(loader.getEntry).not.toHaveBeenCalled();\n });\n\n it(\"resolves a list of references, preserving order\", async () => {\n const loader = makeLoader();\n const result = await resolveContentEntryValue(\n [\n { id: \"1\", modelId: \"blog\" },\n { id: \"2\", modelId: \"blog\" }\n ],\n true,\n loader\n );\n expect(loader.getEntry).toHaveBeenCalledTimes(2);\n expect(result).toEqual([entry(\"1\"), entry(\"2\")]);\n });\n\n it(\"filters out unresolved (deleted) references in a list\", async () => {\n const loader = makeLoader({\n getEntry: vi.fn(async ({ entryId }: { entryId: string }) =>\n entryId === \"2\" ? null : entry(entryId)\n )\n });\n const result = await resolveContentEntryValue(\n [\n { id: \"1\", modelId: \"blog\" },\n { id: \"2\", modelId: \"blog\" }\n ],\n true,\n loader\n );\n expect(result).toEqual([entry(\"1\")]);\n });\n\n it(\"returns an empty array for a null list value\", async () => {\n const loader = makeLoader();\n const result = await resolveContentEntryValue(null, true, loader);\n expect(result).toEqual([]);\n expect(loader.getEntry).not.toHaveBeenCalled();\n });\n});\n\ndescribe(\"resolveContentEntryValue — query mode\", () => {\n it(\"runs the query and returns items + pageInfo\", async () => {\n const loader = makeLoader();\n const result = await resolveContentEntryValue(\n {\n modelId: \"blog\",\n sort: { field: \"values_title\", order: \"asc\" },\n limit: 5,\n search: \"cat\"\n },\n false,\n loader\n );\n expect(loader.listEntries).toHaveBeenCalledWith({\n modelId: \"blog\",\n sort: { values_title: \"asc\" },\n limit: 5,\n search: \"cat\"\n });\n expect(result).toMatchObject({\n items: [entry(\"1\"), entry(\"2\")],\n pageInfo: { cursor: \"c1\", hasMore: true, totalCount: 5 }\n });\n });\n\n it(\"always embeds the continuation query\", async () => {\n const loader = makeLoader();\n const result = (await resolveContentEntryValue(\n { modelId: \"blog\", limit: 4 },\n false,\n loader\n )) as Record<string, unknown>;\n expect(result.query).toEqual({\n modelId: \"blog\",\n sort: undefined,\n limit: 4,\n search: undefined\n });\n });\n\n it(\"runs a minimal query (modelId only)\", async () => {\n const loader = makeLoader();\n await resolveContentEntryValue({ modelId: \"blog\" }, false, loader);\n expect(loader.listEntries).toHaveBeenCalledWith({\n modelId: \"blog\",\n sort: undefined,\n limit: undefined,\n search: undefined\n });\n });\n});\n\ndescribe(\"isQueryValue\", () => {\n it(\"returns true for { modelId } without id\", () => {\n expect(isQueryValue({ modelId: \"blog\" })).toBe(true);\n expect(isQueryValue({ modelId: \"blog\", sort: { field: \"a\", order: \"asc\" } })).toBe(true);\n });\n\n it(\"returns false for a manual reference { id, modelId }\", () => {\n expect(isQueryValue({ id: \"42\", modelId: \"blog\" })).toBe(false);\n });\n\n it(\"returns false for arrays and non-objects\", () => {\n expect(isQueryValue([{ modelId: \"blog\" }])).toBe(false);\n expect(isQueryValue(null)).toBe(false);\n expect(isQueryValue(\"string\")).toBe(false);\n });\n});\n\ndescribe(\"isAlreadyResolved\", () => {\n it(\"returns true for a query result with items\", () => {\n expect(isAlreadyResolved({ items: [], pageInfo: {} }, false)).toBe(true);\n });\n\n it(\"returns true for a list of resolved entries\", () => {\n expect(isAlreadyResolved([{ values: { title: \"x\" } }], true)).toBe(true);\n });\n\n it(\"returns false for a list of bare references\", () => {\n expect(isAlreadyResolved([{ id: \"1\", modelId: \"blog\" }], true)).toBe(false);\n });\n\n it(\"returns true for a single resolved entry\", () => {\n expect(isAlreadyResolved({ values: { title: \"x\" } }, false)).toBe(true);\n });\n\n it(\"returns false for null\", () => {\n expect(isAlreadyResolved(null, false)).toBe(false);\n expect(isAlreadyResolved(null, true)).toBe(false);\n });\n});\n"],"names":["entry","id","makeLoader","over","vi","entryId","describe","it","loader","result","resolveContentEntryValue","expect","undefined","isQueryValue","isAlreadyResolved"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Component, ComponentManifestInput } from "./types.js";
|
|
2
|
+
export type ExtractInputs<T> = T extends {
|
|
3
|
+
inputs: infer I;
|
|
4
|
+
} ? I : never;
|
|
5
|
+
export declare function createComponent<TComponent extends (props: any) => any, TInputs extends ExtractInputs<Parameters<TComponent>[0]>>(component: TComponent, manifest: ComponentManifestInput<TInputs>): Component;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createSlotInput } from "./createInput.js";
|
|
2
|
+
function createComponent(component, manifest) {
|
|
3
|
+
const inputs = [];
|
|
4
|
+
if (Array.isArray(manifest.inputs)) inputs.push(...manifest.inputs);
|
|
5
|
+
else {
|
|
6
|
+
const inputsObject = manifest.inputs ?? {};
|
|
7
|
+
Object.keys(inputsObject).forEach((name)=>{
|
|
8
|
+
inputs.push({
|
|
9
|
+
...inputsObject[name],
|
|
10
|
+
name
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
const acceptsChildren = manifest.acceptsChildren;
|
|
15
|
+
if (acceptsChildren) {
|
|
16
|
+
const hasChildren = inputs.some((input)=>"children" === input.name);
|
|
17
|
+
if (!hasChildren) inputs.push(createSlotInput({
|
|
18
|
+
name: "children"
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
component,
|
|
23
|
+
manifest: {
|
|
24
|
+
...manifest,
|
|
25
|
+
tags: manifest.tags ?? [],
|
|
26
|
+
inputs
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export { createComponent };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=createComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createComponent.js","sources":["../src/createComponent.ts"],"sourcesContent":["import type {\n Component,\n ComponentInput,\n ComponentManifest,\n ComponentManifestInput\n} from \"./types.js\";\nimport type { InputFactory } from \"./createInput.js\";\nimport { createSlotInput } from \"./createInput.js\";\n\nexport type ExtractInputs<T> = T extends { inputs: infer I } ? I : never;\n\nexport function createComponent<\n TComponent extends (props: any) => any,\n TInputs extends ExtractInputs<Parameters<TComponent>[0]>\n>(component: TComponent, manifest: ComponentManifestInput<TInputs>): Component {\n const inputs: ComponentInput[] = [];\n\n if (Array.isArray(manifest.inputs)) {\n inputs.push(...manifest.inputs);\n } else {\n const inputsObject: Record<string, InputFactory<any>> = manifest.inputs ?? {};\n Object.keys(inputsObject).forEach((name: string) => {\n inputs.push({ ...inputsObject[name], name });\n });\n }\n\n const acceptsChildren = manifest.acceptsChildren;\n\n if (acceptsChildren) {\n const hasChildren = inputs.some(input => input.name === \"children\");\n if (!hasChildren) {\n inputs.push(createSlotInput({ name: \"children\" }));\n }\n }\n\n return {\n component,\n manifest: { ...manifest, tags: manifest.tags ?? [], inputs } as ComponentManifest\n };\n}\n"],"names":["createComponent","component","manifest","inputs","Array","inputsObject","Object","name","acceptsChildren","hasChildren","input","createSlotInput"],"mappings":";AAWO,SAASA,gBAGdC,SAAqB,EAAEC,QAAyC;IAC9D,MAAMC,SAA2B,EAAE;IAEnC,IAAIC,MAAM,OAAO,CAACF,SAAS,MAAM,GAC7BC,OAAO,IAAI,IAAID,SAAS,MAAM;SAC3B;QACH,MAAMG,eAAkDH,SAAS,MAAM,IAAI,CAAC;QAC5EI,OAAO,IAAI,CAACD,cAAc,OAAO,CAAC,CAACE;YAC/BJ,OAAO,IAAI,CAAC;gBAAE,GAAGE,YAAY,CAACE,KAAK;gBAAEA;YAAK;QAC9C;IACJ;IAEA,MAAMC,kBAAkBN,SAAS,eAAe;IAEhD,IAAIM,iBAAiB;QACjB,MAAMC,cAAcN,OAAO,IAAI,CAACO,CAAAA,QAASA,AAAe,eAAfA,MAAM,IAAI;QACnD,IAAI,CAACD,aACDN,OAAO,IAAI,CAACQ,gBAAgB;YAAE,MAAM;QAAW;IAEvD;IAEA,OAAO;QACHV;QACA,UAAU;YAAE,GAAGC,QAAQ;YAAE,MAAMA,SAAS,IAAI,IAAI,EAAE;YAAEC;QAAO;IAC/D;AACJ"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ContentSdk } from "./ContentSdk.js";
|
|
2
|
+
import type { ContentSDKConfig } from "./ContentSdk.js";
|
|
3
|
+
/**
|
|
4
|
+
* A configured content client. It exposes the same methods as `contentSdk`,
|
|
5
|
+
* except `init()`: a client's configuration is fixed when it is created.
|
|
6
|
+
*/
|
|
7
|
+
export type ContentClient = Omit<ContentSdk, "init">;
|
|
8
|
+
/**
|
|
9
|
+
* Create a content client with its own configuration and its own API client, separate
|
|
10
|
+
* from the `contentSdk` singleton and from other clients. Use it when one process serves
|
|
11
|
+
* requests for different tenants or API hosts.
|
|
12
|
+
*
|
|
13
|
+
* Data fetching is per client, and so are the component registry, the content entry cache
|
|
14
|
+
* and the document store. The editing environment is module-level and shared by all
|
|
15
|
+
* clients and `contentSdk`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createContentClient(config: ContentSDKConfig): ContentClient;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ContentSdk } from "./ContentSdk.js";
|
|
2
|
+
import { ComponentRegistry } from "./ComponentRegistry.js";
|
|
3
|
+
import { ContentEntryCache } from "./contentEntry/ContentEntryCache.js";
|
|
4
|
+
import { DocumentStoreManager } from "./DocumentStoreManager.js";
|
|
5
|
+
function createContentClient(config) {
|
|
6
|
+
const client = new ContentSdk({
|
|
7
|
+
componentRegistry: new ComponentRegistry(),
|
|
8
|
+
contentEntryCache: new ContentEntryCache(),
|
|
9
|
+
documentStoreManager: new DocumentStoreManager()
|
|
10
|
+
});
|
|
11
|
+
client.init(config);
|
|
12
|
+
return client;
|
|
13
|
+
}
|
|
14
|
+
export { createContentClient };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=createContentClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createContentClient.js","sources":["../src/createContentClient.ts"],"sourcesContent":["import { ContentSdk } from \"./ContentSdk.js\";\nimport type { ContentSDKConfig } from \"./ContentSdk.js\";\nimport { ComponentRegistry } from \"./ComponentRegistry.js\";\nimport { ContentEntryCache } from \"./contentEntry/ContentEntryCache.js\";\nimport { DocumentStoreManager } from \"./DocumentStoreManager.js\";\n\n/**\n * A configured content client. It exposes the same methods as `contentSdk`,\n * except `init()`: a client's configuration is fixed when it is created.\n */\nexport type ContentClient = Omit<ContentSdk, \"init\">;\n\n/**\n * Create a content client with its own configuration and its own API client, separate\n * from the `contentSdk` singleton and from other clients. Use it when one process serves\n * requests for different tenants or API hosts.\n *\n * Data fetching is per client, and so are the component registry, the content entry cache\n * and the document store. The editing environment is module-level and shared by all\n * clients and `contentSdk`.\n */\nexport function createContentClient(config: ContentSDKConfig): ContentClient {\n const client = new ContentSdk({\n componentRegistry: new ComponentRegistry(),\n contentEntryCache: new ContentEntryCache(),\n documentStoreManager: new DocumentStoreManager()\n });\n client.init(config);\n return client;\n}\n"],"names":["createContentClient","config","client","ContentSdk","ComponentRegistry","ContentEntryCache","DocumentStoreManager"],"mappings":";;;;AAqBO,SAASA,oBAAoBC,MAAwB;IACxD,MAAMC,SAAS,IAAIC,WAAW;QAC1B,mBAAmB,IAAIC;QACvB,mBAAmB,IAAIC;QACvB,sBAAsB,IAAIC;IAC9B;IACAJ,OAAO,IAAI,CAACD;IACZ,OAAOC;AACX"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { createContentClient } from "./createContentClient.js";
|
|
3
|
+
import { contentSdk } from "./ContentSdk.js";
|
|
4
|
+
const pageResponse = (path)=>({
|
|
5
|
+
data: {
|
|
6
|
+
websiteBuilder: {
|
|
7
|
+
getPageByPath: {
|
|
8
|
+
data: {
|
|
9
|
+
id: `page:${path}`,
|
|
10
|
+
properties: {
|
|
11
|
+
id: `page:${path}`,
|
|
12
|
+
path
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
error: null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
describe("createContentClient", ()=>{
|
|
21
|
+
let requests;
|
|
22
|
+
let delays;
|
|
23
|
+
beforeEach(()=>{
|
|
24
|
+
requests = [];
|
|
25
|
+
delays = {};
|
|
26
|
+
const fetchMock = vi.fn(async (url, init)=>{
|
|
27
|
+
const headers = init.headers;
|
|
28
|
+
const body = JSON.parse(init.body);
|
|
29
|
+
const path = body.variables.path;
|
|
30
|
+
requests.push({
|
|
31
|
+
url,
|
|
32
|
+
tenant: headers["X-Tenant"],
|
|
33
|
+
authorization: headers["Authorization"],
|
|
34
|
+
path
|
|
35
|
+
});
|
|
36
|
+
const delay = delays[path] ?? 0;
|
|
37
|
+
await new Promise((resolve)=>setTimeout(resolve, delay));
|
|
38
|
+
return {
|
|
39
|
+
json: async ()=>pageResponse(path)
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
43
|
+
});
|
|
44
|
+
afterEach(()=>{
|
|
45
|
+
vi.unstubAllGlobals();
|
|
46
|
+
});
|
|
47
|
+
it("sends each client's own tenant, host and key", async ()=>{
|
|
48
|
+
const clientA = createContentClient({
|
|
49
|
+
apiHost: "https://a.example.com",
|
|
50
|
+
apiKey: "key-a",
|
|
51
|
+
apiTenant: "tenant-a"
|
|
52
|
+
});
|
|
53
|
+
const clientB = createContentClient({
|
|
54
|
+
apiHost: "https://b.example.com",
|
|
55
|
+
apiKey: "key-b",
|
|
56
|
+
apiTenant: "tenant-b"
|
|
57
|
+
});
|
|
58
|
+
await clientA.getPage("/a");
|
|
59
|
+
await clientB.getPage("/b");
|
|
60
|
+
expect(requests).toEqual([
|
|
61
|
+
{
|
|
62
|
+
url: "https://a.example.com/graphql",
|
|
63
|
+
tenant: "tenant-a",
|
|
64
|
+
authorization: "Bearer key-a",
|
|
65
|
+
path: "/a"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
url: "https://b.example.com/graphql",
|
|
69
|
+
tenant: "tenant-b",
|
|
70
|
+
authorization: "Bearer key-b",
|
|
71
|
+
path: "/b"
|
|
72
|
+
}
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
it("keeps clients isolated when their calls interleave", async ()=>{
|
|
76
|
+
const clientA = createContentClient({
|
|
77
|
+
apiHost: "https://a.example.com",
|
|
78
|
+
apiKey: "key-a",
|
|
79
|
+
apiTenant: "tenant-a"
|
|
80
|
+
});
|
|
81
|
+
const clientB = createContentClient({
|
|
82
|
+
apiHost: "https://b.example.com",
|
|
83
|
+
apiKey: "key-b",
|
|
84
|
+
apiTenant: "tenant-b"
|
|
85
|
+
});
|
|
86
|
+
delays["/a"] = 20;
|
|
87
|
+
delays["/b"] = 0;
|
|
88
|
+
const [pageA, pageB] = await Promise.all([
|
|
89
|
+
clientA.getPage("/a"),
|
|
90
|
+
clientB.getPage("/b")
|
|
91
|
+
]);
|
|
92
|
+
expect(pageA?.properties.path).toBe("/a");
|
|
93
|
+
expect(pageB?.properties.path).toBe("/b");
|
|
94
|
+
const requestA = requests.find((request)=>"/a" === request.path);
|
|
95
|
+
const requestB = requests.find((request)=>"/b" === request.path);
|
|
96
|
+
expect(requestA).toMatchObject({
|
|
97
|
+
url: "https://a.example.com/graphql",
|
|
98
|
+
tenant: "tenant-a",
|
|
99
|
+
authorization: "Bearer key-a"
|
|
100
|
+
});
|
|
101
|
+
expect(requestB).toMatchObject({
|
|
102
|
+
url: "https://b.example.com/graphql",
|
|
103
|
+
tenant: "tenant-b",
|
|
104
|
+
authorization: "Bearer key-b"
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it("does not change what the contentSdk singleton sends, and init() does not change a client", async ()=>{
|
|
108
|
+
contentSdk.init({
|
|
109
|
+
apiHost: "https://singleton.example.com",
|
|
110
|
+
apiKey: "key-singleton",
|
|
111
|
+
apiTenant: "root"
|
|
112
|
+
});
|
|
113
|
+
const client = createContentClient({
|
|
114
|
+
apiHost: "https://client.example.com",
|
|
115
|
+
apiKey: "key-client",
|
|
116
|
+
apiTenant: "tenant-client"
|
|
117
|
+
});
|
|
118
|
+
await contentSdk.getPage("/singleton");
|
|
119
|
+
contentSdk.init({
|
|
120
|
+
apiHost: "https://singleton-2.example.com",
|
|
121
|
+
apiKey: "key-singleton-2",
|
|
122
|
+
apiTenant: "root-2"
|
|
123
|
+
});
|
|
124
|
+
await client.getPage("/client");
|
|
125
|
+
await contentSdk.getPage("/singleton-2");
|
|
126
|
+
expect(requests).toEqual([
|
|
127
|
+
{
|
|
128
|
+
url: "https://singleton.example.com/graphql",
|
|
129
|
+
tenant: "root",
|
|
130
|
+
authorization: "Bearer key-singleton",
|
|
131
|
+
path: "/singleton"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
url: "https://client.example.com/graphql",
|
|
135
|
+
tenant: "tenant-client",
|
|
136
|
+
authorization: "Bearer key-client",
|
|
137
|
+
path: "/client"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
url: "https://singleton-2.example.com/graphql",
|
|
141
|
+
tenant: "root-2",
|
|
142
|
+
authorization: "Bearer key-singleton-2",
|
|
143
|
+
path: "/singleton-2"
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
147
|
+
it("keeps the preview flag per client", ()=>{
|
|
148
|
+
const live = createContentClient({
|
|
149
|
+
apiHost: "https://a.example.com",
|
|
150
|
+
apiKey: "key-a",
|
|
151
|
+
apiTenant: "tenant-a"
|
|
152
|
+
});
|
|
153
|
+
const preview = createContentClient({
|
|
154
|
+
apiHost: "https://a.example.com",
|
|
155
|
+
apiKey: "key-a",
|
|
156
|
+
apiTenant: "tenant-a",
|
|
157
|
+
preview: true,
|
|
158
|
+
previewParams: "wb.type=page&wb.path=/a&wb.id=page-1"
|
|
159
|
+
});
|
|
160
|
+
expect(live.isPreviewing()).toBe(false);
|
|
161
|
+
expect(preview.isPreviewing()).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
//# sourceMappingURL=createContentClient.test.js.map
|