diff --git a/.changeset/25123-scaffold-peer-ledger-retired.md b/.changeset/25123-scaffold-peer-ledger-retired.md new file mode 100644 index 00000000000..aa784cbc5ad --- /dev/null +++ b/.changeset/25123-scaffold-peer-ledger-retired.md @@ -0,0 +1,11 @@ +--- +'@objectstack/cli': patch +'create-objectstack': patch +--- + +fix(cli): retire the scaffold's five stale `peerDependencyRules` entries on the better-auth 1.7.12 pin + +Clause-②: no +Metadata-protocol: none + +A project scaffolded by `objectstack init` or `npx create-objectstack` no longer carries a `peerDependencyRules.allowedVersions` block in its `pnpm-workspace.yaml`. All five entries suppressed peer mismatches that better-auth 1.7.12 no longer has: `better-auth` declares no `better-sqlite3` peer, and `@better-auth/core`, `@better-auth/oauth-provider`, `@better-auth/scim` and `@better-auth/sso` each peer `@better-auth/utils` as `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 satisfies. `allowedVersions` only silences a report, so a scaffold resolves the same lockfile as before and its first `pnpm install` stays free of unmet-peer warnings. Projects scaffolded earlier can keep or delete the block; either way nothing changes. diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 534cdb0e4e8..73c71be9fc9 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -128,120 +128,38 @@ export const SCAFFOLD_BUILT_DEPENDENCIES = ['better-sqlite3', 'esbuild']; * states, keyed `>` — pnpm's scoped `allowedVersions` * spelling, so each entry widens exactly one declaration and nothing else. * - * Every one of them is reported by `pnpm install` on a brand-new scaffold, and - * none is a real incompatibility. They are declared here because that report is - * the first thing a newcomer sees, on the one screen where they are deciding - * whether this project is solid, and there is nothing they did to cause it. - * - * - `better-auth>better-sqlite3` — better-auth peers `^12.0.0` while the tree - * resolves 13.x. The peer is OPTIONAL and governs one configuration only: a - * raw better-sqlite3 `Database` handed to better-auth's `database` option. - * ObjectStack never does that — `AuthManager.createDatabaseConfig()` passes - * an ObjectQL adapter factory (or `undefined`, better-auth's own in-memory - * adapter). So the upstream range is stale and 13 is right — widening is - * the correct remedy, not pinning our own declaration back to 12. - * - * RE-MEASURED on the pinned 1.7.2 (#16813). The original reading was taken - * on 1.7.1 (commit 675ab574e) and was behavioural: better-auth's own Kysely dialect — - * migrations, sign-up, sign-in, adapter find/update/delete — behaves - * identically on better-sqlite3 13.0.3 and on 12.11.1. 1.7.2 makes that - * structural instead of empirical: of the 464 files in the published - * `better-auth@1.7.2` tarball, exactly ONE names better-sqlite3 — - * `package.json`, i.e. the peer declaration itself. Zero code files - * reference it (positive control: `kysely` names 9). better-auth never - * imports the package; it accepts a `Database` the CALLER constructs and - * hands it to Kysely, and its own sqlite test path uses node's built-in - * `node:sqlite` `DatabaseSync`. There is therefore no better-auth call site - * that could touch an API moved between better-sqlite3 12 and 13 — the - * range is a statement about an instance we never supply. Re-read - * 2026-09-11 on the 1.7.3 pin (the rendered file's prose carried this - * reading until it shrank to one line per block): the published package - * still names better-sqlite3 only in that peer declaration. - * - * ⚠️ TWO CORRECTIONS to what this entry used to say, both measured: - * • the 13.x copy better-auth binds to is `@objectstack/cli`'s OWN - * `optionalDependencies` entry, NOT `@objectstack/driver-sql`'s. On the - * chain that actually reports (`cli` → `runtime` → `plugin-auth` → - * `better-auth`) the CLI is the ancestor, so its copy is the one pnpm - * resolves the peer against — pnpm names it in the warning itself - * ("found 13.0.3 in @objectstack/cli"). Editing driver-sql alone would - * not move this line. - * • pinning the CLI back to `^12` is not a neutral alternative. Measured - * on a bare project depending on `@objectstack/cli@17.3.0`, it clears - * the report only by installing a SECOND native better-sqlite3 - * (12.11.1 alongside 13.0.3), and the 12 copy is dead weight — the CLI - * loads better-sqlite3 itself (`src/utils/sqlite-occupancy.ts`) and - * knex resolves 13.x through driver-sql regardless. This - * `allowedVersions` entry clears the same report with the resolution - * byte-identical (0 lines of lockfile diff). + * An entry belongs here only while `pnpm install` on a brand-new scaffold + * reports that skew and the skew is measured harmless: that report is the first + * thing a newcomer sees, on the one screen where they are deciding whether this + * project is solid, and there is nothing they did to cause it. An entry whose + * skew is gone retires at once — a suppression with no skew behind it leaves + * the NEXT real skew pre-silenced. + * + * EMPTY today, so `renderPnpmWorkspaceYaml` emits no `peerDependencyRules` + * block at all. Every entry this map has carried is retired, each read on the + * better-auth `1.7.12` pin off the lockfile's `peerDependencies` blocks and + * the published manifests: + * + * - RETIRED: `better-auth>better-sqlite3` (`'13'`). better-auth used to peer + * an optional `better-sqlite3@^12.0.0` while the tree resolves 13.x (1.7.3 + * still declared it). `better-auth@1.7.12` declares no better-sqlite3 peer + * at all, so there is nothing left to widen. The CLI's own 13.x declaration + * is held by `test/better-sqlite3-peer-declaration.pin.test.ts`. + * + * - RETIRED: `@better-auth/core`, `/oauth-provider`, `/scim` and `/sso`, each + * `>@better-auth/utils` (`'0.5.0'`). The four used to peer an EXACT + * `@better-auth/utils@0.4.2` while the tree resolves 0.5.0. At 1.7.12 all + * four peer `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 satisfies. * * - RETIRED (#3653): `@better-auth/scim>better-call` — the rc.1-era scim pin - * peered an exact `better-call@1.3.7` against the host's 1.4.0, and this - * map declared that skew away. Stable scim peers `better-call@1.4.0`, so - * the skew is genuinely gone and the entry retired WITH the pin move, as - * its ratchet demanded. Measured on 1.7.1 when #3653 landed; re-read - * 2026-09-12 on `1.7.3`, the pin today, off the installed manifest — - * `peerDependencies['better-call']` is the exact string `1.4.0`. - * - * - `@better-auth/utils` — `@better-auth/core`, `/oauth-provider`, - * `/scim` and `/sso` each peer an EXACT `@better-auth/utils@0.4.2`, while a - * scaffolded tree hands them 0.5.0. The 0.5.0 comes from `better-call@1.4.0` - * (better-auth's own HTTP layer), which DEPENDS on `^0.5.0`; - * `@objectstack/plugin-auth` names the four packages as direct dependencies - * without naming utils, so pnpm satisfies their peer from better-call's copy - * rather than from better-auth's own exact 0.4.2 dependency. - * - * Measured compatible rather than assumed. Those four import exactly three - * symbols across two subpaths — `base64`/`base64Url` (`/base64`), - * `createHash` (`/hash`) and, in core only, `createRandomStringGenerator` - * (`/random`). 0.5.0 exports all three with identical signatures; `/random` - * is unchanged apart from formatting, `/base64` swaps `new Uint8Array(data)` - * for a helper that IS `new Uint8Array(data)` on non-strings, and `/hash` - * only widens its input coercion for views not backed by a plain - * ArrayBuffer. Run against the input shapes those call sites actually pass, - * 0.4.2 and 0.5.0 agree on every value; run end to end (better-auth with the - * sso, oauth-provider and scim plugins), a tree where the four resolve 0.5.0 - * and one where they resolve 0.4.2 produce the same transcript — sign-up, - * sign-in, session, both OAuth metadata documents, the RFC 7636 PKCE - * challenge, and the SCIM and SSO endpoint outcomes. + * peered an exact `better-call@1.3.7` against the host's 1.4.0. Stable + * scim peers better-call exactly at the version the tree resolves: the + * string `1.4.4` on the 1.7.12 pin today. * - * ⛔ A resolution change is the WRONG remedy here, and was measured too: - * pinning utils back to 0.4.2 clears the four lines only by dragging - * `better-call@1.4.0` off its own declared `^0.5.0` — manufacturing one real - * range violation to silence four benign ones. - * - * Spelled `0.5.0` exactly, not `0.5`: 0.5.0 is the version that was - * measured, and a future 0.6.0 SHOULD report again rather than inherit this - * finding. - * - * ⚠️ These four do NOT retire with the SCIM rc pin, even though one of them - * names scim. Stable `@better-auth/scim@1.7.3` still peers - * `@better-auth/utils@0.4.2` EXACTLY — re-read (2026-09-11) off the - * installed package manifest, unchanged from 1.7.1 and 1.7.2 — so this skew - * outlives that pin. They retire - * when the four packages accept 0.5.0 upstream, or when - * `SCAFFOLD_PNPM_RANGE` reaches `>=10.31` — pnpm 10.31 changed peer - * resolution so that all four land on 0.4.2 by themselves. Measured on the - * rendered scaffold, one clean resolve per pnpm version: - * - * pnpm 10.15.0 – 10.30.0 all four reported as unmet peers. - * pnpm >= 10.31.0 resolved to 0.4.2; nothing to report. - * - * `allowedVersions` suppresses the report ONLY; it moves no resolution — the - * lockfile a scaffold resolves is byte-identical with and without this block - * (verified by digest on pnpm 10.15.0 and 10.30.0). + * `allowedVersions` suppresses the report ONLY; it moves no resolution, so a + * retirement leaves the lockfile a scaffold resolves unchanged. */ -export const SCAFFOLD_ALLOWED_PEER_VERSIONS: Record = { - 'better-auth>better-sqlite3': '13', - // '@better-auth/scim>better-call' retired with the scim rc pin (#3653) — - // stable scim peers better-call@1.4.0 exactly (re-read 2026-09-12 on the - // 1.7.3 pin), the copy every install already resolves. init.test.ts pins - // its ABSENCE now. - '@better-auth/core>@better-auth/utils': '0.5.0', - '@better-auth/oauth-provider>@better-auth/utils': '0.5.0', - '@better-auth/scim>@better-auth/utils': '0.5.0', - '@better-auth/sso>@better-auth/utils': '0.5.0', -}; +export const SCAFFOLD_ALLOWED_PEER_VERSIONS: Record = {}; /** * Lowest pnpm that can actually install this scaffold, declared as @@ -696,8 +614,9 @@ export function renderScaffoldPackageJson( * allowBuilds and `SCAFFOLD_BUILT_DEPENDENCIES` (why each * package needs its build). * peerDependencyRules `SCAFFOLD_ALLOWED_PEER_VERSIONS`: one measured - * paragraph per entry, its retirement condition, - * and the retired `@better-auth/scim>better-call`. + * paragraph per entry and the retired entries. + * Emitted only while that map is non-empty; it is + * empty today, so the block is absent. * * The file carried 57 comment lines here, and 66 in the template, until the * maintainer asked for it to stay minimal with the rationale moved into the diff --git a/packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts b/packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts index 4224bf1b088..4dc6724e786 100644 --- a/packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts +++ b/packages/cli/test/better-sqlite3-peer-declaration.pin.test.ts @@ -2,7 +2,7 @@ /** * **The pin for `@objectstack/cli`'s own `better-sqlite3` declaration**, and - * for its agreement with the scaffold widening that exists because of it + * for the retirement of the scaffold widening that once existed because of it * (#16813). * * ## The report this is about @@ -49,20 +49,15 @@ * missing was not a different range but a gate holding the range to the * reasoning that justifies it, which is what this file is. * - * ## Why the two assertions are one pin and not two + * ## The scaffold widening is retired * - * `SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']` widens - * better-auth's peer to a **major**, and the only reason that major is right - * is the declaration below. Held apart, either can move alone and stay green: - * a declaration lifted to `^14` leaves the scaffold suppressing a report it no - * longer matches (so the NEXT real skew arrives pre-silenced), and a - * declaration dropped to `^12` leaves a suppression with no skew behind it — - * the exact shape `init.test.ts` refuses for the retired - * `@better-auth/scim>better-call` entry. So the major is read out of the - * manifest and compared, rather than written down twice. - * - * `init.test.ts` pins the widening's literal value and its rendering; this - * file pins what the widening is ABOUT. Neither restates the other. + * `SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']` used to widen + * better-auth's peer to this declaration's major, and this file held the two + * equal. `better-auth@1.7.12` declares no better-sqlite3 peer at all, so the + * report above no longer exists and the widening retired with it: a + * suppression with no skew behind it leaves the NEXT real skew pre-silenced. + * This file now pins its absence; the 13.x declaration below stands on the + * readings above on its own. * * The manifest read stays inside this package (`test/` → package root), so it * is not a `check:cross-package-test-inputs` escape and needs no declaration. @@ -103,7 +98,7 @@ describe('@objectstack/cli better-sqlite3 declaration (#16813)', () => { expect(manifest.dependencies?.['better-sqlite3']).toBeUndefined(); }); - it('keeps the declaration on one major, so the scaffold can widen to it', () => { + it('keeps the declaration on one major, so the retreat check below can read it', () => { const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? ''; expect( soleMajor(declared), @@ -111,20 +106,18 @@ describe('@objectstack/cli better-sqlite3 declaration (#16813)', () => { ).not.toBeNull(); }); - it('widens better-auth\'s peer to the major this package actually declares', () => { - // ⛔ Not "to 13" — that is `init.test.ts`'s assertion. This one says the - // widening and the declaration are the SAME major, so neither can move - // without the other. - const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? ''; - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[WIDENING_KEY]).toBe(soleMajor(declared)); + it('carries no scaffold widening for better-auth\'s retired better-sqlite3 peer', () => { + // better-auth@1.7.12 declares no better-sqlite3 peer, so there is nothing + // to widen. Re-adding the entry needs a new measured skew, not a revert. + expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[WIDENING_KEY]).toBeUndefined(); }); it('refuses a retreat to better-sqlite3 12 (the report is not the defect)', () => { // The failure this guards is a well-meant one: reading the unmet-peer // report as a defect and "fixing" it here. Measured, that clears the line // only by installing a second native better-sqlite3 that nothing loads — - // see this file's header for the three readings. If better-auth ever - // WIDENS its peer upstream, the right change is to retire the scaffold + // see this file's header for the three readings. When better-auth dropped + // the peer upstream (1.7.12), the right change was to retire the scaffold // entry, not to move this declaration. const declared = manifest.optionalDependencies?.['better-sqlite3'] ?? ''; expect(soleMajor(declared)).not.toBe('12'); diff --git a/packages/cli/test/init.test.ts b/packages/cli/test/init.test.ts index ea9a8c8218c..db1a7608e99 100644 --- a/packages/cli/test/init.test.ts +++ b/packages/cli/test/init.test.ts @@ -163,10 +163,11 @@ describe('explicit empty workspace declaration in the rendered file', () => { }); it('adds no other top-level setting to the rendered file', () => { - // The rest of the file is what it was: the same four keys, same order. A + // The rest of the file is what it was: the same three keys, same order + // (`peerDependencyRules` is absent while no peer skew is declared). A // "restore the packages key" edit that also drags a setting in fails here. const keys = [...settings.matchAll(/^([A-Za-z][\w-]*):/gm)].map((m) => m[1]); - expect(keys).toEqual(['packages', 'onlyBuiltDependencies', 'allowBuilds', 'peerDependencyRules']); + expect(keys).toEqual(['packages', 'onlyBuiltDependencies', 'allowBuilds']); }); }); @@ -311,119 +312,64 @@ describe('pnpm 11 build approvals in the rendered workspace file', () => { }); }); -// A brand-new scaffold's first `pnpm install` reported two unmet peers, on the -// one screen where a newcomer decides whether this project is solid, with -// nothing they did to cause it (commit 675ab574e). Both ranges belong to third-party -// packages we cannot edit, so the remedy is pnpm's scoped `allowedVersions` — -// and it must travel INSIDE the scaffold, because a `peerDependencyRules` block -// in this repo's own pnpm-workspace.yaml does not ship with published packages. +// A brand-new scaffold's first `pnpm install` reported unmet peers, on the one +// screen where a newcomer decides whether this project is solid, with nothing +// they did to cause it (commit 675ab574e). Those ranges belonged to third-party +// packages we cannot edit, so the remedy was pnpm's scoped `allowedVersions`, +// carried INSIDE the scaffold because a `peerDependencyRules` block in this +// repo's own pnpm-workspace.yaml does not ship with published packages. On the +// better-auth 1.7.12 pin every skew it declared is gone, and an entry whose +// skew is gone retires at once: a suppression with no skew behind it would +// hide the NEXT real peer break from a newcomer's first screen. Re-adding any +// entry needs a new measured skew, not a revert. describe('benign peer-skew declarations (#10326)', () => { // Comments in the rendered YAML explain these keys; they must not be what // satisfies an assertion about the keys themselves. const settings = renderPnpmWorkspaceYaml().replace(/^\s*#.*$/gm, ''); - it('widens better-auth\'s stale better-sqlite3 peer rather than pinning ours back', () => { - // better-auth peers `^12.0.0` while the tree resolves 13.x. The peer is - // OPTIONAL and governs one configuration only — a raw better-sqlite3 - // `Database` passed to better-auth's `database` option — which ObjectStack - // never does (AuthManager passes an ObjectQL adapter factory). Measured on - // the configuration it does govern, 1.7.1 behaves identically on 13.0.3 and - // 12.11.1; re-measured on the pinned 1.7.2 (#16813), the published package - // does not reference better-sqlite3 in any file, so no call site of ours - // can reach an API that moved 12 -> 13. 13 is right, the range is stale. - // The declaration this widening is ABOUT is pinned in - // `test/better-sqlite3-peer-declaration.pin.test.ts`. - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS['better-auth>better-sqlite3']).toBe('13'); - expect(settings).toMatch(/^ {4}'better-auth>better-sqlite3': '13'$/m); - }); - - it('keeps the retired @better-auth/scim>better-call suppression OUT (#3653)', () => { - // FLIPPED from a presence ratchet when the scim pin moved off the rc. - // Stable @better-auth/scim 1.7.1 peers better-call@1.4.0 exactly — the - // copy every install already resolves — so the skew the entry declared - // away no longer exists, and a suppression with no skew behind it would - // hide the NEXT real better-call peer break from a newcomer's first - // screen. Re-adding it needs a new measured skew, not a revert. - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS['@better-auth/scim>better-call']).toBeUndefined(); - expect(settings).not.toMatch(/'@better-auth\/scim>better-call'/); - }); - it.each([ - ['@better-auth/core'], - ['@better-auth/oauth-provider'], - ['@better-auth/scim'], - ['@better-auth/sso'], - ])('widens %s\'s exact @better-auth/utils peer to the version the tree resolves', (declaring) => { - // Each of the four peers an EXACT `@better-auth/utils@0.4.2`, while the - // tree hands them 0.5.0 — better-call (better-auth's own HTTP layer) - // depends on `^0.5.0`, and that is the copy plugin-auth's direct - // dependencies resolve their peer against. Measured on the surface the - // range governs: the four import three symbols in total (base64/base64Url, - // createHash, and createRandomStringGenerator in core), 0.5.0 declares all - // three unchanged, and both versions return identical values on the inputs - // those call sites pass — confirmed end to end through better-auth with the - // sso, oauth-provider and scim plugins. Pinning utils back instead would - // drag better-call off its own `^0.5.0`, trading four reported skews for a - // real one. - const key = `${declaring}>@better-auth/utils`; - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[key]).toBe('0.5.0'); - expect(settings).toMatch( - new RegExp(`^ {4}'${key.replace(/[/*+?^${}()|[\]\\]/g, '\\$&')}': '0\\.5\\.0'$`, 'm'), - ); - }); - - it('covers every declaring package that peers @better-auth/utils, not some of them', () => { - // The defect this replaces was PARTIAL coverage: two skews were declared - // and four more were not, so the first screen was clean for a third of the - // report. A set assertion is what fails when a fifth declaration appears - // and nobody measures it, or when one of these four is dropped while the - // others stay. - const declaring = Object.keys(SCAFFOLD_ALLOWED_PEER_VERSIONS) - .filter((k) => k.endsWith('>@better-auth/utils')) - .map((k) => k.slice(0, -'>@better-auth/utils'.length)) - .sort(); - expect(declaring).toEqual([ - '@better-auth/core', - '@better-auth/oauth-provider', - '@better-auth/scim', - '@better-auth/sso', - ]); - }); - - it('kept the @better-auth/utils widening when the better-call pin retired (#3653)', () => { - // @better-auth/scim appeared in TWO entries for two unrelated reasons, and - // they retire on different days — which HAPPENED in #3653: the better-call - // one went with the rc pin (stable 1.7.1 peers better-call 1.4.0), while - // the utils one outlives it (stable 1.7.1 still peers @better-auth/utils - // 0.4.2; its own retirement key is the pnpm 10.31 floor). Deleting both - // together — the obvious move when the rc pin lifted — would have silently - // put the utils report back on a newcomer's first screen; this pin is what - // stops that regression from ever landing quietly. - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS['@better-auth/scim>better-call']).toBeUndefined(); - expect(SCAFFOLD_ALLOWED_PEER_VERSIONS['@better-auth/scim>@better-auth/utils']).toBe('0.5.0'); - }); - - it('renders the rules under peerDependencyRules.allowedVersions', () => { - expect(settings).toMatch(/^peerDependencyRules:$/m); - expect(settings).toMatch(/^ {2}allowedVersions:$/m); - }); - - it('scopes every rule to one declaring package, never a bare peer name', () => { - // A bare `better-sqlite3: '13'` would silence that peer for every package - // declaring it — including one whose complaint would be real. - const keys = Object.keys(SCAFFOLD_ALLOWED_PEER_VERSIONS); - expect(keys.length).toBeGreaterThan(0); - for (const key of keys) { - expect(key, `"${key}" must be spelled >`).toContain('>'); - } - }); - - it('emits nothing at all when the caller supplies no rules', () => { + // better-auth@1.7.12 declares no better-sqlite3 peer at all (1.7.3 peered + // `^12.0.0`). The CLI's own 13.x declaration is pinned in + // `test/better-sqlite3-peer-declaration.pin.test.ts`. + ['better-auth>better-sqlite3'], + // Retired with the scim rc pin (#3653): stable scim peers better-call + // exactly at the copy every install resolves. + ['@better-auth/scim>better-call'], + // At 1.7.12 the four peer `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 + // satisfies; they used to peer an EXACT 0.4.2. + ['@better-auth/core>@better-auth/utils'], + ['@better-auth/oauth-provider>@better-auth/utils'], + ['@better-auth/scim>@better-auth/utils'], + ['@better-auth/sso>@better-auth/utils'], + ])('keeps the retired %s suppression OUT', (key) => { + expect(SCAFFOLD_ALLOWED_PEER_VERSIONS[key]).toBeUndefined(); + expect(settings).not.toContain(`'${key}'`); + }); + + it('declares no peer skew at all on the current pin', () => { + // A set assertion, so an entry added without a measured skew fails here + // rather than riding in beside the retirements above. + expect(SCAFFOLD_ALLOWED_PEER_VERSIONS).toEqual({}); + }); + + it('emits no peerDependencyRules block when the map is empty', () => { // The block is data-driven, so an empty map must not leave a dangling // `allowedVersions:` header claiming a declaration that is not there. + expect(settings).not.toMatch(/^peerDependencyRules:/m); + expect(settings).not.toMatch(/allowedVersions:/); const bare = renderPnpmWorkspaceYaml(SCAFFOLD_BUILT_DEPENDENCIES, {}); expect(bare).not.toMatch(/^peerDependencyRules:$/m); }); + + it('renders supplied rules under peerDependencyRules.allowedVersions', () => { + // The renderer path a future measured entry would take, held with a + // fixture now that the shipped map is empty. + const rendered = renderPnpmWorkspaceYaml(SCAFFOLD_BUILT_DEPENDENCIES, { 'pkg-a>peer-b': '2' }) + .replace(/^\s*#.*$/gm, ''); + expect(rendered).toMatch(/^peerDependencyRules:$/m); + expect(rendered).toMatch(/^ {2}allowedVersions:$/m); + expect(rendered).toMatch(/^ {4}'pkg-a>peer-b': '2'$/m); + }); }); describe('sanitizeNamespace', () => { diff --git a/packages/cli/test/scaffold-workspace-consistency.test.ts b/packages/cli/test/scaffold-workspace-consistency.test.ts index 3aa34de739e..04cf74eb7cb 100644 --- a/packages/cli/test/scaffold-workspace-consistency.test.ts +++ b/packages/cli/test/scaffold-workspace-consistency.test.ts @@ -290,15 +290,11 @@ describe('the two scaffold paths render the same pnpm build approvals (#10499)', const cli = declaredPeerSkews(RENDERED[CLI]); const template = declaredPeerSkews(RENDERED[TEMPLATE]); - // Non-vacuity: two empty maps compare equal while declaring nothing, which - // is the state that puts an unmet-peer report on a newcomer's first screen. - for (const [producer, skews] of [[CLI, cli], [TEMPLATE, template]] as const) { - expect( - Object.keys(skews).length, - `${producer} declares no peer skew at all — a scaffolded project's first ` + - '`pnpm install` then opens with an unmet-peer report the user did not cause', - ).toBeGreaterThan(0); - } + // Both maps are EMPTY on the better-auth 1.7.12 pin: every skew they + // declared is gone, so both files retire the block (see + // `SCAFFOLD_ALLOWED_PEER_VERSIONS`). Two empty maps comparing equal is the + // intended state, not a vacuous pass; a skew that reappears in one file + // only is still what this limb fails on. expect( cli, diff --git a/packages/create-objectstack/src/template-consistency.test.ts b/packages/create-objectstack/src/template-consistency.test.ts index 118a59c203c..683869072f4 100644 --- a/packages/create-objectstack/src/template-consistency.test.ts +++ b/packages/create-objectstack/src/template-consistency.test.ts @@ -481,7 +481,7 @@ describe('templates survive npm packing', () => { // ratcheted there. The two are one file (#22162): the same bytes, one comment // line per block, held by packages/cli/test/scaffold-workspace-consistency.test.ts. // So the measurements behind every block of this file — the pnpm version table, -// why each build is approved, each peer skew and when it retires — live in +// why each build is approved, each retired peer skew and why — live in // init.ts's docblocks (`renderPnpmWorkspaceYaml`, `SCAFFOLD_BUILT_DEPENDENCIES`, // `SCAFFOLD_ALLOWED_PEER_VERSIONS`), not in the file a new project opens. describe('blank template explicit empty workspace', () => { @@ -517,7 +517,7 @@ describe('blank template explicit empty workspace', () => { it('adds no other top-level setting to the bundled file', () => { const keys = [...settings.matchAll(/^([A-Za-z][\w-]*):/gm)].map((m) => m[1]); - expect(keys).toEqual(['packages', 'onlyBuiltDependencies', 'allowBuilds', 'peerDependencyRules']); + expect(keys).toEqual(['packages', 'onlyBuiltDependencies', 'allowBuilds']); }); }); @@ -564,13 +564,17 @@ describe('blank template pnpm build approvals (#3119)', () => { }); }); -// A brand-new scaffold's very first `pnpm install` reported two unmet peers — -// on the one screen where a newcomer is deciding whether this project is solid, -// with nothing they did to cause it and nothing they can do about it (until commit 675ab574e). -// Both are third-party ranges we cannot edit, so the declaration is pnpm's -// scoped `allowedVersions`, and it has to travel INSIDE the scaffold: a -// `peerDependencyRules` block in this repo's own pnpm-workspace.yaml would not -// ship with the published packages, exactly as the overrides note there says. +// A brand-new scaffold's very first `pnpm install` reported unmet peers — on +// the one screen where a newcomer is deciding whether this project is solid, +// with nothing they did to cause it (until commit 675ab574e). They were +// third-party ranges we cannot edit, so the declaration was pnpm's scoped +// `allowedVersions`, carried INSIDE the scaffold. On the better-auth 1.7.12 pin +// every skew it declared is gone, and an entry whose skew is gone retires at +// once: a suppression with no skew behind it would hide the NEXT real peer +// break from an `npx create-objectstack` user's first screen. Re-adding any +// entry needs a new measured skew, not a revert. The `objectstack init` +// renderer retires the same entries, and scaffold-workspace-consistency.test.ts +// in packages/cli is what fails if the two ever disagree. describe('blank template peer-skew declarations (#10326)', () => { const wsPath = path.join(pkgRoot, 'src', 'templates', 'blank', 'pnpm-workspace.yaml'); // Same comment-stripping as the block above: the prose explaining these keys @@ -578,81 +582,32 @@ describe('blank template peer-skew declarations (#10326)', () => { const settings = fs.existsSync(wsPath) ? fs.readFileSync(wsPath, 'utf8').replace(/^\s*#.*$/gm, '') : ''; - const allowed = /^ {2}allowedVersions:\n((?:[ \t]+.*\n?)*)/m.exec(settings)?.[1] ?? ''; - - it('declares the stale better-auth > better-sqlite3 peer', () => { - // better-auth peers `^12.0.0`; the tree resolves 13.x — @objectstack/cli's - // own optionalDependency, which is the copy pnpm names in the report on the - // cli -> runtime -> plugin-auth -> better-auth chain (#16813; this comment - // used to credit @objectstack/driver-sql, which is not the binding copy). - // The peer is optional and governs only a raw better-sqlite3 `Database` - // handed to better-auth's `database` option — a path ObjectStack never - // takes (AuthManager passes an ObjectQL adapter factory). Measured on the - // path it does govern, 1.7.1 behaves identically on 13.0.3 and 12.11.1; - // re-measured on 1.7.2, the published package references better-sqlite3 in - // no file at all, so there is no call site to be incompatible. - expect( - /^\s*'better-auth>better-sqlite3':\s*'13'\s*$/m.test(allowed), - "allowedVersions must widen better-auth's stale better-sqlite3 peer to 13", - ).toBe(true); - }); - it('keeps the retired @better-auth/scim > better-call suppression OUT (#3653)', () => { - // FLIPPED from a presence ratchet when the scim pin moved off the rc: - // stable @better-auth/scim 1.7.1 peers better-call@1.4.0 exactly, so the - // rc-era skew the entry declared away is gone. A suppression with no skew - // behind it would hide the NEXT real better-call peer break from an - // `npx create-objectstack` user's first screen — re-adding it needs a new - // measured skew, not a revert. - expect( - /'@better-auth\/scim>better-call'/.test(allowed), - 'the retired @better-auth/scim>better-call suppression must stay out of allowedVersions', - ).toBe(false); + it('ships a pnpm-workspace.yaml to read', () => { + // Every absence assertion below is vacuous on a missing file. + expect(settings).toMatch(/^allowBuilds:$/m); }); it.each([ - ['@better-auth/core'], - ['@better-auth/oauth-provider'], - ['@better-auth/scim'], - ['@better-auth/sso'], - ])('declares %s\'s exact @better-auth/utils peer against the resolved 0.5.0', (declaring) => { - // These four peer an EXACT @better-auth/utils 0.4.2 while the tree resolves - // 0.5.0 through better-call's `^0.5.0` dependency. Measured on the surface - // the range governs — the three symbols the four import — 0.4.2 and 0.5.0 - // agree on every value, so the report is the only difference. This template - // ships to `npx create-objectstack` users; the `objectstack init` renderer - // declares the same four, and scaffold-workspace-consistency.test.ts in - // packages/cli is what fails if the two ever disagree. - const key = `${declaring}>@better-auth/utils`.replace(/[/*+?^${}()|[\]\\]/g, '\\$&'); - expect( - new RegExp(`^\\s*'${key}':\\s*'0\\.5\\.0'\\s*$`, 'm').test(allowed), - `allowedVersions must widen ${declaring}'s @better-auth/utils peer to 0.5.0`, - ).toBe(true); - }); - - it('covers every declaring package that peers @better-auth/utils', () => { - // The defect was PARTIAL coverage — two skews declared, four left showing. - // A set assertion fails both ways: a fifth declaration nobody measured, and - // a drop of one of these four while the rest stay. - const declaring = [...allowed.matchAll(/^\s*'([^']+)>@better-auth\/utils':/gm)] - .map((m) => m[1]) - .sort(); - expect(declaring).toEqual([ - '@better-auth/core', - '@better-auth/oauth-provider', - '@better-auth/scim', - '@better-auth/sso', - ]); + // better-auth@1.7.12 declares no better-sqlite3 peer at all. + ['better-auth>better-sqlite3'], + // Retired with the scim rc pin (#3653). + ['@better-auth/scim>better-call'], + // At 1.7.12 the four peer `^0.4.2 || ^0.5.0`, which the resolved 0.5.0 + // satisfies; they used to peer an EXACT 0.4.2. + ['@better-auth/core>@better-auth/utils'], + ['@better-auth/oauth-provider>@better-auth/utils'], + ['@better-auth/scim>@better-auth/utils'], + ['@better-auth/sso>@better-auth/utils'], + ])('keeps the retired %s suppression OUT', (key) => { + expect(settings, `the retired ${key} suppression must stay out of the template`) + .not.toContain(`'${key}'`); }); - it('scopes every rule to one declaring package, never a bare peer name', () => { - // A bare `better-sqlite3: '13'` would silence that peer for EVERY package - // that declares it, including ones whose complaint would be real. - const entries = [...allowed.matchAll(/^\s*'([^']+)':/gm)].map((m) => m[1]); - expect(entries.length).toBeGreaterThan(0); - for (const key of entries) { - expect(key, `"${key}" must be spelled >`).toContain('>'); - } + it('declares no peerDependencyRules block while no skew is declared', () => { + // A bare header with no rules would advertise a declaration that is not there. + expect(settings).not.toMatch(/^peerDependencyRules:/m); + expect(settings).not.toMatch(/allowedVersions:/); }); }); diff --git a/packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml b/packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml index ef6296429b3..c2e34d1025e 100644 --- a/packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml +++ b/packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml @@ -10,12 +10,3 @@ onlyBuiltDependencies: allowBuilds: better-sqlite3: true esbuild: true - -# Upstream peer ranges measured harmless; this only silences the warning. -peerDependencyRules: - allowedVersions: - 'better-auth>better-sqlite3': '13' - '@better-auth/core>@better-auth/utils': '0.5.0' - '@better-auth/oauth-provider>@better-auth/utils': '0.5.0' - '@better-auth/scim>@better-auth/utils': '0.5.0' - '@better-auth/sso>@better-auth/utils': '0.5.0' diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d9b99fcddf1..a3fdbb96ed4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -161,9 +161,10 @@ patchedDependencies: # better-sqlite3 12.11.1 to satisfy it — which is why CI never saw what a # scaffolded project shows on its first screen. #10326 measured 1.7.1 as # behaviourally identical on 13.0.3 and 12.11.1 and left this workspace's -# resolution untouched; only the scaffolds declare it (their -# `better-auth>better-sqlite3` and `@better-auth/utils` entries do NOT -# retire with #3653 — their conditions are separate and unmet). +# resolution untouched; only the scaffolds declared it. Their +# `better-auth>better-sqlite3` and `@better-auth/utils` entries outlived +# #3653 and retired on the 1.7.12 pin, where better-auth declares no +# better-sqlite3 peer and the four utils peers accept `^0.5.0`. # `scripts/check-prerelease-pin-watch.mjs` now has nothing to watch and # says so — the self-retiring exit its own header promised. # - @better-auth/oauth-provider: GHSA-p2fr-6hmx-4528 — same better-auth