Repository navigation
Commit 1c5fe3c
fix(driver-memory)!: retire the legacy default export that CJS require() could not reach (#27323)
Fixes #26206
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — no `skills/**` file and no `content/docs/**` page
names the retired `default` export or loads the package as a string
plugin; the docs teach `InMemoryDriver` by name, which stays
## 维护者速读
- **改了什么**:`@objectstack/driver-memory` 的包入口不再提供 `default` 导出(一个旧式的 `{
id, version, onEnable }` 对象)。`InMemoryDriver` 等所有具名导出不变。这是本卡最后一项,前两项已由
#26321、#26352 落地。
- **为什么改**:这个默认导出不是内核插件(内核约定是 init/start/destroy),而且 CommonJS 用户
`require()` 时拿不到它,类型声明却说有(attw 报 `FalseExportDefault`)。退役后 attw 对该包为 0
问题。
- **风险与回滚**:本仓内默认导入者为 0,示例、脚手架模板、文档、技能里也没有人把它当字符串插件写进
`plugins`。外部若在配置里写了 `plugins:
['@objectstack/driver-memory']`,原来会注册一个内存驱动,现在会静默不注册(changeset 写明改用 `new
DriverPlugin(new InMemoryDriver(), 'memory')`)。cloud / hotcrm 未能测到。回滚 =
revert 本 PR。
-
**先看**:`packages/drivers/driver-memory/src/index.ts`(删掉的默认导出);`.changeset/26206-driver-memory-default.md`(BREAKING
说明、ADR-0087 处置与 FROM →
TO);`packages/drivers/driver-memory/src/default-export-retired.pin.test.ts`。
- **两分钟验证**:`node scripts/check-adr-0087-registration.mjs --base
origin/main`,预期 exit 0 且打印 `verified:
@objectstack/driver-memory#default`。
- **需要你做的**:无,席位落地(收窄已发布导出面,先过一次契约档复核)。
## What changes
- `packages/drivers/driver-memory/src/index.ts`: the `export default {
id, version, onEnable }` object leaves the published entry. Every named
export stays. A comment at the old site says why the entry has no
default and which named door to use.
- Choice, as the card asks: **drop the default** in favour of the named
exports, not "make the CJS default reachable". Reasons:
1. A reachable CJS default means `require('@objectstack/driver-memory')`
itself returns the default object. The entry also has 22 named exports,
so that needs an `export =` shape with the named exports merged onto the
object. That changes what every CommonJS named import reads today, and
it keeps an object that is not a kernel `Plugin`.
2. Nothing reads it. In this tree: 0 default imports, 0 `require()`
calls, 0 `.default` reads after a dynamic import, and 0 string `plugins`
entries naming the package (evidence surface below). Control: 14 files
(code, docs and skills) import named exports with a static `from`, and 3
code files destructure `InMemoryDriver` from a dynamic `import()`.
- `packages/drivers/driver-memory/README.md`: the `default` row leaves
the Key Exports table, and one line says the package has no default
export.
- `.changeset/26206-driver-memory-default.md`: `patch`
(Metadata-protocol `none`), BREAKING banner, ADR-0087 `not-required
(runtime-export-retired @objectstack/driver-memory#default)`, and one
FROM → TO line. No staged window.
-
`packages/drivers/driver-memory/src/default-export-retired.pin.test.ts`
(2 cases): the entry has no `default`, and the named exports stay and
`InMemoryDriver` still constructs.
### What changes for a string `plugins` entry (PM assumption 3)
`packages/cli/src/commands/serve.ts:2765` (`mod.default || mod`) loads
the project's own config file, not a plugin. The string-plugin door is
`materializeStackPlugin` in `packages/core/src/stack-plugins.ts:65`: it
loads the module and takes `mod.default || mod`, and an object with no
`init` is wrapped in `AppPlugin`, which runs a bundle's `onEnable` with
a `drivers.register` that calls `ql.registerDriver`. Measured at that
seam (real core `dist`, real driver-memory `dist`, a recording
`wrapBundle`):
- before: `wrapBundle` received `id, version, onEnable`, and calling
`onEnable` with a driver registry registered
`InMemoryDriver(com.objectstack.driver.memory)`.
- after: `wrapBundle` receives the module namespace (`InMemoryDriver`,
…, no `id`, no `onEnable`). `AppPlugin` then takes its no-id, no-payload
branch (`packages/runtime/src/app-plugin.ts`) and becomes an empty no-op
plugin, so no driver is registered and nothing says so.
So a config that writes `plugins: ['@objectstack/driver-memory']`
changes behaviour. No producer of that spelling was found (search
below). The changeset names the replacement for that case.
## Tests
<details><summary>attw 0.18.5 on an npm-pack staging of driver-memory:
before and after</summary>
Before (base `65b492081`, attw exit 1):
```text
node10: ❗️ Incorrect default export
node16 (from CJS): ❗️ Incorrect default export
node16 (from ESM): 🟢 (ESM)
bundler: 🟢
```
After (`161653aed`, attw exit 0):
```text
No problems found 🌟
node10: 🟢
node16 (from CJS): 🟢 (CJS)
node16 (from ESM): 🟢 (ESM)
bundler: 🟢
```
The packed `dist/index.d.ts` has 0 `export default` lines after the
change.
</details>
<details><summary>driver-memory build, typecheck and tests at
161653a</summary>
`pnpm --filter @objectstack/driver-memory build && … typecheck && …
test` under the verify lock: `VERDICT command-exit 0`; `Test Files 73
passed (73)`, `Tests 1760 passed (1760)`. `tsc --noEmit --listFiles`
counts the new pin test once (it is compiled by the typecheck program).
</details>
<details><summary>Ablation (one-off, no permanent file)</summary>
The fix was committed first. `node scripts/ablation-replace.mjs`
replaced the comment line at the old site with `export default { id:
'ablated' };` (anchor 1 → 0, blob `a1f49a03fbd9` → `f5bf6cfac4ce`) and
ran the pin test: `× the published entry has no default export`,
`AssertionError: expected true to be false`, `Tests 1 failed | 1 passed
(2)`. Restored: blob == HEAD (`a1f49a03fbd9`), `git diff HEAD` empty.
The test imports `./index.js`, so it reads `src`, and no build was
needed for this leg.
A first attempt used a replacement that contained its own anchor. The
tool refused it before running anything ("the anchor count moved 1 ->
1"), so that attempt measured nothing.
</details>
<details><summary>Full dogfood battery at 161653a (the title carries a
bang)</summary>
After `pnpm turbo run build --filter='./packages/**' --concurrency=2`
(75/75 tasks, 0 cached, `VERDICT command-exit 0`), `pnpm --filter
@objectstack/dogfood test` under the verify lock, no shard, no path:
`VERDICT command-exit 0`, `Test Files 348 passed | 3 skipped (351)`,
`Tests 2806 passed | 20 skipped (2826)`. No failures.
</details>
## Acceptance notes
- **Evidence surface for readers of the default** (tree
`objectstack-ai/objectstack` at `65b492081`): `git grep` over the whole
tree (`examples/**`, `packages/create-objectstack/**` templates,
`content/docs/**`, `skills/**`, `ui/**`, every package's tests and
`scripts/**`) for `import X from` / `import X, { … } from` / `import *
as X from` of the package (0), `require('@objectstack/driver-memory')`
(0), a dynamic `import()` whose result is read as `.default` or
destructured as `default` (0; the 3 code hits all destructure
`InMemoryDriver`, the other 2 hits are comments), a `plugins:` line
naming the package (0), and the default's id
`com.objectstack.driver.memory` (hits are the driver class's own `name`,
tests and CLI banner text, none of which reads the default object).
`content/docs/plugins/index.mdx` and `content/docs/plugins/packages.mdx`
teach `InMemoryDriver` by name.
- NOT MEASURED: `objectstack-ai/cloud` and `objectstack-ai/hotcrm` (this
session's GitHub reads are bound to this repository; org code search
answered 403), and npm dependants.
- The four sibling drivers (`driver-sql`, `driver-mongodb`,
`driver-sqlite-wasm`, `driver-turso`) export the same legacy `{ id,
version, onEnable }` default. attw 0.18.5 on npm-pack stagings of all
four at `161653aed` reports **no problems** (exit 0). So they are not
part of this card's acceptance, and this PR does not touch them. Why
attw flags driver-memory's build and not theirs (the CJS output has the
same shape) was not established.
- The silent no-op above is not specific to this package: a string
`plugins` entry naming any module without a default export and without
`init` gets the module namespace wrapped into an empty `AppPlugin`. That
was read at the seam, not measured through `os serve`, so it is recorded
here and not filed.
- ADR-0087 gate (PM assumption 1): `check-adr-0087-registration --base
origin/main` exit 0, `verified: @objectstack/driver-memory#default
(export, packages/drivers/driver-memory/src/index.ts)`. The blocker fix
`cdb949a288` is an ancestor of the base (`merge-base --is-ancestor` exit
0).
## Verification
Local, at `161653aed`, derived with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` over this diff (58
commands). Every command ran, and each exit code was captured before any
pipe.
- 55 exited 0 on the first run.
- 3 exited 3, PREREQUISITE NOT MET, so they measured nothing:
`check-plugin-teardown-shape --self-test` (its pinned fixture commit was
outside the shallow clone) and `check:dual-build-cjs-loads` /
`check:lean-entry-closure` (they read built output). They were re-run
after `git fetch --depth=1` of the fixture commit and the full packages
build, and all 3 exited 0: `112 published require entry point(s) across
69 package(s) load`, `Admitted set held exactly (15 packages)`, `48
cases pass`.
- `dispatch-gates --ran`: `58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN`
(exit 0).
- Outside the derived list: `check-platform-checklist --changed-from
origin/main` exit 0 (0 anchored files), `check:published-readme-exports`
exit 0, `check:published-readme-links` exit 0, `check-nul-bytes` exit 0.
- `test:repo` and repo-wide lint were not run locally. They belong to CI
(`Test Core`, `Lint & Repo Gates`) on this PR.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017xRBcVyH9FzSkzAGmz8LNE)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent cf43b6c commit 1c5fe3c
4 files changed
Lines changed: 58 additions & 18 deletions
File tree
- .changeset
- packages/drivers/driver-memory
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
65 | 64 | | |
66 | 65 | | |
67 | 66 | | |
68 | 67 | | |
69 | 68 | | |
70 | 69 | | |
71 | 70 | | |
| 71 | + | |
| 72 | + | |
72 | 73 | | |
73 | 74 | | |
74 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
0 commit comments