@schukai/monster 4.153.0 → 4.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/source/components/form/message-state-button.mjs +1 -1
- package/source/components/form/select-link.mjs +12 -1
- package/source/components/state/log.mjs +3 -7
- package/source/components/state/thread.mjs +3 -7
- package/source/components/time/day.mjs +0 -19
- package/source/components/time/month-calendar.mjs +5 -1
- package/source/data/buildmap.mjs +6 -5
- package/source/data/buildtree.mjs +0 -8
- package/source/data/datasource.mjs +3 -10
- package/source/data/diff.mjs +13 -12
- package/source/data/extend.mjs +23 -1
- package/source/data/pathfinder.mjs +228 -32
- package/source/data/transformer.mjs +2 -2
- package/source/dom/customcontrol.mjs +35 -9
- package/source/dom/customelement.mjs +293 -31
- package/source/dom/slotted.mjs +46 -31
- package/source/dom/template.mjs +4 -1
- package/source/dom/updater.mjs +57 -17
- package/source/types/base.mjs +7 -6
- package/source/types/basewithoptions.mjs +1 -8
- package/source/types/internal.mjs +4 -10
- package/source/types/node.mjs +1 -3
- package/source/types/observerlist.mjs +9 -17
- package/source/types/proxyobserver.mjs +51 -12
- package/source/types/tokenlist.mjs +13 -8
- package/source/util/clone.mjs +44 -14
- package/test/cases/browser-bundle.mjs +75 -0
- package/test/cases/components/time/month-calendar.mjs +115 -0
- package/test/cases/data/buildmap.mjs +14 -0
- package/test/cases/data/buildtree.mjs +49 -0
- package/test/cases/data/diff.mjs +86 -0
- package/test/cases/data/extend.mjs +52 -0
- package/test/cases/data/pathfinder-resolution-baseline.mjs +322 -0
- package/test/cases/data/pathfinder.mjs +163 -0
- package/test/cases/data/transformer.mjs +26 -0
- package/test/cases/dom/customcontrol.mjs +85 -0
- package/test/cases/dom/customelement-rebind-baseline.mjs +323 -0
- package/test/cases/dom/customelement-teardown.mjs +408 -0
- package/test/cases/dom/customelement.mjs +408 -0
- package/test/cases/dom/dimension.mjs +34 -10
- package/test/cases/dom/slotted-nodes.mjs +48 -1
- package/test/cases/dom/template.mjs +11 -0
- package/test/cases/dom/updater-remove.mjs +198 -0
- package/test/cases/dom/updater.mjs +118 -0
- package/test/cases/packaging.mjs +213 -0
- package/test/cases/types/base.mjs +50 -0
- package/test/cases/types/observerlist.mjs +103 -1
- package/test/cases/types/proxyobserver.mjs +189 -0
- package/test/cases/types/regex.mjs +4 -0
- package/test/cases/types/tokenlist.mjs +64 -1
- package/test/cases/util/clone.mjs +115 -0
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","files":["source","test/cases","test/util","CHANGELOG.md"],"homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","exports":{".":"./source/monster.mjs","./CHANGELOG.md":"./CHANGELOG.md","./LICENSE":"./LICENSE","./README.md":"./README.md","./package.json":"./package.json","./source/*":"./source/*","./test/*":"./test/*"},"files":["source","test/cases","test/util","CHANGELOG.md"],"homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"sideEffects":["./source/monster.mjs","./source/components/**","./source/i18n/**","./test/**"],"type":"module","version":"4.154.0"}
|
|
@@ -67,11 +67,22 @@ class SelectLink extends CustomElement {
|
|
|
67
67
|
|
|
68
68
|
[assembleMethodSymbol]() {
|
|
69
69
|
super[assembleMethodSymbol]();
|
|
70
|
-
bindSelects.call(this);
|
|
71
70
|
return this;
|
|
72
71
|
}
|
|
73
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Binding belongs to the connect lifecycle, not to assembly: disconnecting
|
|
75
|
+
* unbinds, so a reconnect has to bind again. bindSelects() is idempotent.
|
|
76
|
+
*
|
|
77
|
+
* @inheritdoc
|
|
78
|
+
*/
|
|
79
|
+
connectedCallback() {
|
|
80
|
+
super.connectedCallback();
|
|
81
|
+
bindSelects.call(this);
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
disconnectedCallback() {
|
|
85
|
+
super.disconnectedCallback();
|
|
75
86
|
unbindSelects.call(this);
|
|
76
87
|
}
|
|
77
88
|
}
|
|
@@ -136,13 +136,9 @@ class Log extends CustomElement {
|
|
|
136
136
|
*/
|
|
137
137
|
getOption(path, defaultValue = undefined) {
|
|
138
138
|
if (path === "entries" || path?.startsWith("entries.")) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}).getVia(path);
|
|
143
|
-
} catch (e) {
|
|
144
|
-
return defaultValue;
|
|
145
|
-
}
|
|
139
|
+
return new Pathfinder({
|
|
140
|
+
entries: this[entriesSymbol],
|
|
141
|
+
}).getVia(path, defaultValue);
|
|
146
142
|
}
|
|
147
143
|
|
|
148
144
|
return super.getOption(path, defaultValue);
|
|
@@ -143,13 +143,9 @@ class Thread extends CustomElement {
|
|
|
143
143
|
*/
|
|
144
144
|
getOption(path, defaultValue = undefined) {
|
|
145
145
|
if (path === "entries" || path?.startsWith("entries.")) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}).getVia(path);
|
|
150
|
-
} catch (e) {
|
|
151
|
-
return defaultValue;
|
|
152
|
-
}
|
|
146
|
+
return new Pathfinder({
|
|
147
|
+
entries: this[entriesSymbol],
|
|
148
|
+
}).getVia(path, defaultValue);
|
|
153
149
|
}
|
|
154
150
|
|
|
155
151
|
return super.getOption(path, defaultValue);
|
|
@@ -363,25 +363,6 @@ function getLabels() {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
/**
|
|
367
|
-
* @private
|
|
368
|
-
* @param format
|
|
369
|
-
* @returns {*[]}
|
|
370
|
-
*/
|
|
371
|
-
function getWeekdays(format = "long") {
|
|
372
|
-
const locale = getLocaleOfDocument();
|
|
373
|
-
|
|
374
|
-
const weekdays = [];
|
|
375
|
-
for (let i = 1; i < 8; i++) {
|
|
376
|
-
const date = new Date(1970, 0, 4 + i); // 4. Jan. 1970 = Sonntag
|
|
377
|
-
weekdays.push(
|
|
378
|
-
new Intl.DateTimeFormat(locale, { weekday: format }).format(date),
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return weekdays;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
366
|
/**
|
|
386
367
|
* @private
|
|
387
368
|
* @return {void}
|
|
@@ -363,7 +363,11 @@ function getAppointmentRowsUsingCalendar(
|
|
|
363
363
|
* @returns {*[]}
|
|
364
364
|
*/
|
|
365
365
|
function getWeekdays(format = "long") {
|
|
366
|
-
|
|
366
|
+
// Intl needs the language tag. Handing it the Locale instance makes
|
|
367
|
+
// CanonicalizeLocaleList treat the object as array like, and its missing
|
|
368
|
+
// length yields an empty locale list, so the formatter silently falls back
|
|
369
|
+
// to the host system locale and the document language is ignored.
|
|
370
|
+
const locale = getLocaleOfDocument().toString();
|
|
367
371
|
|
|
368
372
|
const weekdays = [];
|
|
369
373
|
for (let i = 1; i < 8; i++) {
|
package/source/data/buildmap.mjs
CHANGED
|
@@ -181,9 +181,9 @@ function buildFlatMap(subject, selector, key, parentMap) {
|
|
|
181
181
|
for (const [k, o] of map) {
|
|
182
182
|
const copyKey = clone(key);
|
|
183
183
|
|
|
184
|
-
currentPath
|
|
185
|
-
copyKey.push(
|
|
186
|
-
}
|
|
184
|
+
for (const segment of currentPath) {
|
|
185
|
+
copyKey.push(segment === WILDCARD ? k : segment);
|
|
186
|
+
}
|
|
187
187
|
|
|
188
188
|
const kk = copyKey.join(DELIMITER);
|
|
189
189
|
const sub = buildFlatMap.call(
|
|
@@ -362,7 +362,8 @@ function buildFlatMap(subject, selector, key, parentMap) {
|
|
|
362
362
|
* @return {*}
|
|
363
363
|
*/
|
|
364
364
|
export function build(subject, definition, defaultValue) {
|
|
365
|
-
if (definition === undefined)
|
|
365
|
+
if (definition === undefined)
|
|
366
|
+
return defaultValue !== undefined ? defaultValue : subject;
|
|
366
367
|
validateString(definition);
|
|
367
368
|
|
|
368
369
|
const regexp = /(?<placeholder>\${(?<path>[a-z\^A-Z.\-_0-9]*)})/gm;
|
|
@@ -384,7 +385,7 @@ export function build(subject, definition, defaultValue) {
|
|
|
384
385
|
let v = finder.getVia(path);
|
|
385
386
|
if (v === undefined) v = defaultValue;
|
|
386
387
|
|
|
387
|
-
definition = definition.replaceAll(placeholder, v);
|
|
388
|
+
definition = definition.replaceAll(placeholder, () => `${v}`);
|
|
388
389
|
});
|
|
389
390
|
|
|
390
391
|
return definition;
|
|
@@ -21,12 +21,6 @@ import { extend } from "./extend.mjs";
|
|
|
21
21
|
|
|
22
22
|
export { buildTree };
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* @private
|
|
26
|
-
* @type {symbol}
|
|
27
|
-
*/
|
|
28
|
-
const parentSymbol = Symbol("parent");
|
|
29
|
-
|
|
30
24
|
/**
|
|
31
25
|
* @private
|
|
32
26
|
* @type {symbol}
|
|
@@ -171,8 +165,6 @@ function buildTree(subject, selector, idKey, parentIDKey, options) {
|
|
|
171
165
|
throw new Error("the object has no value for the specified id");
|
|
172
166
|
}
|
|
173
167
|
|
|
174
|
-
o[parentSymbol] = ref;
|
|
175
|
-
|
|
176
168
|
const node = new Node(o);
|
|
177
169
|
this.has(ref)
|
|
178
170
|
? this.get(ref).add(node)
|
|
@@ -165,16 +165,9 @@ class Datasource extends Base {
|
|
|
165
165
|
* @return {*}
|
|
166
166
|
*/
|
|
167
167
|
getOption(path, defaultValue) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
value = new Pathfinder(
|
|
172
|
-
this[internalSymbol].getRealSubject()["options"],
|
|
173
|
-
).getVia(path);
|
|
174
|
-
} catch (e) {}
|
|
175
|
-
|
|
176
|
-
if (value === undefined) return defaultValue;
|
|
177
|
-
return value;
|
|
168
|
+
return new Pathfinder(
|
|
169
|
+
this[internalSymbol].getRealSubject()["options"],
|
|
170
|
+
).getVia(path, defaultValue);
|
|
178
171
|
}
|
|
179
172
|
|
|
180
173
|
/**
|
package/source/data/diff.mjs
CHANGED
|
@@ -35,20 +35,15 @@ function diff(first, second) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
+
* Own keys of both sides. Arrays are covered by this as well: their indices are
|
|
39
|
+
* own enumerable keys, and reporting them as string path segments is the
|
|
40
|
+
* established contract for every diff path.
|
|
38
41
|
* @private
|
|
39
42
|
* @param a
|
|
40
43
|
* @param b
|
|
41
|
-
* @
|
|
42
|
-
* @return {Set<string>|Set<number>}
|
|
44
|
+
* @return {Set<string>}
|
|
43
45
|
*/
|
|
44
|
-
function getKeys(a, b
|
|
45
|
-
if (isArray(type)) {
|
|
46
|
-
const keys =
|
|
47
|
-
a.length > b.length ? new Array(a.length) : new Array(b.length);
|
|
48
|
-
keys.fill(0);
|
|
49
|
-
return new Set(keys.map((_, i) => i));
|
|
50
|
-
}
|
|
51
|
-
|
|
46
|
+
function getKeys(a, b) {
|
|
52
47
|
return new Set(Object.keys(a).concat(Object.keys(b)));
|
|
53
48
|
}
|
|
54
49
|
|
|
@@ -68,7 +63,7 @@ function doDiff(a, b, path, diff) {
|
|
|
68
63
|
const currDiff = diff || [];
|
|
69
64
|
|
|
70
65
|
if (typeA === typeB && (typeA === "object" || typeA === "array")) {
|
|
71
|
-
const keys = getKeys(a, b
|
|
66
|
+
const keys = getKeys(a, b);
|
|
72
67
|
if (keys.size === 0) {
|
|
73
68
|
return currDiff;
|
|
74
69
|
}
|
|
@@ -83,7 +78,7 @@ function doDiff(a, b, path, diff) {
|
|
|
83
78
|
}
|
|
84
79
|
});
|
|
85
80
|
} else {
|
|
86
|
-
const o = getOperator(a, b
|
|
81
|
+
const o = getOperator(a, b);
|
|
87
82
|
if (o !== undefined) {
|
|
88
83
|
currDiff.push(buildResult(a, b, o, path));
|
|
89
84
|
}
|
|
@@ -153,6 +148,12 @@ function isNotEqual(a, b) {
|
|
|
153
148
|
return a.getTime() !== b.getTime();
|
|
154
149
|
}
|
|
155
150
|
|
|
151
|
+
// Two NaN values at the same path are the same value. Object.is() is not used
|
|
152
|
+
// here because it would additionally report 0 and -0 as a change.
|
|
153
|
+
if (Number.isNaN(a) && Number.isNaN(b)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
156
157
|
return a !== b;
|
|
157
158
|
}
|
|
158
159
|
|
package/source/data/extend.mjs
CHANGED
|
@@ -33,6 +33,16 @@ const unsafeMutationKeys = new Set(["__proto__", "prototype", "constructor"]);
|
|
|
33
33
|
* @param args
|
|
34
34
|
*/
|
|
35
35
|
function extend(...args) {
|
|
36
|
+
return extendValues(args, new Map());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @private
|
|
41
|
+
* @param {array} args
|
|
42
|
+
* @param {Map} path source objects currently being merged, mapped to their target
|
|
43
|
+
* @return {object}
|
|
44
|
+
*/
|
|
45
|
+
function extendValues(args, path) {
|
|
36
46
|
let o;
|
|
37
47
|
let i;
|
|
38
48
|
|
|
@@ -65,6 +75,13 @@ function extend(...args) {
|
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
if ((isObject(v) && typeOf(v) === "object") || isArray(v)) {
|
|
78
|
+
// A source object that is still being merged further up the call
|
|
79
|
+
// chain is a cycle. Reuse its target instead of recursing forever.
|
|
80
|
+
if (path.has(v)) {
|
|
81
|
+
o[k] = path.get(v);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
68
85
|
if (o[k] === undefined) {
|
|
69
86
|
if (isArray(v)) {
|
|
70
87
|
o[k] = [];
|
|
@@ -87,7 +104,12 @@ function extend(...args) {
|
|
|
87
104
|
continue;
|
|
88
105
|
}
|
|
89
106
|
|
|
90
|
-
|
|
107
|
+
path.set(v, o[k]);
|
|
108
|
+
try {
|
|
109
|
+
o[k] = extendValues([o[k], v], path);
|
|
110
|
+
} finally {
|
|
111
|
+
path.delete(v);
|
|
112
|
+
}
|
|
91
113
|
} else {
|
|
92
114
|
if (isArray(o)) {
|
|
93
115
|
o.push(v);
|
|
@@ -133,8 +133,14 @@ class Pathfinder extends Base {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
+
* Reads the value at the given path.
|
|
137
|
+
*
|
|
138
|
+
* Without a second argument a path that cannot be traversed throws, and a
|
|
139
|
+
* missing leaf resolves to undefined. With a second argument the value is
|
|
140
|
+
* returned instead of the error, and also instead of an undefined result.
|
|
136
141
|
*
|
|
137
142
|
* @param {string|array} path
|
|
143
|
+
* @param {*} defaultValue returned when the path cannot be resolved
|
|
138
144
|
* @since 1.4.0
|
|
139
145
|
* @return {*}
|
|
140
146
|
* @throws {TypeError} unsupported type
|
|
@@ -143,8 +149,18 @@ class Pathfinder extends Base {
|
|
|
143
149
|
* @throws {TypeError} value is not an integer
|
|
144
150
|
* @throws {Error} unsupported action for this data type
|
|
145
151
|
*/
|
|
146
|
-
getVia(path) {
|
|
147
|
-
|
|
152
|
+
getVia(path, defaultValue) {
|
|
153
|
+
// Without a second argument the throwing contract is unchanged.
|
|
154
|
+
if (arguments.length < 2) {
|
|
155
|
+
return getValueViaPath.call(this, this.object, path);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const value = resolveValueViaPath.call(this, this.object, path, false);
|
|
159
|
+
if (value === NOT_FOUND || value === undefined) {
|
|
160
|
+
return defaultValue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return value;
|
|
148
164
|
}
|
|
149
165
|
|
|
150
166
|
/**
|
|
@@ -189,13 +205,173 @@ class Pathfinder extends Base {
|
|
|
189
205
|
* @since 1.4.0
|
|
190
206
|
*/
|
|
191
207
|
exists(path) {
|
|
208
|
+
return (
|
|
209
|
+
resolveValueViaPath.call(this, this.object, path, true) !== NOT_FOUND
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Returned instead of throwing when a path cannot be resolved.
|
|
216
|
+
* @private
|
|
217
|
+
* @type {symbol}
|
|
218
|
+
*/
|
|
219
|
+
const NOT_FOUND = Symbol("@schukai/monster/data/pathfinder@@notFound");
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Split paths are memoized. getOption() resolves about 1393 option paths across
|
|
223
|
+
* the library, and the same handful of strings is used over and over.
|
|
224
|
+
* @private
|
|
225
|
+
* @type {Map<string, string[]>}
|
|
226
|
+
*/
|
|
227
|
+
const pathSplitCache = new Map();
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @private
|
|
231
|
+
* @type {number}
|
|
232
|
+
*/
|
|
233
|
+
const PATH_SPLIT_CACHE_LIMIT = 2048;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The returned array is shared and must never be mutated by the caller.
|
|
237
|
+
* @private
|
|
238
|
+
* @param {string} path
|
|
239
|
+
* @return {string[]}
|
|
240
|
+
*/
|
|
241
|
+
function splitPath(path) {
|
|
242
|
+
const cached = pathSplitCache.get(path);
|
|
243
|
+
if (cached !== undefined) {
|
|
244
|
+
return cached;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const parts = path.split(DELIMITER);
|
|
248
|
+
|
|
249
|
+
if (pathSplitCache.size >= PATH_SPLIT_CACHE_LIMIT) {
|
|
250
|
+
pathSplitCache.clear();
|
|
251
|
+
}
|
|
252
|
+
pathSplitCache.set(path, parts);
|
|
253
|
+
|
|
254
|
+
return parts;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* A complete integer path segment, or undefined when the segment is not one.
|
|
259
|
+
* @private
|
|
260
|
+
* @param {*} value
|
|
261
|
+
* @return {number|undefined}
|
|
262
|
+
*/
|
|
263
|
+
function toPathIndex(value) {
|
|
264
|
+
if (!/^-?\d+$/.test(`${value}`)) {
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const index = Number(value);
|
|
269
|
+
if (!Number.isInteger(index)) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return index;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Non throwing counterpart of getValueViaPath(). Returns NOT_FOUND instead of
|
|
278
|
+
* throwing, so that resolving an option does not construct an exception, and
|
|
279
|
+
* walks the path iteratively instead of copying and shifting it per segment.
|
|
280
|
+
*
|
|
281
|
+
* The membership semantics of `check` are the ones exists() relies on: a key
|
|
282
|
+
* that is present with value undefined counts as found, and a malformed path is
|
|
283
|
+
* simply not found rather than an error.
|
|
284
|
+
*
|
|
285
|
+
* @private
|
|
286
|
+
* @param {*} subject
|
|
287
|
+
* @param {string|array} path
|
|
288
|
+
* @param {boolean} check
|
|
289
|
+
* @return {*|symbol} the resolved value or NOT_FOUND
|
|
290
|
+
*/
|
|
291
|
+
function resolveValueViaPath(subject, path, check) {
|
|
292
|
+
let parts;
|
|
293
|
+
if (isArray(path)) {
|
|
294
|
+
if (path.length === 0) {
|
|
295
|
+
return subject;
|
|
296
|
+
}
|
|
297
|
+
parts = path;
|
|
298
|
+
} else if (isString(path)) {
|
|
299
|
+
if (path === "") {
|
|
300
|
+
return subject;
|
|
301
|
+
}
|
|
302
|
+
parts = splitPath(path);
|
|
303
|
+
} else {
|
|
304
|
+
return NOT_FOUND;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// A wildcard builds a Map through iterate(), which stays on the throwing
|
|
308
|
+
// implementation. Those paths are rare, so fall back for them.
|
|
309
|
+
if (parts.includes(this.wildCard)) {
|
|
192
310
|
try {
|
|
193
|
-
getValueViaPath.call(this,
|
|
194
|
-
|
|
195
|
-
|
|
311
|
+
return getValueViaPath.call(this, subject, path, check === true);
|
|
312
|
+
} catch (e) {
|
|
313
|
+
return NOT_FOUND;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let cursor = subject;
|
|
318
|
+
const last = parts.length - 1;
|
|
319
|
+
|
|
320
|
+
for (let i = 0; i <= last; i++) {
|
|
321
|
+
if (!(isObject(cursor) || isArray(cursor))) {
|
|
322
|
+
return NOT_FOUND;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (cursor instanceof WeakSet) {
|
|
326
|
+
return NOT_FOUND;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (typeof WeakRef === "function" && cursor instanceof WeakRef) {
|
|
330
|
+
return NOT_FOUND;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const segment = parts[i];
|
|
334
|
+
const isLast = i === last;
|
|
335
|
+
let value;
|
|
336
|
+
|
|
337
|
+
if (cursor instanceof Map || cursor instanceof WeakMap) {
|
|
338
|
+
if (isLast && check === true && !cursor.has(segment)) {
|
|
339
|
+
return NOT_FOUND;
|
|
340
|
+
}
|
|
341
|
+
value = cursor.get(segment);
|
|
342
|
+
} else if (cursor instanceof Set) {
|
|
343
|
+
const index = toPathIndex(segment);
|
|
344
|
+
if (index === undefined) {
|
|
345
|
+
return NOT_FOUND;
|
|
346
|
+
}
|
|
347
|
+
if (isLast && check === true && !(index >= 0 && index < cursor.size)) {
|
|
348
|
+
return NOT_FOUND;
|
|
349
|
+
}
|
|
350
|
+
value = [...cursor][index];
|
|
351
|
+
} else if (isArray(cursor)) {
|
|
352
|
+
const index = toPathIndex(segment);
|
|
353
|
+
if (index === undefined) {
|
|
354
|
+
return NOT_FOUND;
|
|
355
|
+
}
|
|
356
|
+
if (isLast && check === true && !Reflect.has(cursor, index)) {
|
|
357
|
+
return NOT_FOUND;
|
|
358
|
+
}
|
|
359
|
+
value = cursor[index];
|
|
360
|
+
} else {
|
|
361
|
+
if (isLast && check === true && !Reflect.has(cursor, segment)) {
|
|
362
|
+
return NOT_FOUND;
|
|
363
|
+
}
|
|
364
|
+
value = cursor[segment];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (isLast) {
|
|
368
|
+
return value;
|
|
369
|
+
}
|
|
196
370
|
|
|
197
|
-
|
|
371
|
+
cursor = value;
|
|
198
372
|
}
|
|
373
|
+
|
|
374
|
+
return NOT_FOUND;
|
|
199
375
|
}
|
|
200
376
|
|
|
201
377
|
/**
|
|
@@ -217,16 +393,21 @@ function iterate(subject, path, check) {
|
|
|
217
393
|
|
|
218
394
|
const result = new Map();
|
|
219
395
|
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
396
|
+
if (subject instanceof Map) {
|
|
397
|
+
for (const [key, value] of subject) {
|
|
398
|
+
result.set(key, getValueViaPath.call(this, value, path, check));
|
|
399
|
+
}
|
|
400
|
+
} else if (subject instanceof Set) {
|
|
401
|
+
let index = 0;
|
|
402
|
+
for (const value of subject) {
|
|
403
|
+
result.set(`${index++}`, getValueViaPath.call(this, value, path, check));
|
|
404
|
+
}
|
|
405
|
+
} else if (isObject(subject) || isArray(subject)) {
|
|
225
406
|
for (const [key, value] of Object.entries(subject)) {
|
|
226
407
|
result.set(key, getValueViaPath.call(this, value, path, check));
|
|
227
408
|
}
|
|
228
409
|
} else {
|
|
229
|
-
const key = path.split(DELIMITER).shift();
|
|
410
|
+
const key = isArray(path) ? path[0] : path.split(DELIMITER).shift();
|
|
230
411
|
result.set(key, getValueViaPath.call(this, subject, path, check));
|
|
231
412
|
}
|
|
232
413
|
|
|
@@ -272,7 +453,7 @@ function getValueViaPath(subject, path, check) {
|
|
|
272
453
|
let current = parts.shift();
|
|
273
454
|
|
|
274
455
|
if (current === this.wildCard) {
|
|
275
|
-
return iterate.call(this, subject, parts
|
|
456
|
+
return iterate.call(this, subject, parts, check);
|
|
276
457
|
}
|
|
277
458
|
|
|
278
459
|
if (isObject(subject) || isArray(subject)) {
|
|
@@ -280,23 +461,21 @@ function getValueViaPath(subject, path, check) {
|
|
|
280
461
|
if (subject instanceof Map || subject instanceof WeakMap) {
|
|
281
462
|
anchor = subject.get(current);
|
|
282
463
|
} else if (subject instanceof Set) {
|
|
283
|
-
current =
|
|
284
|
-
validateInteger(current);
|
|
464
|
+
current = parsePathIndex(current);
|
|
285
465
|
anchor = [...subject]?.[current];
|
|
286
466
|
} else if (subject instanceof WeakSet) {
|
|
287
467
|
throw Error("unsupported action for this data type (WeakSet)");
|
|
288
468
|
} else if (typeof WeakRef === "function" && subject instanceof WeakRef) {
|
|
289
469
|
throw Error("unsupported action for this data type (WeakRef)");
|
|
290
470
|
} else if (isArray(subject)) {
|
|
291
|
-
current =
|
|
292
|
-
validateInteger(current);
|
|
471
|
+
current = parsePathIndex(current);
|
|
293
472
|
anchor = subject?.[current];
|
|
294
473
|
} else {
|
|
295
474
|
anchor = subject?.[current];
|
|
296
475
|
}
|
|
297
476
|
|
|
298
477
|
if (isObject(anchor) || isArray(anchor)) {
|
|
299
|
-
return getValueViaPath.call(this, anchor, parts
|
|
478
|
+
return getValueViaPath.call(this, anchor, parts, check);
|
|
300
479
|
}
|
|
301
480
|
|
|
302
481
|
if (parts.length > 0) {
|
|
@@ -304,12 +483,18 @@ function getValueViaPath(subject, path, check) {
|
|
|
304
483
|
}
|
|
305
484
|
|
|
306
485
|
if (check === true) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
current
|
|
310
|
-
)
|
|
486
|
+
let exists;
|
|
487
|
+
if (subject instanceof Map || subject instanceof WeakMap) {
|
|
488
|
+
exists = subject.has(current);
|
|
489
|
+
} else if (subject instanceof Set) {
|
|
490
|
+
exists = current >= 0 && current < subject.size;
|
|
491
|
+
} else if (isArray(subject)) {
|
|
492
|
+
exists = Reflect.has(subject, current);
|
|
493
|
+
} else {
|
|
494
|
+
exists = Reflect.has(subject, current);
|
|
495
|
+
}
|
|
311
496
|
|
|
312
|
-
if (!
|
|
497
|
+
if (!exists) {
|
|
313
498
|
throw Error("unknown value " + current);
|
|
314
499
|
}
|
|
315
500
|
}
|
|
@@ -411,16 +596,25 @@ function setValueViaPath(subject, path, value) {
|
|
|
411
596
|
* @param {*} value
|
|
412
597
|
*/
|
|
413
598
|
function assignProperty(object, key, value) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
599
|
+
// Writing undefined keeps the key. Deletion is deliberately left to
|
|
600
|
+
// deleteVia(), because a default option declared as undefined must stay
|
|
601
|
+
// present for setOptionFromAttribute() to keep accepting changes for it.
|
|
602
|
+
object[key] = value;
|
|
603
|
+
}
|
|
418
604
|
|
|
419
|
-
|
|
420
|
-
|
|
605
|
+
/**
|
|
606
|
+
* Convert a complete integer path segment without accepting trailing text.
|
|
607
|
+
* @param {*} value
|
|
608
|
+
* @return {number}
|
|
609
|
+
* @private
|
|
610
|
+
*/
|
|
611
|
+
function parsePathIndex(value) {
|
|
612
|
+
if (!/^-?\d+$/.test(`${value}`)) {
|
|
613
|
+
throw new TypeError("value is not an integer");
|
|
421
614
|
}
|
|
422
|
-
|
|
423
|
-
|
|
615
|
+
const index = Number(value);
|
|
616
|
+
validateInteger(index);
|
|
617
|
+
return index;
|
|
424
618
|
}
|
|
425
619
|
|
|
426
620
|
/**
|
|
@@ -486,7 +680,9 @@ function deleteValueViaPath(subject, path) {
|
|
|
486
680
|
* @private
|
|
487
681
|
*/
|
|
488
682
|
function validateMutationPath(path) {
|
|
489
|
-
const parts = isArray(path) ? path : path.
|
|
683
|
+
const parts = (isArray(path) ? path : [path]).flatMap((part) =>
|
|
684
|
+
`${part}`.split(DELIMITER),
|
|
685
|
+
);
|
|
490
686
|
for (const part of parts) {
|
|
491
687
|
if (UNSAFE_MUTATION_PATH_SEGMENTS.has(`${part}`)) {
|
|
492
688
|
throw new Error(`unsafe mutation path segment: ${part}`);
|
|
@@ -530,7 +530,7 @@ function transform(value) {
|
|
|
530
530
|
|
|
531
531
|
const useKey = keys?.[key];
|
|
532
532
|
|
|
533
|
-
if (value
|
|
533
|
+
if (useKey !== undefined && Reflect.has(value, useKey)) {
|
|
534
534
|
return value?.[useKey];
|
|
535
535
|
}
|
|
536
536
|
|
|
@@ -555,7 +555,7 @@ function transform(value) {
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
if (isObject(value) || isArray(value)) {
|
|
558
|
-
if (value
|
|
558
|
+
if (Reflect.has(value, key)) {
|
|
559
559
|
return value?.[key];
|
|
560
560
|
}
|
|
561
561
|
|