2026 05 13 package api and repo polish#3
Conversation
…ectness - Merge TemplatePathImpl into path-impl.ts — eliminates circular import; PathImpl.each/deep now construct TemplatePathImpl directly - Add TemplatePathImpl.to() and .merge() overrides that return TemplatePathImpl at runtime, preserving wildcard expansion through chained calls - Extract mergeSegments() helper shared by both PathImpl and TemplatePathImpl - Retype TemplatePath<T,V>: omit and re-declare to/merge with TemplatePath<T,U> return types so callers get the correct type without manual casts - Fix ResolvedType<P> to match TemplatePath (checks get()→V[] first) - Add as-unknown-as-Path<T,V> casts on all PathImpl navigation method returns (parent, merge, subtract, slice, to) so the conditional intersection type is visible to callers - Fix Path conditional intersection: each/deep hidden when V extends Primitive - Add update() to BasePath; TemplatePath.update() applies per-item transform - Fix unsafePath to accept two generics unsafePath<T, V> - Add exports field to package.json; set sideEffects:false; enable sourcemaps - Expand test suite to 156 tests (114 runtime + 42 type-level); all passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
README: - Rewrite to ~110 lines: before/after hero, 5 shields.io badges, MIT license - Add "Works well with" table linking to per-package integration docs - Remove emoji, marketing framing, and inline API reference (moved to docs) - Fix all path<T>(lambda) examples to use annotation form Docs site (docs/): - Astro 6 + Starlight 0.39, GitHub Pages deploy at sergeyshmakov.github.io/data-path - Getting started: installation, quick-start - Guides: data-access, templates, path-algebra, relational, runtime-variables - Integrations: React Hook Form, TanStack Form, TanStack Table, Zustand, Zod, React useState - Reference: api-cheatsheet, types - Blue accent palette, github-dark/light code themes, edit links package.json: - description: concise single-sentence tagline - license: ISC → MIT; keywords expanded to 13 LICENSE: ISC → MIT CONTRIBUTING.md: rewrite — no emoji, no first-person, ~50 lines GitHub community files: - .github/ISSUE_TEMPLATE/bug.yml, feature.yml, config.yml - .github/pull_request_template.md - .github/CODE_OF_CONDUCT.md (Contributor Covenant 2.1 by reference) - .github/workflows/deploy-docs.yml (withastro/action, deploy-pages) - dependabot.yml: add /docs npm ecosystem + github-actions ecosystem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
package.json: - "type": "module" — ESM-first; tsup now emits index.js (ESM) + index.cjs (CJS) - exports: import → dist/index.js, require → dist/index.cjs (drop legacy main/module) - homepage: updated to GitHub Pages docs URL - release.plugins: add @semantic-release/changelog + @semantic-release/git for automated CHANGELOG.md generation and release commits commitlint.config.js → commitlint.config.cjs (required by "type":"module") README: sync tagline with package.json description Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c28d0c4b0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
13 issues found across 52 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/src/content/docs/integrations/zod.mdx">
<violation number="1" location="docs/src/content/docs/integrations/zod.mdx:31">
P2: Don't round-trip Zod path segments through `join('.')` here. `unsafePath()` splits on dots, so field names containing `.` will resolve to the wrong path, and the same lossiness affects the map example below.</violation>
</file>
<file name="docs/src/content/docs/guides/relational.mdx">
<violation number="1" location="docs/src/content/docs/guides/relational.mdx:31">
P2: `includes()` is reversed in this example: `profilePath.includes(namePath)` should be false, and `namePath.includes(profilePath)` should be true.</violation>
</file>
<file name="docs/src/content/docs/getting-started/quick-start.mdx">
<violation number="1" location="docs/src/content/docs/getting-started/quick-start.mdx:65">
P2: Fix the result-type comment for `.map(firstNamePath.fn)`; it should include `undefined`.</violation>
</file>
<file name=".github/workflows/deploy-docs.yml">
<violation number="1" location=".github/workflows/deploy-docs.yml:23">
P2: Fetch full Git history for the docs build so Starlight's `lastUpdated` timestamps stay accurate.</violation>
</file>
<file name="docs/src/content/docs/guides/data-access.mdx">
<violation number="1" location="docs/src/content/docs/guides/data-access.mdx:69">
P2: Clarify that the `set(get(...))` equivalence applies only to plain paths; template paths update each matched item separately.</violation>
</file>
<file name="docs/src/content/docs/integrations/tanstack-table.mdx">
<violation number="1" location="docs/src/content/docs/integrations/tanstack-table.mdx:80">
P2: This generic type query is invalid TypeScript, so the example won't type-check. Use `Path<T, V>` here instead.</violation>
</file>
<file name="docs/src/content/docs/integrations/zustand.mdx">
<violation number="1" location="docs/src/content/docs/integrations/zustand.mdx:33">
P3: This overstates Zustand's default equality behavior. `useStore` uses strict `Object.is` by default; shallow comparison is opt-in.</violation>
</file>
<file name=".github/dependabot.yml">
<violation number="1" location=".github/dependabot.yml:28">
P2: Dependabot ignores `day` on monthly schedules, so these new entries won't run on Mondays; they'll run on the first day of the month instead.</violation>
</file>
<file name="docs/src/content/docs/reference/types.mdx">
<violation number="1" location="docs/src/content/docs/reference/types.mdx:19">
P3: Include `null` and `undefined` in the primitive list; optional/nullable leaf paths are also non-traversable.</violation>
<violation number="2" location="docs/src/content/docs/reference/types.mdx:109">
P2: Document that `.match()` can return `null` when the paths are unrelated.</violation>
</file>
<file name="skills/data-path/SKILL.md">
<violation number="1" location="skills/data-path/SKILL.md:82">
P3: `parent()` returns a Path object (or null), not the string itself; add `.$` here if you mean to show the string form.</violation>
</file>
<file name="docs/src/content/docs/integrations/react-hook-form.mdx">
<violation number="1" location="docs/src/content/docs/integrations/react-hook-form.mdx:96">
P2: Constrain `T` to `FieldValues` and import the RHF types; `UseFormRegister<T>` requires a `FieldValues`-bounded generic.</violation>
</file>
<file name="docs/src/content/docs/guides/path-algebra.mdx">
<violation number="1" location="docs/src/content/docs/guides/path-algebra.mdx:29">
P2: Make each code fence self-contained; several examples reuse identifiers from other fences or omit required declarations, so the snippets don't compile when copied individually.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4baecf5c66
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
BREAKING CHANGES (no published users yet):
- Rename BasePath.includes(other) -> BasePath.covers(other)
- Rename MatchRelation "includes"/"included-by" -> "covers"/"covered-by"
(the old name collided with Array.prototype.includes semantics and
consistently caused reviewers to misread "inverse of startsWith")
Runtime fixes:
- TemplatePathImpl.parent/slice/subtract now return a TemplatePathImpl
when the resulting segments still contain * / **; previously returned
a concrete PathImpl whose .get() treated wildcards as literal keys.
- PathImpl.to/merge now also branch on wildcards in the appended/merged
segments (template argument via the {segments} arm of ResolvablePath).
- BasePath.to/merge widened to Path<T,U> | TemplatePath<T,U>;
TemplatePath subtract/parent/slice widened to Path | TemplatePath [| null].
Docs and skill:
- zod.mdx: stop recommending unsafePath(issue.path.join("."))
(lossy on field names containing "."); use {segments: issue.path}.
- data-access.mdx: clarify update() set(get()) equivalence is plain-Path
only; TemplatePath.update is per-item.
- react-hook-form.mdx: constrain T extends FieldValues, import FieldPath,
cast register(fieldPath.$ as FieldPath<T>).
- tanstack-table.mdx: replace invalid ReturnType<typeof path<T,V>> with
Path<T,V>; specialize helper to the columnHelper row type.
- types.mdx: .match() returns MatchResult | null (added null guard);
Primitive list now includes null/undefined.
- zustand.mdx: replace incorrect "shallow equality" claim with Object.is
semantics + structural-clone reference identity.
- path-algebra.mdx: every code fence now self-contained.
- relational.mdx: added "Mental model" section, renamed includes->covers.
- api-cheatsheet.mdx + skills/*: sync signatures with widened returns.
CI/config:
- deploy-docs.yml: fetch-depth: 0 so Starlight lastUpdated is accurate
(default shallow clone made every page share one timestamp).
- dependabot.yml: drop ignored day: "monday" from monthly schedules.
Tests: 192 pass (132 runtime + 60 typed); typecheck clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the ad-hoc `biome check --write --staged` line with `npx lint-staged`. lint-staged re-stages files modified by biome, so formatter auto-fixes land in the same commit instead of requiring a follow-up "chore: lint staged" commit. - Add lint-staged dev dep - Add `lint-staged` config to package.json mapping JS/TS/JSON/MD/etc. to `biome check --write --no-errors-on-unmatched` - Reorder .husky/pre-commit: lint-staged first (so fixes are staged), then `npm test` Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 25 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="package.json">
<violation number="1" location="package.json:96">
P1: This adds a lint-staged version that requires Node 22.22.1, but the repo still advertises Node >=20.0.0. Contributors on the documented minimum will hit the pre-commit hook/runtime failure.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d955bb77d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="package.json">
<violation number="1" location="package.json:106">
P2: This bumps the published Node engine requirement to 22 without updating the documented support floor, so Node 20 users will now see install warnings/errors even though the repo still claims Node 20 support.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
The engines.node field was unintentionally bumped to >=22, which would have dropped Node 20 users. Reverted to >=20 and aligned lint-staged to ^16.4.0 (latest version still compatible with Node >=20.17). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34d2959ed1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
hasWildcard scanned segment strings, so composing two concrete paths
flipped to TemplatePathImpl whenever a legitimate "*"/"**" property
key appeared (e.g. r.a.to(x => x["*"]), unsafePath("*")). Detect
template-ness from the argument's identity instead: instanceof
TemplatePathImpl. The makeFromSegments helper is still used by
TemplatePathImpl.parent/slice/subtract where the segments are known
to come from .each()/.deep().
Flipped the {segments-with-wildcard} test to assert the new literal
interpretation and added three regression tests (lambda, unsafePath,
merge). Codex-bot P2 follow-up to the 00:10 fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: WILDCARD and DEEP_WILDCARD are now unique Symbol
sentinels. The `Segment` type widens to include them. Legitimate object
keys named "*" or "**" are now preserved as literal string segments and
never reinterpreted as wildcards by any method.
Resolves a class of bugs where the same path object behaved differently
depending on which method was called:
- `unsafePath("a.*.b").get(data)` reads the literal "*" key
`unsafePath("a.*.b").covers(concrete)` treated "*" as a wildcard
Same path, two semantics — confirmed by probe pre-refactor.
- `each().to(unsafePath("*"))` reinterpreted the appended literal "*"
as a wildcard once it landed inside a TemplatePathImpl.
- `concretePath.equals(eachTemplate)` returned true when segments
matched by string — but the two paths have different `.get()`
behaviour.
After the refactor:
- `unsafePath("a.*.b")` is fully concrete; "*" is a literal key for all
methods including `.startsWith`, `.covers`, `.match`.
- `.equals` correctly distinguishes literal "*" from wildcard sentinel.
- The `instanceof TemplatePathImpl` check in PathImpl.to/merge is no
longer needed — segment content is again sufficient.
- `.toString()` / `.$` still render sentinels as "*"/"**" so dot-notation
output is unchanged.
Also fixed: `matchesPrefix` now treats `**` as zero-or-more (was
rejecting `a.**.b` against `a.b` due to a length-guard bug).
WILDCARD and DEEP_WILDCARD are now exported for users building
templates from raw segment arrays.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WILDCARD and DEEP_WILDCARD are now unique Symbol sentinels. The
`Segment` type widens to include them. Legitimate object keys named
"*" or "**" are now preserved as literal string segments and never
reinterpreted as wildcards by any method.
Resolves a class of bugs where the same path object behaved differently
depending on which method was called:
- `unsafePath("a.*.b").get(data)` reads the literal "*" key
`unsafePath("a.*.b").covers(concrete)` treated "*" as a wildcard
Same path, two semantics — confirmed by probe pre-refactor.
- `each().to(unsafePath("*"))` reinterpreted the appended literal "*"
as a wildcard once it landed inside a TemplatePathImpl.
- `concretePath.equals(eachTemplate)` returned true when segments
matched by string — but the two paths have different `.get()`
behaviour.
After the refactor:
- `unsafePath("a.*.b")` is fully concrete; "*" is a literal key for all
methods including `.startsWith`, `.covers`, `.match`.
- `.equals` correctly distinguishes literal "*" from wildcard sentinel.
- The `instanceof TemplatePathImpl` check in PathImpl.to/merge is no
longer needed — segment content is again sufficient.
- `.toString()` / `.$` still render sentinels as "*"/"**" so dot-notation
output is unchanged.
Also fixed: `matchesPrefix` now treats `**` as zero-or-more (was
rejecting `a.**.b` against `a.b` due to a length-guard bug).
WILDCARD and DEEP_WILDCARD are now exported for users building
templates from raw segment arrays.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db450e141e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ub.com/sergeyshmakov/data-path into 2026-05-13-package-api-amd-repo-polish
There was a problem hiding this comment.
2 issues found across 14 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/src/content/docs/reference/types.mdx">
<violation number="1" location="docs/src/content/docs/reference/types.mdx:110">
P2: Import `unsafePath` in this example; it is used later in the snippet but never imported.</violation>
<violation number="2" location="docs/src/content/docs/reference/types.mdx:116">
P2: Fix the generic type in this example so the declared root type matches the nested data shape.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
…ncrete When `this` contained DEEP_WILDCARD, matchesPrefix could succeed against a longer `other` by collapsing `**`, and the old code interpreted that as "this is a literal prefix of other" → "parent". It also returned `null` for `a.**.b vs a.b` because patternMatches required equal lengths. Two fixes: - patternMatches now supports `**` collapsing zero-or-more segments (was equal-length-only). This is the relation the docs describe as "covers". - match() checks wildcard coverage BEFORE literal parent/child, and gates the parent/child branches on the shorter side being wildcard-free. A wildcard prefix is not a literal prefix. Tests: - deep template vs longer concrete → covers / covered-by (was parent/child) - deep template vs shorter concrete (** collapses to zero) → covers (was null) - single-* template vs equal-length concrete unchanged Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The example declared the root as Record<string, number> but the path
was three levels deep and the sample data was nested two more levels —
{ a: { "*": { b: 1 }, x: { b: 2 } } }. Type-checked but lied about
the leaf type.
- Introduce explicit type aliases (Data, Bag) so the root shape is
visible and matches the path / data.
- unsafePath now takes <Bag, number> so .get() returns number | undefined
rather than unknown.
- Switch the template example to the lambda form path((d: Data) => d.items)
since path<T>() returns a Path<T,T> with no proxy access; the old
path<T>().items.each() didn't actually compile.
- Drop the unused DEEP_WILDCARD import.
Verified by spinning up a tmp spec that runs both snippets and passes
under --typecheck.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7d1c76f2f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
🎉 This PR is included in version 2.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary by cubic
Revamped the
data-pathAPI with stricter types, better DX, and an auto-deployed docs site. Wildcards are now unique symbol sentinels, preserving literal'*'/'**'keys across all operations;**correctly matches zero-or-more and.match()returns the right coverage relation in deep wildcard cases.New Features
.update(); on template paths it applies the transform to each matched item..to()/.merge()chains..match()now returns only the structural relation (MatchResult | null); params removed.unsafePath<T, V>()supports explicit leaf typing;.fnis(T) => V | undefined.docs/using@astrojs/starlight; auto-deployed via GitHub Pages.Refactors
WILDCARD/DEEP_WILDCARDunique Symbols;Segmentincludes these sentinels..$/toString()render*/**. Literal'*'/'**'keys are preserved and never treated as wildcards in.get(),.startsWith(),.covers(),.equals(), or.match(); constants exported for segment-based construction. Fixed**to match zero-or-more in prefix/match logic.AbstractPathImpl; removed the separate TemplatePath implementation and circular imports..each()/.deep()on primitive leaves; fixedResolvedType; expanded exported types..includes()->.covers();MatchRelationuses"covers"/"covered-by";.match()returnsMatchResult | null..parent(),.slice(),.subtract()return aTemplatePathwhen wildcards remain;.to()/.merge()widen toPath<T, U> | TemplatePath<T, U>.exports(CJS viadist/index.cjs),sideEffects: false; MIT license; README and community files added; Dependabot for/docsand Actions; publish and docs deploy workflows updated;huskypre‑commit switched tolint-staged.engines.nodeto>=20and alignedlint-stagedto a Node 20–compatible version..match()now returns"covers"/"covered-by"for deep wildcard vs concrete paths (including zero-length**collapse), instead of misclassifying as"parent"/"child".typescript≥ 6,lint-staged≥ 17) and ensured/docsecosystem configuration.Written for commit 27f3347. Summary will update on new commits.