@contentful/experiences-sdk-core 0.12.6 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/index.cjs +542 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{types.d.ts → index.d.cts} +129 -2
- package/dist/index.d.mts +448 -0
- package/dist/index.d.ts +448 -5
- package/dist/index.js +532 -9
- package/dist/index.js.map +1 -1
- package/package.json +9 -4
- package/dist/attribution.d.ts +0 -6
- package/dist/attribution.js +0 -232
- package/dist/attribution.js.map +0 -1
- package/dist/debug-logger.d.ts +0 -25
- package/dist/debug-logger.js +0 -45
- package/dist/debug-logger.js.map +0 -1
- package/dist/design-properties.d.ts +0 -21
- package/dist/design-properties.js +0 -31
- package/dist/design-properties.js.map +0 -1
- package/dist/resolve-experience.d.ts +0 -55
- package/dist/resolve-experience.js +0 -229
- package/dist/resolve-experience.js.map +0 -1
- package/dist/tracking-attributes.d.ts +0 -34
- package/dist/tracking-attributes.js +0 -12
- package/dist/tracking-attributes.js.map +0 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experiences-sdk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Runtime-neutral types + experience resolution for Contentful Experiences",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"main": "./dist/index.
|
|
8
|
-
"
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.mts",
|
|
9
10
|
"sideEffects": false,
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
13
|
"import": {
|
|
13
|
-
"types": "./dist/index.d.
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
14
15
|
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
15
20
|
}
|
|
16
21
|
},
|
|
17
22
|
"./package.json": "./package.json"
|
package/dist/attribution.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { DebugLogger } from './debug-logger.js';
|
|
2
|
-
import { ExperienceSourceMap, PortableRenderNode, PlanAttribution } from './types.js';
|
|
3
|
-
|
|
4
|
-
declare function resolveAttribution(sourceMap: ExperienceSourceMap | undefined, nodes: PortableRenderNode[], log: DebugLogger): PlanAttribution | undefined;
|
|
5
|
-
|
|
6
|
-
export { resolveAttribution };
|
package/dist/attribution.js
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
const SUPPORTED_VERSION = 1;
|
|
2
|
-
const DEFINING_LAYER_KIND = {
|
|
3
|
-
Experience: "ExperienceTemplate",
|
|
4
|
-
Fragment: "Component"
|
|
5
|
-
};
|
|
6
|
-
function asRecord(value) {
|
|
7
|
-
return typeof value === "object" && value !== null ? value : void 0;
|
|
8
|
-
}
|
|
9
|
-
function asIndex(value) {
|
|
10
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
11
|
-
}
|
|
12
|
-
function asString(value) {
|
|
13
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
14
|
-
}
|
|
15
|
-
function rowAt(rows, ref) {
|
|
16
|
-
const index = asIndex(ref);
|
|
17
|
-
return index === void 0 ? void 0 : asRecord(rows[index]);
|
|
18
|
-
}
|
|
19
|
-
function isPath(value) {
|
|
20
|
-
return Array.isArray(value) && value.every((s) => typeof s === "string" || Number.isInteger(s));
|
|
21
|
-
}
|
|
22
|
-
function parseLayer(value) {
|
|
23
|
-
const record = asRecord(value);
|
|
24
|
-
if (record === void 0) return void 0;
|
|
25
|
-
return { record, kind: asString(record.kind), id: asString(record.id) };
|
|
26
|
-
}
|
|
27
|
-
function scopeKind(layer) {
|
|
28
|
-
if (layer.kind === "Experience") return "Experience";
|
|
29
|
-
if (layer.kind === "ExperienceFragment") return "Fragment";
|
|
30
|
-
return void 0;
|
|
31
|
-
}
|
|
32
|
-
function createContext(sourceMap, log) {
|
|
33
|
-
const map = asRecord(sourceMap);
|
|
34
|
-
if (map?.version !== SUPPORTED_VERSION) {
|
|
35
|
-
log.log(`ignoring source map: unsupported version ${String(map?.version)}`);
|
|
36
|
-
return void 0;
|
|
37
|
-
}
|
|
38
|
-
const nodes = asRecord(map.nodes);
|
|
39
|
-
if (!Array.isArray(map.layers) || !Array.isArray(map.variants) || nodes === void 0) {
|
|
40
|
-
log.log("ignoring source map: missing layers, variants or nodes");
|
|
41
|
-
return void 0;
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
nodes,
|
|
45
|
-
layers: map.layers.map(parseLayer),
|
|
46
|
-
variants: map.variants,
|
|
47
|
-
entries: Array.isArray(map.entries) ? map.entries : [],
|
|
48
|
-
dataAssemblies: Array.isArray(map.dataAssemblies) ? map.dataAssemblies : [],
|
|
49
|
-
log,
|
|
50
|
-
scopes: {},
|
|
51
|
-
scopeEntries: /* @__PURE__ */ new Map(),
|
|
52
|
-
bindingEntries: /* @__PURE__ */ new Map()
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
function reportableScopes(ctx, chain) {
|
|
56
|
-
const outerFirst = [...chain].reverse();
|
|
57
|
-
const scopes = [];
|
|
58
|
-
for (const [position, row] of outerFirst.entries()) {
|
|
59
|
-
const layer = ctx.layers[row];
|
|
60
|
-
const kind = layer === void 0 ? void 0 : scopeKind(layer);
|
|
61
|
-
if (layer?.id === void 0 || kind === void 0) continue;
|
|
62
|
-
if (scopes.some((scope) => scope.row === row)) continue;
|
|
63
|
-
const innerRow = outerFirst[position + 1];
|
|
64
|
-
const innerLayer = innerRow === void 0 ? void 0 : ctx.layers[innerRow];
|
|
65
|
-
const definesThisScope = innerLayer?.kind === DEFINING_LAYER_KIND[kind];
|
|
66
|
-
scopes.push({
|
|
67
|
-
row,
|
|
68
|
-
layer,
|
|
69
|
-
entityId: layer.id,
|
|
70
|
-
kind,
|
|
71
|
-
entityKindId: definesThisScope ? innerLayer?.id : void 0
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return scopes;
|
|
75
|
-
}
|
|
76
|
-
function occurrenceKey(row, listPath, runStart) {
|
|
77
|
-
return `s${row}@${listPath}#${runStart}`;
|
|
78
|
-
}
|
|
79
|
-
function readVariant(ctx, scope) {
|
|
80
|
-
const refs = scope.layer.record.variants;
|
|
81
|
-
if (!Array.isArray(refs) || refs.length === 0) return { variantId: "default" };
|
|
82
|
-
if (refs.length > 1) {
|
|
83
|
-
ctx.log.log(`layer "${scope.entityId}" lists ${refs.length} variants; using the first`);
|
|
84
|
-
}
|
|
85
|
-
const variant = rowAt(ctx.variants, refs[0]);
|
|
86
|
-
const optimizationId = asString(variant?.optimizationId);
|
|
87
|
-
const variantId = asString(variant?.variantId) ?? "default";
|
|
88
|
-
const variantIndex = variant?.variantIndex;
|
|
89
|
-
const fields = { variantId };
|
|
90
|
-
if (optimizationId !== void 0) fields.optimizationId = optimizationId;
|
|
91
|
-
if (typeof variantIndex === "number") fields.variantIndex = variantIndex;
|
|
92
|
-
return fields;
|
|
93
|
-
}
|
|
94
|
-
function buildScope(ctx, scope, key, parentExperience) {
|
|
95
|
-
const attribution = {
|
|
96
|
-
key,
|
|
97
|
-
entityId: scope.entityId,
|
|
98
|
-
entityKind: scope.kind,
|
|
99
|
-
...readVariant(ctx, scope)
|
|
100
|
-
};
|
|
101
|
-
if (scope.entityKindId !== void 0) {
|
|
102
|
-
attribution.entityKindId = scope.entityKindId;
|
|
103
|
-
}
|
|
104
|
-
if (scope.kind === "Fragment" && parentExperience !== void 0) {
|
|
105
|
-
attribution.parentExperienceId = parentExperience.entityId;
|
|
106
|
-
if (parentExperience.variantId !== void 0) {
|
|
107
|
-
attribution.parentVariantId = parentExperience.variantId;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return attribution;
|
|
111
|
-
}
|
|
112
|
-
function entriesBoundAt(ctx, assemblyRow, path) {
|
|
113
|
-
const cacheKey = `${assemblyRow}|${JSON.stringify(path)}`;
|
|
114
|
-
const cached = ctx.bindingEntries.get(cacheKey);
|
|
115
|
-
if (cached !== void 0) return cached;
|
|
116
|
-
const found = /* @__PURE__ */ new Set();
|
|
117
|
-
const visited = /* @__PURE__ */ new Set();
|
|
118
|
-
const followAssembly = (row, path2) => {
|
|
119
|
-
const visitKey = `${row}|${JSON.stringify(path2)}`;
|
|
120
|
-
const assembly = asRecord(ctx.dataAssemblies[row]);
|
|
121
|
-
if (assembly === void 0 || visited.has(visitKey)) return;
|
|
122
|
-
visited.add(visitKey);
|
|
123
|
-
walk(assembly.return, path2);
|
|
124
|
-
};
|
|
125
|
-
const walkLeaf = (leaf, path2) => {
|
|
126
|
-
if (leaf.type === "entry") {
|
|
127
|
-
const entryId = asString(rowAt(ctx.entries, leaf.entry)?.id);
|
|
128
|
-
if (entryId !== void 0) found.add(entryId);
|
|
129
|
-
} else if (leaf.type === "dataAssembly") {
|
|
130
|
-
const row = asIndex(leaf.dataAssembly);
|
|
131
|
-
const leafPath = isPath(leaf.path) ? leaf.path : [];
|
|
132
|
-
if (row !== void 0) followAssembly(row, [...leafPath, ...path2]);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
const walk = (value, path2) => {
|
|
136
|
-
const [head, ...rest] = path2;
|
|
137
|
-
if (Array.isArray(value)) {
|
|
138
|
-
if (head === void 0) value.forEach((child) => walk(child, []));
|
|
139
|
-
else if (typeof head === "number") walk(value[head], rest);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const record = asRecord(value);
|
|
143
|
-
if (record === void 0) return;
|
|
144
|
-
if (typeof record.type === "string") walkLeaf(record, path2);
|
|
145
|
-
else if (head === void 0) Object.values(record).forEach((child) => walk(child, []));
|
|
146
|
-
else walk(record[String(head)], rest);
|
|
147
|
-
};
|
|
148
|
-
followAssembly(assemblyRow, path);
|
|
149
|
-
const ids = [...found];
|
|
150
|
-
ctx.bindingEntries.set(cacheKey, ids);
|
|
151
|
-
return ids;
|
|
152
|
-
}
|
|
153
|
-
function readNodeEntries(ctx, entry) {
|
|
154
|
-
const properties = Array.isArray(entry.contentProperties) ? entry.contentProperties : [];
|
|
155
|
-
const ids = /* @__PURE__ */ new Set();
|
|
156
|
-
for (const property of properties) {
|
|
157
|
-
const item = asRecord(property);
|
|
158
|
-
const assemblyRow = asIndex(item?.dataAssembly);
|
|
159
|
-
const bindings = asRecord(item?.bindings);
|
|
160
|
-
if (item?.type !== "dataAssembly" || assemblyRow === void 0 || bindings === void 0) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
for (const path of Object.values(bindings).filter(isPath)) {
|
|
164
|
-
for (const id of entriesBoundAt(ctx, assemblyRow, path)) ids.add(id);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return [...ids];
|
|
168
|
-
}
|
|
169
|
-
function addScopeEntries(ctx, scope, entryIds) {
|
|
170
|
-
const owned = ctx.scopeEntries.get(scope) ?? /* @__PURE__ */ new Set();
|
|
171
|
-
for (const id of entryIds) owned.add(id);
|
|
172
|
-
ctx.scopeEntries.set(scope, owned);
|
|
173
|
-
}
|
|
174
|
-
function readChain(entry) {
|
|
175
|
-
const { layers } = entry;
|
|
176
|
-
return Array.isArray(layers) && layers.every(Number.isInteger) ? layers : [];
|
|
177
|
-
}
|
|
178
|
-
function attributeNode(ctx, node, parentKeys, previousKeys, listPath, index) {
|
|
179
|
-
const entry = node.nodeId === void 0 ? void 0 : asRecord(ctx.nodes[node.nodeId]);
|
|
180
|
-
if (entry === void 0) return void 0;
|
|
181
|
-
const scopes = reportableScopes(ctx, readChain(entry));
|
|
182
|
-
if (scopes.length === 0) return void 0;
|
|
183
|
-
const keys = /* @__PURE__ */ new Map();
|
|
184
|
-
const attribution = { scopes: [], roots: [] };
|
|
185
|
-
let parentExperience;
|
|
186
|
-
let nearest;
|
|
187
|
-
const previousScopes = previousKeys.entries();
|
|
188
|
-
let sameOccurrencePrefix = true;
|
|
189
|
-
for (const scope of scopes) {
|
|
190
|
-
const previous = previousScopes.next().value;
|
|
191
|
-
sameOccurrencePrefix &&= previous?.[0] === scope.row;
|
|
192
|
-
const isRoot = !parentKeys.has(scope.row);
|
|
193
|
-
const key = parentKeys.get(scope.row) ?? (sameOccurrencePrefix ? previous?.[1] : void 0) ?? occurrenceKey(scope.row, listPath, index);
|
|
194
|
-
sameOccurrencePrefix &&= previous?.[1] === key;
|
|
195
|
-
keys.set(scope.row, key);
|
|
196
|
-
nearest = ctx.scopes[key] ??= buildScope(ctx, scope, key, parentExperience);
|
|
197
|
-
attribution.scopes.push(nearest);
|
|
198
|
-
if (isRoot) attribution.roots.push(nearest);
|
|
199
|
-
if (scope.kind === "Experience" && parentExperience === void 0) parentExperience = nearest;
|
|
200
|
-
}
|
|
201
|
-
const entryIds = readNodeEntries(ctx, entry);
|
|
202
|
-
if (entryIds.length > 0 && nearest !== void 0) {
|
|
203
|
-
attribution.entryIds = entryIds;
|
|
204
|
-
addScopeEntries(ctx, nearest, entryIds);
|
|
205
|
-
}
|
|
206
|
-
node.attribution = attribution;
|
|
207
|
-
return keys;
|
|
208
|
-
}
|
|
209
|
-
function assignSiblings(ctx, siblings, parentKeys, listPath = "") {
|
|
210
|
-
let previousKeys = /* @__PURE__ */ new Map();
|
|
211
|
-
for (const [index, node] of siblings.entries()) {
|
|
212
|
-
const ownKeys = attributeNode(ctx, node, parentKeys, previousKeys, listPath, index);
|
|
213
|
-
previousKeys = ownKeys ?? /* @__PURE__ */ new Map();
|
|
214
|
-
for (const [slotName, children] of Object.entries(node.slots)) {
|
|
215
|
-
assignSiblings(ctx, children, ownKeys ?? parentKeys, `${listPath}/${index}.${slotName}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function resolveAttribution(sourceMap, nodes, log) {
|
|
220
|
-
if (sourceMap === void 0) return void 0;
|
|
221
|
-
const ctx = createContext(sourceMap, log);
|
|
222
|
-
if (ctx === void 0) return void 0;
|
|
223
|
-
assignSiblings(ctx, nodes, /* @__PURE__ */ new Map());
|
|
224
|
-
for (const [scope, ids] of ctx.scopeEntries) scope.entryIds = [...ids];
|
|
225
|
-
const scopes = Object.values(ctx.scopes);
|
|
226
|
-
for (const scope of scopes) Object.freeze(scope);
|
|
227
|
-
return scopes.length > 0 ? { scopes: ctx.scopes } : void 0;
|
|
228
|
-
}
|
|
229
|
-
export {
|
|
230
|
-
resolveAttribution
|
|
231
|
-
};
|
|
232
|
-
//# sourceMappingURL=attribution.js.map
|
package/dist/attribution.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/attribution.ts"],"sourcesContent":["import type { DebugLogger } from './debug-logger.js';\nimport type {\n ExperienceSourceMap,\n NodeAttribution,\n PlanAttribution,\n PortableRenderNode,\n ScopeAttribution,\n} from './types.js';\n\nconst SUPPORTED_VERSION = 1;\n\ntype EntityKind = ScopeAttribution['entityKind'];\ntype VariantFields = Pick<ScopeAttribution, 'optimizationId' | 'variantId' | 'variantIndex'>;\ntype Rec = Record<string, unknown>;\ntype PathSegment = string | number;\n\nconst DEFINING_LAYER_KIND: Record<EntityKind, string> = {\n Experience: 'ExperienceTemplate',\n Fragment: 'Component',\n};\n\nfunction asRecord(value: unknown): Rec | undefined {\n return typeof value === 'object' && value !== null ? (value as Rec) : undefined;\n}\n\nfunction asIndex(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction rowAt(rows: unknown[], ref: unknown): Rec | undefined {\n const index = asIndex(ref);\n return index === undefined ? undefined : asRecord(rows[index]);\n}\n\nfunction isPath(value: unknown): value is PathSegment[] {\n return Array.isArray(value) && value.every((s) => typeof s === 'string' || Number.isInteger(s));\n}\n\ninterface Layer {\n record: Rec;\n kind: string | undefined;\n id: string | undefined;\n}\n\nfunction parseLayer(value: unknown): Layer | undefined {\n const record = asRecord(value);\n if (record === undefined) return undefined;\n return { record, kind: asString(record.kind), id: asString(record.id) };\n}\n\nfunction scopeKind(layer: Layer): EntityKind | undefined {\n if (layer.kind === 'Experience') return 'Experience';\n if (layer.kind === 'ExperienceFragment') return 'Fragment';\n return undefined;\n}\n\ninterface Context {\n nodes: Rec;\n layers: (Layer | undefined)[];\n variants: unknown[];\n entries: unknown[];\n dataAssemblies: unknown[];\n log: DebugLogger;\n scopes: Record<string, ScopeAttribution>;\n scopeEntries: Map<ScopeAttribution, Set<string>>;\n bindingEntries: Map<string, readonly string[]>;\n}\n\nfunction createContext(sourceMap: ExperienceSourceMap, log: DebugLogger): Context | undefined {\n const map = asRecord(sourceMap);\n if (map?.version !== SUPPORTED_VERSION) {\n log.log(`ignoring source map: unsupported version ${String(map?.version)}`);\n return undefined;\n }\n const nodes = asRecord(map.nodes);\n if (!Array.isArray(map.layers) || !Array.isArray(map.variants) || nodes === undefined) {\n log.log('ignoring source map: missing layers, variants or nodes');\n return undefined;\n }\n return {\n nodes,\n layers: map.layers.map(parseLayer),\n variants: map.variants,\n entries: Array.isArray(map.entries) ? map.entries : [],\n dataAssemblies: Array.isArray(map.dataAssemblies) ? map.dataAssemblies : [],\n log,\n scopes: {},\n scopeEntries: new Map(),\n bindingEntries: new Map(),\n };\n}\n\ninterface ChainScope {\n row: number;\n layer: Layer;\n entityId: string;\n kind: EntityKind;\n entityKindId: string | undefined;\n}\n\nfunction reportableScopes(ctx: Context, chain: number[]): ChainScope[] {\n const outerFirst = [...chain].reverse();\n const scopes: ChainScope[] = [];\n\n for (const [position, row] of outerFirst.entries()) {\n const layer = ctx.layers[row];\n const kind = layer === undefined ? undefined : scopeKind(layer);\n if (layer?.id === undefined || kind === undefined) continue;\n if (scopes.some((scope) => scope.row === row)) continue;\n\n const innerRow = outerFirst[position + 1];\n const innerLayer = innerRow === undefined ? undefined : ctx.layers[innerRow];\n const definesThisScope = innerLayer?.kind === DEFINING_LAYER_KIND[kind];\n\n scopes.push({\n row,\n layer,\n entityId: layer.id,\n kind,\n entityKindId: definesThisScope ? innerLayer?.id : undefined,\n });\n }\n return scopes;\n}\n\nfunction occurrenceKey(row: number, listPath: string, runStart: number): string {\n return `s${row}@${listPath}#${runStart}`;\n}\n\nfunction readVariant(ctx: Context, scope: ChainScope): VariantFields {\n const refs = scope.layer.record.variants;\n if (!Array.isArray(refs) || refs.length === 0) return { variantId: 'default' };\n if (refs.length > 1) {\n ctx.log.log(`layer \"${scope.entityId}\" lists ${refs.length} variants; using the first`);\n }\n\n const variant = rowAt(ctx.variants, refs[0]);\n const optimizationId = asString(variant?.optimizationId);\n const variantId = asString(variant?.variantId) ?? 'default';\n const variantIndex = variant?.variantIndex;\n\n const fields: VariantFields = { variantId };\n if (optimizationId !== undefined) fields.optimizationId = optimizationId;\n if (typeof variantIndex === 'number') fields.variantIndex = variantIndex;\n return fields;\n}\n\nfunction buildScope(\n ctx: Context,\n scope: ChainScope,\n key: string,\n parentExperience: ScopeAttribution | undefined\n): ScopeAttribution {\n const attribution: ScopeAttribution = {\n key,\n entityId: scope.entityId,\n entityKind: scope.kind,\n ...readVariant(ctx, scope),\n };\n if (scope.entityKindId !== undefined) {\n attribution.entityKindId = scope.entityKindId;\n }\n if (scope.kind === 'Fragment' && parentExperience !== undefined) {\n attribution.parentExperienceId = parentExperience.entityId;\n if (parentExperience.variantId !== undefined) {\n attribution.parentVariantId = parentExperience.variantId;\n }\n }\n return attribution;\n}\n\nfunction entriesBoundAt(ctx: Context, assemblyRow: number, path: PathSegment[]): readonly string[] {\n const cacheKey = `${assemblyRow}|${JSON.stringify(path)}`;\n const cached = ctx.bindingEntries.get(cacheKey);\n if (cached !== undefined) return cached;\n\n const found = new Set<string>();\n const visited = new Set<string>();\n\n const followAssembly = (row: number, path: PathSegment[]): void => {\n const visitKey = `${row}|${JSON.stringify(path)}`;\n const assembly = asRecord(ctx.dataAssemblies[row]);\n if (assembly === undefined || visited.has(visitKey)) return;\n visited.add(visitKey);\n walk(assembly.return, path);\n };\n\n const walkLeaf = (leaf: Rec, path: PathSegment[]): void => {\n if (leaf.type === 'entry') {\n const entryId = asString(rowAt(ctx.entries, leaf.entry)?.id);\n if (entryId !== undefined) found.add(entryId);\n } else if (leaf.type === 'dataAssembly') {\n const row = asIndex(leaf.dataAssembly);\n const leafPath = isPath(leaf.path) ? leaf.path : [];\n if (row !== undefined) followAssembly(row, [...leafPath, ...path]);\n }\n };\n\n const walk = (value: unknown, path: PathSegment[]): void => {\n const [head, ...rest] = path;\n\n if (Array.isArray(value)) {\n if (head === undefined) value.forEach((child) => walk(child, []));\n else if (typeof head === 'number') walk(value[head], rest);\n return;\n }\n\n const record = asRecord(value);\n if (record === undefined) return;\n if (typeof record.type === 'string') walkLeaf(record, path);\n else if (head === undefined) Object.values(record).forEach((child) => walk(child, []));\n else walk(record[String(head)], rest);\n };\n\n followAssembly(assemblyRow, path);\n const ids = [...found];\n ctx.bindingEntries.set(cacheKey, ids);\n return ids;\n}\n\nfunction readNodeEntries(ctx: Context, entry: Rec): string[] {\n const properties: unknown[] = Array.isArray(entry.contentProperties)\n ? entry.contentProperties\n : [];\n const ids = new Set<string>();\n\n for (const property of properties) {\n const item = asRecord(property);\n const assemblyRow = asIndex(item?.dataAssembly);\n const bindings = asRecord(item?.bindings);\n if (item?.type !== 'dataAssembly' || assemblyRow === undefined || bindings === undefined) {\n continue;\n }\n for (const path of Object.values(bindings).filter(isPath)) {\n for (const id of entriesBoundAt(ctx, assemblyRow, path)) ids.add(id);\n }\n }\n return [...ids];\n}\n\nfunction addScopeEntries(ctx: Context, scope: ScopeAttribution, entryIds: string[]): void {\n const owned = ctx.scopeEntries.get(scope) ?? new Set<string>();\n for (const id of entryIds) owned.add(id);\n ctx.scopeEntries.set(scope, owned);\n}\n\nfunction readChain(entry: Rec): number[] {\n const { layers } = entry;\n return Array.isArray(layers) && layers.every(Number.isInteger) ? (layers as number[]) : [];\n}\n\nfunction attributeNode(\n ctx: Context,\n node: PortableRenderNode,\n parentKeys: Map<number, string>,\n previousKeys: Map<number, string>,\n listPath: string,\n index: number\n): Map<number, string> | undefined {\n const entry = node.nodeId === undefined ? undefined : asRecord(ctx.nodes[node.nodeId]);\n if (entry === undefined) return undefined;\n const scopes = reportableScopes(ctx, readChain(entry));\n if (scopes.length === 0) return undefined;\n\n const keys = new Map<number, string>();\n const attribution: NodeAttribution = { scopes: [], roots: [] };\n let parentExperience: ScopeAttribution | undefined;\n let nearest: ScopeAttribution | undefined;\n\n // A sibling's key is only reusable while every enclosing scope matches as well.\n const previousScopes = previousKeys.entries();\n let sameOccurrencePrefix = true;\n\n for (const scope of scopes) {\n const previous = previousScopes.next().value;\n sameOccurrencePrefix &&= previous?.[0] === scope.row;\n\n const isRoot = !parentKeys.has(scope.row);\n const key: string =\n parentKeys.get(scope.row) ??\n (sameOccurrencePrefix ? previous?.[1] : undefined) ??\n occurrenceKey(scope.row, listPath, index);\n sameOccurrencePrefix &&= previous?.[1] === key;\n keys.set(scope.row, key);\n\n nearest = ctx.scopes[key] ??= buildScope(ctx, scope, key, parentExperience);\n attribution.scopes.push(nearest);\n if (isRoot) attribution.roots.push(nearest);\n if (scope.kind === 'Experience' && parentExperience === undefined) parentExperience = nearest;\n }\n\n const entryIds = readNodeEntries(ctx, entry);\n if (entryIds.length > 0 && nearest !== undefined) {\n attribution.entryIds = entryIds;\n addScopeEntries(ctx, nearest, entryIds);\n }\n node.attribution = attribution;\n return keys;\n}\n\nfunction assignSiblings(\n ctx: Context,\n siblings: PortableRenderNode[],\n parentKeys: Map<number, string>,\n listPath = ''\n): void {\n let previousKeys = new Map<number, string>();\n for (const [index, node] of siblings.entries()) {\n const ownKeys = attributeNode(ctx, node, parentKeys, previousKeys, listPath, index);\n previousKeys = ownKeys ?? new Map();\n\n for (const [slotName, children] of Object.entries(node.slots)) {\n assignSiblings(ctx, children, ownKeys ?? parentKeys, `${listPath}/${index}.${slotName}`);\n }\n }\n}\n\nexport function resolveAttribution(\n sourceMap: ExperienceSourceMap | undefined,\n nodes: PortableRenderNode[],\n log: DebugLogger\n): PlanAttribution | undefined {\n if (sourceMap === undefined) return undefined;\n const ctx = createContext(sourceMap, log);\n if (ctx === undefined) return undefined;\n\n assignSiblings(ctx, nodes, new Map());\n for (const [scope, ids] of ctx.scopeEntries) scope.entryIds = [...ids];\n\n const scopes = Object.values(ctx.scopes);\n for (const scope of scopes) Object.freeze(scope);\n return scopes.length > 0 ? { scopes: ctx.scopes } : undefined;\n}\n"],"mappings":"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","names":["path"]}
|
package/dist/debug-logger.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A scoped debug logger. `scope` names the emitting subsystem (e.g. `core`,
|
|
3
|
-
* `client`, `react`) so interleaved logs stay legible.
|
|
4
|
-
*/
|
|
5
|
-
interface DebugLogger {
|
|
6
|
-
/** Log a message with optional structured data. */
|
|
7
|
-
log(message: string, data?: unknown): void;
|
|
8
|
-
/**
|
|
9
|
-
* Log a message whose payload is expensive to build. The thunk only runs
|
|
10
|
-
* when debug is enabled — keep large payload serialization out of the hot
|
|
11
|
-
* path when debug is off.
|
|
12
|
-
*/
|
|
13
|
-
lazy(message: string, getData: () => unknown): void;
|
|
14
|
-
/** Time an async span and log its duration when it settles. */
|
|
15
|
-
time<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
16
|
-
/** True when this logger is actually emitting. */
|
|
17
|
-
readonly enabled: boolean;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Build a debug logger. Returns a shared no-op when `debug` is falsy so
|
|
21
|
-
* callers can hold onto the result unconditionally and pay nothing when off.
|
|
22
|
-
*/
|
|
23
|
-
declare function createDebugLogger(debug: boolean | undefined, scope: string): DebugLogger;
|
|
24
|
-
|
|
25
|
-
export { type DebugLogger, createDebugLogger };
|
package/dist/debug-logger.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
const PREFIX = "[experiences:debug]";
|
|
2
|
-
function now() {
|
|
3
|
-
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
4
|
-
return performance.now();
|
|
5
|
-
}
|
|
6
|
-
if (typeof Date !== "undefined" && typeof Date.now === "function") return Date.now();
|
|
7
|
-
return 0;
|
|
8
|
-
}
|
|
9
|
-
const NOOP_LOGGER = {
|
|
10
|
-
log() {
|
|
11
|
-
},
|
|
12
|
-
lazy() {
|
|
13
|
-
},
|
|
14
|
-
time: (_label, fn) => fn(),
|
|
15
|
-
enabled: false
|
|
16
|
-
};
|
|
17
|
-
function createDebugLogger(debug, scope) {
|
|
18
|
-
if (!debug || typeof console === "undefined") return NOOP_LOGGER;
|
|
19
|
-
const tag = `${PREFIX}[${scope}]`;
|
|
20
|
-
return {
|
|
21
|
-
enabled: true,
|
|
22
|
-
log(message, data) {
|
|
23
|
-
if (data === void 0) console.log(`${tag} ${message}`);
|
|
24
|
-
else console.log(`${tag} ${message}`, data);
|
|
25
|
-
},
|
|
26
|
-
lazy(message, getData) {
|
|
27
|
-
console.log(`${tag} ${message}`, getData());
|
|
28
|
-
},
|
|
29
|
-
async time(label, fn) {
|
|
30
|
-
const start = now();
|
|
31
|
-
try {
|
|
32
|
-
const result = await fn();
|
|
33
|
-
console.log(`${tag} \u23F1 ${label} \u2014 ${(now() - start).toFixed(1)}ms`);
|
|
34
|
-
return result;
|
|
35
|
-
} catch (err) {
|
|
36
|
-
console.log(`${tag} \u23F1 ${label} \u2014 failed after ${(now() - start).toFixed(1)}ms`, err);
|
|
37
|
-
throw err;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
export {
|
|
43
|
-
createDebugLogger
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=debug-logger.js.map
|
package/dist/debug-logger.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/debug-logger.ts"],"sourcesContent":["/*\n * Debug logging for the SDK. The whole surface is gated on a single `debug`\n * boolean that threads through `fetchExperience` / `resolveExperience` and the\n * renderers (see `ExperienceContext.debug`). When `debug` is off, every call\n * here is a cheap no-op — nothing is logged, no strings are built (callers pass\n * thunks for anything expensive to serialize).\n *\n * This is intentionally console-based, not a pluggable logger. v1 goal is\n * \"turn it on, see what the SDK is doing\"; a customer-supplied sink can land\n * later without changing call sites (the `debug` boolean would widen to\n * `boolean | DebugSink`).\n */\n\n/** Prefix on every debug line so customers can filter their console. */\nconst PREFIX = '[experiences:debug]';\n\n/**\n * Monotonic-ish clock for span timings. Prefers `performance.now()` (present\n * in modern Node and every browser); falls back to `Date.now()`. Returns 0\n * when neither is available so timing degrades to \"no duration\" rather than\n * throwing.\n */\nfunction now(): number {\n if (typeof performance !== 'undefined' && typeof performance.now === 'function') {\n return performance.now();\n }\n if (typeof Date !== 'undefined' && typeof Date.now === 'function') return Date.now();\n return 0;\n}\n\n/**\n * A scoped debug logger. `scope` names the emitting subsystem (e.g. `core`,\n * `client`, `react`) so interleaved logs stay legible.\n */\nexport interface DebugLogger {\n /** Log a message with optional structured data. */\n log(message: string, data?: unknown): void;\n /**\n * Log a message whose payload is expensive to build. The thunk only runs\n * when debug is enabled — keep large payload serialization out of the hot\n * path when debug is off.\n */\n lazy(message: string, getData: () => unknown): void;\n /** Time an async span and log its duration when it settles. */\n time<T>(label: string, fn: () => Promise<T>): Promise<T>;\n /** True when this logger is actually emitting. */\n readonly enabled: boolean;\n}\n\nconst NOOP_LOGGER: DebugLogger = {\n log() {},\n lazy() {},\n time: (_label, fn) => fn(),\n enabled: false,\n};\n\n/**\n * Build a debug logger. Returns a shared no-op when `debug` is falsy so\n * callers can hold onto the result unconditionally and pay nothing when off.\n */\nexport function createDebugLogger(debug: boolean | undefined, scope: string): DebugLogger {\n if (!debug || typeof console === 'undefined') return NOOP_LOGGER;\n\n const tag = `${PREFIX}[${scope}]`;\n return {\n enabled: true,\n log(message, data) {\n if (data === undefined) console.log(`${tag} ${message}`);\n else console.log(`${tag} ${message}`, data);\n },\n lazy(message, getData) {\n console.log(`${tag} ${message}`, getData());\n },\n async time(label, fn) {\n const start = now();\n try {\n const result = await fn();\n console.log(`${tag} ⏱ ${label} — ${(now() - start).toFixed(1)}ms`);\n return result;\n } catch (err) {\n console.log(`${tag} ⏱ ${label} — failed after ${(now() - start).toFixed(1)}ms`, err);\n throw err;\n }\n },\n };\n}\n"],"mappings":"AAcA,MAAM,SAAS;AAQf,SAAS,MAAc;AACrB,MAAI,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,YAAY;AAC/E,WAAO,YAAY,IAAI;AAAA,EACzB;AACA,MAAI,OAAO,SAAS,eAAe,OAAO,KAAK,QAAQ,WAAY,QAAO,KAAK,IAAI;AACnF,SAAO;AACT;AAqBA,MAAM,cAA2B;AAAA,EAC/B,MAAM;AAAA,EAAC;AAAA,EACP,OAAO;AAAA,EAAC;AAAA,EACR,MAAM,CAAC,QAAQ,OAAO,GAAG;AAAA,EACzB,SAAS;AACX;AAMO,SAAS,kBAAkB,OAA4B,OAA4B;AACxF,MAAI,CAAC,SAAS,OAAO,YAAY,YAAa,QAAO;AAErD,QAAM,MAAM,GAAG,MAAM,IAAI,KAAK;AAC9B,SAAO;AAAA,IACL,SAAS;AAAA,IACT,IAAI,SAAS,MAAM;AACjB,UAAI,SAAS,OAAW,SAAQ,IAAI,GAAG,GAAG,IAAI,OAAO,EAAE;AAAA,UAClD,SAAQ,IAAI,GAAG,GAAG,IAAI,OAAO,IAAI,IAAI;AAAA,IAC5C;AAAA,IACA,KAAK,SAAS,SAAS;AACrB,cAAQ,IAAI,GAAG,GAAG,IAAI,OAAO,IAAI,QAAQ,CAAC;AAAA,IAC5C;AAAA,IACA,MAAM,KAAK,OAAO,IAAI;AACpB,YAAM,QAAQ,IAAI;AAClB,UAAI;AACF,cAAM,SAAS,MAAM,GAAG;AACxB,gBAAQ,IAAI,GAAG,GAAG,WAAM,KAAK,YAAO,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,IAAI;AACjE,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,gBAAQ,IAAI,GAAG,GAAG,WAAM,KAAK,yBAAoB,IAAI,IAAI,OAAO,QAAQ,CAAC,CAAC,MAAM,GAAG;AACnF,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DesignToken, ResolveToken, DesignPropValue } from './types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Resolve every design property on a node into a record keyed by name.
|
|
5
|
-
*/
|
|
6
|
-
declare function resolveDesignProperties(designProperties: Record<string, DesignPropValue> | undefined): Record<string, string | number | boolean | DesignToken>;
|
|
7
|
-
/**
|
|
8
|
-
* Resolve `DesignToken` values via `resolveToken`; scalars pass through.
|
|
9
|
-
* Keys that don't resolve — no `resolveToken` configured, or the resolver
|
|
10
|
-
* returns `undefined` for that particular token — pass through as the raw
|
|
11
|
-
* `DesignToken` and their id is collected in `unresolved` for the caller to
|
|
12
|
-
* warn/diagnose (see `warnUnresolvedTokens` in `resolve-experience.ts`, which
|
|
13
|
-
* already dedupes per `resolveExperience()` call, so this function carries
|
|
14
|
-
* no warn-once state of its own).
|
|
15
|
-
*/
|
|
16
|
-
declare function applyTokenResolver(props: Record<string, string | number | boolean | DesignToken>, resolveToken?: ResolveToken): {
|
|
17
|
-
props: Record<string, unknown>;
|
|
18
|
-
unresolved: string[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export { applyTokenResolver, resolveDesignProperties };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
function resolveDesignProperties(designProperties) {
|
|
2
|
-
const out = {};
|
|
3
|
-
if (!designProperties) return out;
|
|
4
|
-
for (const [key, prop] of Object.entries(designProperties)) {
|
|
5
|
-
out[key] = prop.type === "ManualDesignValue" ? prop.value : prop;
|
|
6
|
-
}
|
|
7
|
-
return out;
|
|
8
|
-
}
|
|
9
|
-
function applyTokenResolver(props, resolveToken) {
|
|
10
|
-
const out = {};
|
|
11
|
-
const unresolved = [];
|
|
12
|
-
for (const [key, value] of Object.entries(props)) {
|
|
13
|
-
if (typeof value === "object" && value !== null && value.type === "DesignToken") {
|
|
14
|
-
const resolved = resolveToken?.(value);
|
|
15
|
-
if (resolved === void 0) {
|
|
16
|
-
out[key] = value;
|
|
17
|
-
unresolved.push(value.value);
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
out[key] = resolved;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
out[key] = value;
|
|
24
|
-
}
|
|
25
|
-
return { props: out, unresolved };
|
|
26
|
-
}
|
|
27
|
-
export {
|
|
28
|
-
applyTokenResolver,
|
|
29
|
-
resolveDesignProperties
|
|
30
|
-
};
|
|
31
|
-
//# sourceMappingURL=design-properties.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/design-properties.ts"],"sourcesContent":["/* Design-property and token resolution for the core render-plan builder. */\n\nimport type { DesignPropValue, DesignToken, ResolveToken } from './types.js';\n\n/**\n * Resolve every design property on a node into a record keyed by name.\n */\nexport function resolveDesignProperties(\n designProperties: Record<string, DesignPropValue> | undefined\n): Record<string, string | number | boolean | DesignToken> {\n const out: Record<string, string | number | boolean | DesignToken> = {};\n if (!designProperties) return out;\n for (const [key, prop] of Object.entries(designProperties)) {\n out[key] = prop.type === 'ManualDesignValue' ? prop.value : prop;\n }\n return out;\n}\n\n/**\n * Resolve `DesignToken` values via `resolveToken`; scalars pass through.\n * Keys that don't resolve — no `resolveToken` configured, or the resolver\n * returns `undefined` for that particular token — pass through as the raw\n * `DesignToken` and their id is collected in `unresolved` for the caller to\n * warn/diagnose (see `warnUnresolvedTokens` in `resolve-experience.ts`, which\n * already dedupes per `resolveExperience()` call, so this function carries\n * no warn-once state of its own).\n */\nexport function applyTokenResolver(\n props: Record<string, string | number | boolean | DesignToken>,\n resolveToken?: ResolveToken\n): { props: Record<string, unknown>; unresolved: string[] } {\n const out: Record<string, unknown> = {};\n const unresolved: string[] = [];\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'object' && value !== null && value.type === 'DesignToken') {\n const resolved = resolveToken?.(value);\n if (resolved === undefined) {\n out[key] = value;\n unresolved.push(value.value);\n continue;\n }\n out[key] = resolved;\n continue;\n }\n out[key] = value;\n }\n return { props: out, unresolved };\n}\n"],"mappings":"AAOO,SAAS,wBACd,kBACyD;AACzD,QAAM,MAA+D,CAAC;AACtE,MAAI,CAAC,iBAAkB,QAAO;AAC9B,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AAC1D,QAAI,GAAG,IAAI,KAAK,SAAS,sBAAsB,KAAK,QAAQ;AAAA,EAC9D;AACA,SAAO;AACT;AAWO,SAAS,mBACd,OACA,cAC0D;AAC1D,QAAM,MAA+B,CAAC;AACtC,QAAM,aAAuB,CAAC;AAC9B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,SAAS,eAAe;AAC/E,YAAM,WAAW,eAAe,KAAK;AACrC,UAAI,aAAa,QAAW;AAC1B,YAAI,GAAG,IAAI;AACX,mBAAW,KAAK,MAAM,KAAK;AAC3B;AAAA,MACF;AACA,UAAI,GAAG,IAAI;AACX;AAAA,IACF;AACA,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO,EAAE,OAAO,KAAK,WAAW;AAClC;","names":[]}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { ExperienceSourceMap, ResolveToken, ExperiencePayload, PortableRenderPlan } from './types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Structural type the resolver walker depends on. Matches the React /
|
|
5
|
-
* Svelte adapter `Config` shape but doesn't require importing them —
|
|
6
|
-
* render-core stays decoupled from any framework.
|
|
7
|
-
*
|
|
8
|
-
* Registry values are typed as `unknown` because each adapter accepts
|
|
9
|
-
* either a bare framework component (function / Svelte class / etc.) OR
|
|
10
|
-
* a config-object shape with `{ component, defaults?, resolveData? }`.
|
|
11
|
-
* The resolver only cares about `resolveData`; it duck-types each entry
|
|
12
|
-
* at runtime and ignores anything without it.
|
|
13
|
-
*/
|
|
14
|
-
interface ResolverConfig {
|
|
15
|
-
components: Record<string, unknown>;
|
|
16
|
-
experienceTemplates?: Record<string, unknown>;
|
|
17
|
-
/**
|
|
18
|
-
* Resolves `DesignToken` design properties to runtime values. Mirrors the
|
|
19
|
-
* adapter `Config.resolveToken`, so server and client agree without the
|
|
20
|
-
* caller re-supplying it. Used during server-side pre-resolution.
|
|
21
|
-
*/
|
|
22
|
-
resolveToken?: ResolveToken;
|
|
23
|
-
}
|
|
24
|
-
interface ResolveExperienceOptions {
|
|
25
|
-
/**
|
|
26
|
-
* Arbitrary per-render metadata exposed to every resolver as
|
|
27
|
-
* `ctx.experience.metadata`. Defaults to `{}`.
|
|
28
|
-
*/
|
|
29
|
-
metadata?: Record<string, unknown>;
|
|
30
|
-
/**
|
|
31
|
-
* Observability switch. When on, `resolveExperience` logs the resolution
|
|
32
|
-
* steps and per-node `resolveData` fan-out timings. Threads through to the
|
|
33
|
-
* resolver context as `ctx.experience.debug`. Defaults to `false`.
|
|
34
|
-
*/
|
|
35
|
-
debug?: boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Source map carried onto the plan. An explicit map wins over
|
|
38
|
-
* `payload.extensions.sourceMap`. Omit to use the payload's map, if it
|
|
39
|
-
* carries one.
|
|
40
|
-
*/
|
|
41
|
-
sourceMap?: ExperienceSourceMap;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Turns an Experience payload (XDA response shape) into a PortableRenderPlan
|
|
45
|
-
* ready to hand to a renderer. Walks the tree, classifies props, captures
|
|
46
|
-
* slots, and runs any component-declared `resolveData` hooks (sync or async)
|
|
47
|
-
* in parallel.
|
|
48
|
-
*
|
|
49
|
-
* Implementation note: the function is always async — even when no component
|
|
50
|
-
* declares a resolver, the cost is one microtask. Customers get a single
|
|
51
|
-
* uniform call site.
|
|
52
|
-
*/
|
|
53
|
-
declare function resolveExperience(payload: ExperiencePayload, config: ResolverConfig, options?: ResolveExperienceOptions): Promise<PortableRenderPlan>;
|
|
54
|
-
|
|
55
|
-
export { type ResolveExperienceOptions, type ResolverConfig, resolveExperience };
|