### Set Up Local Development Environment Source: https://github.com/sergeyshmakov/data-path/blob/main/CONTRIBUTING.md Clone the repository, install dependencies, and start the development server in watch mode. Use `npm run dev` for automatic rebuilding on save. ```bash git clone https://github.com/sergeyshmakov/data-path.git cd data-path npm ci npm run dev # watch mode — rebuilds on save npm test # run tests and type checks ``` -------------------------------- ### Permission guard example Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/relational.mdx Demonstrates a practical use case for `startsWith` in a permission guard function. This example prevents writes to a specific subtree (settings) by checking if the target path starts with the settings path. ```typescript type Store = { users: User[]; settings: { theme: string } }; const settingsPath = path((s: Store) => s.settings); function canWrite(targetPath: Path): boolean { // Allow writes only outside the settings subtree return !targetPath.startsWith(settingsPath); } canWrite(path((s: Store) => s.users[0].name)) // true canWrite(path((s: Store) => s.settings.theme)) // false ``` -------------------------------- ### Install data-path with npm Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/getting-started/installation.mdx Use this command to install the data-path package. Ensure you have Node.js and npm installed. ```bash npm install data-path ``` -------------------------------- ### Quick Start with data-path in TypeScript Source: https://github.com/sergeyshmakov/data-path/blob/main/README.md Demonstrates basic usage of the data-path library. Import the 'path' function, define a type for your data, create a path using a lambda expression, and access its string representation, get, set, and function properties. ```typescript import { path } from "data-path"; type User = { profile: { firstName: string; lastName: string }; tags: string[] }; const firstNamePath = path((u: User) => u.profile.firstName); firstNamePath.$ // "profile.firstName" firstNamePath.get(user) // "Alice" | undefined firstNamePath.set(user, "Bob") // returns a new User — original unchanged firstNamePath.fn // stable (u: User) => string | undefined ``` -------------------------------- ### Verify data-path installation Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/getting-started/installation.mdx This TypeScript code snippet demonstrates how to import and use the 'path' function from data-path. If it compiles and logs the expected output, the installation is successful. ```typescript import { path } from "data-path"; type User = { name: string }; const p = path((u: User) => u.name); console.log(p.$); // "name" ``` -------------------------------- ### Install data-path AI Skill Source: https://github.com/sergeyshmakov/data-path/blob/main/README.md Install the data-path AI skill using npx for use with Context7. This command makes the package's capabilities available to AI tooling. ```bash npx ctx7 skills install /sergeyshmakov/data-path data-path ``` -------------------------------- ### Importing data-path Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/SKILL.md Import the necessary functions from the data-path library. Ensure data-path is installed via npm. ```typescript import { path, unsafePath } from "data-path" ``` -------------------------------- ### Basic Zustand Store Setup with data-path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/zustand.mdx Demonstrates setting up a Zustand store with `data-path` for immutable nested state updates. Imports `create` from Zustand and `path` from `data-path`. Defines store state and actions using path selectors. ```typescript import { create } from "zustand"; import { path } from "data-path"; type StoreState = { user: { profile: { firstName: string; theme: "light" | "dark" }; }; setFirstName: (name: string) => void; setTheme: (theme: "light" | "dark") => void; }; const firstNamePath = path((s: StoreState) => s.user.profile.firstName); const themePath = path((s: StoreState) => s.user.profile.theme); const useStore = create((set) => ({ user: { profile: { firstName: "Alice", theme: "light" } }, setFirstName: (name) => set((state) => firstNamePath.set(state, name)), setTheme: (theme) => set((state) => themePath.set(state, theme)), })); ``` -------------------------------- ### Navigation Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/references/api.md Methods for navigating the path hierarchy, such as getting the parent path or extending the current path with a relative path. ```APIDOC ## Navigation ### `path.parent()` Returns path without the last segment, or `null` for root/empty. On a `TemplatePath`, the return widens to `Path | TemplatePath | null` so wildcards are preserved when they remain in the parent. ### `path.to(relative)` Extend this path with a relative path rooted at `V`. Returns `Path | TemplatePath` — a `TemplatePath` when `relative` carries `*`/`**`, otherwise a `Path`. `path.to()` accepts a `ResolvablePath`: a lambda `(v: V) => U`, a pre-built `Path` or `TemplatePath`, or a `{segments}` object. ``` -------------------------------- ### Data Access Methods Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Methods for getting, setting, and updating data using paths. ```APIDOC ## Data Access | Method | Signature | Description | |---|---|---| | `.get(data)` | `(T) => V | undefined` | Read value at path; returns `undefined` if any segment is missing | | `.set(data, value)` | `(T, V) => T` | Immutable write — returns a structural clone | | `.update(data, fn)` | `(T, (V | undefined) => V) => T` | Read-modify-write in one call | On template paths, `.get()` returns `V[]` and `.set()` / `.update()` apply to all matched paths. ``` -------------------------------- ### Basic TanStack Form Usage with Data-Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/tanstack-form.mdx Demonstrates basic integration of data-path with TanStack Form for typed field names. Ensure you have @tanstack/react-form and data-path installed. ```tsx import { useForm } from "@tanstack/react-form"; import { path } from "data-path"; type FormValues = { profile: { firstName: string; lastName: string }; email: string; }; const firstNamePath = path((f: FormValues) => f.profile.firstName); function ProfileForm() { const form = useForm({ defaultValues: { profile: { firstName: "", lastName: "" }, email: "" }, onSubmit: ({ value }) => console.log(value), }); return (
{ e.preventDefault(); form.handleSubmit(); }}> {(field) => ( field.handleChange(e.target.value)} /> )}
); } ``` -------------------------------- ### Relational operations with wildcards Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/relational.mdx When paths contain wildcards (e.g., from `.each()` or `.deep()`), use `covers` or `startsWith` depending on which reads more naturally. The template path can cover a concrete path, or a concrete path can start with a template prefix. ```typescript interface R { items: Array<{ name: string }> } const allNames = path((r: R) => r.items).each((i: { name: string }) => i.name); const one = path((r: R) => r.items[0].name); allNames.covers(one) // true — the template covers this concrete path one.startsWith(allNames) // true — the concrete falls under the template prefix ``` ```typescript interface Root { tree: { a: { b: { c: string } } } } const deep = path((r: Root) => r.tree).deep(); // ["tree", "**"] const leaf = path((r: Root) => r.tree.a.b.c); // ["tree", "a", "b", "c"] leaf.startsWith(deep) // true — "**" matches "a.b.c" deep.covers(leaf) // true ``` -------------------------------- ### Check if a path starts with another path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/relational.mdx Use `startsWith` to determine if a path begins with every segment of another path. This is useful for checking if a more specific path falls under a broader category. ```typescript import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); namePath.startsWith(profilePath) // true — "profile.name" starts with "profile" profilePath.startsWith(namePath) // false ``` -------------------------------- ### Expanding template path matches Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/SKILL.md Get an array of concrete paths that match a given template path within an object. ```typescript templatePath.expand(data) to get concrete paths for each match ``` -------------------------------- ### Extend Template Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/path-algebra.mdx When `.to()` is used on a template path, it preserves the template nature, allowing for operations like `.each()` to be chained effectively. This example shows how to extract all names from a list of users. ```typescript import { path } from "data-path"; type AppData = { users: Array<{ name: string }> }; const data: AppData = { users: [{ name: "Alice" }, { name: "Bob" }] }; const allNames = path((u: AppData) => u.users).each().to((u) => u.name); allNames.$; // "users.*.name" allNames.get(data); // ["Alice", "Bob"] ``` -------------------------------- ### Usage of Reusable Field Component Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-hook-form.mdx Example of how to use the reusable TextField component, passing the typed path generated by data-path and the register function from React Hook Form. ```tsx f.profile.firstName)} register={register} /> ``` -------------------------------- ### Retrieve data with get Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/data-access.mdx Safely traverse data structures using `get`. It returns `undefined` if any intermediate segment is `null` or `undefined`, preventing runtime errors. The return type is `V | undefined`. ```typescript const user: User = { profile: { name: "Alice" }, tags: [] }; namePath.get(user) // "Alice" namePath.get(null) // undefined — no throw namePath.get({} as User) // undefined — "profile" is missing ``` -------------------------------- ### Path Creation Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Methods for creating Path and TemplatePath instances. ```APIDOC ## Path Creation | Expression | |---|---| | `path()` | Root path with zero segments | | `path((p: T) => p.a.b)` | Lambda form — annotate the parameter to infer both `T` and `V` | | `path((p) => p.a.b)` | Both generics explicit | | `path(base, (p) => p.c)` | Extend an existing base path | | `unsafePath("a.b")` | From a raw dot-notation string | | `unsafePath("a.b")` | From a raw string with explicit leaf type | ``` -------------------------------- ### Basic TanStack Table Column Definitions with Data Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/tanstack-table.mdx Demonstrates how to use data-path's `.fn` and `.$` properties to define accessor functions and IDs for TanStack Table columns, ensuring type safety and automatic synchronization for nested properties. ```typescript import { createColumnHelper } from "@tanstack/react-table"; import { path } from "data-path"; type User = { id: string; contact: { email: string }; name: string }; const columnHelper = createColumnHelper(); const emailPath = path((u: User) => u.contact.email); const namePath = path((u: User) => u.name); const columns = [ columnHelper.accessor(namePath.fn, { id: namePath.$, header: "Name", cell: (info) => info.getValue(), }), columnHelper.accessor(emailPath.fn, { id: emailPath.$, header: "Email", cell: (info) => info.getValue(), }), ]; ``` -------------------------------- ### Path Creation Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/references/api.md Methods for creating root paths, paths from lambdas with inferred or explicit types, extending existing paths, and creating paths from raw strings. ```APIDOC ## Path Creation ### `path()` Create root path (no segments). ### `path((p: T) => p.a.b)` Create path from lambda; type inferred from parameter annotation. ### `path(p => p.a.b)` Create path from lambda; generics explicit. ### `path(base, p => p.c)` Extend existing path; `p` is typed as the output type of `base`. ### `unsafePath("a.b")` Create path from raw dot-separated string. ``` -------------------------------- ### Navigating to parent path Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/SKILL.md Get the parent path of the current path. This operation can be chained, but always guard with `?.` as the root path has no parent. ```typescript path((u: User) => u.profile.name).parent() returns a Path whose segments are ["profile"] ``` ```typescript (use `.parent()?.$` if you want the string "profile"). Returns `null` at root — always guard with `?.` when chaining: `p.parent()?.parent()`. ``` -------------------------------- ### Runtime variables — dynamic indices and closures Source: https://context7.com/sergeyshmakov/data-path/llms.txt Explains how values in scope during path creation are captured, enabling dynamic path generation. ```APIDOC ## Runtime variables — dynamic indices and closures The lambda passed to `path()` is real JavaScript executed once at creation time. Any value in scope — loop counter, function parameter, computed key — is captured and baked into the path's segment list. ### Example: Factory function ```ts import { path } from "data-path"; type AppData = { users: Array<{ name: string; email: string }> }; // Factory function — each call produces a distinct path function userEmailPath(i: number) { return path((d: AppData) => d.users[i].email); } userEmailPath(0).$; // "users.0.email" userEmailPath(7).$; // "users.7.email" ``` ### Example: Loop closure ```ts // Loop closure — correct capture at each iteration const fieldPaths = [0, 1, 2].map((i) => path((d: AppData) => d.users[i].name) ); fieldPaths.map((p) => p.$); // ["users.0.name", "users.1.name", "users.2.name"] ``` ### Example: Computed property key ```ts // Computed property key type Config = { settings: Record }; const key = "theme"; const themePath = path((c: Config) => c.settings[key]); themePath.$; // "settings.theme" ``` ``` -------------------------------- ### Get Parent Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/path-algebra.mdx Returns a new path with the last segment removed. Returns `null` if the path is already at the root (zero segments). ```typescript import { path } from "data-path"; type User = { profile: { name: string } }; const namePath = path((u: User) => u.profile.name); namePath.parent()?.$; // "profile" namePath.parent()?.parent()?.$; // "" (root path) namePath.parent()?.parent()?.parent(); // null — already at root ``` -------------------------------- ### Zustand Theme Update Comparison Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/zustand.mdx Compares the manual spreading method for updating nested state in Zustand with the `data-path` approach. Shows how `path.set()` simplifies the update logic. ```typescript // Before — manual spreading for each level setTheme: (theme) => set((state) => ({ ...state, user: { ...state.user, profile: { ...state.user.profile, theme }, }, })), // After — path handles the clone setTheme: (theme) => set((state) => themePath.set(state, theme)), ``` -------------------------------- ### Zustand Integration: State Management with Paths Source: https://context7.com/sergeyshmakov/data-path/llms.txt Shows how to use 'data-path' with Zustand for efficient and type-safe state management. Paths are defined at the module level to avoid lifecycle issues. The .set() method performs structural cloning, and .update() is used for read-modify-write operations. ```ts import { create } from "zustand"; import { path } from "data-path"; type StoreState = { user: { profile: { firstName: string; theme: "light" | "dark" } }; setFirstName: (name: string) => void; toggleTheme: () => void; }; // Define paths once at module level — no lifecycle concerns const profilePath = path((s: StoreState) => s.user.profile); const firstNamePath = profilePath.to((p) => p.firstName); const themePath = profilePath.to((p) => p.theme); const useStore = create((set) => ({ user: { profile: { firstName: "Alice", theme: "light" } }, // path.set() handles structural clone — no manual spreading setFirstName: (name) => set((state) => firstNamePath.set(state, name)), // path.update() for read-modify-write toggleTheme: () => set((state) => themePath.update(state, (t) => (t === "light" ? "dark" : "light")), ) })); ``` -------------------------------- ### Slice Path Segments Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/path-algebra.mdx Extracts a subsection of a path based on start and end indices, similar to `Array.prototype.slice`. Useful for dynamically built paths where a sub-segment is needed. ```typescript import { path } from "data-path"; type Company = { departments: Array<{ employees: Array<{ profile: { name: string } }>; }>; }; const p = path((c: Company) => c.departments[0].employees[5].profile.name); p.$; // "departments.0.employees.5.profile.name" p.slice(0, 3).$; // "departments.0.employees" p.slice(-2).$; // "profile.name" ``` -------------------------------- ### Path Properties Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Properties available on Path and TemplatePath instances. ```APIDOC ## Path Properties | Property | Type | Description | |---|---|---| | `path.$` | `string` | Dot-notation string (`"profile.firstName"`) | | `path.segments` | `readonly (string | number)[]` | Array of segments | | `path.length` | `number` | Number of segments | | `path.fn` | `(data: T) => V | undefined` | Stable accessor — same reference on every access | On template paths, `fn` returns `V[]` instead of `V | undefined`. ``` -------------------------------- ### Comparing data-path to Manual Spreading Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-usestate.mdx Illustrates the difference between updating nested state manually with object spreading and using data-path's concise `.set()` method. The behavior is identical. ```ts // Before — spreading each level setState((prev) => ({ ...prev, user: { ...prev.user, profile: { ...prev.user.profile, theme: "dark" }, }, })); // After — structural clone handled automatically setState((prev) => themePath.set(prev, "dark")); ``` -------------------------------- ### Run Code Linting and Formatting Source: https://github.com/sergeyshmakov/data-path/blob/main/CONTRIBUTING.md Execute the linting and fixing command to ensure code style consistency. This command is also run automatically by the pre-commit hook. ```bash npm run lint:fix ``` -------------------------------- ### Traversal and Composition Methods Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Methods for combining and manipulating paths. ```APIDOC ## Traversal and Composition | Method | Signature | Description | |---|---|---| | `.to(lambda | path)` | `Path | TemplatePath` | Extend the path. Result is a `TemplatePath` when `relative` carries wildcard sentinels (built via `.each()`/`.deep()`); otherwise `Path` | | `.parent()` | `Path | null` | Remove the last segment; `null` at root | | `.merge(other)` | `Path | TemplatePath` | Overlap-aware concatenation. Result is a `TemplatePath` when `other` carries wildcard sentinels | | `.subtract(prefix)` | `Path | null` | Remove a prefix; `null` if prefix doesn't match | | `.slice(start?, end?)` | `Path` | Slice segments like `Array.prototype.slice` | ``` -------------------------------- ### Single-Level Wildcard Matching Source: https://context7.com/sergeyshmakov/data-path/llms.txt Use `.each()` to append a `*` wildcard, matching all keys or indices at the current collection level. It's useful for targeting all items in an array or object properties for operations like getting, setting, or updating. ```typescript import { path } from "data-path"; type AppData = { users: Array<{ name: string; active: boolean }> }; const data: AppData = { users: [{ name: "Alice", active: true }, { name: "Bob", active: false }], }; // Without expression — targets the items themselves const allUsers = path((d: AppData) => d.users).each(); allUsers.$; // "users.*" allUsers.get(data); // [{ name: "Alice", active: true }, { name: "Bob", active: false }] // With expression — navigates from each matched item const allNames = path((d: AppData) => d.users).each((u) => u.name); allNames.$; // "users.*.name" allNames.get(data); // ["Alice", "Bob"] // Bulk write — same value applied to every match allNames.set(data, "Hidden").users.map((u) => u.name); // ["Hidden", "Hidden"] // Per-item transform allNames.update(data, (n) => (n ?? "").toUpperCase()).users.map((u) => u.name); // ["ALICE", "BOB"] ``` -------------------------------- ### Bulk Read with .get() Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/templates.mdx On a template path, `.get()` returns an array of all matched values from the provided data. ```typescript type AppData = { users: Array<{ name: string; active: boolean }> }; const data: AppData = { users: [{ name: "Alice", active: true }, { name: "Bob", active: false }], }; const allNamesPath = path((d: AppData) => d.users).each((u) => u.name); allNamesPath.get(data) // ["Alice", "Bob"] ``` -------------------------------- ### Define a concrete path with Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/types.mdx Use Path for concrete paths without wildcards. T is the root type and V is the leaf type. The '$' property provides the dot-notation name, and 'get(data)' retrieves the value. ```typescript import type { Path } from "data-path"; function register(fieldPath: Path, value: string) { // fieldPath.$ is the dot-notation name // fieldPath.get(data) returns string | undefined } ``` -------------------------------- ### Check Prefix with `.startsWith()` Source: https://context7.com/sergeyshmakov/data-path/llms.txt Returns true if this path's segments begin with all segments of another path. Supports wildcard segments. ```typescript import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); namePath.startsWith(profilePath); // true — "profile.name" begins with "profile" profilePath.startsWith(namePath); // false // Works with wildcards and inline lambdas type R = { items: Array<{ name: string }> }; const allNames = path((r: R) => r.items).each((i: { name: string }) => i.name); const one = path((r: R) => r.items[0].name); one.startsWith(allNames); // true — concrete falls under the template prefix ``` -------------------------------- ### Literal vs. Wildcard Keys Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/templates.mdx Demonstrates the difference between literal string keys and sentinel wildcard symbols when accessing data. Use `path()` for wildcard expansion and `unsafePath()` for literal key access. ```typescript import { path, unsafePath } from "data-path"; // Literal "*" key — no wildcard semantics: unsafePath<{ "*": number }>("*").get({ "*": 1, "**": 2 }); // 1 // Wildcard sentinel — expands to every key: path>().each().get({ "*": 1, "**": 2 }); // [1, 2] ``` -------------------------------- ### Minimal tsconfig.json for data-path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/getting-started/installation.mdx A basic TypeScript configuration file that is compatible with data-path. Ensure 'strict: true' is enabled for full type inference. ```json { "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "Bundler", "strict": true } } ``` -------------------------------- ### TanStack Table Integration: Column Definitions Source: https://context7.com/sergeyshmakov/data-path/llms.txt Demonstrates integrating 'data-path' with TanStack Table for defining table columns. The .fn property of a path matches the accessor signature required by TanStack Table, and the .$ property provides a stable accessor ID. ```ts import { createColumnHelper } from "@tanstack/react-table"; import type { ColumnHelper } from "@tanstack/react-table"; import type { Path } from "data-path"; import { path } from "data-path"; type User = { id: string; name: string; contact: { email: string } }; const columnHelper = createColumnHelper(); const namePath = path((u: User) => u.name); const emailPath = path((u: User) => u.contact.email); // .fn is (u: User) => V | undefined — matches TanStack's accessor signature // .$ keeps accessor id in sync with the lambda automatically const columns = [ columnHelper.accessor(namePath.fn, { id: namePath.$, header: "Name" }), columnHelper.accessor(emailPath.fn, { id: emailPath.$, header: "Email" }), ]; ``` -------------------------------- ### Create a typed path with path() Source: https://context7.com/sergeyshmakov/data-path/llms.txt Builds a Path from a lambda expression, an existing base path, or as a root. The lambda is executed once against a Proxy that records every property access; the resulting segment list is frozen into the path object. ```typescript import { path } from "data-path"; type User = { profile: { firstName: string; lastName: string }; tags: string[]; }; // Annotation form — recommended: TypeScript infers both T (User) and V (string) const firstNamePath = path((u: User) => u.profile.firstName); // firstNamePath.$ → "profile.firstName" // firstNamePath.segments → ["profile", "firstName"] // firstNamePath.length → 2 // Both generics explicit const tagsPath = path((u) => u.tags); // Root path — zero segments; extend later via .to() or path(base, expr) const root = path(); const nameViaRoot = root.to((u) => u.profile.firstName); nameViaRoot.$; // "profile.firstName" // Extend an existing base path — second lambda receives the base's resolved type const profileBase = path((u: User) => u.profile); const fullName = path(profileBase, (p) => p.firstName); fullName.$; // "profile.firstName" ``` -------------------------------- ### Relational Operations Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/references/api.md Methods for comparing paths, checking for prefixes, coverage, equality, and matching relations. ```APIDOC ## Relational ### `path.startsWith(other)` `true` if `other` is a prefix of this path. ### `path.covers(other)` `true` if this path is a prefix of `other` (this location covers `other` in the data tree). ### `path.equals(other)` `true` if paths are segment-by-segment identical. ### `path.match(other)` Returns `MatchResult` or `null` when unrelated. All relational methods accept a `ResolvablePath`. ``` -------------------------------- ### Bulk Write with .set() Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/templates.mdx Applies the same value to every matched path in the data. Returns a new data structure with the updates, leaving the original unchanged. ```typescript type AppData = { users: Array<{ name: string; active: boolean }> }; const data: AppData = { users: [{ name: "Alice", active: true }, { name: "Bob", active: false }], }; const allNamesPath = path((d: AppData) => d.users).each((u) => u.name); const anonymized = allNamesPath.set(data, "Hidden"); anonymized.users[0].name // "Hidden" anonymized.users[1].name // "Hidden" data.users[0].name // "Alice" — original unchanged ``` -------------------------------- ### .covers() — containment check Source: https://context7.com/sergeyshmakov/data-path/llms.txt Checks if the current path is a prefix of another path, indicating containment within the data tree. This is the inverse of `startsWith`. ```APIDOC ## .covers() — containment check Returns `true` if this path is a prefix of `other` — i.e. this location in the data tree covers the location `other` points to. The inverse direction of `startsWith`. ### Method ```ts profilePath.covers(namePath); ``` ### Parameters - **other** (Path) - The path to compare against. ### Returns - **boolean** - `true` if the current path covers the `other` path, `false` otherwise. ### Example ```ts import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); profilePath.covers(namePath); // true — "profile" covers "profile.name" namePath.covers(profilePath); // false ``` ``` -------------------------------- ### Runtime variable vs. wildcard Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/runtime-variables.mdx Illustrates when to use runtime variables for known indices versus wildcards (e.g., .each()) for unknown-count bulk operations. ```typescript // Known index — runtime variable const thirdItemPath = path((d: AppData) => d.users[2].name); // All items — template wildcard const allNamesPath = path((d: AppData) => d.users).each((u) => u.name); ``` -------------------------------- ### Relational Methods Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Methods for comparing and relating paths. ```APIDOC ## Relational | Method | Signature | Description | |---|---|---| | `.startsWith(other)` | `boolean` | `true` if this path begins with every segment of `other` | | `.covers(other)` | `boolean` | `true` if this path is a prefix of `other` (this location covers `other` in the data tree) | | `.equals(other)` | `boolean` | `true` if both paths have identical segments | | `.match(other)` | `MatchResult | null` | Structural relationship, or `null` if none | ``` -------------------------------- ### Use Lambdas with ResolvablePath Methods Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/types.mdx Methods like `.startsWith()` and `.subtract()` accept a `ResolvablePath`, which can be an inline lambda function. This allows for dynamic path resolution based on object properties. ```typescript namePath.startsWith((u: User) => u.profile); namePath.subtract((u: User) => u.profile); ``` -------------------------------- ### Watching and Setting Field Values Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-hook-form.mdx Demonstrates how to watch a specific field using its typed path and how to programmatically set a field's value using the setValue function from React Hook Form. ```tsx const { watch, setValue } = useForm(); // watch a specific field const firstName = watch(firstNamePath.$); // programmatic write setValue(firstNamePath.$, "Alice"); ``` -------------------------------- ### Before vs After String Literals in TypeScript Source: https://github.com/sergeyshmakov/data-path/blob/main/README.md Illustrates the difference between using plain string literals for property paths and the type-safe approach provided by the data-path library. The latter offers compile-time checking and refactoring safety. ```typescript // Before — string literals, invisible to the compiler register("users.0.profile.firstName"); table.getColumn("contact.email"); set(state => ({ ...state, settings: { ...state.settings, theme } })); ``` ```typescript // After — typed, IDE-autocompleted, refactor-safe register(path((u: FormData) => u.users[0].profile.firstName).$); table.getColumn(emailPath.$); set(state => themePath.set(state, theme)); ``` -------------------------------- ### Sorting and Filtering with Data Path in TanStack Table Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/tanstack-table.mdx Shows how to leverage data-path's string representation for configuring column sorting and filtering in TanStack Table, and how to use path methods for custom row filtering logic. ```typescript const columns = [ columnHelper.accessor(emailPath.fn, { id: emailPath.$, header: "Email", enableSorting: true, sortingFn: "alphanumeric", }), ]; // In global filter, compare against path string const rowMatchesFilter = (row: User, filter: string) => emailPath.get(row)?.includes(filter) ?? false; ``` -------------------------------- ### Defining a basic path Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/SKILL.md Create a type-safe path using a lambda expression that infers the type from the provided object structure. ```typescript path((u: User) => u.profile.field) ``` -------------------------------- ### Handling Array Items with data-path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-usestate.mdx Shows how to dynamically create paths for array items using an index, enabling updates to specific elements within a list managed by useState. ```tsx type ListState = { items: Array<{ label: string; done: boolean }> }; const labelPath = (i: number) => path((s: ListState) => s.items[i].label); const donePath = (i: number) => path((s: ListState) => s.items[i].done); function TodoList() { const [state, setState] = useState({ items: [] }); const toggle = (i: number) => setState((prev) => donePath(i).update(prev, (v) => !v) ); return (
    {state.items.map((item, i) => (
  • toggle(i)}> {item.label}
  • ))}
); } ``` -------------------------------- ### Basic useState Usage with data-path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-usestate.mdx Demonstrates how to use data-path to set and update nested state within a React component using useState. Imports are required. ```tsx import { useState } from "react"; import { path } from "data-path"; type AppState = { user: { profile: { firstName: string; theme: "light" | "dark" }; }; }; const firstNamePath = path((s: AppState) => s.user.profile.firstName); const themePath = path((s: AppState) => s.user.profile.theme); function ProfileSettings() { const [state, setState] = useState({ user: { profile: { firstName: "Alice", theme: "light" } }, }); const setFirstName = (name: string) => setState((prev) => firstNamePath.set(prev, name)); const toggleTheme = () => setState((prev) => themePath.update(prev, (t) => (t === "light" ? "dark" : "light")) ); return (
setFirstName(e.target.value)} />
); } ``` -------------------------------- ### Resolve to Concrete Paths with .expand() Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/guides/templates.mdx Walks the data and returns one `Path` per matched leaf. `expand()` only visits keys that exist in the data; missing intermediates are skipped. ```typescript type AppData = { users: Array<{ name: string; active: boolean }> }; const data: AppData = { users: [{ name: "Alice", active: true }, { name: "Bob", active: false }], }; const allNamesPath = path((d: AppData) => d.users).each((u) => u.name); const paths = allNamesPath.expand(data); paths[0].$ // "users.0.name" paths[1].$ // "users.1.name" ``` -------------------------------- ### Template Path Methods Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Specific methods for working with TemplatePaths, including wildcard operations. ```APIDOC ## Template Paths | Method | Signature | Description | |---|---|---| | `.each()` | `TemplatePath>` | Wildcard over every key of the current collection | | `.each(expr)` | `TemplatePath` | Wildcard then navigate each item | | `.deep()` | `TemplatePath` | Recursive descent wildcard | | `.deep(expr)` | `TemplatePath` | Recursive descent then navigate | | `.expand(data)` | `Path[]` | Resolve template to concrete paths that exist in `data` | `each` and `deep` are only available when `V` is not a primitive type. Template paths also support `.to()` and `.merge()` — both return `TemplatePath` because `this` already carries wildcards. On a `TemplatePath`, the structural methods are widened to keep wildcards when present: | Method on `TemplatePath` | Signature | |---|---| | `.parent()` | `Path | TemplatePath | null` | | `.slice(start?, end?)` | `Path | TemplatePath` | | `.subtract(prefix)` | `Path | TemplatePath | null` | If the resulting segments still contain `*` / `**`, you get a `TemplatePath` (so `.get()` continues to expand matches); otherwise a concrete `Path`. Narrow at the call site if you need to disambiguate. ``` -------------------------------- ### .match() — structural relationship Source: https://context7.com/sergeyshmakov/data-path/llms.txt Determines the structural relationship between two paths, returning a description or null if unrelated. ```APIDOC ## .match() — structural relationship Returns a `MatchResult` describing the structural relationship between two paths, or `null` if they are unrelated (no shared prefix, no wildcard relationship). ### Method ```ts namePath.match(profilePath); ``` ### Parameters - **other** (Path) - The path to compare against. ### Returns - **MatchResult | null** - An object describing the relationship (e.g., `{ relation: "child" }`, `{ relation: "parent" }`, `{ relation: "equals" }`, `{ relation: "covers" }`, `{ relation: "covered-by" }`) or `null` if unrelated. ### Example ```ts import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); const agePath = path((u: User) => u.profile.age); namePath.match(profilePath); // { relation: "child" } — namePath is deeper profilePath.match(namePath); // { relation: "parent" } — profilePath is shallower namePath.match(namePath); // { relation: "equals" } namePath.match(agePath); // null — sibling, no relationship ``` ``` -------------------------------- ### Check Path Containment with .covers() Source: https://context7.com/sergeyshmakov/data-path/llms.txt Use `.covers()` to determine if one path is a prefix of another, indicating containment within the data tree. This is the inverse of `startsWith`. ```typescript import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); profilePath.covers(namePath); // true — "profile" covers "profile.name" namePath.covers(profilePath); // false // Permission guard pattern type Store = { users: unknown[]; settings: { theme: string } }; const settingsPath = path((s: Store) => s.settings); function canWrite(target: typeof namePath) { return !target.startsWith(settingsPath); // block anything inside settings } canWrite(path((s: Store) => s.settings.theme) as any); // false ``` -------------------------------- ### Extending a base path with .to() Source: https://github.com/sergeyshmakov/data-path/blob/main/skills/data-path/SKILL.md Create a new path by extending an existing base path with additional property accessors. This composes paths. ```typescript const employeePath = path((c: Company) => c.departments[0].employees[5]) ``` ```typescript employeePath.to(e => e.profile.firstName) ``` -------------------------------- ### String Literals vs. Typed Paths in TypeScript Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/index.mdx Demonstrates the contrast between using unsafe string literals for property access and the type-safe, autocompleted approach provided by the data-path library. Use typed paths to ensure compile-time safety and refactorability. ```typescript // Before — string literals, invisible to the compiler register("users.0.profile.firstName"); table.getColumn("contact.email"); ``` ```typescript // After — typed, autocompleted, refactor-safe register(path((u: FormData) => u.users[0].profile.firstName).$); table.getColumn(emailPath.$); ``` -------------------------------- ### Defining Paths at Module Level Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-usestate.mdx Demonstrates defining paths outside of React components at the module level. These paths are stable and can be reused across different parts of the application. ```ts // Module level — created once, stable across renders const themePath = path((s: AppState) => s.user.profile.theme); function ThemeToggle() { const [state, setState] = useState(defaultState); const toggle = () => setState((prev) => themePath.update(prev, (t) => (t === "light" ? "dark" : "light")) ); // ... } ``` -------------------------------- ### Using data-path with useReducer Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/react-usestate.mdx Shows how to integrate data-path with React's useReducer hook by passing paths and values through actions for state updates. ```ts type Action = | { type: "SET_THEME"; theme: "light" | "dark" } | { type: "SET_NAME"; name: string }; function reducer(state: AppState, action: Action): AppState { switch (action.type) { case "SET_THEME": return themePath.set(state, action.theme); case "SET_NAME": return firstNamePath.set(state, action.name); default: return state; } } ``` -------------------------------- ### Create a Path Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/getting-started/quick-start.mdx Define a path by passing a lambda function that navigates the object structure. TypeScript infers types from the parameter annotation. ```typescript import { path } from "data-path"; type User = { profile: { firstName: string; lastName: string }; tags: string[]; }; const firstNamePath = path((u: User) => u.profile.firstName); ``` -------------------------------- ### .equals() — exact segment comparison Source: https://context7.com/sergeyshmakov/data-path/llms.txt Compares two paths for exact segment-by-segment equality, including wildcard sentinels. ```APIDOC ## .equals() — exact segment comparison Returns `true` if both paths have identical segments (segment-by-segment equality, including wildcard sentinels). ### Method ```ts p1.equals(p2); ``` ### Parameters - **other** (Path) - The path to compare against. ### Returns - **boolean** - `true` if the paths are identical, `false` otherwise. ### Example ```ts import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const p1 = path((u: User) => u.profile.name); const p2 = path((u: User) => u.profile.name); const p3 = path((u: User) => u.profile.age); p1.equals(p2); // true p1.equals(p3); // false ``` ``` -------------------------------- ### ResolvablePath Type Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/reference/api-cheatsheet.mdx Explanation of the ResolvablePath type and its accepted forms. ```APIDOC ## ResolvablePath All relational and algebra methods accept a `ResolvablePath` argument, which means any of: - A `Path` or `TemplatePath` instance - A lambda `(proxy: T) => unknown` (evaluated once against a proxy) - An object with a `segments` property ``` -------------------------------- ### Determine Structural Relationships with .match() Source: https://context7.com/sergeyshmakov/data-path/llms.txt Utilize `.match()` to understand the structural relationship between two paths, returning details like 'child', 'parent', or 'equals', or `null` if unrelated. It also handles wildcard relationships. ```typescript import { path } from "data-path"; type User = { profile: { name: string; age: number } }; const namePath = path((u: User) => u.profile.name); const profilePath = path((u: User) => u.profile); const agePath = path((u: User) => u.profile.age); namePath.match(profilePath); // { relation: "child" } — namePath is deeper profilePath.match(namePath); // { relation: "parent" } — profilePath is shallower namePath.match(namePath); // { relation: "equals" } namePath.match(agePath); // null — sibling, no relationship // With wildcards type R = { items: Array<{ name: string }> }; const allNames = path((r: R) => r.items).each((i: { name: string }) => i.name); const oneItem = path((r: R) => r.items[0].name); allNames.match(oneItem); // { relation: "covers" } — template covers concrete oneItem.match(allNames); // { relation: "covered-by" } ``` -------------------------------- ### Shared Path Definitions for TanStack Form Source: https://github.com/sergeyshmakov/data-path/blob/main/docs/src/content/docs/integrations/tanstack-form.mdx Illustrates defining paths once with data-path and reusing them for both TanStack Form field registration and programmatic value access. ```typescript const emailPath = path((f: FormValues) => f.email); // Used for field registration // Used for programmatic access const current = emailPath.get(form.state.values); ```