@webiny/website-builder-sdk 0.0.0-unstable.a4f3e4ea8b → 0.0.0-unstable.a85ef555b2

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.
Files changed (117) hide show
  1. package/ComponentInputTraverser.js +4 -0
  2. package/ComponentInputTraverser.js.map +1 -1
  3. package/ComponentResolver.d.ts +3 -1
  4. package/ComponentResolver.js +4 -2
  5. package/ComponentResolver.js.map +1 -1
  6. package/ContentSdk.d.ts +64 -1
  7. package/ContentSdk.js +80 -8
  8. package/ContentSdk.js.map +1 -1
  9. package/DocumentStoreManager.d.ts +9 -2
  10. package/DocumentStoreManager.js +24 -5
  11. package/DocumentStoreManager.js.map +1 -1
  12. package/EditingSdk.d.ts +7 -1
  13. package/EditingSdk.js +5 -3
  14. package/EditingSdk.js.map +1 -1
  15. package/ElementFactory.js +3 -1
  16. package/ElementFactory.js.map +1 -1
  17. package/ElementFactory.test.js +102 -0
  18. package/ElementFactory.test.js.map +1 -1
  19. package/LiveSdk.d.ts +3 -1
  20. package/LiveSdk.js +3 -2
  21. package/LiveSdk.js.map +1 -1
  22. package/PreviewDocument.d.ts +2 -1
  23. package/PreviewDocument.js +2 -2
  24. package/PreviewDocument.js.map +1 -1
  25. package/PreviewSdk.d.ts +3 -1
  26. package/PreviewSdk.js +3 -2
  27. package/PreviewSdk.js.map +1 -1
  28. package/ResizeObserver.js +1 -1
  29. package/ResizeObserver.js.map +1 -1
  30. package/asset/deliveryUrl.test.js +1 -1
  31. package/asset/deliveryUrl.test.js.map +1 -1
  32. package/asset/imageSrcSet.test.js +1 -1
  33. package/asset/imageSrcSet.test.js.map +1 -1
  34. package/asset/normalize.d.ts +1 -1
  35. package/asset/normalize.js +18 -14
  36. package/asset/normalize.js.map +1 -1
  37. package/asset/normalize.test.js +15 -8
  38. package/asset/normalize.test.js.map +1 -1
  39. package/componentLoader/ComponentLoader.d.ts +16 -0
  40. package/componentLoader/ComponentLoader.js +0 -0
  41. package/componentLoader/RegistryOnlyComponentLoader.d.ts +9 -0
  42. package/componentLoader/RegistryOnlyComponentLoader.js +8 -0
  43. package/componentLoader/RegistryOnlyComponentLoader.js.map +1 -0
  44. package/componentLoader/createComponentLoader.d.ts +7 -0
  45. package/componentLoader/createComponentLoader.js +10 -0
  46. package/componentLoader/createComponentLoader.js.map +1 -0
  47. package/componentLoader/defaultComponentLoader.d.ts +7 -0
  48. package/componentLoader/defaultComponentLoader.js +8 -0
  49. package/componentLoader/defaultComponentLoader.js.map +1 -0
  50. package/componentLoader/index.d.ts +3 -0
  51. package/componentLoader/index.js +3 -0
  52. package/contentClientState.test.d.ts +1 -0
  53. package/contentClientState.test.js +384 -0
  54. package/contentClientState.test.js.map +1 -0
  55. package/contentEntry/ContentEntryBindingsResolver.d.ts +8 -5
  56. package/contentEntry/ContentEntryBindingsResolver.js +30 -23
  57. package/contentEntry/ContentEntryBindingsResolver.js.map +1 -1
  58. package/contentEntry/ContentEntryCache.d.ts +7 -4
  59. package/contentEntry/ContentEntryCache.js +10 -9
  60. package/contentEntry/ContentEntryCache.js.map +1 -1
  61. package/createContentClient.d.ts +17 -0
  62. package/createContentClient.js +16 -0
  63. package/createContentClient.js.map +1 -0
  64. package/createContentClient.test.d.ts +1 -0
  65. package/createContentClient.test.js +165 -0
  66. package/createContentClient.test.js.map +1 -0
  67. package/dataProviders/ApiClient.js.map +1 -1
  68. package/dataProviders/DefaultDataProvider.d.ts +4 -0
  69. package/dataProviders/DefaultDataProvider.js +35 -0
  70. package/dataProviders/DefaultDataProvider.js.map +1 -1
  71. package/dataProviders/GET_EXPERIMENT_PAUSED.d.ts +1 -0
  72. package/dataProviders/GET_EXPERIMENT_PAUSED.js +17 -0
  73. package/dataProviders/GET_EXPERIMENT_PAUSED.js.map +1 -0
  74. package/dataProviders/GET_PAGE_BY_ID.d.ts +1 -1
  75. package/dataProviders/GET_PAGE_BY_ID.js +2 -0
  76. package/dataProviders/GET_PAGE_BY_ID.js.map +1 -1
  77. package/dataProviders/GET_PAGE_BY_PATH.d.ts +1 -1
  78. package/dataProviders/GET_PAGE_BY_PATH.js +2 -0
  79. package/dataProviders/GET_PAGE_BY_PATH.js.map +1 -1
  80. package/dataProviders/GET_PAGE_EXPERIMENT.d.ts +1 -0
  81. package/dataProviders/GET_PAGE_EXPERIMENT.js +32 -0
  82. package/dataProviders/GET_PAGE_EXPERIMENT.js.map +1 -0
  83. package/dataProviders/GET_VARIANT_CONTENT.d.ts +1 -0
  84. package/dataProviders/GET_VARIANT_CONTENT.js +24 -0
  85. package/dataProviders/GET_VARIANT_CONTENT.js.map +1 -0
  86. package/dataProviders/NullDataProvider.d.ts +4 -0
  87. package/dataProviders/NullDataProvider.js +9 -0
  88. package/dataProviders/NullDataProvider.js.map +1 -1
  89. package/dataProviders/queries.test.d.ts +1 -0
  90. package/dataProviders/queries.test.js +45 -0
  91. package/dataProviders/queries.test.js.map +1 -0
  92. package/experiments/PostHogAnalyticsProvider.d.ts +31 -0
  93. package/experiments/PostHogAnalyticsProvider.js +28 -0
  94. package/experiments/PostHogAnalyticsProvider.js.map +1 -0
  95. package/experiments/analyticsProvider.d.ts +4 -0
  96. package/experiments/analyticsProvider.js +11 -0
  97. package/experiments/analyticsProvider.js.map +1 -0
  98. package/experiments/bucketing.d.ts +27 -0
  99. package/experiments/bucketing.js +91 -0
  100. package/experiments/bucketing.js.map +1 -0
  101. package/experiments/bucketing.test.d.ts +1 -0
  102. package/experiments/bucketing.test.js +183 -0
  103. package/experiments/bucketing.test.js.map +1 -0
  104. package/experiments/index.d.ts +5 -0
  105. package/experiments/index.js +5 -0
  106. package/experiments/render.d.ts +82 -0
  107. package/experiments/render.js +149 -0
  108. package/experiments/render.js.map +1 -0
  109. package/experiments/types.d.ts +108 -0
  110. package/experiments/types.js +4 -0
  111. package/experiments/types.js.map +1 -0
  112. package/headersProvider.d.ts +1 -2
  113. package/headersProvider.js.map +1 -1
  114. package/index.d.ts +5 -0
  115. package/index.js +5 -0
  116. package/package.json +6 -6
  117. package/types.d.ts +5 -0
@@ -0,0 +1,384 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { createContentClient } from "./createContentClient.js";
3
+ import { ContentSdk, contentSdk } from "./ContentSdk.js";
4
+ import { componentRegistry } from "./ComponentRegistry.js";
5
+ import { contentEntryCache } from "./contentEntry/ContentEntryCache.js";
6
+ import { DEFAULT_MAX_DOCUMENT_STORES, DocumentStoreManager, documentStoreManager } from "./DocumentStoreManager.js";
7
+ import { resolveVisitorContext } from "./experiments/render.js";
8
+ import { setHeadersProvider } from "./headersProvider.js";
9
+ import { setDefaultComponentLoader } from "./componentLoader/defaultComponentLoader.js";
10
+ const componentLabels = {
11
+ "tenant-a": "Hero from tenant A",
12
+ "tenant-b": "Hero from tenant B"
13
+ };
14
+ const configFor = (tenant)=>({
15
+ apiHost: `https://${tenant}.example.com`,
16
+ apiKey: `key-${tenant}`,
17
+ apiTenant: tenant
18
+ });
19
+ const createBlueprint = (name, label)=>({
20
+ component: ()=>label,
21
+ manifest: {
22
+ name,
23
+ label,
24
+ inputs: [
25
+ {
26
+ type: "contentEntry",
27
+ name: "entry",
28
+ renderer: "Webiny/ContentEntry",
29
+ models: [
30
+ "article"
31
+ ]
32
+ }
33
+ ],
34
+ tags: []
35
+ }
36
+ });
37
+ const createDocument = (componentName)=>({
38
+ id: "page-1",
39
+ version: 1,
40
+ state: {},
41
+ properties: {
42
+ id: "page-1",
43
+ path: "/"
44
+ },
45
+ extensions: {},
46
+ metadata: {},
47
+ bindings: {},
48
+ elements: {
49
+ root: {
50
+ type: "Webiny/Element",
51
+ id: "root",
52
+ component: {
53
+ name: componentName
54
+ }
55
+ }
56
+ }
57
+ });
58
+ const tenantComponentLoader = ({ apiClient })=>({
59
+ async loadComponent (ref) {
60
+ const data = await apiClient.query({
61
+ query: "query LoadComponent($name: String!) { component(name: $name) { label } }",
62
+ variables: {
63
+ name: ref.name
64
+ }
65
+ });
66
+ return createBlueprint(ref.name, data.component.label);
67
+ }
68
+ });
69
+ const resolveLabel = (client, document)=>{
70
+ const resolved = client.resolveElement({
71
+ element: document.elements.root,
72
+ elementBindings: {},
73
+ state: document.state
74
+ });
75
+ if (!resolved) return null;
76
+ return {
77
+ label: resolved[0].manifest.label,
78
+ entry: resolved[0].inputs.entry
79
+ };
80
+ };
81
+ describe("content client state", ()=>{
82
+ let requestedTenants;
83
+ let requestedPageIds;
84
+ beforeEach(()=>{
85
+ requestedTenants = [];
86
+ requestedPageIds = {};
87
+ const fetchMock = vi.fn(async (_url, init)=>{
88
+ const headers = init.headers;
89
+ const tenant = headers["X-Tenant"];
90
+ const body = JSON.parse(init.body);
91
+ requestedTenants.push(tenant);
92
+ const delay = "tenant-a" === tenant ? 20 : 0;
93
+ await new Promise((resolve)=>setTimeout(resolve, delay));
94
+ if (body.query.includes("getPageById")) {
95
+ const id = body.variables.id;
96
+ requestedPageIds[tenant] = [
97
+ ...requestedPageIds[tenant] ?? [],
98
+ id
99
+ ];
100
+ const page = {
101
+ id,
102
+ properties: {
103
+ id,
104
+ path: "/",
105
+ title: `Draft of ${tenant}`
106
+ }
107
+ };
108
+ return {
109
+ json: async ()=>({
110
+ data: {
111
+ websiteBuilder: {
112
+ getPageById: {
113
+ data: page,
114
+ error: null
115
+ }
116
+ }
117
+ }
118
+ })
119
+ };
120
+ }
121
+ return {
122
+ json: async ()=>({
123
+ data: {
124
+ component: {
125
+ label: componentLabels[tenant]
126
+ }
127
+ }
128
+ })
129
+ };
130
+ });
131
+ vi.stubGlobal("fetch", fetchMock);
132
+ });
133
+ afterEach(()=>{
134
+ vi.unstubAllGlobals();
135
+ });
136
+ it("resolves same-name components and entries per client, concurrently", async ()=>{
137
+ const name = "Acme/Hero";
138
+ const document = createDocument(name);
139
+ const clientA = createContentClient({
140
+ ...configFor("tenant-a"),
141
+ componentLoader: tenantComponentLoader
142
+ });
143
+ const clientB = createContentClient({
144
+ ...configFor("tenant-b"),
145
+ componentLoader: tenantComponentLoader
146
+ });
147
+ clientA.getContentEntryCache().set("root:entry", {
148
+ id: "entry-a"
149
+ });
150
+ clientB.getContentEntryCache().set("root:entry", {
151
+ id: "entry-b"
152
+ });
153
+ const render = async (client)=>{
154
+ await client.loadDocumentComponents(document);
155
+ return resolveLabel(client, document);
156
+ };
157
+ const [resultA, resultB] = await Promise.all([
158
+ render(clientA),
159
+ render(clientB)
160
+ ]);
161
+ expect(requestedTenants.sort()).toEqual([
162
+ "tenant-a",
163
+ "tenant-b"
164
+ ]);
165
+ expect(resultA).toEqual({
166
+ label: "Hero from tenant A",
167
+ entry: {
168
+ id: "entry-a"
169
+ }
170
+ });
171
+ expect(resultB).toEqual({
172
+ label: "Hero from tenant B",
173
+ entry: {
174
+ id: "entry-b"
175
+ }
176
+ });
177
+ expect(componentRegistry.get(name)).toBeUndefined();
178
+ expect(contentEntryCache.has("root:entry")).toBe(false);
179
+ });
180
+ it("keeps the singleton and clients apart, and init() doesn't touch clients", ()=>{
181
+ const name = "Acme/SingletonOnly";
182
+ const client = createContentClient(configFor("tenant-a"));
183
+ contentSdk.init(configFor("tenant-root"));
184
+ contentSdk.registerComponent(createBlueprint(name, "Singleton component"));
185
+ expect(componentRegistry.get(name)).toBeDefined();
186
+ expect(client.getComponentRegistry().get(name)).toBeUndefined();
187
+ expect(resolveLabel(client, createDocument(name))).toBeNull();
188
+ client.registerComponent(createBlueprint("Acme/ClientOnly", "Client component"));
189
+ expect(componentRegistry.get("Acme/ClientOnly")).toBeUndefined();
190
+ contentSdk.init(configFor("tenant-other"));
191
+ expect(client.getComponentRegistry().get("Acme/ClientOnly")).toBeDefined();
192
+ expect(resolveLabel(contentSdk, createDocument(name))?.label).toBe("Singleton component");
193
+ });
194
+ it("uses the module-level state for contentSdk and a plain new ContentSdk(), as before", ()=>{
195
+ const sdk = new ContentSdk();
196
+ expect(contentSdk.getComponentRegistry()).toBe(componentRegistry);
197
+ expect(contentSdk.getContentEntryCache()).toBe(contentEntryCache);
198
+ expect(contentSdk.getDocumentStoreManager()).toBe(documentStoreManager);
199
+ expect(sdk.getComponentRegistry()).toBe(componentRegistry);
200
+ expect(sdk.getContentEntryCache()).toBe(contentEntryCache);
201
+ expect(sdk.getDocumentStoreManager()).toBe(documentStoreManager);
202
+ const client = createContentClient(configFor("tenant-a"));
203
+ expect(client.getComponentRegistry()).not.toBe(componentRegistry);
204
+ expect(client.getContentEntryCache()).not.toBe(contentEntryCache);
205
+ expect(client.getDocumentStoreManager()).not.toBe(documentStoreManager);
206
+ });
207
+ it("still requires init() on the singleton path", ()=>{
208
+ const sdk = new ContentSdk();
209
+ expect(()=>sdk.registerComponent(createBlueprint("Acme/X", "X"))).toThrow("ContentSdk has not been initialized!");
210
+ });
211
+ it("calls a custom loader with the refs of unregistered components only", async ()=>{
212
+ const refs = [];
213
+ const loader = {
214
+ async loadComponent (ref) {
215
+ refs.push(ref);
216
+ return createBlueprint(ref.name, `Loaded ${ref.name}`);
217
+ }
218
+ };
219
+ const client = createContentClient({
220
+ ...configFor("tenant-a"),
221
+ componentLoader: ()=>loader
222
+ });
223
+ client.registerComponent(createBlueprint("Acme/Registered", "Registered"));
224
+ const document = createDocument("Acme/Remote");
225
+ document.elements.second = {
226
+ type: "Webiny/Element",
227
+ id: "second",
228
+ component: {
229
+ name: "Acme/Registered"
230
+ }
231
+ };
232
+ document.elements.third = {
233
+ type: "Webiny/Element",
234
+ id: "third",
235
+ component: {
236
+ name: "Acme/Remote"
237
+ }
238
+ };
239
+ await client.loadDocumentComponents(document);
240
+ expect(refs).toEqual([
241
+ {
242
+ name: "Acme/Remote"
243
+ }
244
+ ]);
245
+ expect(resolveLabel(client, document)?.label).toBe("Loaded Acme/Remote");
246
+ await client.loadDocumentComponents(document);
247
+ expect(refs).toHaveLength(1);
248
+ });
249
+ it("uses the default loader, created per client with its own config", async ()=>{
250
+ const tenants = [];
251
+ setDefaultComponentLoader(({ config })=>{
252
+ tenants.push(config.apiTenant);
253
+ return {
254
+ async loadComponent (ref) {
255
+ return createBlueprint(ref.name, `Default for ${config.apiTenant}`);
256
+ }
257
+ };
258
+ });
259
+ try {
260
+ const document = createDocument("Acme/FromDefault");
261
+ const clientA = createContentClient(configFor("tenant-a"));
262
+ const clientB = createContentClient(configFor("tenant-b"));
263
+ await Promise.all([
264
+ clientA.loadDocumentComponents(document),
265
+ clientB.loadDocumentComponents(document)
266
+ ]);
267
+ expect(tenants.sort()).toEqual([
268
+ "tenant-a",
269
+ "tenant-b"
270
+ ]);
271
+ expect(resolveLabel(clientA, document)?.label).toBe("Default for tenant-a");
272
+ expect(resolveLabel(clientB, document)?.label).toBe("Default for tenant-b");
273
+ } finally{
274
+ setDefaultComponentLoader(void 0);
275
+ }
276
+ });
277
+ it("prefers the client's own loader over the default", async ()=>{
278
+ const defaultLoad = vi.fn();
279
+ setDefaultComponentLoader(()=>({
280
+ loadComponent: defaultLoad
281
+ }));
282
+ try {
283
+ const client = createContentClient({
284
+ ...configFor("tenant-a"),
285
+ componentLoader: ()=>({
286
+ async loadComponent (ref) {
287
+ return createBlueprint(ref.name, "Own loader");
288
+ }
289
+ })
290
+ });
291
+ const document = createDocument("Acme/Own");
292
+ await client.loadDocumentComponents(document);
293
+ expect(defaultLoad).not.toHaveBeenCalled();
294
+ expect(resolveLabel(client, document)?.label).toBe("Own loader");
295
+ } finally{
296
+ setDefaultComponentLoader(void 0);
297
+ }
298
+ });
299
+ it("fetches preview pages through each client, with the client's preview headers", async ()=>{
300
+ const previewClient = (tenant, pageId)=>createContentClient({
301
+ ...configFor(tenant),
302
+ preview: true,
303
+ headersProvider: async ()=>new Headers({
304
+ "X-Preview-Params": `wb.type=page&wb.path=/&wb.id=${pageId}`
305
+ })
306
+ });
307
+ const clientA = previewClient("tenant-a", "draft-a");
308
+ const clientB = previewClient("tenant-b", "draft-b");
309
+ const [pageA, pageB] = await Promise.all([
310
+ clientA.getPage("/"),
311
+ clientB.getPage("/")
312
+ ]);
313
+ expect(clientA.isPreviewing()).toBe(true);
314
+ expect(requestedPageIds).toEqual({
315
+ "tenant-a": [
316
+ "draft-a"
317
+ ],
318
+ "tenant-b": [
319
+ "draft-b"
320
+ ]
321
+ });
322
+ expect(pageA?.properties.title).toBe("Draft of tenant-a");
323
+ expect(pageB?.properties.title).toBe("Draft of tenant-b");
324
+ });
325
+ it("falls back to the global headers provider for preview, as before", async ()=>{
326
+ setHeadersProvider(async ()=>new Headers({
327
+ "X-Preview-Params": "wb.type=page&wb.path=/&wb.id=draft-global"
328
+ }));
329
+ const client = createContentClient({
330
+ ...configFor("tenant-b"),
331
+ preview: true
332
+ });
333
+ const page = await client.getPage("/");
334
+ expect(requestedPageIds).toEqual({
335
+ "tenant-b": [
336
+ "draft-global"
337
+ ]
338
+ });
339
+ expect(page?.properties.title).toBe("Draft of tenant-b");
340
+ });
341
+ it("loads nothing without a loader or a default", async ()=>{
342
+ const client = createContentClient(configFor("tenant-a"));
343
+ const document = createDocument("Acme/Unknown");
344
+ await client.loadDocumentComponents(document);
345
+ expect(requestedTenants).toEqual([]);
346
+ expect(resolveLabel(client, document)).toBeNull();
347
+ });
348
+ it("bounds the document stores and evicts the least recently used one", ()=>{
349
+ const stores = new DocumentStoreManager({
350
+ maxEntries: 2
351
+ });
352
+ const first = stores.getStore("a");
353
+ stores.getStore("b");
354
+ expect(stores.getStore("a")).toBe(first);
355
+ stores.getStore("c");
356
+ expect(stores.hasStore("a")).toBe(true);
357
+ expect(stores.hasStore("b")).toBe(false);
358
+ expect(stores.hasStore("c")).toBe(true);
359
+ expect(stores.getStores().size).toBe(2);
360
+ stores.clear();
361
+ expect(stores.getStores().size).toBe(0);
362
+ });
363
+ it("keeps every store below the default bound", ()=>{
364
+ const stores = new DocumentStoreManager();
365
+ for(let i = 0; i < 1000; i++)stores.getStore(`page-${i}`);
366
+ expect(stores.getStores().size).toBe(1000);
367
+ expect(DEFAULT_MAX_DOCUMENT_STORES).toBe(10000);
368
+ });
369
+ it("reads request headers from the client's provider, else the global one", async ()=>{
370
+ setHeadersProvider(async ()=>new Headers({
371
+ cookie: "wb_ab_vid=global-visitor"
372
+ }));
373
+ const fromGlobal = await resolveVisitorContext();
374
+ expect(fromGlobal.context.visitorId).toBe("global-visitor");
375
+ const fromOption = await resolveVisitorContext({
376
+ headersProvider: async ()=>new Headers({
377
+ cookie: "wb_ab_vid=client-visitor"
378
+ })
379
+ });
380
+ expect(fromOption.context.visitorId).toBe("client-visitor");
381
+ });
382
+ });
383
+
384
+ //# sourceMappingURL=contentClientState.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentClientState.test.js","sources":["../src/contentClientState.test.ts"],"sourcesContent":["import { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport { createContentClient } from \"./createContentClient.js\";\nimport { ContentSdk, 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\";\nimport { DocumentStoreManager } from \"./DocumentStoreManager.js\";\nimport { DEFAULT_MAX_DOCUMENT_STORES } from \"./DocumentStoreManager.js\";\nimport type { ComponentLoader } from \"./componentLoader/ComponentLoader.js\";\nimport type { Component, Document, ElementComponent } from \"./types.js\";\nimport { resolveVisitorContext } from \"./experiments/render.js\";\nimport { setHeadersProvider } from \"./headersProvider.js\";\nimport { setDefaultComponentLoader } from \"./componentLoader/defaultComponentLoader.js\";\n\n// What the stubbed API returns for a component, per tenant.\nconst componentLabels: Record<string, string> = {\n \"tenant-a\": \"Hero from tenant A\",\n \"tenant-b\": \"Hero from tenant B\"\n};\n\nconst configFor = (tenant: string): ContentSDKConfig => {\n return {\n apiHost: `https://${tenant}.example.com`,\n apiKey: `key-${tenant}`,\n apiTenant: tenant\n };\n};\n\nconst createBlueprint = (name: string, label: string): Component => {\n return {\n component: () => label,\n manifest: {\n name,\n label,\n inputs: [\n {\n type: \"contentEntry\",\n name: \"entry\",\n renderer: \"Webiny/ContentEntry\",\n models: [\"article\"]\n }\n ],\n tags: []\n }\n };\n};\n\nconst createDocument = (componentName: string): Document => {\n return {\n id: \"page-1\",\n version: 1,\n state: {},\n properties: { id: \"page-1\", path: \"/\" },\n extensions: {},\n metadata: {} as Document[\"metadata\"],\n bindings: {},\n elements: {\n root: {\n type: \"Webiny/Element\",\n id: \"root\",\n component: { name: componentName }\n }\n }\n };\n};\n\n// A loader that fetches the component through the client's own API client.\nconst tenantComponentLoader: NonNullable<ContentSDKConfig[\"componentLoader\"]> = ({ apiClient }) => {\n return {\n async loadComponent(ref: ElementComponent) {\n const data = await apiClient.query({\n query: \"query LoadComponent($name: String!) { component(name: $name) { label } }\",\n variables: { name: ref.name }\n });\n return createBlueprint(ref.name, data.component.label);\n }\n };\n};\n\nconst resolveLabel = (client: Pick<ContentSdk, \"resolveElement\">, document: Document) => {\n const resolved = client.resolveElement({\n element: document.elements.root,\n elementBindings: {},\n state: document.state\n });\n if (!resolved) {\n return null;\n }\n return { label: resolved[0].manifest.label, entry: resolved[0].inputs.entry };\n};\n\ndescribe(\"content client state\", () => {\n let requestedTenants: string[];\n let requestedPageIds: Record<string, string[]>;\n\n beforeEach(() => {\n requestedTenants = [];\n requestedPageIds = {};\n\n const fetchMock = vi.fn(async (_url: string, init: RequestInit) => {\n const headers = init.headers as Record<string, string>;\n const tenant = headers[\"X-Tenant\"];\n const body = JSON.parse(init.body as string);\n requestedTenants.push(tenant);\n\n // Tenant A answers last, so the two loads interleave.\n const delay = tenant === \"tenant-a\" ? 20 : 0;\n await new Promise(resolve => setTimeout(resolve, delay));\n\n if (body.query.includes(\"getPageById\")) {\n const id: string = body.variables.id;\n requestedPageIds[tenant] = [...(requestedPageIds[tenant] ?? []), id];\n const page = { id, properties: { id, path: \"/\", title: `Draft of ${tenant}` } };\n return {\n json: async () => ({\n data: { websiteBuilder: { getPageById: { data: page, error: null } } }\n })\n };\n }\n\n return {\n json: async () => ({ data: { component: { label: componentLabels[tenant] } } })\n };\n });\n\n vi.stubGlobal(\"fetch\", fetchMock);\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n });\n\n it(\"resolves same-name components and entries per client, concurrently\", async () => {\n const name = \"Acme/Hero\";\n const document = createDocument(name);\n\n const clientA = createContentClient({\n ...configFor(\"tenant-a\"),\n componentLoader: tenantComponentLoader\n });\n const clientB = createContentClient({\n ...configFor(\"tenant-b\"),\n componentLoader: tenantComponentLoader\n });\n\n clientA.getContentEntryCache().set(\"root:entry\", { id: \"entry-a\" });\n clientB.getContentEntryCache().set(\"root:entry\", { id: \"entry-b\" });\n\n const render = async (client: typeof clientA) => {\n await client.loadDocumentComponents(document);\n return resolveLabel(client, document);\n };\n\n const [resultA, resultB] = await Promise.all([render(clientA), render(clientB)]);\n\n expect(requestedTenants.sort()).toEqual([\"tenant-a\", \"tenant-b\"]);\n expect(resultA).toEqual({ label: \"Hero from tenant A\", entry: { id: \"entry-a\" } });\n expect(resultB).toEqual({ label: \"Hero from tenant B\", entry: { id: \"entry-b\" } });\n\n // Nothing reached the singleton's state.\n expect(componentRegistry.get(name)).toBeUndefined();\n expect(contentEntryCache.has(\"root:entry\")).toBe(false);\n });\n\n it(\"keeps the singleton and clients apart, and init() doesn't touch clients\", () => {\n const name = \"Acme/SingletonOnly\";\n const client = createContentClient(configFor(\"tenant-a\"));\n\n contentSdk.init(configFor(\"tenant-root\"));\n contentSdk.registerComponent(createBlueprint(name, \"Singleton component\"));\n\n expect(componentRegistry.get(name)).toBeDefined();\n expect(client.getComponentRegistry().get(name)).toBeUndefined();\n expect(resolveLabel(client, createDocument(name))).toBeNull();\n\n client.registerComponent(createBlueprint(\"Acme/ClientOnly\", \"Client component\"));\n expect(componentRegistry.get(\"Acme/ClientOnly\")).toBeUndefined();\n\n contentSdk.init(configFor(\"tenant-other\"));\n expect(client.getComponentRegistry().get(\"Acme/ClientOnly\")).toBeDefined();\n expect(resolveLabel(contentSdk, createDocument(name))?.label).toBe(\"Singleton component\");\n });\n\n it(\"uses the module-level state for contentSdk and a plain new ContentSdk(), as before\", () => {\n const sdk = new ContentSdk();\n\n expect(contentSdk.getComponentRegistry()).toBe(componentRegistry);\n expect(contentSdk.getContentEntryCache()).toBe(contentEntryCache);\n expect(contentSdk.getDocumentStoreManager()).toBe(documentStoreManager);\n expect(sdk.getComponentRegistry()).toBe(componentRegistry);\n expect(sdk.getContentEntryCache()).toBe(contentEntryCache);\n expect(sdk.getDocumentStoreManager()).toBe(documentStoreManager);\n\n const client = createContentClient(configFor(\"tenant-a\"));\n expect(client.getComponentRegistry()).not.toBe(componentRegistry);\n expect(client.getContentEntryCache()).not.toBe(contentEntryCache);\n expect(client.getDocumentStoreManager()).not.toBe(documentStoreManager);\n });\n\n it(\"still requires init() on the singleton path\", () => {\n const sdk = new ContentSdk();\n expect(() => sdk.registerComponent(createBlueprint(\"Acme/X\", \"X\"))).toThrow(\n \"ContentSdk has not been initialized!\"\n );\n });\n\n it(\"calls a custom loader with the refs of unregistered components only\", async () => {\n const refs: ElementComponent[] = [];\n const loader: ComponentLoader = {\n async loadComponent(ref) {\n refs.push(ref);\n return createBlueprint(ref.name, `Loaded ${ref.name}`);\n }\n };\n\n const client = createContentClient({\n ...configFor(\"tenant-a\"),\n componentLoader: () => loader\n });\n client.registerComponent(createBlueprint(\"Acme/Registered\", \"Registered\"));\n\n const document = createDocument(\"Acme/Remote\");\n document.elements.second = {\n type: \"Webiny/Element\",\n id: \"second\",\n component: { name: \"Acme/Registered\" }\n };\n document.elements.third = {\n type: \"Webiny/Element\",\n id: \"third\",\n component: { name: \"Acme/Remote\" }\n };\n\n await client.loadDocumentComponents(document);\n expect(refs).toEqual([{ name: \"Acme/Remote\" }]);\n expect(resolveLabel(client, document)?.label).toBe(\"Loaded Acme/Remote\");\n\n // Registered now, so the loader isn't asked again.\n await client.loadDocumentComponents(document);\n expect(refs).toHaveLength(1);\n });\n\n it(\"uses the default loader, created per client with its own config\", async () => {\n const tenants: string[] = [];\n setDefaultComponentLoader(({ config }) => {\n tenants.push(config.apiTenant);\n return {\n async loadComponent(ref) {\n return createBlueprint(ref.name, `Default for ${config.apiTenant}`);\n }\n };\n });\n\n try {\n const document = createDocument(\"Acme/FromDefault\");\n const clientA = createContentClient(configFor(\"tenant-a\"));\n const clientB = createContentClient(configFor(\"tenant-b\"));\n\n await Promise.all([\n clientA.loadDocumentComponents(document),\n clientB.loadDocumentComponents(document)\n ]);\n\n expect(tenants.sort()).toEqual([\"tenant-a\", \"tenant-b\"]);\n expect(resolveLabel(clientA, document)?.label).toBe(\"Default for tenant-a\");\n expect(resolveLabel(clientB, document)?.label).toBe(\"Default for tenant-b\");\n } finally {\n setDefaultComponentLoader(undefined);\n }\n });\n\n it(\"prefers the client's own loader over the default\", async () => {\n const defaultLoad = vi.fn();\n setDefaultComponentLoader(() => ({ loadComponent: defaultLoad }));\n\n try {\n const client = createContentClient({\n ...configFor(\"tenant-a\"),\n componentLoader: () => ({\n async loadComponent(ref) {\n return createBlueprint(ref.name, \"Own loader\");\n }\n })\n });\n const document = createDocument(\"Acme/Own\");\n\n await client.loadDocumentComponents(document);\n\n expect(defaultLoad).not.toHaveBeenCalled();\n expect(resolveLabel(client, document)?.label).toBe(\"Own loader\");\n } finally {\n setDefaultComponentLoader(undefined);\n }\n });\n\n it(\"fetches preview pages through each client, with the client's preview headers\", async () => {\n const previewClient = (tenant: string, pageId: string) => {\n return createContentClient({\n ...configFor(tenant),\n preview: true,\n headersProvider: async () =>\n new Headers({ \"X-Preview-Params\": `wb.type=page&wb.path=/&wb.id=${pageId}` })\n });\n };\n const clientA = previewClient(\"tenant-a\", \"draft-a\");\n const clientB = previewClient(\"tenant-b\", \"draft-b\");\n\n const [pageA, pageB] = await Promise.all([clientA.getPage(\"/\"), clientB.getPage(\"/\")]);\n\n expect(clientA.isPreviewing()).toBe(true);\n expect(requestedPageIds).toEqual({ \"tenant-a\": [\"draft-a\"], \"tenant-b\": [\"draft-b\"] });\n expect(pageA?.properties.title).toBe(\"Draft of tenant-a\");\n expect(pageB?.properties.title).toBe(\"Draft of tenant-b\");\n });\n\n it(\"falls back to the global headers provider for preview, as before\", async () => {\n setHeadersProvider(\n async () =>\n new Headers({ \"X-Preview-Params\": \"wb.type=page&wb.path=/&wb.id=draft-global\" })\n );\n const client = createContentClient({ ...configFor(\"tenant-b\"), preview: true });\n\n const page = await client.getPage(\"/\");\n\n expect(requestedPageIds).toEqual({ \"tenant-b\": [\"draft-global\"] });\n expect(page?.properties.title).toBe(\"Draft of tenant-b\");\n });\n\n it(\"loads nothing without a loader or a default\", async () => {\n const client = createContentClient(configFor(\"tenant-a\"));\n const document = createDocument(\"Acme/Unknown\");\n\n await client.loadDocumentComponents(document);\n\n expect(requestedTenants).toEqual([]);\n expect(resolveLabel(client, document)).toBeNull();\n });\n\n it(\"bounds the document stores and evicts the least recently used one\", () => {\n const stores = new DocumentStoreManager({ maxEntries: 2 });\n const first = stores.getStore(\"a\");\n stores.getStore(\"b\");\n\n // Touch \"a\", so \"b\" is now the least recently used.\n expect(stores.getStore(\"a\")).toBe(first);\n stores.getStore(\"c\");\n\n expect(stores.hasStore(\"a\")).toBe(true);\n expect(stores.hasStore(\"b\")).toBe(false);\n expect(stores.hasStore(\"c\")).toBe(true);\n expect(stores.getStores().size).toBe(2);\n\n stores.clear();\n expect(stores.getStores().size).toBe(0);\n });\n\n it(\"keeps every store below the default bound\", () => {\n const stores = new DocumentStoreManager();\n for (let i = 0; i < 1000; i++) {\n stores.getStore(`page-${i}`);\n }\n expect(stores.getStores().size).toBe(1000);\n expect(DEFAULT_MAX_DOCUMENT_STORES).toBe(10_000);\n });\n\n it(\"reads request headers from the client's provider, else the global one\", async () => {\n setHeadersProvider(async () => new Headers({ cookie: \"wb_ab_vid=global-visitor\" }));\n\n const fromGlobal = await resolveVisitorContext();\n expect(fromGlobal.context.visitorId).toBe(\"global-visitor\");\n\n const fromOption = await resolveVisitorContext({\n headersProvider: async () => new Headers({ cookie: \"wb_ab_vid=client-visitor\" })\n });\n expect(fromOption.context.visitorId).toBe(\"client-visitor\");\n });\n});\n"],"names":["componentLabels","configFor","tenant","createBlueprint","name","label","createDocument","componentName","tenantComponentLoader","apiClient","ref","data","resolveLabel","client","document","resolved","describe","requestedTenants","requestedPageIds","beforeEach","fetchMock","vi","_url","init","headers","body","JSON","delay","Promise","resolve","setTimeout","id","page","afterEach","it","clientA","createContentClient","clientB","render","resultA","resultB","expect","componentRegistry","contentEntryCache","contentSdk","sdk","ContentSdk","documentStoreManager","refs","loader","tenants","setDefaultComponentLoader","config","undefined","defaultLoad","previewClient","pageId","Headers","pageA","pageB","setHeadersProvider","stores","DocumentStoreManager","first","i","DEFAULT_MAX_DOCUMENT_STORES","fromGlobal","resolveVisitorContext","fromOption"],"mappings":"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"}
@@ -1,30 +1,33 @@
1
1
  import type { ResolvedElement } from "../types.js";
2
2
  import type { IBindingsResolver, ResolveElementParams } from "../BindingsResolver.js";
3
+ import type { ContentEntryCache } from "./ContentEntryCache.js";
3
4
  /**
4
5
  * Decorator for `IBindingsResolver` that resolves `contentEntry` inputs.
5
6
  *
6
7
  * The inner resolver evaluates binding expressions against the document state,
7
8
  * producing raw values (`{ id, modelId }` references or query params). This
8
9
  * decorator then replaces those raw values with pre-resolved CMS entries from
9
- * a supplied map, or (on the client) triggers async resolution via the
10
- * `contentEntryCache` singleton.
10
+ * a supplied map, or (on the client) triggers async resolution via a
11
+ * `ContentEntryCache`. The cache defaults to the `contentEntryCache` singleton.
11
12
  *
12
13
  * **Server path**: `resolveContentEntries()` pre-fetches all entries, builds
13
14
  * the `resolvedEntries` map, and passes it here. The decorator synchronously
14
15
  * swaps raw values for resolved entries during render.
15
16
  *
16
17
  * **Client path (editor preview)**: `resolvedEntries` may be empty. The
17
- * decorator falls back to `contentEntryCache.resolve()` which kicks off an
18
+ * decorator falls back to the cache's `resolve()`, which kicks off an
18
19
  * async fetch; the mobx-observable cache triggers re-render when it lands.
19
20
  */
20
21
  export declare class ContentEntryBindingsResolver implements IBindingsResolver {
21
22
  private readonly inner;
22
23
  private readonly resolvedEntries;
23
- constructor(inner: IBindingsResolver, resolvedEntries?: ReadonlyMap<string, unknown>);
24
+ private readonly cache;
25
+ constructor(inner: IBindingsResolver, resolvedEntries?: ReadonlyMap<string, unknown>, cache?: ContentEntryCache);
24
26
  resolveElement(params: ResolveElementParams): ResolvedElement[];
27
+ private isUnsafePropertyKey;
25
28
  /**
26
29
  * Walk the input AST and replace raw content-entry values with resolved
27
- * entries from the map or the module-level cache.
30
+ * entries from the map or the cache.
28
31
  */
29
32
  private enrichContentEntryInputs;
30
33
  }
@@ -1,42 +1,49 @@
1
1
  import { contentEntryCache } from "./ContentEntryCache.js";
2
2
  import { environment } from "../Environment.js";
3
3
  class ContentEntryBindingsResolver {
4
- constructor(inner, resolvedEntries = new Map()){
4
+ constructor(inner, resolvedEntries = new Map(), cache = contentEntryCache){
5
5
  this.inner = inner;
6
6
  this.resolvedEntries = resolvedEntries;
7
+ this.cache = cache;
7
8
  }
8
9
  resolveElement(params) {
9
10
  const results = this.inner.resolveElement(params);
10
11
  for (const instance of results)this.enrichContentEntryInputs(params.element.id, params.inputAst, instance.inputs);
11
12
  return results;
12
13
  }
14
+ isUnsafePropertyKey(key) {
15
+ return "__proto__" === key || "constructor" === key || "prototype" === key;
16
+ }
13
17
  enrichContentEntryInputs(elementId, ast, inputs) {
14
18
  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;
19
+ const inputName = node.name;
20
+ if (!this.isUnsafePropertyKey(inputName)) {
21
+ if ("contentEntry" === node.input.type) {
22
+ const key = `${elementId}:${inputName}`;
23
+ const rawValue = inputs[inputName];
24
+ const list = node.input.list ?? false;
25
+ if (this.resolvedEntries.has(key)) {
26
+ inputs[inputName] = this.resolvedEntries.get(key);
27
+ continue;
28
+ }
29
+ if (this.cache.has(key)) {
30
+ inputs[inputName] = this.cache.get(key);
31
+ continue;
32
+ }
33
+ if (environment.isClient() && this.cache.getLoader()) {
34
+ const cacheKey = `${key}:${JSON.stringify(rawValue ?? null)}`;
35
+ this.cache.resolve(cacheKey, rawValue, list);
36
+ const cached = this.cache.get(cacheKey);
37
+ if (void 0 !== cached) inputs[inputName] = cached;
38
+ }
26
39
  }
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;
40
+ if (node.children.length > 0) {
41
+ const value = inputs[inputName];
42
+ if (node.list && Array.isArray(value)) {
43
+ for (const item of value)if ("object" == typeof item && null !== item) this.enrichContentEntryInputs(elementId, node.children, item);
44
+ } else if ("object" == typeof value && null !== value && !Array.isArray(value)) this.enrichContentEntryInputs(elementId, node.children, value);
32
45
  }
33
46
  }
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
47
  }
41
48
  }
42
49
  }
@@ -1 +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"}
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 type { ContentEntryCache } from \"./ContentEntryCache.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 a\n * `ContentEntryCache`. The cache defaults to the `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 the cache's `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 private readonly cache: ContentEntryCache = contentEntryCache\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 private isUnsafePropertyKey(key: string): boolean {\n return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n }\n\n /**\n * Walk the input AST and replace raw content-entry values with resolved\n * entries from the map or the 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 const inputName = node.name;\n if (this.isUnsafePropertyKey(inputName)) {\n continue;\n }\n\n if (node.input.type === \"contentEntry\") {\n const key = `${elementId}:${inputName}`;\n const rawValue = inputs[inputName];\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[inputName] = this.resolvedEntries.get(key);\n continue;\n }\n\n // 2. Cache (populated by previous renders or async resolves on\n // the client).\n if (this.cache.has(key)) {\n inputs[inputName] = this.cache.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() && this.cache.getLoader()) {\n const cacheKey = `${key}:${JSON.stringify(rawValue ?? null)}`;\n this.cache.resolve(cacheKey, rawValue, list);\n const cached = this.cache.get(cacheKey);\n if (cached !== undefined) {\n inputs[inputName] = cached;\n }\n }\n }\n\n // Recurse into children (object / object-list nodes).\n if (node.children.length > 0) {\n const value = inputs[inputName];\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","cache","contentEntryCache","params","results","instance","key","elementId","ast","inputs","node","inputName","rawValue","list","environment","cacheKey","JSON","cached","undefined","value","Array","item"],"mappings":";;AAwBO,MAAMA;IACT,YACqBC,KAAwB,EACxBC,kBAAgD,IAAIC,KAAK,EACzDC,QAA2BC,iBAAiB,CAC/D;aAHmBJ,KAAK,GAALA;aACAC,eAAe,GAAfA;aACAE,KAAK,GAALA;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;IAEQ,oBAAoBE,GAAW,EAAW;QAC9C,OAAOA,AAAQ,gBAARA,OAAuBA,AAAQ,kBAARA,OAAyBA,AAAQ,gBAARA;IAC3D;IAMQ,yBACJC,SAAiB,EACjBC,GAAmB,EACnBC,MAA2B,EACvB;QACJ,KAAK,MAAMC,QAAQF,IAAK;YACpB,MAAMG,YAAYD,KAAK,IAAI;YAC3B,KAAI,IAAI,CAAC,mBAAmB,CAACC;gBAI7B,IAAID,AAAoB,mBAApBA,KAAK,KAAK,CAAC,IAAI,EAAqB;oBACpC,MAAMJ,MAAM,GAAGC,UAAU,CAAC,EAAEI,WAAW;oBACvC,MAAMC,WAAWH,MAAM,CAACE,UAAU;oBAClC,MAAME,OAAOH,KAAK,KAAK,CAAC,IAAI,IAAI;oBAGhC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAACJ,MAAM;wBAC/BG,MAAM,CAACE,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAACL;wBAC7C;oBACJ;oBAIA,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAACA,MAAM;wBACrBG,MAAM,CAACE,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAACL;wBACnC;oBACJ;oBAIA,IAAIQ,YAAY,QAAQ,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI;wBAClD,MAAMC,WAAW,GAAGT,IAAI,CAAC,EAAEU,KAAK,SAAS,CAACJ,YAAY,OAAO;wBAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAACG,UAAUH,UAAUC;wBACvC,MAAMI,SAAS,IAAI,CAAC,KAAK,CAAC,GAAG,CAACF;wBAC9B,IAAIE,AAAWC,WAAXD,QACAR,MAAM,CAACE,UAAU,GAAGM;oBAE5B;gBACJ;gBAGA,IAAIP,KAAK,QAAQ,CAAC,MAAM,GAAG,GAAG;oBAC1B,MAAMS,QAAQV,MAAM,CAACE,UAAU;oBAC/B,IAAID,KAAK,IAAI,IAAIU,MAAM,OAAO,CAACD,QAC3B;wBAAA,KAAK,MAAME,QAAQF,MACf,IAAI,AAAgB,YAAhB,OAAOE,QAAqBA,AAAS,SAATA,MAC5B,IAAI,CAAC,wBAAwB,CAACd,WAAWG,KAAK,QAAQ,EAAEW;oBAEhE,OACG,IAAI,AAAiB,YAAjB,OAAOF,SAAsBA,AAAU,SAAVA,SAAkB,CAACC,MAAM,OAAO,CAACD,QACrE,IAAI,CAAC,wBAAwB,CAACZ,WAAWG,KAAK,QAAQ,EAAES;gBAEhE;;QACJ;IACJ;AACJ"}
@@ -2,8 +2,10 @@ import { type ContentEntryLoader, type ResolvedContentEntry } from "./resolveCon
2
2
  type RawValue = unknown | unknown[] | null | undefined;
3
3
  /**
4
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.
5
+ * `RefCache` pattern: an observable store that both the server pre-pass
6
+ * (`resolveAutoLoad`) and the editor preview path can seed / read. The
7
+ * `contentEntryCache` singleton is used by `contentSdk`; each content client
8
+ * created with `createContentClient` holds its own instance.
7
9
  *
8
10
  * - **Server / SSR**: `resolveAutoLoad` calls `set(key, resolved)` for every
9
11
  * entry it pre-resolves. `BindingsResolver.resolveSingleInstance` reads via
@@ -15,9 +17,10 @@ type RawValue = unknown | unknown[] | null | undefined;
15
17
  * so when the editor changes the selection/query the key changes and the
16
18
  * entry is re-resolved.
17
19
  */
18
- declare class ContentEntryCacheImpl {
20
+ export declare class ContentEntryCache {
19
21
  private cache;
20
22
  private loader;
23
+ private pending;
21
24
  constructor();
22
25
  get(key: string): ResolvedContentEntry | undefined;
23
26
  has(key: string): boolean;
@@ -42,5 +45,5 @@ declare class ContentEntryCacheImpl {
42
45
  resolve(key: string, value: RawValue, list: boolean): void;
43
46
  clear(): void;
44
47
  }
45
- export declare const contentEntryCache: ContentEntryCacheImpl;
48
+ export declare const contentEntryCache: ContentEntryCache;
46
49
  export {};