Skip to content

Commit 40f5c6e

Browse files
fix(metadata-core,service-cluster,create-objectstack): ESM-only subpaths declare their types under import (#26321)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9Lx3to7qftVwKKnKpjVXPLv)_ Part of #26206 Clause-②: no. Amended 20:45Z: split. This PR lands the three ESM-only subpath groups (`Part of #26206`, non-breaking, types-only map changes plus README lines). The driver-memory `default` retirement waits on an ADR-0087 gate card (predicate 2 word-scans `default` across metadata surfaces and can never certify that operand) Metadata-protocol: none ## What changes Three subpaths are ESM-only, and their export maps now say so. Each subpath's `types` moved inside its `import` condition (`{ import: { types, default } }`, the conditional shape #26198 / #26279 use). Each package's README gets one paragraph saying the subpath is ESM-only. | Entry | Before (`node16-cjs`) | Choice | Why | |:---|:---|:---|:---| | `@objectstack/metadata-core/testing` | `CJSResolvesToESM` | declared ESM-only | It imports `vitest`, whose own `require()` entry throws ("Vitest cannot be imported in a CommonJS module using require()"), so a CJS build could not load. Its `require` condition was already removed on purpose (the tsup config's split-by-format comment). | | `@objectstack/service-cluster/testing` | `CJSResolvesToESM` | declared ESM-only | Same reason: it imports `vitest`. | | `create-objectstack/created-summary` | `CJSResolvesToESM` | declared ESM-only | Its one consumer is `@objectstack/cli`'s `init` (ESM). A CJS build would add a `require` condition, which widens the accept set and doesn't fit `Clause-②: no`. | | `@objectstack/cli` `.` | `CJSResolvesToESM` | ESM-only, no manifest change | See below. | | `@objectstack/cli/console` | `CJSResolvesToESM` | ESM-only, no manifest change | See below. | | `@objectstack/cli/hook-body` | `CJSResolvesToESM` | ESM-only, no manifest change | See below. | For the first three, runtime behaviour does not change. `require()` of each subpath already failed with `ERR_PACKAGE_PATH_NOT_EXPORTED`, because the map carried `types` + `import` and no `require` or `default`. The unconditional `types` was the inconsistency: TypeScript under `node16`/`nodenext` still resolved the declarations for a CommonJS caller, and attw reported that as `CJSResolvesToESM`. Now TypeScript resolves them for `import` only, which is what Node does. ### `@objectstack/cli`: ESM-only, documented here (README follow-up) The cli's three library entries (`.`, `./console`, `./hook-body`) map `{ types, default }` to ESM files, and they stay that way. Measured on Node 22.22.0, `require()` of all three works today through Node's `require(esm)`. The `.` barrel loads every oclif command class. - Any ESM-only map change (an `import`-only condition, or a `require` stub that throws) would break those working `require()` callers. That's a narrowing, and this card is `Clause-②: no`. - A CJS build of the `.` barrel means a second build of the whole oclif program, which is out of proportion for a p3. So the attw `CJSResolvesToESM` items on these three stay, as intended ESM-only entries: the files are ESM, and CommonJS callers reach them through `import()` or Node's `require(esm)`. This card's claim kept `packages/cli/README.md` out of scope while #26215 held it, so this paragraph is the written declaration for now. **Follow-up:** add one line to `packages/cli/README.md` saying `.`, `./console` and `./hook-body` are ESM entries. #26215 has since merged, so nothing blocks that line. ### Not in this PR: `@objectstack/driver-memory`'s default export The card's driver-memory half (`FalseExportDefault`) is split out. Nothing in this repository imports the default, which is the legacy `{ id, version, onEnable }` object the kernel never calls, so retiring it is the right fix. But it retires a published export, and ADR-0087's `runtime-export-retired` disposition cannot currently certify an operand named `default`. Predicate 2 (no-metadata-surface) scans metadata surfaces for the export's name and hits every `default:` key, starting with `examples/app-crm/src/objects/account.object.ts`. The retirement waits on that gate, so #26206 stays open for it. ## Evidence attw 0.18.5 on `npm pack --ignore-scripts` of each package, built at `6633075b` (before) and at this head (after): | Package | Before | After | |:---|:---|:---| | `@objectstack/cli` | `CJSResolvesToESM` on `.`, `./console`, `./hook-body` | unchanged (intended, see above) | | `create-objectstack` | `CJSResolvesToESM` on `./created-summary` | 0 `CJSResolvesToESM` (`./created-summary` answers `NoResolution` under `node16-cjs`, matching Node) | | `@objectstack/metadata-core` | `CJSResolvesToESM` on `./testing` | 0 `CJSResolvesToESM` (`NoResolution` under `node16-cjs`) | | `@objectstack/service-cluster` | `CJSResolvesToESM` on `./testing` | 0 `CJSResolvesToESM` (`NoResolution` under `node16-cjs`) | `NoResolution` under `node10` on every subpath is unchanged and outside the contract (legacy `node10` has no `exports` support). Runtime probes, at this head: - `import()` of all three subpaths resolves and loads. - `require()` of all three still answers `ERR_PACKAGE_PATH_NOT_EXPORTED`. - `require()` of the three cli entries still loads. ## Acceptance notes - Tests and typecheck ran at `fbc38f81`. That commit carries this head's three map changes, plus the driver-memory half that `64128263` then restored byte-for-byte to `main`. - Tests (`pnpm --filter … run test`): metadata-core 823/823, service-cluster 105/105, create-objectstack 269/269. - Typecheck (`run typecheck`), green on 12 packages: the three changed packages, plus every in-repo consumer of these subpaths. metadata-fs, metadata-protocol, rest, runtime, service-cluster-redis and cli (`init.ts`) are all ESM (`nodenext`, or `bundler` for test configs). driver-memory, service-datasource and plugin-dev were covered for the dropped half. - `node scripts/check-adr-0087-registration.mjs --base origin/main` exit 0, with 1 non-breaking changeset seen. `node scripts/check-changeset-no-major.mjs` exit 0. - At `64128263`, all 71 `dispatch-gates --commands` families were run locally, each exit captured before any pipe. `--ran` reconciliation: 71 derived, 71 run, 0 NOT-MEASURED. The list is in the report comment on #26206. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WTCPNVkuDk7DNaZSdM3DZv --- _Generated by [Claude Code](https://claude.ai/code/session_01WTCPNVkuDk7DNaZSdM3DZv)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a8e7393 commit 40f5c6e

7 files changed

Lines changed: 38 additions & 6 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/metadata-core': patch
3+
'@objectstack/service-cluster': patch
4+
'create-objectstack': patch
5+
---
6+
7+
fix(metadata-core,service-cluster,create-objectstack): ESM-only subpaths declare their types under `import`
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
`@objectstack/metadata-core/testing`, `@objectstack/service-cluster/testing` and `create-objectstack/created-summary` are ESM-only, and now say so in their export maps: the subpath's `types` moved inside its `import` condition. `require()` of these subpaths already failed at runtime with `ERR_PACKAGE_PATH_NOT_EXPORTED`. Before this change, TypeScript under `node16`/`nodenext` resolved their declarations for a CommonJS caller anyway (attw `CJSResolvesToESM`). Now it resolves them for `import` only, which is what Node does. ESM callers, and every resolution mode that reads the `import` condition, see no change. Each package's README now says the subpath is ESM-only.

‎packages/create-objectstack/README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ npm run validate # verify metadata: schema + predicates + bindings
105105
See the docs:
106106
[Your First Project](https://objectstack.ai/docs/getting-started/your-first-project).
107107

108+
## Library subpath
109+
110+
`create-objectstack/created-summary` (the "what was written" tree summary that
111+
`objectstack init` reuses) is **ESM-only**: the package is `"type": "module"`
112+
and the subpath declares only an `import` condition. Load it with `import`;
113+
`require()` is refused with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
114+
108115
## License
109116

110117
Apache-2.0. See [LICENSING.md](../../LICENSING.md).

‎packages/create-objectstack/package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
],
1111
"exports": {
1212
"./created-summary": {
13-
"types": "./dist/created-summary.d.ts",
14-
"import": "./dist/created-summary.js"
13+
"import": {
14+
"types": "./dist/created-summary.d.ts",
15+
"default": "./dist/created-summary.js"
16+
}
1517
}
1618
},
1719
"scripts": {

‎packages/metadata-core/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,8 @@ import {
3838
```
3939

4040
See ADR-0008 §2 for the full architectural rationale.
41+
42+
`@objectstack/metadata-core/testing` (the repository contract suites) is
43+
**ESM-only**: it imports `vitest`, whose own `require()` entry throws, so
44+
the subpath declares only an `import` condition. Load it with `import`;
45+
`require()` is refused with `ERR_PACKAGE_PATH_NOT_EXPORTED`.

‎packages/metadata-core/package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
}
2020
},
2121
"./testing": {
22-
"types": "./dist/testing.d.ts",
23-
"import": "./dist/testing.js"
22+
"import": {
23+
"types": "./dist/testing.d.ts",
24+
"default": "./dist/testing.js"
25+
}
2426
}
2527
},
2628
"files": [

‎packages/services/service-cluster/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ import { ClusterServicePlugin } from '@objectstack/service-cluster';
77
```
88

99
Documentation: [Cluster](https://objectstack.ai/docs/kernel/cluster)
10+
11+
`@objectstack/service-cluster/testing` (the pub/sub, lock, KV and counter contract suites) is **ESM-only**: it imports `vitest`, whose own `require()` entry throws, so the subpath declares only an `import` condition. Load it with `import`; `require()` is refused with `ERR_PACKAGE_PATH_NOT_EXPORTED`.

‎packages/services/service-cluster/package.json‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
}
2020
},
2121
"./testing": {
22-
"types": "./dist/testing.d.ts",
23-
"import": "./dist/testing.js"
22+
"import": {
23+
"types": "./dist/testing.d.ts",
24+
"default": "./dist/testing.js"
25+
}
2426
}
2527
},
2628
"scripts": {

0 commit comments

Comments
 (0)