data-path 1.0.3 → 2.0.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/LICENSE +17 -3
- package/README.md +60 -409
- package/dist/index.cjs +418 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +376 -0
- package/dist/index.d.ts +243 -186
- package/dist/index.js +212 -351
- package/dist/index.js.map +1 -0
- package/package.json +53 -12
- package/dist/index.d.mts +0 -319
- package/dist/index.mjs +0 -499
package/dist/index.js
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
path: () => path,
|
|
24
|
-
unsafePath: () => unsafePath
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
1
|
// src/constants.ts
|
|
29
2
|
var PATH_SEGMENTS = /* @__PURE__ */ Symbol("PATH_SEGMENTS");
|
|
30
|
-
var WILDCARD = "
|
|
31
|
-
var DEEP_WILDCARD = "
|
|
3
|
+
var WILDCARD = /* @__PURE__ */ Symbol("WILDCARD");
|
|
4
|
+
var DEEP_WILDCARD = /* @__PURE__ */ Symbol("DEEP_WILDCARD");
|
|
32
5
|
|
|
33
6
|
// src/utils.ts
|
|
34
7
|
function isCanonicalArrayIndex(key) {
|
|
@@ -41,7 +14,7 @@ function resolveSegments(target) {
|
|
|
41
14
|
const result = target(proxy);
|
|
42
15
|
return result?.[PATH_SEGMENTS] ?? [];
|
|
43
16
|
}
|
|
44
|
-
if (target && typeof target === "object" && "segments" in target) {
|
|
17
|
+
if (target != null && typeof target === "object" && "segments" in target) {
|
|
45
18
|
return target.segments;
|
|
46
19
|
}
|
|
47
20
|
return [];
|
|
@@ -53,11 +26,9 @@ function createPathProxy(segments) {
|
|
|
53
26
|
get(target, key) {
|
|
54
27
|
if (key === PATH_SEGMENTS)
|
|
55
28
|
return target[PATH_SEGMENTS];
|
|
56
|
-
if (typeof key === "
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
return typeof key === "symbol" ? void 0 : createPathProxy(segments);
|
|
29
|
+
if (typeof key === "symbol") return void 0;
|
|
30
|
+
const next = isCanonicalArrayIndex(key) ? Number(key) : key;
|
|
31
|
+
return createPathProxy([...segments, next]);
|
|
61
32
|
}
|
|
62
33
|
}
|
|
63
34
|
);
|
|
@@ -67,10 +38,12 @@ function segmentsEqual(a, b) {
|
|
|
67
38
|
return a.every((s, i) => s === b[i]);
|
|
68
39
|
}
|
|
69
40
|
function matchesPrefix(full, prefix) {
|
|
70
|
-
|
|
41
|
+
let minPrefixLen = 0;
|
|
42
|
+
for (const s of prefix) if (s !== DEEP_WILDCARD) minPrefixLen++;
|
|
43
|
+
if (minPrefixLen > full.length) return false;
|
|
71
44
|
let p = 0;
|
|
72
45
|
let f = 0;
|
|
73
|
-
while (p < prefix.length
|
|
46
|
+
while (p < prefix.length) {
|
|
74
47
|
if (prefix[p] === DEEP_WILDCARD) {
|
|
75
48
|
if (p === prefix.length - 1) return true;
|
|
76
49
|
const restPrefix = prefix.slice(p + 1);
|
|
@@ -79,64 +52,73 @@ function matchesPrefix(full, prefix) {
|
|
|
79
52
|
}
|
|
80
53
|
return false;
|
|
81
54
|
}
|
|
55
|
+
if (f >= full.length) return false;
|
|
56
|
+
if (full[f] === DEEP_WILDCARD && prefix[p] !== DEEP_WILDCARD) return false;
|
|
82
57
|
if (prefix[p] !== WILDCARD && prefix[p] !== full[f]) return false;
|
|
83
58
|
p++;
|
|
84
59
|
f++;
|
|
85
60
|
}
|
|
86
61
|
return p === prefix.length;
|
|
87
62
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (pattern[i] !== WILDCARD && pattern[i] !== DEEP_WILDCARD && pattern[i] !== concrete[i])
|
|
92
|
-
return false;
|
|
63
|
+
function hasWildcardSegment(segments) {
|
|
64
|
+
for (const s of segments) {
|
|
65
|
+
if (s === WILDCARD || s === DEEP_WILDCARD) return true;
|
|
93
66
|
}
|
|
94
|
-
return
|
|
67
|
+
return false;
|
|
95
68
|
}
|
|
96
69
|
|
|
97
|
-
// src/impl/path-impl.ts
|
|
98
|
-
var
|
|
99
|
-
function setTemplatePathCtor(ctor) {
|
|
100
|
-
TemplatePathCtor = ctor;
|
|
101
|
-
}
|
|
102
|
-
var PathImpl = class _PathImpl {
|
|
70
|
+
// src/impl/base-path-impl.ts
|
|
71
|
+
var AbstractPathImpl = class {
|
|
103
72
|
segments;
|
|
104
73
|
constructor(segments) {
|
|
105
74
|
this.segments = segments;
|
|
106
75
|
}
|
|
107
|
-
/**
|
|
108
|
-
* The number of segments in this path.
|
|
109
|
-
*/
|
|
110
76
|
get length() {
|
|
111
77
|
return this.segments.length;
|
|
112
78
|
}
|
|
113
|
-
/**
|
|
114
|
-
* The string representation of the path (e.g. "users.0.name").
|
|
115
|
-
* Useful for binding paths to form libraries or UI components.
|
|
116
|
-
*
|
|
117
|
-
* @example
|
|
118
|
-
* path<Root>().users[0].name.$; // "users.0.name"
|
|
119
|
-
*/
|
|
120
79
|
get $() {
|
|
121
80
|
return this.toString();
|
|
122
81
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Returns the string representation of the path (e.g. "users.0.name").
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* path<Root>().users[0].name.toString(); // "users.0.name"
|
|
128
|
-
*/
|
|
129
82
|
toString() {
|
|
130
|
-
return this.segments.
|
|
83
|
+
return this.segments.map(
|
|
84
|
+
(s) => s === WILDCARD ? "*" : s === DEEP_WILDCARD ? "**" : String(s)
|
|
85
|
+
).join(".");
|
|
86
|
+
}
|
|
87
|
+
startsWith(other) {
|
|
88
|
+
return matchesPrefix(this.segments, resolveSegments(other));
|
|
89
|
+
}
|
|
90
|
+
covers(other) {
|
|
91
|
+
return matchesPrefix(resolveSegments(other), this.segments);
|
|
92
|
+
}
|
|
93
|
+
equals(other) {
|
|
94
|
+
return segmentsEqual(this.segments, resolveSegments(other));
|
|
95
|
+
}
|
|
96
|
+
match(other) {
|
|
97
|
+
const otherSegs = resolveSegments(other);
|
|
98
|
+
if (segmentsEqual(this.segments, otherSegs)) return { relation: "equals" };
|
|
99
|
+
const thisHasWild = hasWildcardSegment(this.segments);
|
|
100
|
+
const otherHasWild = hasWildcardSegment(otherSegs);
|
|
101
|
+
if (thisHasWild && matchesPrefix(otherSegs, this.segments))
|
|
102
|
+
return { relation: "covers" };
|
|
103
|
+
if (otherHasWild && matchesPrefix(this.segments, otherSegs))
|
|
104
|
+
return { relation: "covered-by" };
|
|
105
|
+
if (!thisHasWild && !otherHasWild) {
|
|
106
|
+
if (matchesPrefix(this.segments, otherSegs) && this.segments.length > otherSegs.length)
|
|
107
|
+
return { relation: "child" };
|
|
108
|
+
if (matchesPrefix(otherSegs, this.segments) && otherSegs.length > this.segments.length)
|
|
109
|
+
return { relation: "parent" };
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/impl/path-impl.ts
|
|
116
|
+
var PathImpl = class extends AbstractPathImpl {
|
|
117
|
+
fn;
|
|
118
|
+
constructor(segments) {
|
|
119
|
+
super(segments);
|
|
120
|
+
this.fn = (data) => this.get(data);
|
|
131
121
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Extracts the value at this path from the given data object.
|
|
134
|
-
* Safely handles missing intermediate properties by returning `undefined` instead of throwing an error.
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* const namePath = path<User>().name;
|
|
138
|
-
* const name = namePath.get({ name: "Alice" }); // "Alice"
|
|
139
|
-
*/
|
|
140
122
|
get(data) {
|
|
141
123
|
let current = data;
|
|
142
124
|
for (const seg of this.segments) {
|
|
@@ -145,25 +127,6 @@ var PathImpl = class _PathImpl {
|
|
|
145
127
|
}
|
|
146
128
|
return current;
|
|
147
129
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Returns an accessor function that extracts the value at this path from the given data object.
|
|
150
|
-
* Useful for array methods like `.map()` or `.filter()`.
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* const names = users.map(path<User>().name.fn);
|
|
154
|
-
*/
|
|
155
|
-
get fn() {
|
|
156
|
-
return (data) => this.get(data);
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Sets the value at this path in the given data object, returning a new updated object (immutable).
|
|
160
|
-
* If intermediate properties are missing, they are automatically created as objects or arrays
|
|
161
|
-
* depending on the segment types (numeric keys become arrays).
|
|
162
|
-
*
|
|
163
|
-
* @example
|
|
164
|
-
* const namePath = path<User>().name;
|
|
165
|
-
* const updatedUser = namePath.set({ name: "Alice" }, "Bob"); // { name: "Bob" }
|
|
166
|
-
*/
|
|
167
130
|
set(data, value) {
|
|
168
131
|
if (this.segments.length === 0) return value;
|
|
169
132
|
const setAt = (obj, segs, val) => {
|
|
@@ -190,308 +153,211 @@ var PathImpl = class _PathImpl {
|
|
|
190
153
|
const baseObj = typeof data === "object" && data !== null ? Array.isArray(data) ? [...data] : { ...data } : data;
|
|
191
154
|
return setAt(baseObj, this.segments, value);
|
|
192
155
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
156
|
+
update(data, updater) {
|
|
157
|
+
return this.set(data, updater(this.get(data)));
|
|
158
|
+
}
|
|
159
|
+
parent() {
|
|
160
|
+
if (this.segments.length === 0) return null;
|
|
161
|
+
return makeConcrete(
|
|
162
|
+
this.segments.slice(0, -1)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
subtract(prefix) {
|
|
166
|
+
const a = this.segments;
|
|
167
|
+
const b = resolveSegments(prefix);
|
|
168
|
+
if (b.length > a.length) return null;
|
|
169
|
+
if (!segmentsEqual(a.slice(0, b.length), b)) return null;
|
|
170
|
+
return makeConcrete(a.slice(b.length));
|
|
171
|
+
}
|
|
172
|
+
slice(start, end) {
|
|
173
|
+
return makeConcrete(
|
|
174
|
+
this.segments.slice(start, end)
|
|
175
|
+
);
|
|
176
|
+
}
|
|
200
177
|
each(expr) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return new TemplatePathCtor([...this.segments, WILDCARD, ...tailSegments]);
|
|
178
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
179
|
+
return new TemplatePathImpl([
|
|
180
|
+
...this.segments,
|
|
181
|
+
WILDCARD,
|
|
182
|
+
...tail
|
|
183
|
+
]);
|
|
208
184
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Traverses deeply into a structure, matching any nested property.
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* const root = path<Root>();
|
|
214
|
-
* const allIds = root.deep(node => node.id); // Path matches any 'id' at any depth
|
|
215
|
-
*/
|
|
216
185
|
deep(expr) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const proxy = createPathProxy([]);
|
|
220
|
-
const result = expr(proxy);
|
|
221
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
222
|
-
}
|
|
223
|
-
return new TemplatePathCtor([
|
|
186
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
187
|
+
return new TemplatePathImpl([
|
|
224
188
|
...this.segments,
|
|
225
189
|
DEEP_WILDCARD,
|
|
226
|
-
...
|
|
190
|
+
...tail
|
|
227
191
|
]);
|
|
228
192
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
* const b = path<Root>().users;
|
|
235
|
-
* a.startsWith(b); // true
|
|
236
|
-
*/
|
|
237
|
-
startsWith(other) {
|
|
238
|
-
return matchesPrefix(this.segments, resolveSegments(other));
|
|
193
|
+
to(relative) {
|
|
194
|
+
return makeFromSegments([
|
|
195
|
+
...this.segments,
|
|
196
|
+
...resolveSegments(relative)
|
|
197
|
+
]);
|
|
239
198
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
* const a = path<Root>().users;
|
|
245
|
-
* const b = path<Root>().users[0].name;
|
|
246
|
-
* a.includes(b); // true
|
|
247
|
-
*/
|
|
248
|
-
includes(other) {
|
|
249
|
-
return matchesPrefix(resolveSegments(other), this.segments);
|
|
199
|
+
merge(other) {
|
|
200
|
+
return makeFromSegments(
|
|
201
|
+
mergeSegments(this.segments, resolveSegments(other))
|
|
202
|
+
);
|
|
250
203
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
*/
|
|
259
|
-
equals(other) {
|
|
260
|
-
return segmentsEqual(this.segments, resolveSegments(other));
|
|
204
|
+
};
|
|
205
|
+
function makeFromSegments(segments) {
|
|
206
|
+
return hasWildcard(segments) ? new TemplatePathImpl(segments) : new PathImpl(segments);
|
|
207
|
+
}
|
|
208
|
+
function hasWildcard(segments) {
|
|
209
|
+
for (const s of segments) {
|
|
210
|
+
if (s === WILDCARD || s === DEEP_WILDCARD) return true;
|
|
261
211
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
if (patternMatches(otherSegs, this.segments)) {
|
|
285
|
-
return { relation: "included-by", params: {} };
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
var TemplatePathImpl = class _TemplatePathImpl extends AbstractPathImpl {
|
|
215
|
+
// fn returns V[] (not V|undefined) — no covariance conflict since we don't
|
|
216
|
+
// extend PathImpl; the declared type here matches TemplatePath<T,V>.fn.
|
|
217
|
+
fn;
|
|
218
|
+
constructor(segments) {
|
|
219
|
+
super(segments);
|
|
220
|
+
this.fn = (data) => this.get(data);
|
|
221
|
+
}
|
|
222
|
+
get(data) {
|
|
223
|
+
return this.expand(data).map(
|
|
224
|
+
// expand() only returns paths where the key exists in data, so get() is safe
|
|
225
|
+
(p) => p.get(data)
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
set(data, value) {
|
|
229
|
+
const paths = this.expand(data);
|
|
230
|
+
let current = data;
|
|
231
|
+
for (const p of paths) {
|
|
232
|
+
current = p.set(current, value);
|
|
286
233
|
}
|
|
287
|
-
return
|
|
234
|
+
return current;
|
|
288
235
|
}
|
|
289
236
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* @example
|
|
294
|
-
* const base = path<Root>().users;
|
|
295
|
-
* const full = base.merge(p => p[0].name); // equivalent to path<Root>().users[0].name
|
|
237
|
+
* Applies `updater` to each matched value individually (per-item transform).
|
|
238
|
+
* Use `.set(data, constant)` to assign the same value to every match.
|
|
296
239
|
*/
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const aSuffix = a.slice(-len);
|
|
303
|
-
const bPrefix = b.slice(0, len);
|
|
304
|
-
if (segmentsEqual(aSuffix, bPrefix)) {
|
|
305
|
-
overlapLen = len;
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
240
|
+
update(data, updater) {
|
|
241
|
+
const paths = this.expand(data);
|
|
242
|
+
let current = data;
|
|
243
|
+
for (const p of paths) {
|
|
244
|
+
current = p.set(current, updater(p.get(current)));
|
|
308
245
|
}
|
|
309
|
-
|
|
310
|
-
return new _PathImpl(merged);
|
|
246
|
+
return current;
|
|
311
247
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
* const remainder = full.subtract(base); // equivalent to path()[0].name
|
|
320
|
-
*/
|
|
321
|
-
subtract(other) {
|
|
248
|
+
parent() {
|
|
249
|
+
if (this.segments.length === 0) return null;
|
|
250
|
+
return makeFromSegments(
|
|
251
|
+
this.segments.slice(0, -1)
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
subtract(prefix) {
|
|
322
255
|
const a = this.segments;
|
|
323
|
-
const b = resolveSegments(
|
|
256
|
+
const b = resolveSegments(prefix);
|
|
324
257
|
if (b.length > a.length) return null;
|
|
325
|
-
if (segmentsEqual(a, b)) return
|
|
326
|
-
|
|
327
|
-
return new _PathImpl(a.slice(b.length));
|
|
328
|
-
}
|
|
329
|
-
if (segmentsEqual(a.slice(-b.length), b)) {
|
|
330
|
-
return new _PathImpl(a.slice(0, -b.length));
|
|
331
|
-
}
|
|
332
|
-
return null;
|
|
258
|
+
if (!segmentsEqual(a.slice(0, b.length), b)) return null;
|
|
259
|
+
return makeFromSegments(a.slice(b.length));
|
|
333
260
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Returns a new path containing a subset of the segments, similar to Array.prototype.slice.
|
|
336
|
-
*
|
|
337
|
-
* @example
|
|
338
|
-
* const full = path<Root>().users[0].name;
|
|
339
|
-
* full.slice(0, 1); // equivalent to path<Root>().users
|
|
340
|
-
*/
|
|
341
261
|
slice(start, end) {
|
|
342
|
-
|
|
343
|
-
|
|
262
|
+
return makeFromSegments(
|
|
263
|
+
this.segments.slice(start, end)
|
|
264
|
+
);
|
|
344
265
|
}
|
|
345
|
-
/**
|
|
346
|
-
* Extends the current path using a lambda expression starting from the resolved value.
|
|
347
|
-
*
|
|
348
|
-
* @example
|
|
349
|
-
* const userPath = path<Root>().users[0];
|
|
350
|
-
* const namePath = userPath.to(u => u.name);
|
|
351
|
-
*/
|
|
352
|
-
to(expr) {
|
|
353
|
-
const proxy = createPathProxy([]);
|
|
354
|
-
const result = expr(proxy);
|
|
355
|
-
const tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
356
|
-
return new _PathImpl([...this.segments, ...tailSegments]);
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
// src/impl/template-path-impl.ts
|
|
361
|
-
var TemplatePathImpl = class _TemplatePathImpl extends PathImpl {
|
|
362
|
-
/**
|
|
363
|
-
* Traverses into a collection (Array or Record) to operate on each item, returning a TemplatePath.
|
|
364
|
-
*
|
|
365
|
-
* @example
|
|
366
|
-
* const users = path<Root>().users;
|
|
367
|
-
* const userNames = users.each(u => u.name); // TemplatePath matching all names
|
|
368
|
-
*/
|
|
369
266
|
each(expr) {
|
|
370
|
-
|
|
371
|
-
if (expr) {
|
|
372
|
-
const proxy = createPathProxy([]);
|
|
373
|
-
const result = expr(proxy);
|
|
374
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
375
|
-
}
|
|
267
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
376
268
|
return new _TemplatePathImpl([
|
|
377
269
|
...this.segments,
|
|
378
270
|
WILDCARD,
|
|
379
|
-
...
|
|
271
|
+
...tail
|
|
380
272
|
]);
|
|
381
273
|
}
|
|
382
|
-
/**
|
|
383
|
-
* Traverses deeply into a structure, matching any nested property, returning a TemplatePath.
|
|
384
|
-
*
|
|
385
|
-
* @example
|
|
386
|
-
* const root = path<Root>();
|
|
387
|
-
* const allIds = root.deep(node => node.id); // TemplatePath matching any 'id' at any depth
|
|
388
|
-
*/
|
|
389
274
|
deep(expr) {
|
|
390
|
-
|
|
391
|
-
if (expr) {
|
|
392
|
-
const proxy = createPathProxy([]);
|
|
393
|
-
const result = expr(proxy);
|
|
394
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
395
|
-
}
|
|
275
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
396
276
|
return new _TemplatePathImpl([
|
|
397
277
|
...this.segments,
|
|
398
278
|
DEEP_WILDCARD,
|
|
399
|
-
...
|
|
279
|
+
...tail
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
to(relative) {
|
|
283
|
+
const tail = resolveSegments(relative);
|
|
284
|
+
return new _TemplatePathImpl([
|
|
285
|
+
...this.segments,
|
|
286
|
+
...tail
|
|
400
287
|
]);
|
|
401
288
|
}
|
|
289
|
+
merge(other) {
|
|
290
|
+
return new _TemplatePathImpl(
|
|
291
|
+
mergeSegments(this.segments, resolveSegments(other))
|
|
292
|
+
);
|
|
293
|
+
}
|
|
402
294
|
/**
|
|
403
|
-
* Resolves this template
|
|
404
|
-
* that exist in the given data.
|
|
405
|
-
*
|
|
406
|
-
* @example
|
|
407
|
-
* const template = path<Root>().users.each().name;
|
|
408
|
-
* const concretePaths = template.expand(data); // [path<Root>().users[0].name, ...]
|
|
295
|
+
* Resolves this template to all concrete paths that exist in `data`.
|
|
409
296
|
*/
|
|
410
297
|
expand(data) {
|
|
411
298
|
const results = [];
|
|
412
|
-
const walk = (
|
|
413
|
-
if (
|
|
414
|
-
results.push(new PathImpl(
|
|
299
|
+
const walk = (current, idx, acc) => {
|
|
300
|
+
if (idx >= this.segments.length) {
|
|
301
|
+
results.push(new PathImpl(acc));
|
|
415
302
|
return;
|
|
416
303
|
}
|
|
417
|
-
const seg = this.segments[
|
|
304
|
+
const seg = this.segments[idx];
|
|
418
305
|
if (seg === WILDCARD) {
|
|
419
|
-
if (
|
|
420
|
-
const keys = Array.isArray(
|
|
306
|
+
if (current != null && typeof current === "object") {
|
|
307
|
+
const keys = Array.isArray(current) ? Array.from(current.keys()) : Object.keys(current);
|
|
421
308
|
for (const key of keys) {
|
|
422
|
-
walk(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
);
|
|
309
|
+
walk(current[key], idx + 1, [
|
|
310
|
+
...acc,
|
|
311
|
+
key
|
|
312
|
+
]);
|
|
427
313
|
}
|
|
428
314
|
}
|
|
429
315
|
} else if (seg === DEEP_WILDCARD) {
|
|
430
|
-
walk(
|
|
431
|
-
if (
|
|
432
|
-
const keys = Array.isArray(
|
|
316
|
+
walk(current, idx + 1, acc);
|
|
317
|
+
if (current != null && typeof current === "object") {
|
|
318
|
+
const keys = Array.isArray(current) ? Array.from(current.keys()) : Object.keys(current);
|
|
433
319
|
for (const key of keys) {
|
|
434
|
-
walk(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
);
|
|
320
|
+
walk(current[key], idx, [
|
|
321
|
+
...acc,
|
|
322
|
+
key
|
|
323
|
+
]);
|
|
439
324
|
}
|
|
440
325
|
}
|
|
441
326
|
} else {
|
|
442
|
-
if (
|
|
443
|
-
walk(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
);
|
|
327
|
+
if (current != null && typeof current === "object" && seg in current) {
|
|
328
|
+
walk(current[seg], idx + 1, [
|
|
329
|
+
...acc,
|
|
330
|
+
seg
|
|
331
|
+
]);
|
|
448
332
|
}
|
|
449
333
|
}
|
|
450
334
|
};
|
|
451
335
|
walk(data, 0, []);
|
|
452
336
|
return results;
|
|
453
337
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
* const allNames = companies.map(path<Company>().departments.each().name.fn);
|
|
470
|
-
*/
|
|
471
|
-
// @ts-expect-error Overriding fn to return an array instead of a single value
|
|
472
|
-
get fn() {
|
|
473
|
-
return (data) => this.get(data);
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Sets the provided value to all matching paths immutably.
|
|
477
|
-
*
|
|
478
|
-
* @example
|
|
479
|
-
* const updatedData = path<Root>().users.each().name.set(data, "Bob");
|
|
480
|
-
*/
|
|
481
|
-
set(data, value) {
|
|
482
|
-
const paths = this.expand(data);
|
|
483
|
-
let current = data;
|
|
484
|
-
for (const p of paths) {
|
|
485
|
-
current = p.set(current, value);
|
|
338
|
+
};
|
|
339
|
+
function makeConcrete(segments) {
|
|
340
|
+
return new PathImpl(segments);
|
|
341
|
+
}
|
|
342
|
+
function evalExpr(expr) {
|
|
343
|
+
const proxy = createPathProxy([]);
|
|
344
|
+
const result = expr(proxy);
|
|
345
|
+
return result?.[PATH_SEGMENTS] ?? [];
|
|
346
|
+
}
|
|
347
|
+
function mergeSegments(a, b) {
|
|
348
|
+
let overlapLen = 0;
|
|
349
|
+
for (let len = Math.min(a.length, b.length); len >= 1; len--) {
|
|
350
|
+
if (segmentsEqual(a.slice(-len), b.slice(0, len))) {
|
|
351
|
+
overlapLen = len;
|
|
352
|
+
break;
|
|
486
353
|
}
|
|
487
|
-
return current;
|
|
488
354
|
}
|
|
489
|
-
|
|
355
|
+
return overlapLen > 0 ? [...a.slice(0, -overlapLen), ...b] : [...a, ...b];
|
|
356
|
+
}
|
|
490
357
|
|
|
491
358
|
// src/path.ts
|
|
492
|
-
setTemplatePathCtor(TemplatePathImpl);
|
|
493
359
|
function path(baseOrExpr, expr) {
|
|
494
|
-
if (
|
|
360
|
+
if (baseOrExpr === void 0) {
|
|
495
361
|
return new PathImpl([]);
|
|
496
362
|
}
|
|
497
363
|
if (typeof baseOrExpr === "function") {
|
|
@@ -501,18 +367,11 @@ function path(baseOrExpr, expr) {
|
|
|
501
367
|
return new PathImpl(segments);
|
|
502
368
|
}
|
|
503
369
|
const baseSegments = baseOrExpr.segments;
|
|
504
|
-
if (expr) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
return new PathImpl([...baseSegments, ...tailSegments]);
|
|
510
|
-
} else if (typeof expr === "object" && "segments" in expr) {
|
|
511
|
-
return new PathImpl([
|
|
512
|
-
...baseSegments,
|
|
513
|
-
...expr.segments
|
|
514
|
-
]);
|
|
515
|
-
}
|
|
370
|
+
if (expr !== void 0) {
|
|
371
|
+
const proxy = createPathProxy([]);
|
|
372
|
+
const result = expr(proxy);
|
|
373
|
+
const tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
374
|
+
return new PathImpl([...baseSegments, ...tailSegments]);
|
|
516
375
|
}
|
|
517
376
|
return new PathImpl(baseSegments);
|
|
518
377
|
}
|
|
@@ -520,8 +379,10 @@ function unsafePath(raw) {
|
|
|
520
379
|
const segments = raw ? raw.split(".").map((s) => s === "" ? s : isCanonicalArrayIndex(s) ? Number(s) : s) : [];
|
|
521
380
|
return new PathImpl(segments);
|
|
522
381
|
}
|
|
523
|
-
|
|
524
|
-
|
|
382
|
+
export {
|
|
383
|
+
DEEP_WILDCARD,
|
|
384
|
+
WILDCARD,
|
|
525
385
|
path,
|
|
526
386
|
unsafePath
|
|
527
|
-
}
|
|
387
|
+
};
|
|
388
|
+
//# sourceMappingURL=index.js.map
|