Skip to content

Commit f6b7c53

Browse files
claude[bot]claude
andauthored
fix(cli): re-measure the better-auth better-sqlite3 peer record, correct what it credits, and pin the declaration it justifies (#17095)
* fix(cli): re-measure and correct the better-auth better-sqlite3 peer record, and pin the declaration it justifies better-auth peers `better-sqlite3@^12.0.0` while `@objectstack/cli` declares `^13.0.3`, so every fresh resolve of a tree containing the CLI prints an unmet peer. The reading that decides what to do about it existed only inside the scaffold generator, was taken on better-auth 1.7.1, and credited the wrong package for the 13.x copy. Re-measured on the pinned 1.7.2 and on a bare project depending on `@objectstack/cli@17.3.0`; the declaration is correct and stays. Corrects the record in both scaffold producers and adds the gate that holds the declaration to the reasoning behind it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8 * chore(changeset): record the better-sqlite3 peer re-measurement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5865b02 commit f6b7c53

6 files changed

Lines changed: 250 additions & 27 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@objectstack/cli": patch
3+
"create-objectstack": patch
4+
---
5+
6+
fix(cli): re-measure the `better-auth` > `better-sqlite3` peer record, correct what it credits, and pin the declaration it justifies (#16813)
7+
8+
A tree containing `@objectstack/cli` reports an unmet peer on every fresh
9+
resolve — `better-auth` peers `better-sqlite3@^12.0.0`, the CLI declares
10+
`^13.0.3` — and the reading that decides what to do about it lived only inside
11+
the scaffold generator's prose. No range moves here and no resolution moves:
12+
what changes is the recorded reason, which had two measured errors in it, plus
13+
a gate that now holds the declaration to that reason.
14+
15+
**The declaration is correct and stays at `^13`.** Three readings, taken rather
16+
than inherited:
17+
18+
- The peer is `optional`, and it governs exactly one configuration — a raw
19+
better-sqlite3 `Database` passed to better-auth's `database` option.
20+
`AuthManager.createDatabaseConfig()` returns an ObjectQL adapter factory, or
21+
`undefined` for better-auth's in-memory adapter. Never a `Database`.
22+
- better-auth cannot be incompatible with better-sqlite3 13, because it never
23+
touches it: of the 464 files in the published `better-auth@1.7.2` tarball,
24+
exactly one names better-sqlite3 — `package.json`, the peer declaration
25+
itself — and no code file references it (positive control: `kysely` names 9).
26+
It accepts a `Database` the caller constructs; its own sqlite test path uses
27+
node's built-in `node:sqlite`.
28+
- Pinning back to `^12` is not a neutral alternative. Measured on a bare
29+
project depending on `@objectstack/cli@17.3.0`, it clears the report only by
30+
resolving a **second** native better-sqlite3 (12.11.1 beside 13.0.3) that
31+
nothing loads. The scaffold's existing `allowedVersions` entry clears the
32+
same report with the lockfile byte-identical.
33+
34+
**Two corrections to the record.** It credited `@objectstack/driver-sql` for
35+
the 13.x copy; on the chain that actually reports
36+
(`cli``runtime``plugin-auth``better-auth`) the binding copy is the
37+
CLI's own `optionalDependencies` entry, which pnpm names in the warning itself.
38+
And it was measured on better-auth 1.7.1 while the family has been pinned at
39+
1.7.2 since — re-measured, with the empirical reading replaced by a structural
40+
one.
41+
42+
The scaffold's rendered `pnpm-workspace.yaml` comment changes wording in both
43+
producers (`objectstack init` and the `create-objectstack` blank template); the
44+
declarations, the widening entry and the resolution are untouched.

packages/cli/src/commands/init.ts

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,44 @@ export const SCAFFOLD_BUILT_DEPENDENCIES = ['better-sqlite3', 'esbuild'];
9898
* the first thing a newcomer sees, on the one screen where they are deciding
9999
* whether this project is solid, and there is nothing they did to cause it.
100100
*
101-
* - `better-auth>better-sqlite3` — better-auth 1.7.1 peers `^12.0.0` while the
102-
* tree resolves 13.x (`@objectstack/driver-sql`'s optional dependency). The
103-
* peer is OPTIONAL and governs one configuration only: a raw better-sqlite3
104-
* `Database` handed to better-auth's `database` option. ObjectStack never
105-
* does that — `AuthManager.createDatabaseConfig()` passes an ObjectQL
106-
* adapter factory. Measured on the configuration the range *does* govern
107-
* (better-auth's own Kysely dialect: migrations, sign-up, sign-in, adapter
108-
* find/update/delete), 1.7.1 behaves identically on better-sqlite3 13.0.3
109-
* and on 12.11.1. So the upstream range is stale and 13 is right — widening
110-
* is the correct remedy, not pinning our own declaration back to 12.
101+
* - `better-auth>better-sqlite3` — better-auth peers `^12.0.0` while the tree
102+
* resolves 13.x. The peer is OPTIONAL and governs one configuration only: a
103+
* raw better-sqlite3 `Database` handed to better-auth's `database` option.
104+
* ObjectStack never does that — `AuthManager.createDatabaseConfig()` passes
105+
* an ObjectQL adapter factory (or `undefined`, better-auth's own in-memory
106+
* adapter). So the upstream range is stale and 13 is right — widening is
107+
* the correct remedy, not pinning our own declaration back to 12.
108+
*
109+
* RE-MEASURED on the pinned 1.7.2 (#16813). The original reading was taken
110+
* on 1.7.1 (#10326) and was behavioural: better-auth's own Kysely dialect —
111+
* migrations, sign-up, sign-in, adapter find/update/delete — behaves
112+
* identically on better-sqlite3 13.0.3 and on 12.11.1. 1.7.2 makes that
113+
* structural instead of empirical: of the 464 files in the published
114+
* `better-auth@1.7.2` tarball, exactly ONE names better-sqlite3 —
115+
* `package.json`, i.e. the peer declaration itself. Zero code files
116+
* reference it (positive control: `kysely` names 9). better-auth never
117+
* imports the package; it accepts a `Database` the CALLER constructs and
118+
* hands it to Kysely, and its own sqlite test path uses node's built-in
119+
* `node:sqlite` `DatabaseSync`. There is therefore no better-auth call site
120+
* that could touch an API moved between better-sqlite3 12 and 13 — the
121+
* range is a statement about an instance we never supply.
122+
*
123+
* ⚠️ TWO CORRECTIONS to what this entry used to say, both measured:
124+
* • the 13.x copy better-auth binds to is `@objectstack/cli`'s OWN
125+
* `optionalDependencies` entry, NOT `@objectstack/driver-sql`'s. On the
126+
* chain that actually reports (`cli` → `runtime` → `plugin-auth` →
127+
* `better-auth`) the CLI is the ancestor, so its copy is the one pnpm
128+
* resolves the peer against — pnpm names it in the warning itself
129+
* ("found 13.0.3 in @objectstack/cli"). Editing driver-sql alone would
130+
* not move this line.
131+
* • pinning the CLI back to `^12` is not a neutral alternative. Measured
132+
* on a bare project depending on `@objectstack/cli@17.3.0`, it clears
133+
* the report only by installing a SECOND native better-sqlite3
134+
* (12.11.1 alongside 13.0.3), and the 12 copy is dead weight — the CLI
135+
* loads better-sqlite3 itself (`src/utils/sqlite-occupancy.ts`) and
136+
* knex resolves 13.x through driver-sql regardless. This
137+
* `allowedVersions` entry clears the same report with the resolution
138+
* byte-identical (0 lines of lockfile diff).
111139
*
112140
* - RETIRED (#3653): `@better-auth/scim>better-call` — the rc.1-era scim pin
113141
* peered an exact `better-call@1.3.7` against the host's 1.4.0, and this
@@ -474,11 +502,16 @@ export function renderPnpmWorkspaceYaml(
474502
'# package states, and that pnpm reports on a first install. None is a',
475503
'# real incompatibility:',
476504
'#',
477-
'# better-auth peers better-sqlite3 ^12.0.0 while the tree resolves 13.x.',
478-
'# That peer is optional and covers handing better-auth a raw',
479-
'# better-sqlite3 `Database`; ObjectStack hands it an ObjectQL adapter',
480-
'# instead. Measured on the configuration the range does cover,',
481-
'# better-auth 1.7.1 behaves identically on 13.0.3 and on 12.11.1.',
505+
'# better-auth peers better-sqlite3 ^12.0.0 while the tree resolves 13.x',
506+
'# (the copy @objectstack/cli declares for its own sqlite tooling). That',
507+
'# peer is optional and covers handing better-auth a raw better-sqlite3',
508+
'# `Database`; ObjectStack hands it an ObjectQL adapter instead, so',
509+
'# nothing here goes down that path. Re-measured on better-auth 1.7.2:',
510+
'# no file in the published package references better-sqlite3 at all —',
511+
'# it only accepts a Database you construct — so there is no call site',
512+
'# that could depend on what changed between 12 and 13. The upstream',
513+
'# range is stale; pinning back to 12 would just install a second,',
514+
'# unused native copy.',
482515
'#',
483516
'# @better-auth/scim (held at a release candidate deliberately) peers an',
484517
'# exact better-call 1.3.7, while better-auth itself depends on 1.4.0. A',
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* **The pin for `@objectstack/cli`'s own `better-sqlite3` declaration**, and
5+
* for its agreement with the scaffold widening that exists because of it
6+
* (#16813).
7+
*
8+
* ## The report this is about
9+
*
10+
* A consumer that installs `@objectstack/cli` sees, whenever pnpm actually
11+
* runs the resolution step:
12+
*
13+
* └─┬ @objectstack/cli 17.3.0
14+
* └─┬ @objectstack/runtime 17.4.0
15+
* └─┬ @objectstack/plugin-auth 17.4.0
16+
* └─┬ better-auth 1.7.2
17+
* └── ✕ unmet peer better-sqlite3@^12.0.0: found 13.0.3
18+
*
19+
* ⚠️ It is easy to conclude that this file's job is to stop, and that the fix
20+
* is to move the declaration below back inside `^12`. It is not, and the
21+
* measurement that says so is recorded here so the next reader does not have
22+
* to re-take it:
23+
*
24+
* 1. **The peer is optional and governs a configuration we never use.**
25+
* better-auth's `better-sqlite3` peer is
26+
* `peerDependenciesMeta.better-sqlite3.optional === true`, and it covers
27+
* one thing: a raw better-sqlite3 `Database` handed to better-auth's
28+
* `database` option. `AuthManager.createDatabaseConfig()` returns an
29+
* ObjectQL adapter factory, or `undefined` for better-auth's in-memory
30+
* adapter — never a `Database`.
31+
* 2. **better-auth cannot be incompatible with better-sqlite3 13, because it
32+
* never touches it.** Of the 464 files in the published
33+
* `better-auth@1.7.2` tarball, exactly one names better-sqlite3 —
34+
* `package.json`, i.e. the peer declaration itself. Zero code files
35+
* reference it (positive control: `kysely` names 9). It accepts a
36+
* `Database` the caller constructs; its own sqlite test path uses node's
37+
* built-in `node:sqlite`.
38+
* 3. **Pinning back to `^12` costs a second native module and buys nothing.**
39+
* Measured on a bare project depending on `@objectstack/cli@17.3.0`:
40+
* rewriting only this declaration to `^12.11.1` does clear the report, but
41+
* the resolved tree then carries better-sqlite3 **12.11.1 and 13.0.3**,
42+
* and the 12 copy is never loaded — this package loads better-sqlite3
43+
* itself (`src/utils/sqlite-occupancy.ts`) and knex resolves 13.x through
44+
* `@objectstack/driver-sql` either way. The scaffold's
45+
* `allowedVersions` entry clears the same report with the resolution
46+
* byte-identical (0 lines of lockfile diff).
47+
*
48+
* ⇒ 13 is the correct declaration and the upstream range is stale. What was
49+
* missing was not a different range but a gate holding the range to the
50+
* reasoning that justifies it, which is what this file is.
51+
*
52+
* ## Why the two assertions are one pin and not two
53+
*
54+
* `SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']` widens
55+
* better-auth's peer to a **major**, and the only reason that major is right
56+
* is the declaration below. Held apart, either can move alone and stay green:
57+
* a declaration lifted to `^14` leaves the scaffold suppressing a report it no
58+
* longer matches (so the NEXT real skew arrives pre-silenced), and a
59+
* declaration dropped to `^12` leaves a suppression with no skew behind it —
60+
* the exact shape `init.test.ts` refuses for the retired
61+
* `@better-auth/scim>better-call` entry. So the major is read out of the
62+
* manifest and compared, rather than written down twice.
63+
*
64+
* `init.test.ts` pins the widening's literal value and its rendering; this
65+
* file pins what the widening is ABOUT. Neither restates the other.
66+
*
67+
* The manifest read stays inside this package (`test/` → package root), so it
68+
* is not a `check:cross-package-test-inputs` escape and needs no declaration.
69+
*/
70+
71+
import { describe, it, expect } from 'vitest';
72+
import { readFileSync } from 'node:fs';
73+
import { resolve } from 'node:path';
74+
import { fileURLToPath } from 'node:url';
75+
// `.js`, not extensionless: this package is `moduleResolution: NodeNext`.
76+
import { SCAFFOLD_ALLOWED_PEER_VERSIONS } from '../src/commands/init.js';
77+
78+
const HERE = resolve(fileURLToPath(import.meta.url), '..');
79+
80+
// One line on purpose — `check:cross-package-test-inputs` reconstructs reads by
81+
// source scan, and a split `resolve(HERE, …)` is a spelling it does not know.
82+
const MANIFEST = resolve(HERE, '../package.json');
83+
84+
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8')) as {
85+
optionalDependencies?: Record<string, string>;
86+
dependencies?: Record<string, string>;
87+
};
88+
89+
const WIDENING_KEY = 'better-auth>better-sqlite3';
90+
91+
/** The single major a caret/tilde range admits, or null if it admits more. */
92+
function soleMajor(range: string): string | null {
93+
const m = /^[\^~]?(\d+)\.\d+\.\d+$/.exec(range.trim());
94+
return m ? m[1] : null;
95+
}
96+
97+
describe('@objectstack/cli better-sqlite3 declaration (#16813)', () => {
98+
it('declares better-sqlite3 as an OPTIONAL dependency, not a hard one', () => {
99+
// Optional is load-bearing: `objectstack serve` must start on a host where
100+
// the native build failed, falling back to the wasm driver. A hard
101+
// dependency turns that degraded start into a failed install.
102+
expect(manifest.optionalDependencies?.['better-sqlite3']).toBeTypeOf('string');
103+
expect(manifest.dependencies?.['better-sqlite3']).toBeUndefined();
104+
});
105+
106+
it('keeps the declaration on one major, so the scaffold can widen to it', () => {
107+
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
108+
expect(
109+
soleMajor(declared),
110+
`better-sqlite3 is declared "${declared}"; this pin needs a range admitting exactly one major`,
111+
).not.toBeNull();
112+
});
113+
114+
it('widens better-auth\'s peer to the major this package actually declares', () => {
115+
// ⛔ Not "to 13" — that is `init.test.ts`'s assertion. This one says the
116+
// widening and the declaration are the SAME major, so neither can move
117+
// without the other.
118+
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
119+
expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[WIDENING_KEY]).toBe(soleMajor(declared));
120+
});
121+
122+
it('refuses a retreat to better-sqlite3 12 (the report is not the defect)', () => {
123+
// The failure this guards is a well-meant one: reading the unmet-peer
124+
// report as a defect and "fixing" it here. Measured, that clears the line
125+
// only by installing a second native better-sqlite3 that nothing loads —
126+
// see this file's header for the three readings. If better-auth ever
127+
// WIDENS its peer upstream, the right change is to retire the scaffold
128+
// entry, not to move this declaration.
129+
const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? '';
130+
expect(soleMajor(declared)).not.toBe('12');
131+
});
132+
});

packages/cli/test/init.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,16 @@ describe('benign peer-skew declarations (#10326)', () => {
323323
const settings = renderPnpmWorkspaceYaml().replace(/^\s*#.*$/gm, '');
324324

325325
it('widens better-auth\'s stale better-sqlite3 peer rather than pinning ours back', () => {
326-
// better-auth 1.7.1 peers `^12.0.0` while the tree resolves 13.x. The peer
327-
// is OPTIONAL and governs one configuration only — a raw better-sqlite3
326+
// better-auth peers `^12.0.0` while the tree resolves 13.x. The peer is
327+
// OPTIONAL and governs one configuration only — a raw better-sqlite3
328328
// `Database` passed to better-auth's `database` option — which ObjectStack
329329
// never does (AuthManager passes an ObjectQL adapter factory). Measured on
330330
// the configuration it does govern, 1.7.1 behaves identically on 13.0.3 and
331-
// 12.11.1, so 13 is right and the upstream range is stale.
331+
// 12.11.1; re-measured on the pinned 1.7.2 (#16813), the published package
332+
// does not reference better-sqlite3 in any file, so no call site of ours
333+
// can reach an API that moved 12 -> 13. 13 is right, the range is stale.
334+
// The declaration this widening is ABOUT is pinned in
335+
// `test/better-sqlite3-peer-declaration.pin.test.ts`.
332336
expect(SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']).toBe('13');
333337
expect(settings).toMatch(/^ {4}'better-auth>better-sqlite3': '13'$/m);
334338
});

packages/create-objectstack/src/template-consistency.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,11 +503,16 @@ describe('blank template peer-skew declarations (#10326)', () => {
503503
const allowed = /^ {2}allowedVersions:\n((?:[ \t]+.*\n?)*)/m.exec(settings)?.[1] ?? '';
504504

505505
it('declares the stale better-auth > better-sqlite3 peer', () => {
506-
// better-auth 1.7.1 peers `^12.0.0`; @objectstack/driver-sql resolves 13.x.
506+
// better-auth peers `^12.0.0`; the tree resolves 13.x — @objectstack/cli's
507+
// own optionalDependency, which is the copy pnpm names in the report on the
508+
// cli -> runtime -> plugin-auth -> better-auth chain (#16813; this comment
509+
// used to credit @objectstack/driver-sql, which is not the binding copy).
507510
// The peer is optional and governs only a raw better-sqlite3 `Database`
508511
// handed to better-auth's `database` option — a path ObjectStack never
509512
// takes (AuthManager passes an ObjectQL adapter factory). Measured on the
510-
// path it does govern, 1.7.1 behaves identically on 13.0.3 and 12.11.1.
513+
// path it does govern, 1.7.1 behaves identically on 13.0.3 and 12.11.1;
514+
// re-measured on 1.7.2, the published package references better-sqlite3 in
515+
// no file at all, so there is no call site to be incompatible.
511516
expect(
512517
/^\s*'better-auth>better-sqlite3':\s*'13'\s*$/m.test(allowed),
513518
"allowedVersions must widen better-auth's stale better-sqlite3 peer to 13",

packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,18 @@ allowBuilds:
3636
# first thing a brand-new project shows you — and none is a real
3737
# incompatibility:
3838
#
39-
# better-auth peers better-sqlite3 ^12.0.0 while the tree resolves 13.x. That
40-
# peer is optional and covers handing better-auth a raw better-sqlite3
41-
# `Database`; ObjectStack hands it an ObjectQL adapter instead, so nothing
42-
# here goes down that path. Measured on the configuration the range does
43-
# cover — better-auth's own Kysely dialect, running its migrations, sign-up,
44-
# sign-in and adapter find/update/delete — 1.7.1 behaves identically on
45-
# better-sqlite3 13.0.3 and on 12.11.1. The upstream range is stale.
39+
# better-auth peers better-sqlite3 ^12.0.0 while the tree resolves 13.x — the
40+
# copy @objectstack/cli declares for its own sqlite tooling, and the one pnpm
41+
# names in the report. That peer is optional and covers handing better-auth a
42+
# raw better-sqlite3 `Database`; ObjectStack hands it an ObjectQL adapter
43+
# instead, so nothing here goes down that path. First measured behaviourally
44+
# on 1.7.1 — better-auth's own Kysely dialect, running its migrations,
45+
# sign-up, sign-in and adapter find/update/delete, behaves identically on
46+
# better-sqlite3 13.0.3 and on 12.11.1. Re-measured on the pinned 1.7.2 and
47+
# now structural: no file in the published package references better-sqlite3
48+
# at all, so no better-auth call site can depend on what moved between 12 and
49+
# 13. The upstream range is stale. Pinning our own declaration back to 12
50+
# would clear the report only by installing a second, unused native copy.
4651
#
4752
# (The '@better-auth/scim>better-call' entry that used to sit here retired
4853
# with the scim release-candidate pin — stable @better-auth/scim 1.7.1 peers

0 commit comments

Comments
 (0)