Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f586c19
fix: accept case-insensitive percent-encoding, reject non-digit ports
ob-aion May 19, 2026
9b5e4a3
fix: split userinfo and port on the last delimiter
ob-aion May 19, 2026
d47c0b6
fix: preserve present-empty query and fragment
ob-aion May 19, 2026
3c560d0
fix: complete sitemap XML entity table and tighten URL length
ob-aion May 19, 2026
067782e
fix: require the RFC 6874 %25 zone delimiter in URI hosts
ob-aion May 19, 2026
f6070f7
feat: add resolveURI and removeDotSegments (RFC-3986 §5.2)
ob-aion May 19, 2026
7c9fbf0
refactor: centralize coded-error construction in a fail() helper
ob-aion May 19, 2026
11174bb
perf: hoist per-call regular expressions to module scope
ob-aion May 19, 2026
34fa2ff
feat: add the resolver module source and tests
ob-aion May 19, 2026
12d98ab
chore: tighten TypeScript strictness and bundler configuration
ob-aion May 19, 2026
2675dc3
test: add property-based tests and enforce 100% coverage
ob-aion May 19, 2026
720cdbb
ci: add the reusable-workflow caller and benchmark wiring
ob-aion May 19, 2026
772d32d
docs: document reference resolution and RFC compliance
ob-aion May 19, 2026
bff2cb1
test: add the mitata benchmark and the 1.0.0 baseline
ob-aion May 19, 2026
37566e8
fix: enforce the RFC 6874 ZoneID grammar in URI hosts
ob-aion May 19, 2026
a017185
test: cover empty port and deep dot-segment paths
ob-aion May 19, 2026
f44665a
docs: cite RFC sections in the compliance reference
ob-aion May 19, 2026
552edf3
chore: ignore the local .claude/output directory
ob-aion May 19, 2026
ead850b
ci: forward the npm token secrets needed for the first publish
ob-aion May 20, 2026
6ba6854
docs: restructure the README and surface the full RFC stack
ob-aion May 20, 2026
07706f6
docs: link to the benchmark baseline from Compliance
ob-aion May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: ci

on:
push:
branches: [main]
tags:
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-*'
pull_request:
branches: [main]

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref_type != 'tag' }}

permissions:
contents: write
id-token: write

jobs:
ci:
uses: coroboros/ci/.github/workflows/javascript-npm-packages.yml@v0
secrets:
NPM_CONFIG_FILE: ${{ secrets.NPM_CONFIG_FILE }}
NPM_EXTRA_CONFIG: ${{ secrets.NPM_EXTRA_CONFIG }}
NPM_PACKAGE_REGISTRY: ${{ secrets.NPM_PACKAGE_REGISTRY }}
NPM_PACKAGE_PROXY_REGISTRY: ${{ secrets.NPM_PACKAGE_PROXY_REGISTRY }}
NPM_PACKAGE_REGISTRY_TOKEN: ${{ secrets.NPM_PACKAGE_REGISTRY_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.wrangler/
.dev.vars
.claude/settings.local.json
.claude/output/
CLAUDE.local.md
dist
*.zip
Expand Down
17 changes: 13 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,46 @@ Follows the Coroboros engineering global rules. Repo-specific divergences are st

## Tech Stack
- TypeScript strict, ES modules + CJS dual build (tsdown)
- Vitest for tests, Biome for lint/format
- Vitest + `fast-check` for property tests, Biome for lint/format
- `mitata` for benchmarks (`pnpm bench`)
- Node.js 22 LTS
- Zero runtime dependencies — Punycode uses Node's `node:url` (`domainToASCII` / `domainToUnicode`)

## Commands
- `pnpm build` — bundle ESM + CJS + types to `dist/`
- `pnpm test` — run Vitest suite
- `pnpm test` — run the Vitest suite (incl. property-based)
- `pnpm test:coverage` — Vitest with the 100% coverage gate
- `pnpm lint` / `pnpm lint:fix` — Biome check
- `pnpm typecheck` — tsc --noEmit
- `pnpm bench` — build then run `bench/uri.bench.mjs`
- `pnpm dev` — tsdown watch mode

## Important Files
- `src/index.ts` — public entry point; all exports surface through here
- `src/parser/index.ts` — `parseURI`, `recomposeURI`, `hostToURI` (RFC-3986 Appendix B grammar)
- `src/checkers/index.ts` — URI / URL / Sitemap validators, error taxonomy
- `src/encoders/index.ts`, `src/decoders/index.ts` — RFC-3986 encode/decode
- `src/resolver/index.ts` — `resolveURI`, `removeDotSegments` (RFC-3986 §5.2 verbatim)
- `src/helpers/object.ts` — private `exists` / `is` type guards (inlined, not exported)
- `tsdown.config.ts` — dual build config (ESM + CJS + dts)
- `tests/` — Vitest suites, one test file per source module
- `tests/` — one spec per source module + `uri.property.test.ts` for `fast-check` invariants
- `bench/uri.bench.mjs` — mitata bench vs native `URL` / `URL.canParse`; `bench/baseline.md` documents the 1.0.0 numbers

## Public API (1.0.0 contract)
- `punycode(domain)`, `punydecode(domain)` — domain ASCII/Unicode serialization
- `parseURI(uri)`, `recomposeURI(components)` — RFC-3986 parse / recompose
- `resolveURI(base, reference)`, `removeDotSegments(path)` — RFC-3986 §5.2 reference resolution
- `isDomainLabel(label)`, `isDomain(name)`, `isIP(ip)`, `isIPv4(ip)`, `isIPv6(ip)` — validators
- `checkURI(uri)`, `checkHttpURL(uri)`, `checkHttpsURL(uri)`, `checkWebURL(uri)`, `checkSitemapURL(uri)`, `checkHttpSitemapURL(uri)`, `checkHttpsSitemapURL(uri)` — throw a coded error on invalid input
- `encodeURIComponentString(component, options)`, `encodeURIString(uri, options)`, `encodeWebURL(uri, options)`, `encodeSitemapURL(uri)` — RFC-3986 encoders
- `decodeURIComponentString(component, options)`, `decodeURIString(uri, options)`, `decodeWebURL(uri, options)`, `decodeSitemapURL(uri, options)` — RFC-3986 decoders

## Rules
- **NEVER** break the public API above. The signatures and the error/type shapes are the 1.0.0 contract.
- The **published** `1.0.0` tag is the public contract — once it ships, **NEVER** break the API above (signatures, error codes, type shapes) without a major bump. Until `1.0.0` is published, breaking changes are allowed but every break must be enumerated in the PR.
- **NEVER** add a new runtime dependency without user approval. Zero-dependency is a feature.
- **NEVER** use `axios`, `request`, or `node-fetch` — use native `fetch` (Node 22+).
- Run `pnpm lint && pnpm typecheck && pnpm test` before every commit.
- Run `pnpm bench` against `bench/baseline.md` when touching the parser, encoders or decoders — no regression > 10 % on any bucket at fixed feature set.
- Scoped package — `publishConfig.access = "public"` is mandatory, do not remove.
- **Publish** — CI-owned via OIDC Trusted Publisher + npm provenance. The first `1.0.0` publish bootstraps through the org registry token (CI auto-detects it); once the package exists on npm, configure it as a Trusted Publisher and never re-add a token to `ci.yml`. Manual `pnpm publish` is forbidden — it bypasses provenance and the tag guard.
- **Git** — `main`-only; branch → PR → squash-merge → tag the merge commit. The tag is the only manual step; release automation (version bump, `CHANGELOG.md`, npm publish, GitHub release) is owned by [`coroboros/ci`](https://github.com/coroboros/ci). Never hand-edit `package.json` version or `CHANGELOG.md`. Run `pnpm lint && pnpm typecheck && pnpm test && pnpm build` before tagging.
Loading
Loading