data-path 1.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.
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Core type definitions for data-path.
3
+ * @see spec/idea.md
4
+ */
5
+ /** A path segment: string key or numeric index */
6
+ type Segment = string | number;
7
+ /** Relation returned by .match() */
8
+ type MatchRelation = "includes" | "included-by" | "equals" | "parent" | "child" | null;
9
+ /** Result of .match() — bidirectional relationship + optional params */
10
+ interface MatchResult {
11
+ relation: MatchRelation;
12
+ params?: Record<string, string>;
13
+ }
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
+ /**
19
+ * Extracts the item type from a collection (Array or Record) so that traversal methods
20
+ * (like `.each()`) know what type of item they are iterating over.
21
+ *
22
+ * @template V The collection type (e.g., `string[]` or `Record<string, number>`)
23
+ */
24
+ type CollectionItem<V> = V extends ReadonlyArray<infer U> ? U : V extends Record<PropertyKey, infer U> ? U : unknown;
25
+ /** Primitives that cannot have traversal methods called on them */
26
+ type Primitive = string | number | boolean | symbol | bigint | null | undefined;
27
+ /** Expression type for path construction — receives proxy, returns any (path is inferred from access) */
28
+ type PathExpression<T, R = unknown> = (proxy: T) => R;
29
+ /**
30
+ * Represents the various forms a path can take when provided as an input argument.
31
+ * This enables API flexibility, allowing methods (like `merge`, `match`, `startsWith`)
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.
36
+ */
37
+ type ResolvablePath<T, V = unknown> = BasePath<T, V, string> | {
38
+ segments: readonly Segment[];
39
+ } | PathExpression<T, V>;
40
+ /**
41
+ * Extracted methods for traversing into collections or deep structures.
42
+ * This is separated from `BasePath` so that these methods can be conditionally
43
+ * excluded from the type system when a path points to a primitive value
44
+ * (since primitives cannot be traversed).
45
+ */
46
+ interface TraversablePathMethods<T, V> {
47
+ /**
48
+ * Traverses into a collection (Array or Record) to operate on each item.
49
+ *
50
+ * @example
51
+ * const users = path<Root>().users;
52
+ * const userNames = users.each(u => u.name); // Path matches all names
53
+ */
54
+ each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U, string>;
55
+ /**
56
+ * Traverses deeply into a structure, matching any nested property.
57
+ *
58
+ * @example
59
+ * const root = path<Root>();
60
+ * const allIds = root.deep(node => node.id); // Path matches any 'id' at any depth
61
+ */
62
+ deep<U = DeepReachable<V>>(expr?: (leaf: DeepReachable<V>) => U): TemplatePath<T, U, string>;
63
+ }
64
+ /**
65
+ * The foundational structure for all path objects (both standard and template paths).
66
+ * Contains common properties and operations including value extraction/mutation,
67
+ * relational algebra (comparisons), and structural manipulation.
68
+ *
69
+ * @template T Root data type the path operates on.
70
+ * @template V The expected value type that the path resolves to.
71
+ * @template _P The string representation of the path (optional/unused in runtime, but useful for type-level strings).
72
+ */
73
+ interface BasePath<T = unknown, V = unknown, _P extends string = string> {
74
+ /**
75
+ * The array of segments (string keys or numeric indices) that make up this path.
76
+ */
77
+ readonly segments: readonly Segment[];
78
+ /**
79
+ * The number of segments in this path.
80
+ */
81
+ readonly length: number;
82
+ /**
83
+ * The string representation of the path (e.g. "users.0.name").
84
+ * Useful for binding paths to form libraries or UI components.
85
+ *
86
+ * @example
87
+ * path<Root>().users[0].name.$; // "users.0.name"
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"
95
+ */
96
+ toString(): string;
97
+ /**
98
+ * Extracts the value at this path from the given data object.
99
+ * Safely handles missing intermediate properties by returning `undefined` instead of throwing an error.
100
+ *
101
+ * @example
102
+ * const namePath = path<User>().name;
103
+ * const name = namePath.get({ name: "Alice" }); // "Alice"
104
+ */
105
+ get(data: T): V;
106
+ /**
107
+ * Returns an accessor function that extracts the value at this path from the given data object.
108
+ * Useful for array methods like `.map()` or `.filter()`.
109
+ *
110
+ * @example
111
+ * const names = users.map(path<User>().name.fn);
112
+ */
113
+ readonly fn: (data: T) => V;
114
+ /**
115
+ * Sets the value at this path in the given data object, returning a new updated object (immutable).
116
+ * If intermediate properties are missing, they are automatically created as objects or arrays
117
+ * depending on the segment types (numeric keys become arrays).
118
+ *
119
+ * @example
120
+ * const namePath = path<User>().name;
121
+ * const updatedUser = namePath.set({ name: "Alice" }, "Bob"); // { name: "Bob" }
122
+ */
123
+ set(data: T, value: V): T;
124
+ /**
125
+ * Checks if this path starts with the segments of another path.
126
+ *
127
+ * @example
128
+ * const a = path<Root>().users[0].name;
129
+ * const b = path<Root>().users;
130
+ * a.startsWith(b); // true
131
+ */
132
+ startsWith(other: ResolvablePath<T>): boolean;
133
+ /**
134
+ * Checks if this path encompasses the segments of another path (i.e., this path is a prefix of the other).
135
+ *
136
+ * @example
137
+ * const a = path<Root>().users;
138
+ * const b = path<Root>().users[0].name;
139
+ * a.includes(b); // true
140
+ */
141
+ includes(other: ResolvablePath<T>): boolean;
142
+ /**
143
+ * Checks if this path is exactly equal to another path.
144
+ *
145
+ * @example
146
+ * const a = path<Root>().users;
147
+ * const b = path<Root>().users;
148
+ * a.equals(b); // true
149
+ */
150
+ equals(other: ResolvablePath<T>): boolean;
151
+ /**
152
+ * Matches this path against another path, returning their relationship.
153
+ *
154
+ * @example
155
+ * const a = path<Root>().users[0];
156
+ * const b = path<Root>().users;
157
+ * a.match(b); // { relation: 'child', params: {} }
158
+ */
159
+ match(other: ResolvablePath<T>): MatchResult | null;
160
+ /**
161
+ * Appends another path to the end of this path. If the end of this path matches
162
+ * the beginning of the other path, the overlapping segments are intelligently deduplicated.
163
+ *
164
+ * @example
165
+ * const base = path<Root>().users;
166
+ * const full = base.merge(p => p[0].name); // equivalent to path<Root>().users[0].name
167
+ */
168
+ merge<U>(other: ResolvablePath<T, U>): Path<T, U, string>;
169
+ /**
170
+ * Removes the segments of another path from either the beginning or the end of this path.
171
+ * Returns `null` if the other path is neither a prefix nor a suffix.
172
+ *
173
+ * @example
174
+ * const full = path<Root>().users[0].name;
175
+ * const base = path<Root>().users;
176
+ * const remainder = full.subtract(base); // equivalent to path()[0].name
177
+ */
178
+ subtract(other: ResolvablePath<T>): Path<T, V, string> | null;
179
+ /**
180
+ * Returns a new path containing a subset of the segments, similar to Array.prototype.slice.
181
+ *
182
+ * @example
183
+ * const full = path<Root>().users[0].name;
184
+ * full.slice(0, 1); // equivalent to path<Root>().users
185
+ */
186
+ slice(start?: number, end?: number): Path<T, unknown, string>;
187
+ /**
188
+ * Extends the current path using a lambda expression starting from the resolved value.
189
+ *
190
+ * @example
191
+ * const userPath = path<Root>().users[0];
192
+ * const namePath = userPath.to(u => u.name);
193
+ */
194
+ to<U>(expr: PathExpression<V, U>): Path<T, U, string>;
195
+ }
196
+ /**
197
+ * Represents a strongly-typed object property path.
198
+ *
199
+ * This type uses intersection (`&`) to combine the base operations (`BasePath`)
200
+ * with conditional traversal methods (`TraversablePathMethods`). The conditional
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
207
+ */
208
+ type Path<T = unknown, V = unknown, P extends string = string> = BasePath<T, V, P> & ([V] extends [Primitive] ? {} : TraversablePathMethods<T, V>);
209
+ /**
210
+ * Represents a path containing wildcards (`*` or `**`), useful for operations on multiple items.
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.
215
+ *
216
+ * **Data Access:** Calling `.get()` on a `TemplatePath` will return an array of all matched values.
217
+ * Calling `.set()` will immutably update all matched paths in the object and return the updated object.
218
+ *
219
+ * @template T Root data type
220
+ * @template V Resolved value type at path end
221
+ * @template P Path string (e.g. "a.*.c")
222
+ */
223
+ type TemplatePath<T = unknown, V = unknown, P extends string = string> = (Omit<BasePath<T, V, P>, "get" | "fn"> & {
224
+ /**
225
+ * Extracts an array of values at this template path from the given data object.
226
+ *
227
+ * @example
228
+ * const names = path<Root>().users.each().name.get(data); // string[]
229
+ */
230
+ get(data: T): V[];
231
+ /**
232
+ * Returns an accessor function that extracts an array of values at this template path from the given data object.
233
+ * Useful for array methods like `.map()` or `.filter()`.
234
+ *
235
+ * @example
236
+ * const allNames = companies.map(path<Company>().departments.each().name.fn);
237
+ */
238
+ readonly fn: (data: T) => V[];
239
+ }) & ([V] extends [Primitive] ? {} : {
240
+ /**
241
+ * Traverses into a collection (Array or Record) to operate on each item, returning a TemplatePath.
242
+ *
243
+ * @example
244
+ * const users = path<Root>().users;
245
+ * const userNames = users.each(u => u.name); // TemplatePath matching all names
246
+ */
247
+ each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U, `${string}.${"*"}.${string}`>;
248
+ /**
249
+ * Traverses deeply into a structure, matching any nested property, returning a TemplatePath.
250
+ *
251
+ * @example
252
+ * const root = path<Root>();
253
+ * const allIds = root.deep(node => node.id); // TemplatePath matching any 'id' at any depth
254
+ */
255
+ deep<U = DeepReachable<V>>(expr?: (leaf: DeepReachable<V>) => U): TemplatePath<T, U, `${string}.${"**"}.${string}`>;
256
+ }) & {
257
+ /**
258
+ * Resolves this template path against actual data to return an array of concrete paths
259
+ * that exist in the given data.
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, ...]
266
+ */
267
+ expand(data: T): Path<T, V, string>[];
268
+ };
269
+ /**
270
+ * Constructor overloads for creating paths.
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)`.
276
+ */
277
+ type PathConstructor = {
278
+ <T>(): Path<T, T, "">;
279
+ <T, V = unknown>(expr: PathExpression<T, V>): Path<T, V, string>;
280
+ <T, U, V = unknown>(base: BasePath<T, U, string>, expr: PathExpression<U, V>): Path<T, V, string>;
281
+ };
282
+ /** Unsafe path from string — no type checking on segments */
283
+ type UnsafePathConstructor = <T>(raw: string) => Path<T, unknown, string>;
284
+
285
+ /**
286
+ * Create a typed path from a lambda expression or extend an existing base path.
287
+ *
288
+ * This function serves as the primary entry point for constructing paths. By utilizing
289
+ * a Proxy-based builder (the lambda expression), it captures property accesses and
290
+ * records them as path segments without needing to evaluate actual data.
291
+ *
292
+ * @example
293
+ * // Create a root path
294
+ * const root = path<User>();
295
+ *
296
+ * @example
297
+ * // Create a path via lambda
298
+ * const p = path<User>((u) => u.address.city);
299
+ *
300
+ * @example
301
+ * // Extend an existing path
302
+ * const p2 = path(root, (u) => u.profile);
303
+ */
304
+ declare function path<T>(): Path<T, T, "">;
305
+ declare function path<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V, string>;
306
+ declare function path<T, U, V = unknown>(base: BasePath<T, U, string>, expr: PathExpression<U, V>): Path<T, V, string>;
307
+ /**
308
+ * Create a path from a raw string (e.g., "users.0.name").
309
+ *
310
+ * This is useful when paths are dynamic (like from a database or API response).
311
+ * Type checking on individual segments is bypassed, and segments are automatically
312
+ * parsed into numeric indices where appropriate.
313
+ *
314
+ * @param raw The dot-separated path string.
315
+ * @returns A Path object representing the given string.
316
+ */
317
+ declare function unsafePath<T>(raw: string): Path<T, unknown, string>;
318
+
319
+ export { type DeepReachable, type MatchRelation, type MatchResult, type Path, type PathConstructor, type PathExpression, type ResolvedType, type Segment, type TemplatePath, type UnsafePathConstructor, path, unsafePath };
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Core type definitions for data-path.
3
+ * @see spec/idea.md
4
+ */
5
+ /** A path segment: string key or numeric index */
6
+ type Segment = string | number;
7
+ /** Relation returned by .match() */
8
+ type MatchRelation = "includes" | "included-by" | "equals" | "parent" | "child" | null;
9
+ /** Result of .match() — bidirectional relationship + optional params */
10
+ interface MatchResult {
11
+ relation: MatchRelation;
12
+ params?: Record<string, string>;
13
+ }
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
+ /**
19
+ * Extracts the item type from a collection (Array or Record) so that traversal methods
20
+ * (like `.each()`) know what type of item they are iterating over.
21
+ *
22
+ * @template V The collection type (e.g., `string[]` or `Record<string, number>`)
23
+ */
24
+ type CollectionItem<V> = V extends ReadonlyArray<infer U> ? U : V extends Record<PropertyKey, infer U> ? U : unknown;
25
+ /** Primitives that cannot have traversal methods called on them */
26
+ type Primitive = string | number | boolean | symbol | bigint | null | undefined;
27
+ /** Expression type for path construction — receives proxy, returns any (path is inferred from access) */
28
+ type PathExpression<T, R = unknown> = (proxy: T) => R;
29
+ /**
30
+ * Represents the various forms a path can take when provided as an input argument.
31
+ * This enables API flexibility, allowing methods (like `merge`, `match`, `startsWith`)
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.
36
+ */
37
+ type ResolvablePath<T, V = unknown> = BasePath<T, V, string> | {
38
+ segments: readonly Segment[];
39
+ } | PathExpression<T, V>;
40
+ /**
41
+ * Extracted methods for traversing into collections or deep structures.
42
+ * This is separated from `BasePath` so that these methods can be conditionally
43
+ * excluded from the type system when a path points to a primitive value
44
+ * (since primitives cannot be traversed).
45
+ */
46
+ interface TraversablePathMethods<T, V> {
47
+ /**
48
+ * Traverses into a collection (Array or Record) to operate on each item.
49
+ *
50
+ * @example
51
+ * const users = path<Root>().users;
52
+ * const userNames = users.each(u => u.name); // Path matches all names
53
+ */
54
+ each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U, string>;
55
+ /**
56
+ * Traverses deeply into a structure, matching any nested property.
57
+ *
58
+ * @example
59
+ * const root = path<Root>();
60
+ * const allIds = root.deep(node => node.id); // Path matches any 'id' at any depth
61
+ */
62
+ deep<U = DeepReachable<V>>(expr?: (leaf: DeepReachable<V>) => U): TemplatePath<T, U, string>;
63
+ }
64
+ /**
65
+ * The foundational structure for all path objects (both standard and template paths).
66
+ * Contains common properties and operations including value extraction/mutation,
67
+ * relational algebra (comparisons), and structural manipulation.
68
+ *
69
+ * @template T Root data type the path operates on.
70
+ * @template V The expected value type that the path resolves to.
71
+ * @template _P The string representation of the path (optional/unused in runtime, but useful for type-level strings).
72
+ */
73
+ interface BasePath<T = unknown, V = unknown, _P extends string = string> {
74
+ /**
75
+ * The array of segments (string keys or numeric indices) that make up this path.
76
+ */
77
+ readonly segments: readonly Segment[];
78
+ /**
79
+ * The number of segments in this path.
80
+ */
81
+ readonly length: number;
82
+ /**
83
+ * The string representation of the path (e.g. "users.0.name").
84
+ * Useful for binding paths to form libraries or UI components.
85
+ *
86
+ * @example
87
+ * path<Root>().users[0].name.$; // "users.0.name"
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"
95
+ */
96
+ toString(): string;
97
+ /**
98
+ * Extracts the value at this path from the given data object.
99
+ * Safely handles missing intermediate properties by returning `undefined` instead of throwing an error.
100
+ *
101
+ * @example
102
+ * const namePath = path<User>().name;
103
+ * const name = namePath.get({ name: "Alice" }); // "Alice"
104
+ */
105
+ get(data: T): V;
106
+ /**
107
+ * Returns an accessor function that extracts the value at this path from the given data object.
108
+ * Useful for array methods like `.map()` or `.filter()`.
109
+ *
110
+ * @example
111
+ * const names = users.map(path<User>().name.fn);
112
+ */
113
+ readonly fn: (data: T) => V;
114
+ /**
115
+ * Sets the value at this path in the given data object, returning a new updated object (immutable).
116
+ * If intermediate properties are missing, they are automatically created as objects or arrays
117
+ * depending on the segment types (numeric keys become arrays).
118
+ *
119
+ * @example
120
+ * const namePath = path<User>().name;
121
+ * const updatedUser = namePath.set({ name: "Alice" }, "Bob"); // { name: "Bob" }
122
+ */
123
+ set(data: T, value: V): T;
124
+ /**
125
+ * Checks if this path starts with the segments of another path.
126
+ *
127
+ * @example
128
+ * const a = path<Root>().users[0].name;
129
+ * const b = path<Root>().users;
130
+ * a.startsWith(b); // true
131
+ */
132
+ startsWith(other: ResolvablePath<T>): boolean;
133
+ /**
134
+ * Checks if this path encompasses the segments of another path (i.e., this path is a prefix of the other).
135
+ *
136
+ * @example
137
+ * const a = path<Root>().users;
138
+ * const b = path<Root>().users[0].name;
139
+ * a.includes(b); // true
140
+ */
141
+ includes(other: ResolvablePath<T>): boolean;
142
+ /**
143
+ * Checks if this path is exactly equal to another path.
144
+ *
145
+ * @example
146
+ * const a = path<Root>().users;
147
+ * const b = path<Root>().users;
148
+ * a.equals(b); // true
149
+ */
150
+ equals(other: ResolvablePath<T>): boolean;
151
+ /**
152
+ * Matches this path against another path, returning their relationship.
153
+ *
154
+ * @example
155
+ * const a = path<Root>().users[0];
156
+ * const b = path<Root>().users;
157
+ * a.match(b); // { relation: 'child', params: {} }
158
+ */
159
+ match(other: ResolvablePath<T>): MatchResult | null;
160
+ /**
161
+ * Appends another path to the end of this path. If the end of this path matches
162
+ * the beginning of the other path, the overlapping segments are intelligently deduplicated.
163
+ *
164
+ * @example
165
+ * const base = path<Root>().users;
166
+ * const full = base.merge(p => p[0].name); // equivalent to path<Root>().users[0].name
167
+ */
168
+ merge<U>(other: ResolvablePath<T, U>): Path<T, U, string>;
169
+ /**
170
+ * Removes the segments of another path from either the beginning or the end of this path.
171
+ * Returns `null` if the other path is neither a prefix nor a suffix.
172
+ *
173
+ * @example
174
+ * const full = path<Root>().users[0].name;
175
+ * const base = path<Root>().users;
176
+ * const remainder = full.subtract(base); // equivalent to path()[0].name
177
+ */
178
+ subtract(other: ResolvablePath<T>): Path<T, V, string> | null;
179
+ /**
180
+ * Returns a new path containing a subset of the segments, similar to Array.prototype.slice.
181
+ *
182
+ * @example
183
+ * const full = path<Root>().users[0].name;
184
+ * full.slice(0, 1); // equivalent to path<Root>().users
185
+ */
186
+ slice(start?: number, end?: number): Path<T, unknown, string>;
187
+ /**
188
+ * Extends the current path using a lambda expression starting from the resolved value.
189
+ *
190
+ * @example
191
+ * const userPath = path<Root>().users[0];
192
+ * const namePath = userPath.to(u => u.name);
193
+ */
194
+ to<U>(expr: PathExpression<V, U>): Path<T, U, string>;
195
+ }
196
+ /**
197
+ * Represents a strongly-typed object property path.
198
+ *
199
+ * This type uses intersection (`&`) to combine the base operations (`BasePath`)
200
+ * with conditional traversal methods (`TraversablePathMethods`). The conditional
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
207
+ */
208
+ type Path<T = unknown, V = unknown, P extends string = string> = BasePath<T, V, P> & ([V] extends [Primitive] ? {} : TraversablePathMethods<T, V>);
209
+ /**
210
+ * Represents a path containing wildcards (`*` or `**`), useful for operations on multiple items.
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.
215
+ *
216
+ * **Data Access:** Calling `.get()` on a `TemplatePath` will return an array of all matched values.
217
+ * Calling `.set()` will immutably update all matched paths in the object and return the updated object.
218
+ *
219
+ * @template T Root data type
220
+ * @template V Resolved value type at path end
221
+ * @template P Path string (e.g. "a.*.c")
222
+ */
223
+ type TemplatePath<T = unknown, V = unknown, P extends string = string> = (Omit<BasePath<T, V, P>, "get" | "fn"> & {
224
+ /**
225
+ * Extracts an array of values at this template path from the given data object.
226
+ *
227
+ * @example
228
+ * const names = path<Root>().users.each().name.get(data); // string[]
229
+ */
230
+ get(data: T): V[];
231
+ /**
232
+ * Returns an accessor function that extracts an array of values at this template path from the given data object.
233
+ * Useful for array methods like `.map()` or `.filter()`.
234
+ *
235
+ * @example
236
+ * const allNames = companies.map(path<Company>().departments.each().name.fn);
237
+ */
238
+ readonly fn: (data: T) => V[];
239
+ }) & ([V] extends [Primitive] ? {} : {
240
+ /**
241
+ * Traverses into a collection (Array or Record) to operate on each item, returning a TemplatePath.
242
+ *
243
+ * @example
244
+ * const users = path<Root>().users;
245
+ * const userNames = users.each(u => u.name); // TemplatePath matching all names
246
+ */
247
+ each<U = CollectionItem<V>>(expr?: (item: CollectionItem<V>) => U): TemplatePath<T, U, `${string}.${"*"}.${string}`>;
248
+ /**
249
+ * Traverses deeply into a structure, matching any nested property, returning a TemplatePath.
250
+ *
251
+ * @example
252
+ * const root = path<Root>();
253
+ * const allIds = root.deep(node => node.id); // TemplatePath matching any 'id' at any depth
254
+ */
255
+ deep<U = DeepReachable<V>>(expr?: (leaf: DeepReachable<V>) => U): TemplatePath<T, U, `${string}.${"**"}.${string}`>;
256
+ }) & {
257
+ /**
258
+ * Resolves this template path against actual data to return an array of concrete paths
259
+ * that exist in the given data.
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, ...]
266
+ */
267
+ expand(data: T): Path<T, V, string>[];
268
+ };
269
+ /**
270
+ * Constructor overloads for creating paths.
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)`.
276
+ */
277
+ type PathConstructor = {
278
+ <T>(): Path<T, T, "">;
279
+ <T, V = unknown>(expr: PathExpression<T, V>): Path<T, V, string>;
280
+ <T, U, V = unknown>(base: BasePath<T, U, string>, expr: PathExpression<U, V>): Path<T, V, string>;
281
+ };
282
+ /** Unsafe path from string — no type checking on segments */
283
+ type UnsafePathConstructor = <T>(raw: string) => Path<T, unknown, string>;
284
+
285
+ /**
286
+ * Create a typed path from a lambda expression or extend an existing base path.
287
+ *
288
+ * This function serves as the primary entry point for constructing paths. By utilizing
289
+ * a Proxy-based builder (the lambda expression), it captures property accesses and
290
+ * records them as path segments without needing to evaluate actual data.
291
+ *
292
+ * @example
293
+ * // Create a root path
294
+ * const root = path<User>();
295
+ *
296
+ * @example
297
+ * // Create a path via lambda
298
+ * const p = path<User>((u) => u.address.city);
299
+ *
300
+ * @example
301
+ * // Extend an existing path
302
+ * const p2 = path(root, (u) => u.profile);
303
+ */
304
+ declare function path<T>(): Path<T, T, "">;
305
+ declare function path<T, V = unknown>(expr: PathExpression<T, V>): Path<T, V, string>;
306
+ declare function path<T, U, V = unknown>(base: BasePath<T, U, string>, expr: PathExpression<U, V>): Path<T, V, string>;
307
+ /**
308
+ * Create a path from a raw string (e.g., "users.0.name").
309
+ *
310
+ * This is useful when paths are dynamic (like from a database or API response).
311
+ * Type checking on individual segments is bypassed, and segments are automatically
312
+ * parsed into numeric indices where appropriate.
313
+ *
314
+ * @param raw The dot-separated path string.
315
+ * @returns A Path object representing the given string.
316
+ */
317
+ declare function unsafePath<T>(raw: string): Path<T, unknown, string>;
318
+
319
+ export { type DeepReachable, type MatchRelation, type MatchResult, type Path, type PathConstructor, type PathExpression, type ResolvedType, type Segment, type TemplatePath, type UnsafePathConstructor, path, unsafePath };