SIGN IN SIGN UP

feat(core)!: publish one subpath per root namespace (#95)

## Summary

A consumer that emits declarations names DomainKit types by their
declaring module. `exports` listed only `.`, `./client`, `./server`,
`./testing`, and `./package.json`, so TypeScript had no specifier for
`dist/types/Principal.d.ts` or `dist/types/DnsRecord.d.ts` and raised
TS2883 once per class (Samva's `@samva/contracts` hit it on an HttpApi
group tuple over `DnsRecord.Model` members).

Decision D43: every namespace `src/index.ts` re-exports is its own
subpath, Effect style, with a curated explicit list.

```json
".":            { "types": "./dist/types/index.d.ts",         "import": "./dist/index.mjs" },
"./client":     { "types": "./dist/types/entry/client.d.ts",  "import": "./dist/entry/client.mjs" },
"./server":     { "types": "./dist/types/entry/server.d.ts",  "import": "./dist/entry/server.mjs" },
"./testing":    { "types": "./dist/types/entry/testing.d.ts", "import": "./dist/entry/testing.mjs" },
"./Principal":  { "types": "./dist/types/Principal.d.ts",     "import": "./dist/Principal.mjs" },
"./DnsRecord":  { "types": "./dist/types/DnsRecord.d.ts",     "import": "./dist/DnsRecord.mjs" },
...one entry per root namespace (19): Approval, Cleanup, Cloudflare, Connect, Custody, DnsRecord, DomainKit, DomainName, Plan, Principal, Provider, Providers, Provision, Reason, Receipt, Resolver, Storage, Vercel, Verify
"./package.json": "./package.json"
```

No `./*` pattern, nothing under `internal` or `entry`, nothing dropped
from the root.

## Build

- tsdown runs unbundled (`unbundle: true`, `root: "src"`): one ESM file
per source module (`dist/Principal.mjs`, `dist/DnsRecord.mjs`, ...),
entry points under `dist/entry/`, no hashed chunks. The root and each
subpath share module instances.
- Declarations stay per module from `tsc -p tsconfig.build.json` in
`dist/types/`, so each subpath's `types` and `import` resolve to the
same module.
- Unbundled ESM evaluates the DomainName, internal/error, Reason, Plan,
DnsRecord import cycle in real module order, which the bundled chunk had
been ordering for us. `DnsRecord` and `Plan` now read the field schemas
that cross that cycle through `Schema.suspend`, so any module in it can
be evaluated first (`import ... from "domainkit/DomainName"` before
anything else works).

## Proof

- `tests/artifact/exports.test.ts` derives the namespace list from
`export * as` in `src/index.ts` and pins `Object.keys(exports)` plus
each subpath's `{ types, import }` to it, so a new public module without
an export entry fails the gate.
- `tests/artifact/package-contents.test.ts` requires every `types` and
`import` target in the `npm pack` file list.
- `tests/artifact/packed-consumers.test.ts` imports
`domainkit/Principal` and `domainkit/DnsRecord` at runtime under Node
24, Bun, and workerd and checks `PrincipalModule.Service ===
Principal.Service` and `cname === DnsRecord.cname` against the root. It
also compiles the consumer with `declaration: true` against the tarball
and asserts the emitted file names
`import("domainkit/Principal").Service` and contains neither
`node_modules` nor `dist/`. Emitted lines:

```ts
export declare const first: (items: ReadonlyArray<DnsRecord.Model>) => DnsRecord.A | DnsRecord.AAAA | DnsRecord.CAA | DnsRecord.CNAME | DnsRecord.MX | DnsRecord.NS | DnsRecord.SRV | DnsRecord.TXT;
export declare const planned: Effect.Effect<Plan.Model, DomainKit.Error, import("domainkit/Principal").Service | Provision.Service>;
export declare const principal: import("domainkit/Principal").Interface;
```

The `Principal.Service` item left open in #92 is closed by this.

`@domainkit/react` and `@domainkit/capsuledb` bundle a single
`dist/index.d.mts` and are unchanged; both typecheck against the rebuilt
core.

## Validation

- Core gate (oxfmt, lint, typecheck, 133 tests), `bun run --filter
domainkit build`
- `bun run --filter domainkit release:check` (typecheck, tests, build,
examples typecheck, 10 artifact tests)
- Root `typecheck` (react and capsuledb against the rebuilt core) and
root `release:check`
- Live provider tests are user-owned and were not run

Known artifact: each unbundled module file also exports rolldown's
synthesized namespace object (`Principal_exports`) next to its members;
declarations do not mention it, so it is invisible to TypeScript
consumers.

https://claude.ai/code/session_017P54ng7iisz1dsk18u1w1x
S
Saatvik Arya committed
6c6718b321c4f4eb352f5b0588fcd8c0adc7685b
Parent: 4072375
Committed by GitHub <noreply@github.com> on 9/3/2026, 7:41:54 PM