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.d.ts
CHANGED
|
@@ -1,319 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentinel for a single-level wildcard segment (`each`).
|
|
3
|
+
*
|
|
4
|
+
* Stored as a unique Symbol — NOT the string `"*"` — so that a legitimate
|
|
5
|
+
* object key named `"*"` is preserved as a literal segment and never
|
|
6
|
+
* reinterpreted as a wildcard by `.get`, `.expand`, `.covers`, `.match`, etc.
|
|
7
|
+
*
|
|
8
|
+
* Renders as `"*"` in `toString()` / `.$` for dot-notation compatibility.
|
|
9
|
+
*/
|
|
10
|
+
declare const WILDCARD: unique symbol;
|
|
11
|
+
/**
|
|
12
|
+
* Sentinel for a deep wildcard segment (`deep`).
|
|
13
|
+
*
|
|
14
|
+
* Same rationale as {@link WILDCARD}: stored as a unique Symbol so the
|
|
15
|
+
* literal string `"**"` remains a valid (literal) object key.
|
|
16
|
+
*
|
|
17
|
+
* Renders as `"**"` in `toString()` / `.$`.
|
|
18
|
+
*/
|
|
19
|
+
declare const DEEP_WILDCARD: unique symbol;
|
|
20
|
+
|
|
1
21
|
/**
|
|
2
22
|
* Core type definitions for data-path.
|
|
3
|
-
* @see spec/idea.md
|
|
4
23
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A path segment.
|
|
27
|
+
*
|
|
28
|
+
* - `string` — object key
|
|
29
|
+
* - `number` — array index
|
|
30
|
+
* - `typeof WILDCARD` — single-level wildcard sentinel (inserted by `.each()`)
|
|
31
|
+
* - `typeof DEEP_WILDCARD` — deep wildcard sentinel (inserted by `.deep()`)
|
|
32
|
+
*
|
|
33
|
+
* The wildcard sentinels are unique Symbols (not the strings `"*"` / `"**"`),
|
|
34
|
+
* so legitimate object keys named `"*"` or `"**"` are preserved as literal
|
|
35
|
+
* string segments and never reinterpreted as wildcards by any method.
|
|
36
|
+
*
|
|
37
|
+
* The sentinels render as `"*"` / `"**"` in `toString()` / `.$` so dot-notation
|
|
38
|
+
* output and form-library bindings are unaffected.
|
|
39
|
+
*/
|
|
40
|
+
type Segment = string | number | typeof WILDCARD | typeof DEEP_WILDCARD;
|
|
41
|
+
/**
|
|
42
|
+
* The structural relation returned by `.match()`.
|
|
43
|
+
*
|
|
44
|
+
* Semantics when calling `a.match(b)`:
|
|
45
|
+
* - `"parent"` — `a` is a prefix of `b` (a is the parent, b is deeper)
|
|
46
|
+
* - `"child"` — `b` is a prefix of `a` (b is the parent, a is deeper)
|
|
47
|
+
* - `"equals"` — `a` and `b` are identical
|
|
48
|
+
* - `"covers"` — `a` (with wildcards) covers `b` as a concrete match
|
|
49
|
+
* - `"covered-by"` — `b` (with wildcards) covers `a` as a concrete match
|
|
50
|
+
*/
|
|
51
|
+
type MatchRelation = "covers" | "covered-by" | "equals" | "parent" | "child";
|
|
52
|
+
/** Result of .match() — relation only; params are reserved for named-wildcard support */
|
|
10
53
|
interface MatchResult {
|
|
11
54
|
relation: MatchRelation;
|
|
12
|
-
params?: Record<string, string>;
|
|
13
55
|
}
|
|
14
|
-
/** Resolved type at the end of a path (leaf value type) */
|
|
15
|
-
type ResolvedType<_T, _P extends string> = unknown;
|
|
16
|
-
/** Deep-reachable types for .deep() leaf parameter (enables IDE autocomplete) */
|
|
17
|
-
type DeepReachable<T> = T;
|
|
18
56
|
/**
|
|
19
|
-
* Extracts the
|
|
20
|
-
* (like `.each()`) know what type of item they are iterating over.
|
|
57
|
+
* Extracts the resolved value type from a path object.
|
|
21
58
|
*
|
|
22
|
-
* @
|
|
59
|
+
* @example
|
|
60
|
+
* const agePath = path<User>(p => p.profile.age);
|
|
61
|
+
* type Age = ResolvedType<typeof agePath>; // number
|
|
62
|
+
*/
|
|
63
|
+
type ResolvedType<P> = P extends {
|
|
64
|
+
get(data: any): (infer V)[];
|
|
65
|
+
} ? V : P extends BasePath<any, infer V> ? V : never;
|
|
66
|
+
/**
|
|
67
|
+
* Extracts the item type from a collection (Array or Record).
|
|
68
|
+
* Used by `.each()` to infer the traversal target.
|
|
23
69
|
*/
|
|
24
70
|
type CollectionItem<V> = V extends ReadonlyArray<infer U> ? U : V extends Record<PropertyKey, infer U> ? U : unknown;
|
|
25
|
-
/**
|
|
71
|
+
/** Primitive types that cannot be traversed — `.each()` and `.deep()` are hidden when V extends Primitive */
|
|
26
72
|
type Primitive = string | number | boolean | symbol | bigint | null | undefined;
|
|
27
|
-
/**
|
|
73
|
+
/** Lambda used to build a path — receives a proxy typed as T, infers the path from property access */
|
|
28
74
|
type PathExpression<T, R = unknown> = (proxy: T) => R;
|
|
29
75
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* to accept an existing path object, a raw segments object, or a lambda expression.
|
|
33
|
-
*
|
|
34
|
-
* @template T The root data type.
|
|
35
|
-
* @template V The resolved value type at the end of the path.
|
|
76
|
+
* Flexible path argument — accepts an existing path, a `{segments}` shape, or a lambda expression.
|
|
77
|
+
* Used by all methods that accept a path as an argument.
|
|
36
78
|
*/
|
|
37
|
-
type ResolvablePath<T, V = unknown> = BasePath<T, V
|
|
79
|
+
type ResolvablePath<T, V = unknown> = BasePath<T, V> | {
|
|
38
80
|
segments: readonly Segment[];
|
|
39
81
|
} | PathExpression<T, V>;
|
|
40
82
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* excluded from the type system when a path points to a primitive value
|
|
44
|
-
* (since primitives cannot be traversed).
|
|
83
|
+
* Traversal methods — only present when V is not a primitive type.
|
|
84
|
+
* Conditionally excluded by the Path and TemplatePath types.
|
|
45
85
|
*/
|
|
46
86
|
interface TraversablePathMethods<T, V> {
|
|
47
87
|
/**
|
|
48
|
-
* Traverses into a collection (Array or Record)
|
|
88
|
+
* Traverses into a collection (Array or Record), inserting a `*` wildcard.
|
|
89
|
+
* Returns a TemplatePath that matches every item.
|
|
49
90
|
*
|
|
50
91
|
* @example
|
|
51
|
-
*
|
|
52
|
-
*
|
|
92
|
+
* path<Root>().users.each(u => u.name) // TemplatePath — all names
|
|
93
|
+
* path<Root>().users.each() // TemplatePath — all items
|
|
53
94
|
*/
|
|
54
|
-
each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U
|
|
95
|
+
each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U>;
|
|
55
96
|
/**
|
|
56
|
-
* Traverses deeply into a structure,
|
|
97
|
+
* Traverses deeply into a structure, inserting a `**` wildcard.
|
|
98
|
+
* Returns a TemplatePath that matches the given property at any nesting depth.
|
|
57
99
|
*
|
|
58
100
|
* @example
|
|
59
|
-
*
|
|
60
|
-
*
|
|
101
|
+
* path<Root>().tree.deep(node => node.id) // TemplatePath — any nested 'id'
|
|
102
|
+
* path<Root>().tree.deep() // TemplatePath — every descendant node
|
|
61
103
|
*/
|
|
62
|
-
deep<U =
|
|
104
|
+
deep<U = V>(expr?: (leaf: V) => U): TemplatePath<T, U>;
|
|
63
105
|
}
|
|
64
106
|
/**
|
|
65
|
-
* The foundational
|
|
66
|
-
* Contains common properties and operations including value extraction/mutation,
|
|
67
|
-
* relational algebra (comparisons), and structural manipulation.
|
|
107
|
+
* The foundational interface shared by both Path and TemplatePath.
|
|
68
108
|
*
|
|
69
|
-
* @template T
|
|
70
|
-
* @template V
|
|
71
|
-
* @template _P The string representation of the path (optional/unused in runtime, but useful for type-level strings).
|
|
109
|
+
* @template T Root data type the path operates on
|
|
110
|
+
* @template V Resolved value type at the end of the path
|
|
72
111
|
*/
|
|
73
|
-
interface BasePath<T = unknown, V = unknown
|
|
74
|
-
/**
|
|
75
|
-
* The array of segments (string keys or numeric indices) that make up this path.
|
|
76
|
-
*/
|
|
112
|
+
interface BasePath<T = unknown, V = unknown> {
|
|
113
|
+
/** Ordered array of string keys and numeric indices that compose this path. */
|
|
77
114
|
readonly segments: readonly Segment[];
|
|
78
|
-
/**
|
|
79
|
-
* The number of segments in this path.
|
|
80
|
-
*/
|
|
115
|
+
/** Number of segments in this path. */
|
|
81
116
|
readonly length: number;
|
|
82
117
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
118
|
+
* Dot-notation string representation (e.g. `"users.0.name"`).
|
|
119
|
+
* Convenient for binding paths to form libraries.
|
|
85
120
|
*
|
|
86
121
|
* @example
|
|
87
|
-
* path<Root>(
|
|
88
|
-
*/
|
|
89
|
-
readonly $: _P;
|
|
90
|
-
/**
|
|
91
|
-
* Returns the string representation of the path (e.g. "users.0.name").
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* path<Root>().users[0].name.toString(); // "users.0.name"
|
|
122
|
+
* path<Root>(r => r.users[0].name).$ // "users.0.name"
|
|
95
123
|
*/
|
|
124
|
+
readonly $: string;
|
|
125
|
+
/** Returns the dot-notation string representation. */
|
|
96
126
|
toString(): string;
|
|
97
127
|
/**
|
|
98
|
-
* Extracts the value at this path from
|
|
99
|
-
*
|
|
128
|
+
* Extracts the value at this path from a data object.
|
|
129
|
+
* Returns `undefined` — rather than throwing — when any intermediate segment is missing.
|
|
100
130
|
*
|
|
101
131
|
* @example
|
|
102
|
-
*
|
|
103
|
-
* const name = namePath.get({ name: "Alice" }); // "Alice"
|
|
132
|
+
* path<User>(u => u.profile.name).get(user) // string | undefined
|
|
104
133
|
*/
|
|
105
|
-
get(data: T): V;
|
|
134
|
+
get(data: T): V | undefined;
|
|
106
135
|
/**
|
|
107
|
-
*
|
|
108
|
-
* Useful for array methods like `.map()` or `.filter()`.
|
|
136
|
+
* Pre-bound accessor function. Useful for array higher-order methods.
|
|
109
137
|
*
|
|
110
138
|
* @example
|
|
111
|
-
*
|
|
139
|
+
* users.map(path<User>(u => u.name).fn) // string | undefined[]
|
|
112
140
|
*/
|
|
113
|
-
readonly fn: (data: T) => V;
|
|
141
|
+
readonly fn: (data: T) => V | undefined;
|
|
114
142
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
143
|
+
* Immutably sets the value at this path, returning a structurally-cloned object.
|
|
144
|
+
* Missing intermediates are created automatically:
|
|
145
|
+
* numeric next-segment → array, string next-segment → object.
|
|
118
146
|
*
|
|
119
147
|
* @example
|
|
120
|
-
*
|
|
121
|
-
* const updatedUser = namePath.set({ name: "Alice" }, "Bob"); // { name: "Bob" }
|
|
148
|
+
* path<User>(u => u.name).set(user, "Alice")
|
|
122
149
|
*/
|
|
123
150
|
set(data: T, value: V): T;
|
|
124
151
|
/**
|
|
125
|
-
*
|
|
152
|
+
* Reads the current value, passes it to `updater`, and writes the result back immutably.
|
|
153
|
+
* Combines `.get()` + `.set()` in a single expression.
|
|
154
|
+
* On a `TemplatePath`, `updater` is called once per expanded match (per-item transform).
|
|
126
155
|
*
|
|
127
156
|
* @example
|
|
128
|
-
*
|
|
129
|
-
* const b = path<Root>().users;
|
|
130
|
-
* a.startsWith(b); // true
|
|
157
|
+
* namePath.update(user, name => (name ?? "").toUpperCase())
|
|
131
158
|
*/
|
|
132
|
-
|
|
159
|
+
update(data: T, updater: (current: V | undefined) => V): T;
|
|
133
160
|
/**
|
|
134
|
-
*
|
|
161
|
+
* Returns the parent path (all segments except the last), or `null` for a root/empty path.
|
|
162
|
+
* Value type becomes `unknown` — use a typed path expression if the parent type is needed.
|
|
135
163
|
*
|
|
136
164
|
* @example
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* a.includes(b); // true
|
|
165
|
+
* path<User>(u => u.profile.name).parent()?.$ // "profile"
|
|
166
|
+
* path<User>().parent() // null
|
|
140
167
|
*/
|
|
141
|
-
|
|
168
|
+
parent(): Path<T, unknown> | null;
|
|
142
169
|
/**
|
|
143
|
-
*
|
|
170
|
+
* Returns `true` if this path's segments begin with all segments of `other`
|
|
171
|
+
* (i.e. `other` is a prefix of `this`). Supports wildcard segments.
|
|
172
|
+
*/
|
|
173
|
+
startsWith(other: ResolvablePath<T>): boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Returns `true` if this path's domain covers `other` — i.e. this path's segments
|
|
176
|
+
* are a prefix of `other`'s segments (or match `other` via wildcards). The inverse
|
|
177
|
+
* direction of {@link startsWith}.
|
|
178
|
+
*
|
|
179
|
+
* NOTE: this is NOT analogous to `Array.prototype.includes` / `String.prototype.includes`.
|
|
180
|
+
* Think "covers a location in the data tree", not "contains as an element".
|
|
144
181
|
*
|
|
145
182
|
* @example
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* a.equals(b); // true
|
|
183
|
+
* profilePath.covers(namePath) // true — "profile" covers "profile.name"
|
|
184
|
+
* namePath.covers(profilePath) // false
|
|
149
185
|
*/
|
|
186
|
+
covers(other: ResolvablePath<T>): boolean;
|
|
187
|
+
/** Returns `true` if this path is segment-by-segment identical to `other`. */
|
|
150
188
|
equals(other: ResolvablePath<T>): boolean;
|
|
151
189
|
/**
|
|
152
|
-
*
|
|
190
|
+
* Returns the structural relationship between this path and `other`, or `null` when unrelated.
|
|
191
|
+
*
|
|
192
|
+
* `"parent"` means **this** path is the parent (shorter prefix); `"child"` means **this** is deeper.
|
|
153
193
|
*
|
|
154
194
|
* @example
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* a.match(b); // { relation: 'child', params: {} }
|
|
195
|
+
* profilePath.match(namePath) // { relation: "parent" } — profilePath IS the parent
|
|
196
|
+
* namePath.match(profilePath) // { relation: "child" } — namePath is deeper
|
|
158
197
|
*/
|
|
159
198
|
match(other: ResolvablePath<T>): MatchResult | null;
|
|
160
199
|
/**
|
|
161
|
-
* Appends
|
|
162
|
-
* the
|
|
200
|
+
* Appends `other` (a T-rooted path) with smart suffix/prefix overlap deduplication.
|
|
201
|
+
* When the tail of this path matches the head of `other`, the overlap is collapsed once.
|
|
202
|
+
*
|
|
203
|
+
* If `other` carries wildcards (`*` / `**`), the result is a `TemplatePath` so the
|
|
204
|
+
* appended pattern still expands at `.get()` time; otherwise a concrete `Path`.
|
|
205
|
+
* Narrow at the call site if you need to disambiguate.
|
|
163
206
|
*
|
|
164
207
|
* @example
|
|
165
|
-
* const base = path<Root>(
|
|
166
|
-
*
|
|
208
|
+
* const base = path<Root>(r => r.users[0].profile);
|
|
209
|
+
* base.merge(r => r.users[0].profile.name) // "users.0.profile.name" (no duplication)
|
|
167
210
|
*/
|
|
168
|
-
merge<U>(other: ResolvablePath<T, U>): Path<T, U,
|
|
211
|
+
merge<U>(other: ResolvablePath<T, U>): Path<T, U> | TemplatePath<T, U>;
|
|
169
212
|
/**
|
|
170
|
-
* Removes the
|
|
171
|
-
* Returns `null`
|
|
213
|
+
* Removes `prefix` from the start of this path and returns the remaining tail.
|
|
214
|
+
* Returns `null` when `prefix` is not a leading segment-sequence of this path.
|
|
215
|
+
*
|
|
216
|
+
* The returned path carries the correct root type (`U` — the type `prefix` resolves to),
|
|
217
|
+
* so it can be passed directly to `.to()` or used independently.
|
|
172
218
|
*
|
|
173
219
|
* @example
|
|
174
|
-
* const full
|
|
175
|
-
* const
|
|
176
|
-
*
|
|
220
|
+
* const full = path<Company>(c => c.departments[0].employees[0].name);
|
|
221
|
+
* const prefix = path<Company>(c => c.departments[0]);
|
|
222
|
+
* full.subtract(prefix) // Path<Department, string>
|
|
177
223
|
*/
|
|
178
|
-
subtract(
|
|
224
|
+
subtract<U>(prefix: ResolvablePath<T, U>): Path<U, V> | null;
|
|
179
225
|
/**
|
|
180
|
-
* Returns a new path
|
|
226
|
+
* Returns a new path over a slice of segments, following `Array.prototype.slice` semantics.
|
|
227
|
+
* Value type becomes `unknown` because the type at an arbitrary segment boundary is not statically inferable.
|
|
181
228
|
*
|
|
182
229
|
* @example
|
|
183
|
-
*
|
|
184
|
-
* full.slice(0, 1); // equivalent to path<Root>().users
|
|
230
|
+
* path<Root>(r => r.users[0].name).slice(0, 2).$ // "users.0"
|
|
185
231
|
*/
|
|
186
|
-
slice(start?: number, end?: number): Path<T, unknown
|
|
232
|
+
slice(start?: number, end?: number): Path<T, unknown>;
|
|
187
233
|
/**
|
|
188
|
-
* Extends
|
|
234
|
+
* Extends this path with a relative path rooted at `V`.
|
|
235
|
+
* Accepts a lambda expression, a pre-built `Path<V, U>`, a `TemplatePath<V, U>`, or any `{segments}` object.
|
|
236
|
+
*
|
|
237
|
+
* If `relative` carries wildcards (`*` / `**`), the result is a `TemplatePath` so the
|
|
238
|
+
* appended pattern still expands at `.get()` time; otherwise a concrete `Path`.
|
|
239
|
+
* Narrow at the call site if you need to disambiguate.
|
|
189
240
|
*
|
|
190
241
|
* @example
|
|
191
|
-
*
|
|
192
|
-
*
|
|
242
|
+
* // Lambda form:
|
|
243
|
+
* employeePath.to(e => e.profile.firstName)
|
|
244
|
+
*
|
|
245
|
+
* // Pre-built path form (no extra lambda needed):
|
|
246
|
+
* const firstName = path<Employee>(e => e.profile.firstName);
|
|
247
|
+
* employeePath.to(firstName)
|
|
193
248
|
*/
|
|
194
|
-
to<U>(
|
|
249
|
+
to<U>(relative: ResolvablePath<V, U>): Path<T, U> | TemplatePath<T, U>;
|
|
195
250
|
}
|
|
196
251
|
/**
|
|
197
|
-
*
|
|
252
|
+
* A strongly-typed object property path.
|
|
253
|
+
* `.each()` and `.deep()` are only present when `V` is not a primitive type.
|
|
198
254
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* check `[V] extends [Primitive]` ensures that IDEs will not suggest `.each()` or
|
|
202
|
-
* `.deep()` when the path has resolved to a primitive value (like a string or number).
|
|
203
|
-
*
|
|
204
|
-
* @template T Root data type
|
|
205
|
-
* @template V Resolved value type at path end
|
|
206
|
-
* @template P Path string (e.g. "a.b.c") — literal when inferrable, string when dynamic
|
|
255
|
+
* @template T Root data type
|
|
256
|
+
* @template V Resolved value type at the end of the path
|
|
207
257
|
*/
|
|
208
|
-
type Path<T = unknown, V = unknown
|
|
258
|
+
type Path<T = unknown, V = unknown> = BasePath<T, V> & ([V] extends [Primitive] ? {} : TraversablePathMethods<T, V>);
|
|
209
259
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* It extends the standard `Path` concept but alters the return types of `.each()` and `.deep()`
|
|
213
|
-
* to return another `TemplatePath` (chaining templates). It also adds the `.expand()` method
|
|
214
|
-
* which can resolve this template against actual data to return an array of concrete `Path`s.
|
|
260
|
+
* A path that contains wildcards (`*` or `**`), matching multiple values at once.
|
|
215
261
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
262
|
+
* - `.get(data)` returns an array of all matched values.
|
|
263
|
+
* - `.set(data, value)` immutably sets every match to the same constant.
|
|
264
|
+
* - `.update(data, fn)` applies a per-item transform to every match.
|
|
265
|
+
* - `.expand(data)` resolves the template to an array of concrete `Path` objects.
|
|
218
266
|
*
|
|
219
|
-
* @template T
|
|
220
|
-
* @template V
|
|
221
|
-
* @template P Path string (e.g. "a.*.c")
|
|
267
|
+
* @template T Root data type
|
|
268
|
+
* @template V Item value type at the end of the template path
|
|
222
269
|
*/
|
|
223
|
-
type TemplatePath<T = unknown, V = unknown
|
|
270
|
+
type TemplatePath<T = unknown, V = unknown> = Omit<BasePath<T, V>, "get" | "fn" | "to" | "merge" | "subtract" | "parent" | "slice"> & {
|
|
224
271
|
/**
|
|
225
|
-
*
|
|
272
|
+
* Returns an array of all values matched by this template.
|
|
226
273
|
*
|
|
227
274
|
* @example
|
|
228
|
-
*
|
|
275
|
+
* path<Root>().users.each(u => u.name).get(data) // string[]
|
|
229
276
|
*/
|
|
230
277
|
get(data: T): V[];
|
|
231
278
|
/**
|
|
232
|
-
*
|
|
233
|
-
* Useful for array methods like `.map()` or `.filter()`.
|
|
279
|
+
* Pre-bound accessor function returning an array of all matched values.
|
|
234
280
|
*
|
|
235
281
|
* @example
|
|
236
|
-
*
|
|
282
|
+
* companies.map(path<Company>().departments.each(d => d.name).fn)
|
|
237
283
|
*/
|
|
238
284
|
readonly fn: (data: T) => V[];
|
|
239
|
-
}) & ([V] extends [Primitive] ? {} : {
|
|
240
285
|
/**
|
|
241
|
-
*
|
|
286
|
+
* Resolves this template to an array of concrete paths that exist in `data`.
|
|
242
287
|
*
|
|
243
288
|
* @example
|
|
244
|
-
*
|
|
245
|
-
*
|
|
289
|
+
* path<Root>().users.each().name.expand(data)
|
|
290
|
+
* // [path<Root>().users[0].name, path<Root>().users[1].name, ...]
|
|
246
291
|
*/
|
|
247
|
-
|
|
292
|
+
expand(data: T): Path<T, V>[];
|
|
248
293
|
/**
|
|
249
|
-
*
|
|
294
|
+
* Extends this template with a relative path rooted at `V`, preserving wildcard expansion.
|
|
295
|
+
* Returns a `TemplatePath` so the full chain (including the appended segments) is template-aware.
|
|
250
296
|
*
|
|
251
297
|
* @example
|
|
252
|
-
*
|
|
253
|
-
*
|
|
298
|
+
* path<Root>(r => r.users).each().to(u => u.name)
|
|
299
|
+
* // TemplatePath — collects every user's name
|
|
254
300
|
*/
|
|
255
|
-
|
|
256
|
-
}) & {
|
|
301
|
+
to<U>(relative: ResolvablePath<V, U>): TemplatePath<T, U>;
|
|
257
302
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* Note: Currently, `expand` only supports evaluating a single wildcard (`*` or `**`) per path.
|
|
262
|
-
*
|
|
263
|
-
* @example
|
|
264
|
-
* const template = path<Root>().users.each().name;
|
|
265
|
-
* const concretePaths = template.expand(data); // [path<Root>().users[0].name, ...]
|
|
303
|
+
* Appends `other` with smart overlap deduplication, preserving wildcard behavior.
|
|
304
|
+
* Returns a `TemplatePath` because `this` carries wildcards.
|
|
266
305
|
*/
|
|
267
|
-
|
|
268
|
-
|
|
306
|
+
merge<U>(other: ResolvablePath<T, U>): TemplatePath<T, U>;
|
|
307
|
+
/**
|
|
308
|
+
* Returns the parent path (all segments except the last), or `null` for an empty path.
|
|
309
|
+
* If the parent still contains wildcards (`*` / `**`) it remains a `TemplatePath`;
|
|
310
|
+
* otherwise a concrete `Path` is returned. Narrow at the call site if needed.
|
|
311
|
+
*/
|
|
312
|
+
parent(): Path<T, unknown> | TemplatePath<T, unknown> | null;
|
|
313
|
+
/**
|
|
314
|
+
* Returns a new path over a slice of segments. If the slice still contains wildcards
|
|
315
|
+
* the result is a `TemplatePath`; otherwise a concrete `Path`.
|
|
316
|
+
*/
|
|
317
|
+
slice(start?: number, end?: number): Path<T, unknown> | TemplatePath<T, unknown>;
|
|
318
|
+
/**
|
|
319
|
+
* Removes `prefix` from the start of this path and returns the remaining tail.
|
|
320
|
+
* If the tail still contains wildcards the result is a `TemplatePath<U, V>`;
|
|
321
|
+
* otherwise a concrete `Path<U, V>`. Returns `null` when `prefix` is not a leading segment-sequence.
|
|
322
|
+
*/
|
|
323
|
+
subtract<U>(prefix: ResolvablePath<T, U>): Path<U, V> | TemplatePath<U, V> | null;
|
|
324
|
+
} & ([V] extends [Primitive] ? {} : {
|
|
325
|
+
each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U>;
|
|
326
|
+
deep<U = V>(expr?: (leaf: V) => U): TemplatePath<T, U>;
|
|
327
|
+
});
|
|
269
328
|
/**
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* This allows the `path()` function to be called in several ways:
|
|
273
|
-
* 1. Without arguments: returns a root path `path<T>()`.
|
|
274
|
-
* 2. With a lambda: returns a path built from the expression `path<T>((p) => p.a.b)`.
|
|
275
|
-
* 3. With a base path and a lambda: allows extending an existing path `path(base, (p) => p.c)`.
|
|
329
|
+
* The overloaded call signature of the `path()` function.
|
|
276
330
|
*/
|
|
277
331
|
type PathConstructor = {
|
|
278
|
-
<T>(): Path<T, T
|
|
279
|
-
<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V
|
|
280
|
-
<T, U, V = unknown>(base: BasePath<T, U
|
|
332
|
+
<T>(): Path<T, T>;
|
|
333
|
+
<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V>;
|
|
334
|
+
<T, U, V = unknown>(base: BasePath<T, U>, expr: PathExpression<U, V>): Path<T, V>;
|
|
281
335
|
};
|
|
282
|
-
/**
|
|
283
|
-
type UnsafePathConstructor = <T>(raw: string) => Path<T,
|
|
336
|
+
/** Call signature of the `unsafePath()` function */
|
|
337
|
+
type UnsafePathConstructor = <T, V = unknown>(raw: string) => Path<T, V>;
|
|
284
338
|
|
|
285
339
|
/**
|
|
286
|
-
* Create a typed path from a lambda expression
|
|
340
|
+
* Create a typed path from a lambda expression, from an existing base path, or as a root.
|
|
287
341
|
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
342
|
+
* @example
|
|
343
|
+
* // Lambda — recommended: annotate the parameter so both T and V are inferred
|
|
344
|
+
* const p = path((user: User) => user.profile.firstName);
|
|
291
345
|
*
|
|
292
346
|
* @example
|
|
293
|
-
* //
|
|
294
|
-
* const
|
|
347
|
+
* // Both generics explicit
|
|
348
|
+
* const p = path<User, string>((u) => u.profile.firstName);
|
|
295
349
|
*
|
|
296
350
|
* @example
|
|
297
|
-
* //
|
|
298
|
-
* const
|
|
351
|
+
* // Root path (no segments) — typically extended via .to() or path(root, expr)
|
|
352
|
+
* const root = path<User>();
|
|
299
353
|
*
|
|
300
354
|
* @example
|
|
301
|
-
* // Extend an existing path
|
|
355
|
+
* // Extend an existing base path
|
|
302
356
|
* const p2 = path(root, (u) => u.profile);
|
|
303
357
|
*/
|
|
304
|
-
declare function path<T>(): Path<T, T
|
|
305
|
-
declare function path<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V
|
|
306
|
-
declare function path<T, U, V = unknown>(base: BasePath<T, U
|
|
358
|
+
declare function path<T>(): Path<T, T>;
|
|
359
|
+
declare function path<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V>;
|
|
360
|
+
declare function path<T, U, V = unknown>(base: BasePath<T, U>, expr: PathExpression<U, V>): Path<T, V>;
|
|
307
361
|
/**
|
|
308
|
-
* Create a path from a raw string (e.g
|
|
362
|
+
* Create a path from a raw dot-separated string (e.g. `"users.0.name"`).
|
|
363
|
+
*
|
|
364
|
+
* Useful for dynamic paths from external sources (API responses, Zod issue paths, etc.).
|
|
365
|
+
* Segments that are canonical non-negative integers are stored as numbers; all others as strings.
|
|
309
366
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
367
|
+
* The optional second generic `V` declares the expected leaf type without a cast:
|
|
368
|
+
* ```ts
|
|
369
|
+
* unsafePath<User, string>("profile.firstName").get(user) // string | undefined
|
|
370
|
+
* ```
|
|
313
371
|
*
|
|
314
|
-
* @param raw
|
|
315
|
-
* @returns A Path object representing the given string.
|
|
372
|
+
* @param raw Dot-separated string. Empty string returns a zero-segment root path.
|
|
316
373
|
*/
|
|
317
|
-
declare function unsafePath<T>(raw: string): Path<T,
|
|
374
|
+
declare function unsafePath<T, V = unknown>(raw: string): Path<T, V>;
|
|
318
375
|
|
|
319
|
-
export { type
|
|
376
|
+
export { type BasePath, type CollectionItem, DEEP_WILDCARD, type MatchRelation, type MatchResult, type Path, type PathConstructor, type PathExpression, type Primitive, type ResolvablePath, type ResolvedType, type Segment, type TemplatePath, type TraversablePathMethods, type UnsafePathConstructor, WILDCARD, path, unsafePath };
|