@webiny/website-builder-sdk 6.4.12-beta.4 → 6.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BindingsResolver.d.ts +4 -1
- package/BindingsResolver.js.map +1 -1
- package/ComponentResolver.d.ts +3 -2
- package/ComponentResolver.js +6 -3
- package/ComponentResolver.js.map +1 -1
- package/ContentSdk.d.ts +2 -0
- package/ContentSdk.js +5 -0
- package/ContentSdk.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.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 +102 -0
- package/asset/normalize.js.map +1 -0
- package/asset/normalize.test.d.ts +1 -0
- package/asset/normalize.test.js +256 -0
- package/asset/normalize.test.js.map +1 -0
- package/asset/types.d.ts +2 -0
- package/asset/types.js +0 -0
- package/contentEntry/ContentEntryBindingsResolver.d.ts +30 -0
- package/contentEntry/ContentEntryBindingsResolver.js +45 -0
- package/contentEntry/ContentEntryBindingsResolver.js.map +1 -0
- package/contentEntry/ContentEntryCache.d.ts +46 -0
- package/contentEntry/ContentEntryCache.js +50 -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/createInput.d.ts +10 -2
- package/createInput.js +8 -1
- package/createInput.js.map +1 -1
- package/dataProviders/ApiClient.d.ts +3 -1
- package/dataProviders/ApiClient.js +4 -2
- package/dataProviders/ApiClient.js.map +1 -1
- package/dataProviders/GET_PAGE_BY_ID.d.ts +1 -1
- package/dataProviders/GET_PAGE_BY_ID.js +0 -2
- 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 +0 -2
- package/dataProviders/GET_PAGE_BY_PATH.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 +6 -0
- package/index.js +6 -0
- package/package.json +7 -6
- package/types.d.ts +69 -1
- package/dataProviders/queries.test.js +0 -45
- package/dataProviders/queries.test.js.map +0 -1
- /package/{dataProviders/queries.test.d.ts → asset/deliveryUrl.test.d.ts} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { contentEntryCache } from "./ContentEntryCache.js";
|
|
2
|
+
import { environment } from "../Environment.js";
|
|
3
|
+
class ContentEntryBindingsResolver {
|
|
4
|
+
constructor(inner, resolvedEntries = new Map()){
|
|
5
|
+
this.inner = inner;
|
|
6
|
+
this.resolvedEntries = resolvedEntries;
|
|
7
|
+
}
|
|
8
|
+
resolveElement(params) {
|
|
9
|
+
const results = this.inner.resolveElement(params);
|
|
10
|
+
for (const instance of results)this.enrichContentEntryInputs(params.element.id, params.inputAst, instance.inputs);
|
|
11
|
+
return results;
|
|
12
|
+
}
|
|
13
|
+
enrichContentEntryInputs(elementId, ast, inputs) {
|
|
14
|
+
for (const node of ast){
|
|
15
|
+
if ("contentEntry" === node.input.type) {
|
|
16
|
+
const key = `${elementId}:${node.name}`;
|
|
17
|
+
const rawValue = inputs[node.name];
|
|
18
|
+
const list = node.input.list ?? false;
|
|
19
|
+
if (this.resolvedEntries.has(key)) {
|
|
20
|
+
inputs[node.name] = this.resolvedEntries.get(key);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (contentEntryCache.has(key)) {
|
|
24
|
+
inputs[node.name] = contentEntryCache.get(key);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (environment.isClient() && contentEntryCache.getLoader()) {
|
|
28
|
+
const cacheKey = `${key}:${JSON.stringify(rawValue ?? null)}`;
|
|
29
|
+
contentEntryCache.resolve(cacheKey, rawValue, list);
|
|
30
|
+
const cached = contentEntryCache.get(cacheKey);
|
|
31
|
+
if (void 0 !== cached) inputs[node.name] = cached;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (node.children.length > 0) {
|
|
35
|
+
const value = inputs[node.name];
|
|
36
|
+
if (node.list && Array.isArray(value)) {
|
|
37
|
+
for (const item of value)if ("object" == typeof item && null !== item) this.enrichContentEntryInputs(elementId, node.children, item);
|
|
38
|
+
} else if ("object" == typeof value && null !== value && !Array.isArray(value)) this.enrichContentEntryInputs(elementId, node.children, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export { ContentEntryBindingsResolver };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=ContentEntryBindingsResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentEntry/ContentEntryBindingsResolver.js","sources":["../../src/contentEntry/ContentEntryBindingsResolver.ts"],"sourcesContent":["import type { ResolvedElement } from \"~/types.js\";\nimport type { IBindingsResolver, ResolveElementParams } from \"~/BindingsResolver.js\";\nimport type { InputAstNode } from \"~/ComponentManifestToAstConverter.js\";\nimport { contentEntryCache } from \"./ContentEntryCache.js\";\nimport { environment } from \"~/Environment.js\";\n\n/**\n * Decorator for `IBindingsResolver` that resolves `contentEntry` inputs.\n *\n * The inner resolver evaluates binding expressions against the document state,\n * producing raw values (`{ id, modelId }` references or query params). This\n * decorator then replaces those raw values with pre-resolved CMS entries from\n * a supplied map, or (on the client) triggers async resolution via the\n * `contentEntryCache` singleton.\n *\n * **Server path**: `resolveContentEntries()` pre-fetches all entries, builds\n * the `resolvedEntries` map, and passes it here. The decorator synchronously\n * swaps raw values for resolved entries during render.\n *\n * **Client path (editor preview)**: `resolvedEntries` may be empty. The\n * decorator falls back to `contentEntryCache.resolve()` which kicks off an\n * async fetch; the mobx-observable cache triggers re-render when it lands.\n */\nexport class ContentEntryBindingsResolver implements IBindingsResolver {\n constructor(\n private readonly inner: IBindingsResolver,\n private readonly resolvedEntries: ReadonlyMap<string, unknown> = new Map()\n ) {}\n\n resolveElement(params: ResolveElementParams): ResolvedElement[] {\n const results = this.inner.resolveElement(params);\n\n for (const instance of results) {\n this.enrichContentEntryInputs(params.element.id, params.inputAst, instance.inputs);\n }\n\n return results;\n }\n\n /**\n * Walk the input AST and replace raw content-entry values with resolved\n * entries from the map or the module-level cache.\n */\n private enrichContentEntryInputs(\n elementId: string,\n ast: InputAstNode[],\n inputs: Record<string, any>\n ): void {\n for (const node of ast) {\n if (node.input.type === \"contentEntry\") {\n const key = `${elementId}:${node.name}`;\n const rawValue = inputs[node.name];\n const list = node.input.list ?? false;\n\n // 1. Pre-resolved entries map (seeded by server pre-pass).\n if (this.resolvedEntries.has(key)) {\n inputs[node.name] = this.resolvedEntries.get(key);\n continue;\n }\n\n // 2. Module-level cache (populated by previous renders or\n // async resolves on the client).\n if (contentEntryCache.has(key)) {\n inputs[node.name] = contentEntryCache.get(key);\n continue;\n }\n\n // 3. Client-side async resolution: trigger a background fetch.\n // The observable cache write will cause a re-render.\n if (environment.isClient() && contentEntryCache.getLoader()) {\n const cacheKey = `${key}:${JSON.stringify(rawValue ?? null)}`;\n contentEntryCache.resolve(cacheKey, rawValue, list);\n const cached = contentEntryCache.get(cacheKey);\n if (cached !== undefined) {\n inputs[node.name] = cached;\n }\n }\n }\n\n // Recurse into children (object / object-list nodes).\n if (node.children.length > 0) {\n const value = inputs[node.name];\n if (node.list && Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === \"object\" && item !== null) {\n this.enrichContentEntryInputs(elementId, node.children, item);\n }\n }\n } else if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n this.enrichContentEntryInputs(elementId, node.children, value);\n }\n }\n }\n }\n}\n"],"names":["ContentEntryBindingsResolver","inner","resolvedEntries","Map","params","results","instance","elementId","ast","inputs","node","key","rawValue","list","contentEntryCache","environment","cacheKey","JSON","cached","undefined","value","Array","item"],"mappings":";;AAuBO,MAAMA;IACT,YACqBC,KAAwB,EACxBC,kBAAgD,IAAIC,KAAK,CAC5E;aAFmBF,KAAK,GAALA;aACAC,eAAe,GAAfA;IAClB;IAEH,eAAeE,MAA4B,EAAqB;QAC5D,MAAMC,UAAU,IAAI,CAAC,KAAK,CAAC,cAAc,CAACD;QAE1C,KAAK,MAAME,YAAYD,QACnB,IAAI,CAAC,wBAAwB,CAACD,OAAO,OAAO,CAAC,EAAE,EAAEA,OAAO,QAAQ,EAAEE,SAAS,MAAM;QAGrF,OAAOD;IACX;IAMQ,yBACJE,SAAiB,EACjBC,GAAmB,EACnBC,MAA2B,EACvB;QACJ,KAAK,MAAMC,QAAQF,IAAK;YACpB,IAAIE,AAAoB,mBAApBA,KAAK,KAAK,CAAC,IAAI,EAAqB;gBACpC,MAAMC,MAAM,GAAGJ,UAAU,CAAC,EAAEG,KAAK,IAAI,EAAE;gBACvC,MAAME,WAAWH,MAAM,CAACC,KAAK,IAAI,CAAC;gBAClC,MAAMG,OAAOH,KAAK,KAAK,CAAC,IAAI,IAAI;gBAGhC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAACC,MAAM;oBAC/BF,MAAM,CAACC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAACC;oBAC7C;gBACJ;gBAIA,IAAIG,kBAAkB,GAAG,CAACH,MAAM;oBAC5BF,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGI,kBAAkB,GAAG,CAACH;oBAC1C;gBACJ;gBAIA,IAAII,YAAY,QAAQ,MAAMD,kBAAkB,SAAS,IAAI;oBACzD,MAAME,WAAW,GAAGL,IAAI,CAAC,EAAEM,KAAK,SAAS,CAACL,YAAY,OAAO;oBAC7DE,kBAAkB,OAAO,CAACE,UAAUJ,UAAUC;oBAC9C,MAAMK,SAASJ,kBAAkB,GAAG,CAACE;oBACrC,IAAIE,AAAWC,WAAXD,QACAT,MAAM,CAACC,KAAK,IAAI,CAAC,GAAGQ;gBAE5B;YACJ;YAGA,IAAIR,KAAK,QAAQ,CAAC,MAAM,GAAG,GAAG;gBAC1B,MAAMU,QAAQX,MAAM,CAACC,KAAK,IAAI,CAAC;gBAC/B,IAAIA,KAAK,IAAI,IAAIW,MAAM,OAAO,CAACD,QAC3B;oBAAA,KAAK,MAAME,QAAQF,MACf,IAAI,AAAgB,YAAhB,OAAOE,QAAqBA,AAAS,SAATA,MAC5B,IAAI,CAAC,wBAAwB,CAACf,WAAWG,KAAK,QAAQ,EAAEY;gBAEhE,OACG,IAAI,AAAiB,YAAjB,OAAOF,SAAsBA,AAAU,SAAVA,SAAkB,CAACC,MAAM,OAAO,CAACD,QACrE,IAAI,CAAC,wBAAwB,CAACb,WAAWG,KAAK,QAAQ,EAAEU;YAEhE;QACJ;IACJ;AACJ"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type ContentEntryLoader, type ResolvedContentEntry } from "./resolveContentEntry.js";
|
|
2
|
+
type RawValue = unknown | unknown[] | null | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* SDK-level mobx cache for resolved content-entry inputs. Mirrors the CMS
|
|
5
|
+
* `RefCache` pattern: a singleton observable store that both the server
|
|
6
|
+
* pre-pass (`resolveAutoLoad`) and the editor preview path can seed / read.
|
|
7
|
+
*
|
|
8
|
+
* - **Server / SSR**: `resolveAutoLoad` calls `set(key, resolved)` for every
|
|
9
|
+
* entry it pre-resolves. `BindingsResolver.resolveSingleInstance` reads via
|
|
10
|
+
* `get(key)` synchronously during render — no React context needed.
|
|
11
|
+
*
|
|
12
|
+
* - **Editor preview**: `resolve(key, input, value, loader)` triggers an async
|
|
13
|
+
* fetch whose result is written inside a mobx action, so `observer`
|
|
14
|
+
* components re-render once it lands. Keyed by a signature of the raw value,
|
|
15
|
+
* so when the editor changes the selection/query the key changes and the
|
|
16
|
+
* entry is re-resolved.
|
|
17
|
+
*/
|
|
18
|
+
declare class ContentEntryCacheImpl {
|
|
19
|
+
private cache;
|
|
20
|
+
private loader;
|
|
21
|
+
constructor();
|
|
22
|
+
get(key: string): ResolvedContentEntry | undefined;
|
|
23
|
+
has(key: string): boolean;
|
|
24
|
+
set(key: string, value: ResolvedContentEntry): void;
|
|
25
|
+
/**
|
|
26
|
+
* Register the CMS data loader that `resolve()` will use for async fetches.
|
|
27
|
+
* Called once during initialisation (e.g. by `resolveAutoLoad`). The loader
|
|
28
|
+
* is **not** mobx-observable — it's a plain service reference.
|
|
29
|
+
*/
|
|
30
|
+
setLoader(loader: ContentEntryLoader): void;
|
|
31
|
+
/**
|
|
32
|
+
* Return the registered loader, if any.
|
|
33
|
+
*/
|
|
34
|
+
getLoader(): ContentEntryLoader | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Lazily resolve `value` under `key`. Idempotent and safe to call during
|
|
37
|
+
* render: the observable write happens later, inside an action.
|
|
38
|
+
*
|
|
39
|
+
* Uses the loader registered via `setLoader()`. If no loader is registered,
|
|
40
|
+
* the call is a no-op (the raw value will be used instead).
|
|
41
|
+
*/
|
|
42
|
+
resolve(key: string, value: RawValue, list: boolean): void;
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
export declare const contentEntryCache: ContentEntryCacheImpl;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { makeAutoObservable, runInAction } from "mobx";
|
|
2
|
+
import { resolveContentEntryValue } from "./resolveContentEntry.js";
|
|
3
|
+
const pending = new Set();
|
|
4
|
+
class ContentEntryCacheImpl {
|
|
5
|
+
constructor(){
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
makeAutoObservable(this, {
|
|
8
|
+
setLoader: false,
|
|
9
|
+
getLoader: false
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
return this.cache.get(key);
|
|
14
|
+
}
|
|
15
|
+
has(key) {
|
|
16
|
+
return this.cache.has(key);
|
|
17
|
+
}
|
|
18
|
+
set(key, value) {
|
|
19
|
+
runInAction(()=>{
|
|
20
|
+
this.cache.set(key, value);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
setLoader(loader) {
|
|
24
|
+
this.loader = loader;
|
|
25
|
+
}
|
|
26
|
+
getLoader() {
|
|
27
|
+
return this.loader;
|
|
28
|
+
}
|
|
29
|
+
resolve(key, value, list) {
|
|
30
|
+
if (this.cache.has(key) || pending.has(key) || !this.loader) return;
|
|
31
|
+
pending.add(key);
|
|
32
|
+
resolveContentEntryValue(value, list, this.loader).then((resolved)=>{
|
|
33
|
+
runInAction(()=>{
|
|
34
|
+
this.cache.set(key, resolved);
|
|
35
|
+
});
|
|
36
|
+
pending.delete(key);
|
|
37
|
+
}).catch(()=>{
|
|
38
|
+
pending.delete(key);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
clear() {
|
|
42
|
+
runInAction(()=>{
|
|
43
|
+
this.cache.clear();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const contentEntryCache = new ContentEntryCacheImpl();
|
|
48
|
+
export { contentEntryCache };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=ContentEntryCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentEntry/ContentEntryCache.js","sources":["../../src/contentEntry/ContentEntryCache.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport {\n resolveContentEntryValue,\n type ContentEntryLoader,\n type ResolvedContentEntry\n} from \"./resolveContentEntry.js\";\n\ntype RawValue = unknown | unknown[] | null | undefined;\n\n// Non-observable dedupe set (kept out of the observable class so touching it\n// during render never counts as a mobx mutation-in-render).\nconst pending = new Set<string>();\n\n/**\n * SDK-level mobx cache for resolved content-entry inputs. Mirrors the CMS\n * `RefCache` pattern: a singleton observable store that both the server\n * pre-pass (`resolveAutoLoad`) and the editor preview path can seed / read.\n *\n * - **Server / SSR**: `resolveAutoLoad` calls `set(key, resolved)` for every\n * entry it pre-resolves. `BindingsResolver.resolveSingleInstance` reads via\n * `get(key)` synchronously during render — no React context needed.\n *\n * - **Editor preview**: `resolve(key, input, value, loader)` triggers an async\n * fetch whose result is written inside a mobx action, so `observer`\n * components re-render once it lands. Keyed by a signature of the raw value,\n * so when the editor changes the selection/query the key changes and the\n * entry is re-resolved.\n */\nclass ContentEntryCacheImpl {\n private cache = new Map<string, ResolvedContentEntry>();\n private loader: ContentEntryLoader | undefined;\n\n constructor() {\n makeAutoObservable(this, {\n // The loader is a plain service reference, not an observable value.\n setLoader: false,\n getLoader: false\n });\n }\n\n get(key: string): ResolvedContentEntry | undefined {\n return this.cache.get(key);\n }\n\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n set(key: string, value: ResolvedContentEntry): void {\n runInAction(() => {\n this.cache.set(key, value);\n });\n }\n\n /**\n * Register the CMS data loader that `resolve()` will use for async fetches.\n * Called once during initialisation (e.g. by `resolveAutoLoad`). The loader\n * is **not** mobx-observable — it's a plain service reference.\n */\n setLoader(loader: ContentEntryLoader): void {\n this.loader = loader;\n }\n\n /**\n * Return the registered loader, if any.\n */\n getLoader(): ContentEntryLoader | undefined {\n return this.loader;\n }\n\n /**\n * Lazily resolve `value` under `key`. Idempotent and safe to call during\n * render: the observable write happens later, inside an action.\n *\n * Uses the loader registered via `setLoader()`. If no loader is registered,\n * the call is a no-op (the raw value will be used instead).\n */\n resolve(key: string, value: RawValue, list: boolean): void {\n if (this.cache.has(key) || pending.has(key) || !this.loader) {\n return;\n }\n pending.add(key);\n resolveContentEntryValue(value as any, list, this.loader)\n .then(resolved => {\n runInAction(() => {\n this.cache.set(key, resolved);\n });\n pending.delete(key);\n })\n .catch(() => {\n pending.delete(key);\n });\n }\n\n clear(): void {\n runInAction(() => {\n this.cache.clear();\n });\n }\n}\n\nexport const contentEntryCache = new ContentEntryCacheImpl();\n"],"names":["pending","Set","ContentEntryCacheImpl","Map","makeAutoObservable","key","value","runInAction","loader","list","resolveContentEntryValue","resolved","contentEntryCache"],"mappings":";;AAWA,MAAMA,UAAU,IAAIC;AAiBpB,MAAMC;IAIF,aAAc;aAHN,KAAK,GAAG,IAAIC;QAIhBC,mBAAmB,IAAI,EAAE;YAErB,WAAW;YACX,WAAW;QACf;IACJ;IAEA,IAAIC,GAAW,EAAoC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAACA;IAC1B;IAEA,IAAIA,GAAW,EAAW;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAACA;IAC1B;IAEA,IAAIA,GAAW,EAAEC,KAA2B,EAAQ;QAChDC,YAAY;YACR,IAAI,CAAC,KAAK,CAAC,GAAG,CAACF,KAAKC;QACxB;IACJ;IAOA,UAAUE,MAA0B,EAAQ;QACxC,IAAI,CAAC,MAAM,GAAGA;IAClB;IAKA,YAA4C;QACxC,OAAO,IAAI,CAAC,MAAM;IACtB;IASA,QAAQH,GAAW,EAAEC,KAAe,EAAEG,IAAa,EAAQ;QACvD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAACJ,QAAQL,QAAQ,GAAG,CAACK,QAAQ,CAAC,IAAI,CAAC,MAAM,EACvD;QAEJL,QAAQ,GAAG,CAACK;QACZK,yBAAyBJ,OAAcG,MAAM,IAAI,CAAC,MAAM,EACnD,IAAI,CAACE,CAAAA;YACFJ,YAAY;gBACR,IAAI,CAAC,KAAK,CAAC,GAAG,CAACF,KAAKM;YACxB;YACAX,QAAQ,MAAM,CAACK;QACnB,GACC,KAAK,CAAC;YACHL,QAAQ,MAAM,CAACK;QACnB;IACR;IAEA,QAAc;QACVE,YAAY;YACR,IAAI,CAAC,KAAK,CAAC,KAAK;QACpB;IACJ;AACJ;AAEO,MAAMK,oBAAoB,IAAIV"}
|
|
@@ -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"}
|
package/createInput.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { TextInput, LongTextInput, NumberInput, BooleanInput, ColorInput, FileInput, LexicalInput, SelectInput, RadioInput, ObjectInput, DateTimeInput, ComponentInput, TagsInput, SlotInput } from "./types.js";
|
|
2
|
-
export type InputFactory<Name extends string> = ReturnType<typeof createTextInput<Name>> | ReturnType<typeof createLongTextInput<Name>> | ReturnType<typeof createNumberInput<Name>> | ReturnType<typeof createBooleanInput<Name>> | ReturnType<typeof createColorInput<Name>> | ReturnType<typeof createFileInput<Name>> | ReturnType<typeof createDateInput<Name>> | ReturnType<typeof createLexicalInput<Name>> | ReturnType<typeof createSelectInput<Name>> | ReturnType<typeof createRadioInput<Name>> | ReturnType<typeof createObjectInput<Name>> | ReturnType<typeof createTagsInput<Name>> | ReturnType<typeof createSlotInput<Name>>;
|
|
1
|
+
import type { TextInput, LongTextInput, NumberInput, BooleanInput, ColorInput, FileInput, LexicalInput, SelectInput, RadioInput, ObjectInput, DateTimeInput, ComponentInput, TagsInput, SlotInput, ContentEntryInput } from "./types.js";
|
|
2
|
+
export type InputFactory<Name extends string> = ReturnType<typeof createTextInput<Name>> | ReturnType<typeof createLongTextInput<Name>> | ReturnType<typeof createNumberInput<Name>> | ReturnType<typeof createBooleanInput<Name>> | ReturnType<typeof createColorInput<Name>> | ReturnType<typeof createFileInput<Name>> | ReturnType<typeof createDateInput<Name>> | ReturnType<typeof createLexicalInput<Name>> | ReturnType<typeof createSelectInput<Name>> | ReturnType<typeof createRadioInput<Name>> | ReturnType<typeof createObjectInput<Name>> | ReturnType<typeof createTagsInput<Name>> | ReturnType<typeof createSlotInput<Name>> | ReturnType<typeof createContentEntryInput<Name>>;
|
|
3
3
|
/**
|
|
4
4
|
* TypeScript Overload Resolution and Input Factory Design
|
|
5
5
|
*
|
|
@@ -122,4 +122,12 @@ export declare function createSlotInput<TName extends string>(input: {
|
|
|
122
122
|
export declare function createSlotInput<TName extends string>(input: Omit<SlotInput, "type" | "name">): SlotInput & {
|
|
123
123
|
name: TName;
|
|
124
124
|
};
|
|
125
|
+
export declare function createContentEntryInput<TName extends string>(input: {
|
|
126
|
+
name: TName;
|
|
127
|
+
} & Omit<ContentEntryInput, "type" | "name">): ContentEntryInput & {
|
|
128
|
+
name: TName;
|
|
129
|
+
};
|
|
130
|
+
export declare function createContentEntryInput<TName extends string>(input: Omit<ContentEntryInput, "type" | "name">): ContentEntryInput & {
|
|
131
|
+
name: TName;
|
|
132
|
+
};
|
|
125
133
|
export declare function createInput(input: ComponentInput): ComponentInput;
|
package/createInput.js
CHANGED
|
@@ -93,10 +93,17 @@ function createSlotInput(input) {
|
|
|
93
93
|
...input
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
+
function createContentEntryInput(input) {
|
|
97
|
+
return createInput({
|
|
98
|
+
type: "contentEntry",
|
|
99
|
+
renderer: "Webiny/ContentEntry",
|
|
100
|
+
...input
|
|
101
|
+
});
|
|
102
|
+
}
|
|
96
103
|
function createInput(input) {
|
|
97
104
|
if (input.onChange) input.onChange = functionConverter.serialize(input.onChange);
|
|
98
105
|
return input;
|
|
99
106
|
}
|
|
100
|
-
export { createBooleanInput, createColorInput, createDateInput, createFileInput, createInput, createLexicalInput, createLongTextInput, createNumberInput, createObjectInput, createRadioInput, createSelectInput, createSlotInput, createTagsInput, createTextInput };
|
|
107
|
+
export { createBooleanInput, createColorInput, createContentEntryInput, createDateInput, createFileInput, createInput, createLexicalInput, createLongTextInput, createNumberInput, createObjectInput, createRadioInput, createSelectInput, createSlotInput, createTagsInput, createTextInput };
|
|
101
108
|
|
|
102
109
|
//# sourceMappingURL=createInput.js.map
|