@microsoft/fast-html 1.0.0-alpha.17 → 1.0.0-alpha.19
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/README.md +11 -17
- package/dist/dts/components/index.d.ts +1 -0
- package/dist/dts/components/observer-map.d.ts +143 -0
- package/dist/dts/components/observer-map.spec.d.ts +1 -0
- package/dist/dts/components/schema.d.ts +126 -0
- package/dist/dts/components/schema.spec.d.ts +1 -0
- package/dist/dts/components/template.d.ts +11 -0
- package/dist/dts/components/utilities.d.ts +60 -1
- package/dist/dts/fixtures/observer-map/main.d.ts +1 -0
- package/dist/dts/fixtures/observer-map/observer-map.spec.d.ts +1 -0
- package/dist/dts/index.d.ts +1 -1
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/components/observer-map.js +551 -0
- package/dist/esm/components/observer-map.spec.js +613 -0
- package/dist/esm/components/schema.js +196 -0
- package/dist/esm/components/schema.spec.js +248 -0
- package/dist/esm/components/template.js +104 -86
- package/dist/esm/components/utilities.js +301 -50
- package/dist/esm/components/utilities.spec.js +109 -1
- package/dist/esm/fixtures/dot-syntax/dot-syntax.spec.js +109 -2
- package/dist/esm/fixtures/dot-syntax/main.js +27 -2
- package/dist/esm/fixtures/event/main.js +3 -3
- package/dist/esm/fixtures/observer-map/main.js +304 -0
- package/dist/esm/fixtures/observer-map/observer-map.spec.js +174 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/fast-html.api.json +212 -0
- package/dist/fast-html.d.ts +207 -0
- package/dist/fast-html.untrimmed.d.ts +207 -0
- package/package.json +5 -4
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import { Observable } from "@microsoft/fast-element/observable.js";
|
|
2
|
+
import { assignObservables, getNextProperty } from "./utilities.js";
|
|
3
|
+
const reservedIndexPlaceholder = "__index__";
|
|
4
|
+
/**
|
|
5
|
+
* ObserverMap provides functionality for caching binding paths, extracting root properties,
|
|
6
|
+
* and defining observable properties on class prototypes
|
|
7
|
+
*/
|
|
8
|
+
export class ObserverMap {
|
|
9
|
+
constructor(classPrototype) {
|
|
10
|
+
this.cachePaths = {};
|
|
11
|
+
this.contextCache = [];
|
|
12
|
+
/**
|
|
13
|
+
* Creates a property change handler function for observable properties
|
|
14
|
+
* This handler is called when an observable property transitions from undefined to a defined value
|
|
15
|
+
* @param propertyName - The name of the property for which to create the change handler
|
|
16
|
+
* @returns A function that handles property changes and sets up proxies for object values
|
|
17
|
+
*/
|
|
18
|
+
this.defineChanged = (propertyName) => {
|
|
19
|
+
const getAndAssignObservablesAlias = this.getAndAssignObservables;
|
|
20
|
+
const cachePaths = this.cachePaths;
|
|
21
|
+
function instanceResolverChanged(prev, next) {
|
|
22
|
+
if (prev === undefined && next !== undefined) {
|
|
23
|
+
const proxy = getAndAssignObservablesAlias(this, propertyName, next, cachePaths);
|
|
24
|
+
this[propertyName] = proxy;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return instanceResolverChanged;
|
|
28
|
+
};
|
|
29
|
+
this.classPrototype = classPrototype;
|
|
30
|
+
}
|
|
31
|
+
getRootProperty(config) {
|
|
32
|
+
if (config.self) {
|
|
33
|
+
const contextCacheItem = this.contextCache.find(contextCacheItem => {
|
|
34
|
+
return contextCacheItem.context === config.parentContext;
|
|
35
|
+
});
|
|
36
|
+
if (contextCacheItem) {
|
|
37
|
+
if (contextCacheItem.parent) {
|
|
38
|
+
return this.getRootProperty(Object.assign(Object.assign({}, config), { self: true, parentContext: contextCacheItem.parent }));
|
|
39
|
+
}
|
|
40
|
+
return getNextProperty(contextCacheItem.absolutePath);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return config.self
|
|
44
|
+
? config.parentContext
|
|
45
|
+
: getNextProperty(config.path);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Caches a binding path with context information for later use in generating observable properties
|
|
49
|
+
* @param context - The path context information containing path, self, parentContext, contextPath, type, and level
|
|
50
|
+
*/
|
|
51
|
+
cachePathWithContext(config) {
|
|
52
|
+
// Handle relative path navigation with "../"
|
|
53
|
+
if (config.path.includes("../")) {
|
|
54
|
+
this.handleRelativePathCaching(config);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const rootPath = this.getRootProperty(config);
|
|
58
|
+
this.resolveRootAndContextPath(config.type, config.path, rootPath, config.contextPath);
|
|
59
|
+
switch (config.type) {
|
|
60
|
+
case "access":
|
|
61
|
+
this.handleAccessPath(Object.assign(Object.assign({}, config), { rootPath }));
|
|
62
|
+
break;
|
|
63
|
+
case "event":
|
|
64
|
+
// Event handling not implemented yet
|
|
65
|
+
break;
|
|
66
|
+
case "repeat":
|
|
67
|
+
this.handleRepeatPath(Object.assign(Object.assign({}, config), { rootPath }));
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Handles caching of paths with relative navigation ("../")
|
|
73
|
+
* Determines the correct context level and caches the path accordingly
|
|
74
|
+
*/
|
|
75
|
+
handleRelativePathCaching(config) {
|
|
76
|
+
// Count the number of "../" to determine how many levels to go up
|
|
77
|
+
const upLevels = this.countRelativeNavigationLevels(config.path);
|
|
78
|
+
// Extract the property name after all the "../" sequences
|
|
79
|
+
const propertyName = this.extractPropertyNameFromRelativePath(config.path);
|
|
80
|
+
// Determine the target context based on navigation level
|
|
81
|
+
const targetContext = this.getTargetContextForRelativePath(config.parentContext, upLevels);
|
|
82
|
+
// Create the absolute path based on where we end up
|
|
83
|
+
const absolutePath = targetContext === null ? propertyName : `${targetContext}.${propertyName}`;
|
|
84
|
+
// Cache the path at the determined context level
|
|
85
|
+
if (targetContext === null) {
|
|
86
|
+
// Cache at root level
|
|
87
|
+
this.cacheAtRootLevel({
|
|
88
|
+
propertyName,
|
|
89
|
+
absolutePath: propertyName,
|
|
90
|
+
type: config.type,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Cache at the specified context level
|
|
95
|
+
this.cacheAtContextLevel({
|
|
96
|
+
propertyName,
|
|
97
|
+
absolutePath,
|
|
98
|
+
targetContext,
|
|
99
|
+
type: config.type,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Counts the number of "../" sequences in a path without using regex
|
|
105
|
+
*/
|
|
106
|
+
countRelativeNavigationLevels(path) {
|
|
107
|
+
if (!path || !path.includes("../")) {
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
return path.split("../").length - 1;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extracts the property name from a relative path, removing all "../" sequences
|
|
114
|
+
*/
|
|
115
|
+
extractPropertyNameFromRelativePath(path) {
|
|
116
|
+
// Remove all "../" sequences and get the remaining path
|
|
117
|
+
const cleaned = path.replace(/\.\.\//g, "");
|
|
118
|
+
return cleaned;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Determines the target context after navigating up the specified number of levels
|
|
122
|
+
*/
|
|
123
|
+
getTargetContextForRelativePath(currentContext, upLevels) {
|
|
124
|
+
var _a;
|
|
125
|
+
if (currentContext === null || upLevels === 0) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
let targetContext = currentContext;
|
|
129
|
+
// Navigate up the context hierarchy
|
|
130
|
+
for (let i = 0; i < upLevels; i++) {
|
|
131
|
+
const contextItem = this.contextCache.find(item => item.context === targetContext);
|
|
132
|
+
targetContext = (_a = contextItem === null || contextItem === void 0 ? void 0 : contextItem.parent) !== null && _a !== void 0 ? _a : null;
|
|
133
|
+
}
|
|
134
|
+
return targetContext;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Caches a path at the root level
|
|
138
|
+
*/
|
|
139
|
+
cacheAtRootLevel(config) {
|
|
140
|
+
// For access type, cache the property directly as an access path
|
|
141
|
+
if (config.type === "access") {
|
|
142
|
+
this.cachePaths[config.propertyName] = {
|
|
143
|
+
type: config.type,
|
|
144
|
+
relativePath: config.propertyName,
|
|
145
|
+
absolutePath: config.absolutePath,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Caches a path at a specific context level
|
|
151
|
+
*/
|
|
152
|
+
cacheAtContextLevel(config) {
|
|
153
|
+
// Find the context in cache to determine where to place this
|
|
154
|
+
const contextItem = this.contextCache.find(item => item.context === config.targetContext);
|
|
155
|
+
if (!contextItem) {
|
|
156
|
+
// Fallback to root level if context not found
|
|
157
|
+
this.cacheAtRootLevel(config);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// For now, cache at root level since the context structure is complex
|
|
161
|
+
// This could be enhanced to place at the exact context level if needed
|
|
162
|
+
this.cacheAtRootLevel(config);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Handles caching of access-type paths (property bindings)
|
|
166
|
+
*/
|
|
167
|
+
handleAccessPath(config) {
|
|
168
|
+
if (config.contextPath === null && config.parentContext === null) {
|
|
169
|
+
this.cacheSimpleAccessPath(config);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const context = this.findContextInCache(config.parentContext);
|
|
173
|
+
if (!context) {
|
|
174
|
+
this.cacheSimpleAccessPath(config);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Try to place this access path under an existing repeat structure
|
|
178
|
+
if (!this.tryPlaceInExistingRepeat(config.path, context)) {
|
|
179
|
+
this.cacheAccessPathWithContext(Object.assign(Object.assign({}, config), { context }));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Handles caching of repeat-type paths (array/loop contexts)
|
|
184
|
+
*/
|
|
185
|
+
handleRepeatPath(config) {
|
|
186
|
+
// Add to context cache first
|
|
187
|
+
this.contextCache.push({
|
|
188
|
+
absolutePath: this.getAbsolutePath(Object.assign(Object.assign({}, config), { type: "repeat" })),
|
|
189
|
+
context: config.contextPath,
|
|
190
|
+
parent: config.parentContext,
|
|
191
|
+
});
|
|
192
|
+
// Create path structure if this is a nested repeat
|
|
193
|
+
if (config.self && config.parentContext) {
|
|
194
|
+
this.createNestedRepeatStructure(config.path, config.parentContext, config.contextPath, config.rootPath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Caches a simple access path without context complexity
|
|
199
|
+
*/
|
|
200
|
+
cacheSimpleAccessPath(config) {
|
|
201
|
+
const cachePaths = [config.rootPath, config.path];
|
|
202
|
+
this.resolveContextPath(cachePaths, {
|
|
203
|
+
type: "access",
|
|
204
|
+
relativePath: config.path,
|
|
205
|
+
absolutePath: this.getAbsolutePath(Object.assign(Object.assign({}, config), { contextPath: null, type: "access" })),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Finds a context item in the temporary context cache
|
|
210
|
+
*/
|
|
211
|
+
findContextInCache(parentContext) {
|
|
212
|
+
return this.contextCache.find(item => item.context === parentContext);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Attempts to place an access path under an existing repeat structure
|
|
216
|
+
* @returns true if successfully placed, false otherwise
|
|
217
|
+
*/
|
|
218
|
+
tryPlaceInExistingRepeat(path, context) {
|
|
219
|
+
const contextName = context.context;
|
|
220
|
+
for (const [rootKey, rootValue] of Object.entries(this.cachePaths)) {
|
|
221
|
+
if (this.searchAndPlaceInRepeat(rootValue, [rootKey], path, contextName)) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Recursively searches for and places access paths in matching repeat structures
|
|
229
|
+
*/
|
|
230
|
+
searchAndPlaceInRepeat(pathObj, currentPath, path, contextName) {
|
|
231
|
+
for (const [pathKey, pathValue] of Object.entries(pathObj.paths || {})) {
|
|
232
|
+
const typedPathValue = pathValue;
|
|
233
|
+
if (this.isMatchingRepeatStructure(typedPathValue, contextName)) {
|
|
234
|
+
this.placeAccessInRepeat(currentPath, pathKey, path, contextName);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
// Recursively search nested paths
|
|
238
|
+
if (this.canSearchNested(typedPathValue)) {
|
|
239
|
+
if (this.searchAndPlaceInRepeat(typedPathValue, [...currentPath, pathKey], path, contextName)) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Checks if a cached path is a repeat structure with matching context
|
|
248
|
+
*/
|
|
249
|
+
isMatchingRepeatStructure(pathValue, contextName) {
|
|
250
|
+
return (pathValue.type === "repeat" &&
|
|
251
|
+
pathValue.context === contextName);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Determines if a cached path can be searched for nested structures
|
|
255
|
+
*/
|
|
256
|
+
canSearchNested(pathValue) {
|
|
257
|
+
return pathValue.type === "repeat" || pathValue.type === "default";
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Places an access path within an existing repeat structure
|
|
261
|
+
*/
|
|
262
|
+
placeAccessInRepeat(currentPath, pathKey, path, contextName) {
|
|
263
|
+
const cachePaths = [...currentPath, pathKey, path];
|
|
264
|
+
const absolutePath = this.buildNestedRepeatAbsolutePath({
|
|
265
|
+
currentPath,
|
|
266
|
+
pathKey,
|
|
267
|
+
path,
|
|
268
|
+
contextName,
|
|
269
|
+
});
|
|
270
|
+
this.resolveContextPath(cachePaths, {
|
|
271
|
+
type: "access",
|
|
272
|
+
relativePath: path,
|
|
273
|
+
absolutePath: absolutePath,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Builds absolute paths for nested repeat access patterns
|
|
278
|
+
* @example "root.items.__index__.subitems.__index__.title"
|
|
279
|
+
*/
|
|
280
|
+
buildNestedRepeatAbsolutePath(params) {
|
|
281
|
+
let absolutePath = "root";
|
|
282
|
+
// Build path through the hierarchy
|
|
283
|
+
for (let i = 1; i < params.currentPath.length; i++) {
|
|
284
|
+
const segment = params.currentPath[i];
|
|
285
|
+
absolutePath +=
|
|
286
|
+
i === 1 ? `.${segment}` : `.${reservedIndexPlaceholder}.${segment}`;
|
|
287
|
+
}
|
|
288
|
+
// Add the final repeat and property
|
|
289
|
+
const contextPrefix = `${params.contextName}.`;
|
|
290
|
+
const pathWithoutContext = params.path.startsWith(contextPrefix)
|
|
291
|
+
? params.path.substring(contextPrefix.length)
|
|
292
|
+
: params.path;
|
|
293
|
+
// If the path is just the context name itself, don't append anything after __index__
|
|
294
|
+
absolutePath +=
|
|
295
|
+
params.path === params.contextName
|
|
296
|
+
? `.${reservedIndexPlaceholder}.${params.pathKey}.${reservedIndexPlaceholder}`
|
|
297
|
+
: `.${reservedIndexPlaceholder}.${params.pathKey}.${reservedIndexPlaceholder}.${pathWithoutContext}`;
|
|
298
|
+
return absolutePath;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Caches access paths that have context information
|
|
302
|
+
*/
|
|
303
|
+
cacheAccessPathWithContext(config) {
|
|
304
|
+
const contextPathRelative = this.getRelativeContextPath(config.context);
|
|
305
|
+
// Create repeat structure if needed
|
|
306
|
+
this.ensureRepeatStructureExists(config.rootPath, contextPathRelative, config.context);
|
|
307
|
+
if (config.self && contextPathRelative !== "") {
|
|
308
|
+
const cachePaths = [config.rootPath, contextPathRelative, config.path];
|
|
309
|
+
this.resolveContextPath(cachePaths, {
|
|
310
|
+
type: "access",
|
|
311
|
+
relativePath: config.path,
|
|
312
|
+
absolutePath: this.getAbsolutePath(Object.assign(Object.assign({}, config), { contextPath: null, type: "access" })),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
this.cacheSimpleAccessPath(config);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Extracts the relative path from a context's absolute path
|
|
321
|
+
* For nested contexts, this should match the cache structure path
|
|
322
|
+
*/
|
|
323
|
+
getRelativeContextPath(context) {
|
|
324
|
+
// For nested repeats, we need to find the path in the cache structure
|
|
325
|
+
// The cache is organized as: root.items.users.badges
|
|
326
|
+
// But the absolute path might be: root.items.__index__.users.__index__.badges.__index__
|
|
327
|
+
const absolutePathSplit = context.absolutePath.split(".");
|
|
328
|
+
absolutePathSplit.shift(); // Remove root
|
|
329
|
+
// Remove __index__ placeholders and the final __index__ if present
|
|
330
|
+
const cleanedPath = absolutePathSplit.filter(segment => segment !== reservedIndexPlaceholder);
|
|
331
|
+
// Remove the last segment as it represents the current context position, not the parent path
|
|
332
|
+
if (cleanedPath.length > 1) {
|
|
333
|
+
cleanedPath.pop();
|
|
334
|
+
}
|
|
335
|
+
return cleanedPath.join(".");
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Ensures a repeat structure exists in the cache
|
|
339
|
+
*/
|
|
340
|
+
ensureRepeatStructureExists(rootPath, contextPathRelative, context) {
|
|
341
|
+
if (contextPathRelative !== "") {
|
|
342
|
+
const rootCachedPath = this.cachePaths[rootPath];
|
|
343
|
+
if (!rootCachedPath.paths[contextPathRelative]) {
|
|
344
|
+
rootCachedPath.paths[contextPathRelative] = {
|
|
345
|
+
type: "repeat",
|
|
346
|
+
context: context.context,
|
|
347
|
+
paths: {},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Creates the cache structure for nested repeat patterns
|
|
354
|
+
*/
|
|
355
|
+
createNestedRepeatStructure(path, parentContext, contextPath, rootPath) {
|
|
356
|
+
const context = this.findContextInCache(parentContext);
|
|
357
|
+
if (!context)
|
|
358
|
+
return;
|
|
359
|
+
// For nested repeats, we need to find where the parent context was placed
|
|
360
|
+
// in the cache structure, not use the absolute path calculation
|
|
361
|
+
const parentRepeatPath = this.findParentRepeatPath(parentContext, rootPath);
|
|
362
|
+
const pathSegment = path.split(".").pop();
|
|
363
|
+
const cachePaths = parentRepeatPath
|
|
364
|
+
? [...parentRepeatPath, pathSegment]
|
|
365
|
+
: [rootPath, pathSegment];
|
|
366
|
+
this.resolveContextPath(cachePaths, {
|
|
367
|
+
type: "repeat",
|
|
368
|
+
context: contextPath,
|
|
369
|
+
paths: {},
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Finds the cache path where a parent context's repeat structure is located
|
|
374
|
+
*/
|
|
375
|
+
findParentRepeatPath(parentContext, rootPath) {
|
|
376
|
+
// Search through the cache structure to find where this context is defined
|
|
377
|
+
const searchInStructure = (obj, currentPath) => {
|
|
378
|
+
if (obj.type === "repeat" && obj.context === parentContext) {
|
|
379
|
+
return currentPath;
|
|
380
|
+
}
|
|
381
|
+
if (obj.paths) {
|
|
382
|
+
for (const [key, value] of Object.entries(obj.paths)) {
|
|
383
|
+
const result = searchInStructure(value, [...currentPath, key]);
|
|
384
|
+
if (result)
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
};
|
|
390
|
+
return searchInStructure(this.cachePaths[rootPath], [rootPath]);
|
|
391
|
+
}
|
|
392
|
+
getCachedPathsWithContext() {
|
|
393
|
+
return this.cachePaths;
|
|
394
|
+
}
|
|
395
|
+
resolveContextPath(cachePaths, cachePath) {
|
|
396
|
+
const tempCachePathLastItem = cachePaths.length - 1;
|
|
397
|
+
cachePaths.reduce((previousValue, tempCachePath, index) => {
|
|
398
|
+
var _a;
|
|
399
|
+
if (index === tempCachePathLastItem) {
|
|
400
|
+
// Ensure the previous value has a paths property
|
|
401
|
+
if (!previousValue.paths) {
|
|
402
|
+
previousValue.paths = {};
|
|
403
|
+
}
|
|
404
|
+
previousValue.paths[tempCachePath] = cachePath;
|
|
405
|
+
return previousValue;
|
|
406
|
+
}
|
|
407
|
+
// Navigate to the next level
|
|
408
|
+
const nextValue = index === 0
|
|
409
|
+
? previousValue[tempCachePath]
|
|
410
|
+
: (_a = previousValue.paths) === null || _a === void 0 ? void 0 : _a[tempCachePath];
|
|
411
|
+
// Ensure the next value exists and has paths property if needed
|
|
412
|
+
if (!nextValue) {
|
|
413
|
+
throw new Error(`Cannot resolve context path: missing intermediate path at '${tempCachePath}'`);
|
|
414
|
+
}
|
|
415
|
+
return nextValue;
|
|
416
|
+
}, this.cachePaths);
|
|
417
|
+
}
|
|
418
|
+
resolveRootAndContextPath(type, path, rootPath, contextPath) {
|
|
419
|
+
switch (type) {
|
|
420
|
+
case "access": {
|
|
421
|
+
const containsContext = this.contextCache.find(contextCacheItem => {
|
|
422
|
+
return contextCacheItem.context === rootPath;
|
|
423
|
+
});
|
|
424
|
+
// add a root path if one has not been assigned
|
|
425
|
+
if (!this.cachePaths[rootPath] && !containsContext) {
|
|
426
|
+
this.cachePaths[rootPath] = {
|
|
427
|
+
type: "default",
|
|
428
|
+
paths: {},
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case "repeat": {
|
|
434
|
+
// add a context path if one has not been assigned
|
|
435
|
+
// add a root path if one has not been assigned
|
|
436
|
+
if (rootPath === path) {
|
|
437
|
+
this.cachePaths[rootPath] = {
|
|
438
|
+
type: "repeat",
|
|
439
|
+
context: contextPath,
|
|
440
|
+
paths: {},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
getAbsolutePath(config) {
|
|
448
|
+
const splitPath = [];
|
|
449
|
+
const contextSplitPath = config.contextPath
|
|
450
|
+
? config.contextPath.split(".")
|
|
451
|
+
: [];
|
|
452
|
+
// Split path by "../" and handle each part
|
|
453
|
+
const pathParts = config.path.split("../");
|
|
454
|
+
pathParts.forEach(pathItem => {
|
|
455
|
+
if (pathItem === "") {
|
|
456
|
+
splitPath.unshift("../");
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
splitPath.push(...pathItem.split("."));
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
// Handle level-based path resolution
|
|
463
|
+
for (let i = config.level; i > 0; i--) {
|
|
464
|
+
if (splitPath[0] === "../") {
|
|
465
|
+
splitPath.shift();
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
contextSplitPath.pop();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const contextPathUpdated = contextSplitPath.join(".");
|
|
472
|
+
if (config.self) {
|
|
473
|
+
// For array items, remove the context prefix and build full path
|
|
474
|
+
splitPath.shift();
|
|
475
|
+
// Build the full path by recursively traversing contextCache
|
|
476
|
+
const fullContextPath = this.getPathFromCachedContext(config.parentContext, config.contextPath);
|
|
477
|
+
const pathSuffix = splitPath.join(".");
|
|
478
|
+
if (fullContextPath) {
|
|
479
|
+
return `${fullContextPath}.${reservedIndexPlaceholder}.${pathSuffix}`;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
return `${reservedIndexPlaceholder}.${pathSuffix}`;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (config.type === "repeat") {
|
|
486
|
+
return splitPath.join(".");
|
|
487
|
+
}
|
|
488
|
+
const pathSuffix = splitPath.join(".");
|
|
489
|
+
if (contextPathUpdated) {
|
|
490
|
+
return `${contextPathUpdated}.${pathSuffix}`;
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
return pathSuffix;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Builds the full context path by looking up parent contexts in contextCache
|
|
498
|
+
* @param parentContext - The immediate parent context to start from
|
|
499
|
+
* @param contextPath - The current context path (like "items")
|
|
500
|
+
* @returns The full absolute path including all parent contexts
|
|
501
|
+
*/
|
|
502
|
+
getPathFromCachedContext(parentContext, contextPath) {
|
|
503
|
+
if (!parentContext) {
|
|
504
|
+
return contextPath || "";
|
|
505
|
+
}
|
|
506
|
+
// Find the parent context in contextCache
|
|
507
|
+
const parentContextItem = this.contextCache.find(item => item.context === parentContext);
|
|
508
|
+
if (!parentContextItem) {
|
|
509
|
+
return contextPath || "";
|
|
510
|
+
}
|
|
511
|
+
// The parent's absolutePath is the base path we want to use
|
|
512
|
+
// For array access, we add __index__ between parent and child paths
|
|
513
|
+
const parentAbsolutePath = parentContextItem.absolutePath;
|
|
514
|
+
if (contextPath) {
|
|
515
|
+
// If we have a contextPath, add it to the parent path with __index__
|
|
516
|
+
// Remove trailing dot if present
|
|
517
|
+
const cleanParentPath = parentAbsolutePath.endsWith(".")
|
|
518
|
+
? parentAbsolutePath.slice(0, -1)
|
|
519
|
+
: parentAbsolutePath;
|
|
520
|
+
return `${cleanParentPath}.${reservedIndexPlaceholder}.${contextPath}`;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// If no contextPath, just return the parent's path - this is the base context
|
|
524
|
+
// Remove trailing dot if present
|
|
525
|
+
return parentAbsolutePath.endsWith(".")
|
|
526
|
+
? parentAbsolutePath.slice(0, -1)
|
|
527
|
+
: parentAbsolutePath;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
defineProperties() {
|
|
531
|
+
Object.keys(this.cachePaths).forEach(propertyName => {
|
|
532
|
+
Observable.defineProperty(this.classPrototype, propertyName);
|
|
533
|
+
this.classPrototype[`${propertyName}Changed`] =
|
|
534
|
+
this.defineChanged(propertyName);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Creates a proxy for an object that intercepts property mutations and triggers Observable notifications
|
|
539
|
+
* @param target - The target instance that owns the root property
|
|
540
|
+
* @param rootProperty - The name of the root property for notification purposes
|
|
541
|
+
* @param object - The object to wrap with a proxy
|
|
542
|
+
* @returns A proxy that triggers notifications on property mutations
|
|
543
|
+
*/
|
|
544
|
+
getAndAssignObservables(target, rootProperty, object, cachePaths) {
|
|
545
|
+
let proxiedObject = object;
|
|
546
|
+
if (cachePaths[rootProperty]) {
|
|
547
|
+
proxiedObject = assignObservables(cachePaths[rootProperty], proxiedObject, target, rootProperty);
|
|
548
|
+
}
|
|
549
|
+
return proxiedObject;
|
|
550
|
+
}
|
|
551
|
+
}
|