data-path 1.0.2 → 2.0.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/README.md +60 -407
- package/dist/index.cjs +429 -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 +221 -349
- package/dist/index.js.map +1 -0
- package/package.json +109 -68
- 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,6 +52,7 @@ function matchesPrefix(full, prefix) {
|
|
|
79
52
|
}
|
|
80
53
|
return false;
|
|
81
54
|
}
|
|
55
|
+
if (f >= full.length) return false;
|
|
82
56
|
if (prefix[p] !== WILDCARD && prefix[p] !== full[f]) return false;
|
|
83
57
|
p++;
|
|
84
58
|
f++;
|
|
@@ -86,57 +60,76 @@ function matchesPrefix(full, prefix) {
|
|
|
86
60
|
return p === prefix.length;
|
|
87
61
|
}
|
|
88
62
|
function patternMatches(pattern, concrete) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
63
|
+
function walk(pi, ci) {
|
|
64
|
+
if (pi === pattern.length) return ci === concrete.length;
|
|
65
|
+
const seg = pattern[pi];
|
|
66
|
+
if (seg === DEEP_WILDCARD) {
|
|
67
|
+
for (let skip = 0; ci + skip <= concrete.length; skip++) {
|
|
68
|
+
if (walk(pi + 1, ci + skip)) return true;
|
|
69
|
+
}
|
|
92
70
|
return false;
|
|
71
|
+
}
|
|
72
|
+
if (ci === concrete.length) return false;
|
|
73
|
+
if (seg === WILDCARD) return walk(pi + 1, ci + 1);
|
|
74
|
+
if (seg !== concrete[ci]) return false;
|
|
75
|
+
return walk(pi + 1, ci + 1);
|
|
93
76
|
}
|
|
94
|
-
return
|
|
77
|
+
return walk(0, 0);
|
|
95
78
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
79
|
+
function hasWildcardSegment(segments) {
|
|
80
|
+
for (const s of segments) {
|
|
81
|
+
if (s === WILDCARD || s === DEEP_WILDCARD) return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
101
84
|
}
|
|
102
|
-
|
|
85
|
+
|
|
86
|
+
// src/impl/base-path-impl.ts
|
|
87
|
+
var AbstractPathImpl = class {
|
|
103
88
|
segments;
|
|
104
89
|
constructor(segments) {
|
|
105
90
|
this.segments = segments;
|
|
106
91
|
}
|
|
107
|
-
/**
|
|
108
|
-
* The number of segments in this path.
|
|
109
|
-
*/
|
|
110
92
|
get length() {
|
|
111
93
|
return this.segments.length;
|
|
112
94
|
}
|
|
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
95
|
get $() {
|
|
121
96
|
return this.toString();
|
|
122
97
|
}
|
|
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
98
|
toString() {
|
|
130
|
-
return this.segments.
|
|
99
|
+
return this.segments.map(
|
|
100
|
+
(s) => s === WILDCARD ? "*" : s === DEEP_WILDCARD ? "**" : String(s)
|
|
101
|
+
).join(".");
|
|
102
|
+
}
|
|
103
|
+
startsWith(other) {
|
|
104
|
+
return matchesPrefix(this.segments, resolveSegments(other));
|
|
105
|
+
}
|
|
106
|
+
covers(other) {
|
|
107
|
+
return matchesPrefix(resolveSegments(other), this.segments);
|
|
108
|
+
}
|
|
109
|
+
equals(other) {
|
|
110
|
+
return segmentsEqual(this.segments, resolveSegments(other));
|
|
111
|
+
}
|
|
112
|
+
match(other) {
|
|
113
|
+
const otherSegs = resolveSegments(other);
|
|
114
|
+
if (segmentsEqual(this.segments, otherSegs)) return { relation: "equals" };
|
|
115
|
+
if (patternMatches(this.segments, otherSegs)) return { relation: "covers" };
|
|
116
|
+
if (patternMatches(otherSegs, this.segments))
|
|
117
|
+
return { relation: "covered-by" };
|
|
118
|
+
if (!hasWildcardSegment(otherSegs) && matchesPrefix(this.segments, otherSegs) && this.segments.length > otherSegs.length)
|
|
119
|
+
return { relation: "child" };
|
|
120
|
+
if (!hasWildcardSegment(this.segments) && matchesPrefix(otherSegs, this.segments) && otherSegs.length > this.segments.length)
|
|
121
|
+
return { relation: "parent" };
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/impl/path-impl.ts
|
|
127
|
+
var PathImpl = class extends AbstractPathImpl {
|
|
128
|
+
fn;
|
|
129
|
+
constructor(segments) {
|
|
130
|
+
super(segments);
|
|
131
|
+
this.fn = (data) => this.get(data);
|
|
131
132
|
}
|
|
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
133
|
get(data) {
|
|
141
134
|
let current = data;
|
|
142
135
|
for (const seg of this.segments) {
|
|
@@ -145,25 +138,6 @@ var PathImpl = class _PathImpl {
|
|
|
145
138
|
}
|
|
146
139
|
return current;
|
|
147
140
|
}
|
|
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
141
|
set(data, value) {
|
|
168
142
|
if (this.segments.length === 0) return value;
|
|
169
143
|
const setAt = (obj, segs, val) => {
|
|
@@ -190,308 +164,211 @@ var PathImpl = class _PathImpl {
|
|
|
190
164
|
const baseObj = typeof data === "object" && data !== null ? Array.isArray(data) ? [...data] : { ...data } : data;
|
|
191
165
|
return setAt(baseObj, this.segments, value);
|
|
192
166
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
167
|
+
update(data, updater) {
|
|
168
|
+
return this.set(data, updater(this.get(data)));
|
|
169
|
+
}
|
|
170
|
+
parent() {
|
|
171
|
+
if (this.segments.length === 0) return null;
|
|
172
|
+
return makeConcrete(
|
|
173
|
+
this.segments.slice(0, -1)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
subtract(prefix) {
|
|
177
|
+
const a = this.segments;
|
|
178
|
+
const b = resolveSegments(prefix);
|
|
179
|
+
if (b.length > a.length) return null;
|
|
180
|
+
if (!segmentsEqual(a.slice(0, b.length), b)) return null;
|
|
181
|
+
return makeConcrete(a.slice(b.length));
|
|
182
|
+
}
|
|
183
|
+
slice(start, end) {
|
|
184
|
+
return makeConcrete(
|
|
185
|
+
this.segments.slice(start, end)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
200
188
|
each(expr) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return new TemplatePathCtor([...this.segments, WILDCARD, ...tailSegments]);
|
|
189
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
190
|
+
return new TemplatePathImpl([
|
|
191
|
+
...this.segments,
|
|
192
|
+
WILDCARD,
|
|
193
|
+
...tail
|
|
194
|
+
]);
|
|
208
195
|
}
|
|
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
196
|
deep(expr) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const proxy = createPathProxy([]);
|
|
220
|
-
const result = expr(proxy);
|
|
221
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
222
|
-
}
|
|
223
|
-
return new TemplatePathCtor([
|
|
197
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
198
|
+
return new TemplatePathImpl([
|
|
224
199
|
...this.segments,
|
|
225
200
|
DEEP_WILDCARD,
|
|
226
|
-
...
|
|
201
|
+
...tail
|
|
227
202
|
]);
|
|
228
203
|
}
|
|
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));
|
|
204
|
+
to(relative) {
|
|
205
|
+
return makeFromSegments([
|
|
206
|
+
...this.segments,
|
|
207
|
+
...resolveSegments(relative)
|
|
208
|
+
]);
|
|
239
209
|
}
|
|
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);
|
|
210
|
+
merge(other) {
|
|
211
|
+
return makeFromSegments(
|
|
212
|
+
mergeSegments(this.segments, resolveSegments(other))
|
|
213
|
+
);
|
|
250
214
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
*/
|
|
259
|
-
equals(other) {
|
|
260
|
-
return segmentsEqual(this.segments, resolveSegments(other));
|
|
215
|
+
};
|
|
216
|
+
function makeFromSegments(segments) {
|
|
217
|
+
return hasWildcard(segments) ? new TemplatePathImpl(segments) : new PathImpl(segments);
|
|
218
|
+
}
|
|
219
|
+
function hasWildcard(segments) {
|
|
220
|
+
for (const s of segments) {
|
|
221
|
+
if (s === WILDCARD || s === DEEP_WILDCARD) return true;
|
|
261
222
|
}
|
|
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: {} };
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
var TemplatePathImpl = class _TemplatePathImpl extends AbstractPathImpl {
|
|
226
|
+
// fn returns V[] (not V|undefined) — no covariance conflict since we don't
|
|
227
|
+
// extend PathImpl; the declared type here matches TemplatePath<T,V>.fn.
|
|
228
|
+
fn;
|
|
229
|
+
constructor(segments) {
|
|
230
|
+
super(segments);
|
|
231
|
+
this.fn = (data) => this.get(data);
|
|
232
|
+
}
|
|
233
|
+
get(data) {
|
|
234
|
+
return this.expand(data).map(
|
|
235
|
+
// expand() only returns paths where the key exists in data, so get() is safe
|
|
236
|
+
(p) => p.get(data)
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
set(data, value) {
|
|
240
|
+
const paths = this.expand(data);
|
|
241
|
+
let current = data;
|
|
242
|
+
for (const p of paths) {
|
|
243
|
+
current = p.set(current, value);
|
|
286
244
|
}
|
|
287
|
-
return
|
|
245
|
+
return current;
|
|
288
246
|
}
|
|
289
247
|
/**
|
|
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
|
|
248
|
+
* Applies `updater` to each matched value individually (per-item transform).
|
|
249
|
+
* Use `.set(data, constant)` to assign the same value to every match.
|
|
296
250
|
*/
|
|
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
|
-
}
|
|
251
|
+
update(data, updater) {
|
|
252
|
+
const paths = this.expand(data);
|
|
253
|
+
let current = data;
|
|
254
|
+
for (const p of paths) {
|
|
255
|
+
current = p.set(current, updater(p.get(current)));
|
|
308
256
|
}
|
|
309
|
-
|
|
310
|
-
return new _PathImpl(merged);
|
|
257
|
+
return current;
|
|
311
258
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
* const remainder = full.subtract(base); // equivalent to path()[0].name
|
|
320
|
-
*/
|
|
321
|
-
subtract(other) {
|
|
259
|
+
parent() {
|
|
260
|
+
if (this.segments.length === 0) return null;
|
|
261
|
+
return makeFromSegments(
|
|
262
|
+
this.segments.slice(0, -1)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
subtract(prefix) {
|
|
322
266
|
const a = this.segments;
|
|
323
|
-
const b = resolveSegments(
|
|
267
|
+
const b = resolveSegments(prefix);
|
|
324
268
|
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;
|
|
269
|
+
if (!segmentsEqual(a.slice(0, b.length), b)) return null;
|
|
270
|
+
return makeFromSegments(a.slice(b.length));
|
|
333
271
|
}
|
|
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
272
|
slice(start, end) {
|
|
342
|
-
|
|
343
|
-
|
|
273
|
+
return makeFromSegments(
|
|
274
|
+
this.segments.slice(start, end)
|
|
275
|
+
);
|
|
344
276
|
}
|
|
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
277
|
each(expr) {
|
|
370
|
-
|
|
371
|
-
if (expr) {
|
|
372
|
-
const proxy = createPathProxy([]);
|
|
373
|
-
const result = expr(proxy);
|
|
374
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
375
|
-
}
|
|
278
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
376
279
|
return new _TemplatePathImpl([
|
|
377
280
|
...this.segments,
|
|
378
281
|
WILDCARD,
|
|
379
|
-
...
|
|
282
|
+
...tail
|
|
380
283
|
]);
|
|
381
284
|
}
|
|
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
285
|
deep(expr) {
|
|
390
|
-
|
|
391
|
-
if (expr) {
|
|
392
|
-
const proxy = createPathProxy([]);
|
|
393
|
-
const result = expr(proxy);
|
|
394
|
-
tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
395
|
-
}
|
|
286
|
+
const tail = expr ? evalExpr(expr) : [];
|
|
396
287
|
return new _TemplatePathImpl([
|
|
397
288
|
...this.segments,
|
|
398
289
|
DEEP_WILDCARD,
|
|
399
|
-
...
|
|
290
|
+
...tail
|
|
291
|
+
]);
|
|
292
|
+
}
|
|
293
|
+
to(relative) {
|
|
294
|
+
const tail = resolveSegments(relative);
|
|
295
|
+
return new _TemplatePathImpl([
|
|
296
|
+
...this.segments,
|
|
297
|
+
...tail
|
|
400
298
|
]);
|
|
401
299
|
}
|
|
300
|
+
merge(other) {
|
|
301
|
+
return new _TemplatePathImpl(
|
|
302
|
+
mergeSegments(this.segments, resolveSegments(other))
|
|
303
|
+
);
|
|
304
|
+
}
|
|
402
305
|
/**
|
|
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, ...]
|
|
306
|
+
* Resolves this template to all concrete paths that exist in `data`.
|
|
409
307
|
*/
|
|
410
308
|
expand(data) {
|
|
411
309
|
const results = [];
|
|
412
|
-
const walk = (
|
|
413
|
-
if (
|
|
414
|
-
results.push(new PathImpl(
|
|
310
|
+
const walk = (current, idx, acc) => {
|
|
311
|
+
if (idx >= this.segments.length) {
|
|
312
|
+
results.push(new PathImpl(acc));
|
|
415
313
|
return;
|
|
416
314
|
}
|
|
417
|
-
const seg = this.segments[
|
|
315
|
+
const seg = this.segments[idx];
|
|
418
316
|
if (seg === WILDCARD) {
|
|
419
|
-
if (
|
|
420
|
-
const keys = Array.isArray(
|
|
317
|
+
if (current != null && typeof current === "object") {
|
|
318
|
+
const keys = Array.isArray(current) ? Array.from(current.keys()) : Object.keys(current);
|
|
421
319
|
for (const key of keys) {
|
|
422
|
-
walk(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
);
|
|
320
|
+
walk(current[key], idx + 1, [
|
|
321
|
+
...acc,
|
|
322
|
+
key
|
|
323
|
+
]);
|
|
427
324
|
}
|
|
428
325
|
}
|
|
429
326
|
} else if (seg === DEEP_WILDCARD) {
|
|
430
|
-
walk(
|
|
431
|
-
if (
|
|
432
|
-
const keys = Array.isArray(
|
|
327
|
+
walk(current, idx + 1, acc);
|
|
328
|
+
if (current != null && typeof current === "object") {
|
|
329
|
+
const keys = Array.isArray(current) ? Array.from(current.keys()) : Object.keys(current);
|
|
433
330
|
for (const key of keys) {
|
|
434
|
-
walk(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
);
|
|
331
|
+
walk(current[key], idx, [
|
|
332
|
+
...acc,
|
|
333
|
+
key
|
|
334
|
+
]);
|
|
439
335
|
}
|
|
440
336
|
}
|
|
441
337
|
} else {
|
|
442
|
-
if (
|
|
443
|
-
walk(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
);
|
|
338
|
+
if (current != null && typeof current === "object" && seg in current) {
|
|
339
|
+
walk(current[seg], idx + 1, [
|
|
340
|
+
...acc,
|
|
341
|
+
seg
|
|
342
|
+
]);
|
|
448
343
|
}
|
|
449
344
|
}
|
|
450
345
|
};
|
|
451
346
|
walk(data, 0, []);
|
|
452
347
|
return results;
|
|
453
348
|
}
|
|
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);
|
|
349
|
+
};
|
|
350
|
+
function makeConcrete(segments) {
|
|
351
|
+
return new PathImpl(segments);
|
|
352
|
+
}
|
|
353
|
+
function evalExpr(expr) {
|
|
354
|
+
const proxy = createPathProxy([]);
|
|
355
|
+
const result = expr(proxy);
|
|
356
|
+
return result?.[PATH_SEGMENTS] ?? [];
|
|
357
|
+
}
|
|
358
|
+
function mergeSegments(a, b) {
|
|
359
|
+
let overlapLen = 0;
|
|
360
|
+
for (let len = Math.min(a.length, b.length); len >= 1; len--) {
|
|
361
|
+
if (segmentsEqual(a.slice(-len), b.slice(0, len))) {
|
|
362
|
+
overlapLen = len;
|
|
363
|
+
break;
|
|
486
364
|
}
|
|
487
|
-
return current;
|
|
488
365
|
}
|
|
489
|
-
|
|
366
|
+
return overlapLen > 0 ? [...a.slice(0, -overlapLen), ...b] : [...a, ...b];
|
|
367
|
+
}
|
|
490
368
|
|
|
491
369
|
// src/path.ts
|
|
492
|
-
setTemplatePathCtor(TemplatePathImpl);
|
|
493
370
|
function path(baseOrExpr, expr) {
|
|
494
|
-
if (
|
|
371
|
+
if (baseOrExpr === void 0) {
|
|
495
372
|
return new PathImpl([]);
|
|
496
373
|
}
|
|
497
374
|
if (typeof baseOrExpr === "function") {
|
|
@@ -501,18 +378,11 @@ function path(baseOrExpr, expr) {
|
|
|
501
378
|
return new PathImpl(segments);
|
|
502
379
|
}
|
|
503
380
|
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
|
-
}
|
|
381
|
+
if (expr !== void 0) {
|
|
382
|
+
const proxy = createPathProxy([]);
|
|
383
|
+
const result = expr(proxy);
|
|
384
|
+
const tailSegments = result?.[PATH_SEGMENTS] ?? [];
|
|
385
|
+
return new PathImpl([...baseSegments, ...tailSegments]);
|
|
516
386
|
}
|
|
517
387
|
return new PathImpl(baseSegments);
|
|
518
388
|
}
|
|
@@ -520,8 +390,10 @@ function unsafePath(raw) {
|
|
|
520
390
|
const segments = raw ? raw.split(".").map((s) => s === "" ? s : isCanonicalArrayIndex(s) ? Number(s) : s) : [];
|
|
521
391
|
return new PathImpl(segments);
|
|
522
392
|
}
|
|
523
|
-
|
|
524
|
-
|
|
393
|
+
export {
|
|
394
|
+
DEEP_WILDCARD,
|
|
395
|
+
WILDCARD,
|
|
525
396
|
path,
|
|
526
397
|
unsafePath
|
|
527
|
-
}
|
|
398
|
+
};
|
|
399
|
+
//# sourceMappingURL=index.js.map
|