Repository navigation
refactor(plugin-auth): move AuthManager's plugin assembly helpers into auth-manager-plugin-assembly.ts (#25224 cut 6) - #25400
Conversation
…o auth-manager-plugin-assembly.ts Three method bodies move unedited: addOptionalPlugin, which keeps one optional better-auth plugin's construction failure from taking down core auth; buildJwtPlugin, which builds the jwt plugin with an algorithm this host can use and a non-fatal /get-session hook; and reportJwtSigningFailure, which records and announces a signing failure once. The class keeps all three methods, their docblocks and signatures, and hands each body the members it reads. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr
📓 Docs Drift Check11 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9781c84984b40c0b9888a9e301d2a3c22e60279d && git checkout 9781c84984b40c0b9888a9e301d2a3c22e60279d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7c68b108252ece317efb6672b92ef768da81fff0 eda627786625ca08f614a270b1529f6a4444fc23 && git checkout -B drift-repro 7c68b108252ece317efb6672b92ef768da81fff0 && git merge --no-ff eda627786625ca08f614a270b1529f6a4444fc23
node scripts/docs-audit/affected-docs.mjs --json 7c68b108252ece317efb6672b92ef768da81fff0 |
席位核对 · ACCEPT · 2026-10-23T09:29Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25224
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
The basis for the
Clause-②: noline: this is a move. No accept set, export, route, refusal, code or answer changes. The.d.tsbar is ruling6348891160item 1 (on #25222, covering this card).@objectstack/plugin-auth's publisheddist/index.d.tsanddist/index.d.mtsare raw byte-identical to base (0 diff lines, so the union-order normalisation has nothing to do), with the ruling's positive control. Its entry exports the same 155 names under ESM and CJS (measured below).Cut 6 of the
auth-manager.tssplit: the plugin assembly helpers. ThreeAuthManagermethod bodies (85 lines) move unedited, re-indented one level, into one new module,packages/plugins/plugin-auth/src/auth-manager-plugin-assembly.ts. #25224 stays open for the later cuts.This follows the amended claim
6361212912, which amends6359424739on the file cut 5 left (#25395, landed note6360455119). The shape is cuts 1 to 5's, which is #23050's.Branch:
claude/project-thread-lg3wrj, the harness-pinned branch for this session, restarted fromorigin/maineca6b61922after #25395 merged. It names no issue, so the card relation is this body's first line.auth-manager.tsgoes from 8,968 to 8,916 lines. The cut is +202 / −89 over 2 files: 291 changed lines, under the 3,000-line line. It is not a governed surface (check-governed-merges.mjs --branch HEAD: 0 of the 2 paths hit the register).Why this seam
These are the three helpers
buildPluginListcalls to assemble better-auth's plugins:addOptionalPlugin: one optional plugin whose construction throws is skipped, recorded indegradedFeaturesand logged, so it cannot take down core email/password and session auth (the 15.1.0 incident its docblock records);buildJwtPlugin: thejwtplugin, built with a signing algorithm this host can use (the Ed25519 probe), the signing-key reader, and the/get-sessionheader hook made non-fatal;reportJwtSigningFailure: the record and once-per-build announcement of a JWT signing failure that hook catches.What stays, and why:
resolvePasswordHasherstays. Its body readsisWebContainerRuntime, a module-level function thatauth-manager.tsdoes not export and thatinstallWebContainerRequestStatePolyfillalso reads.index.tsre-exportsauth-manager.tswhole (export * from './auth-manager.js'), so exporting it there would publish it, and moving it would move a declaration the polyfill shares. Per the landed note6360455119, a body that reads an unexported module helper comes back asneeds_decisionbefore it is cut.getDegradedAuthFeaturesstays: it is the public, one-line read of the recordaddOptionalPluginwrites.buildPluginList, which calls all three, is a later cut.createDatabaseConfig,resolveAuthSecretandgenerateSecretare the database and secretcreateAuthInstancehands better-auth, a different area, so they take their own cut. The seat agreed both splits.What moved
addOptionalPlugin:3178degradedFeaturesbuildJwtPlugin:3242config,ed25519ProbeResult(read and written),reportJwtSigningFailurereportJwtSigningFailure:3308degradedFeatures,jwtSigningFailureReported(read and written)Lines are base
eca6b61922. The members come from a walk of everythis.Xin each body with the TypeScript compiler API. No body has a barethis,this[...],argumentsorsuper, or athisinside afunctionor a nested class. No body reads a static (AuthManager.X) or a module-level declaration ofauth-manager.ts. The two assignments to athismember arebuildJwtPlugin's toed25519ProbeResultandreportJwtSigningFailure's tojwtSigningFailureReported.Imports. Six imported names are read only by the moved text, so they leave
auth-manager.tsand the new module imports each from the same place:buildJwtPluginSchema(./auth-schema-config.js, still imported byauth-manager.tsfor ten other names);createSigningKeyReader(./signing-key-read-cache.js, still imported there forretireHeldKeyringsandUNKNOWN_SIGNING_KEY_REREAD_FLOOR_MS);createHostUsableJwksReader,probeEd25519Support,protectGetSessionJwtHookandresolveJwtSigningAlgorithm(./jwt-key-algorithm.js).Evaluation order.
auth-manager.tsno longer imports./jwt-key-algorithm.jsitself. Nothing observable moves: that module has no imports, and its top level is threeas constobject literals, a type alias, interfaces and function declarations, so the point at which it is evaluated cannot be observed.signing-key-read-cache.tsimports it type-only.The shape
.d.tslists each one. Each body is now a host literal of exactly the members that body reads, typed as aPickofPluginAssemblyHost, plus.call.PluginAssemblyHostlives in the new module. It names the 5 members the bodies read, each typed as the class member (AuthManager['degradedFeatures'], …). The module importsAuthManageras a type only, so there is no runtime cycle.reportJwtSigningFailuregoes as a late-bound closure ((...a) => this.reportJwtSigningFailure(...a)). The/get-sessionguard calls it at request time, long afterbuildJwtPluginreturned, and still reaches the method on the manager, so a spy or double set there is still the one it reaches.configanddegradedFeaturesgo as getters. The class reassignsconfigafter construction (setRuntimeBaseUrl,applyConfigPatch), so it is read off the manager when a body reads it, as before.degradedFeaturesis read the same way.ed25519ProbeResultandjwtSigningFailureReportedgo as get/set pairs. The bodies store to both on the manager. So the Ed25519 probe still runs once per manager, the() => this.ed25519ProbeResult!thunk still reads the manager's promise when better-auth calls it, and a signing failure is still announced once per plugin-list build, which resets the flag (buildPluginList, which stays).async. Each returns the moved async function's own promise, so a caller awaits the same promise the body settles. Building the host cannot throw, so no method now throws synchronously where it used to reject.reportJwtSigningFailurewas synchronous and stays so.buildJwtPluginandaddOptionalPluginrun once per plugin-list build;reportJwtSigningFailureruns only on a signing failure.Path readers
None needs migrating: no reader keys on a moved line. Measured as reading no moved line:
api-error-code.pin.test.ts'sauthManagerFilesfloor: nonew APIError(sits in the moved bodies, so the floor reads the same sites over the same files at base and head, and the new module is not added to the list.better-auth-schema-parity.test.tsandmanaged-extension-fields.test.ts: the moved text has noawait import(.buildJwtPlugintakes thejwtfactory as a parameter, and the factory's import stays inbuildPluginList.check-dispatcher-error-vocabulary.mjs,check-auth-mount-ledger.mjsandauth-manager-base-path.test.ts: none of the tokens they read sits in the moved text.check-dispatcher-error-vocabularyreads 56 sites found and 56 classified at base and at head.isSystemread. The census reads 148 elevation read sites at base and at head.scripts/adr-anchors/: the moved text cites no ADR, and no entry's invariant describes it. Theauth-manager.tsentry (ADR-0093 and ADR-0108) is unchanged and still holds.auth-manager.jwt-eddsa-fallback.test.ts(the host without Ed25519, an existing EdDSA key hidden and never deleted, and the signing failure recorded and announced once) andauth-manager.optional-plugin-isolation.test.ts(a throwing optional plugin skipped while sign-up and get-session still answer, and the degraded state reset on rebuild), call each through the class method. All pass at head (below).auth-manager.ts#symbolanchor in the tracked tree outside changelogs was mapped withsymbolSegmentResolutionfromscripts/symbol-anchors.mjs. There are 29, in ADR-0127, ADR-0134, ADR-0135 and four platform-checklist areas, and all 29 aredeclaration-class at base, with the 3 bodies blanked, and at head. None names a moved method. One names a token the moved text also carries:#AuthManager(ADR-0135), in the bodies'[AuthManager]log prefixes. It resolves on the class declaration, which stays.check-adr-anchors' decision-letter layer (the seat asks each cut's report for this count at both ends): 2,016 citations at base and 2,016 at head. The moved text carries noADR-NNNN Dkcitation, so nothing leaves the anchored surface. The number layer reads 52,116 citations across 6,623 files at both. Onorigin/main7c68b10825(below) the layer reads 2,014 alone and 2,014 with this cut.Proof
Builds and suites ran through
os-verify-lock, and every exit code was captured before any pipe. Baseeca6b61922, headeda6277866. A fullturbo run buildran first. Thenplugin-authwas built and its suite run at base, then built and run at head, on the same dependency dists.1. The moved text.
asyncdropped from the two that had it.addOptionalPluginbuildJwtPluginreportJwtSigningFailure'jwtSigning'→'jwtSigned'inreportJwtSigningFailure) makes that method fail (tokens and lines DIFFER), and the run exits 1. Restored afterwards;cmpagainst the saved copy was clean.git diff --color-moved=plain --color-moved-ws=allow-indentation-change eca6b61922 eda6277866over the two source files marks 85 of the 89 deleted lines and 96 of the 202 added lines as moved.asyncsignature lines and two import-name lines (buildJwtPluginSchema,createSigningKeyReader). The./jwt-key-algorithm.jsimport block is marked moved: the module carries it verbatim.PluginAssemblyHost, the module header, the per-function pointer docblocks and the import blocks.2.
.d.tsand exports (ruling6348891160items 1 and 2).dist/index.d.ts3b3aedb6551d6324c40d87afd1cdee50b23bcb2c3384c39a9f71b771deca04723b3aedb6551d6324c40d87afd1cdee50b23bcb2c3384c39a9f71b771deca0472d4a901d16c32f4838c14dd7fdc52d88d9d446b4b1d44afd4012c81b365ef9783d4a901d16c32f4838c14dd7fdc52d88d9d446b4b1d44afd4012c81b365ef9783dist/index.d.mtse2cdb2bc530cc6a554107866875910a90031e42fc89574d9adcb00d38a1becf1e2cdb2bc530cc6a554107866875910a90031e42fc89574d9adcb00d38a1becf1b2c585e677d5ac57274c22d746f6213342fbf1e7c8061b56b94175e8fe21c19db2c585e677d5ac57274c22d746f6213342fbf1e7c8061b56b94175e8fe21c19dnode -e 'const s=require("fs").readFileSync(process.argv[1],"utf8");process.stdout.write(s.replace(/"[^"\n]*"(?:\s*\|\s*"[^"\n]*")+/g,r=>r.split(/\s*\|\s*/).sort().join(" | ")))' dist/index.d.ts | sha256sum.dist/index.d.tswith one private member made public (private buildJwtPlugin;→buildJwtPlugin;, line 1881, a one-line change) hashes raw8eaebd38dabcfa33bbe0db93e6bd2c8836c91a169d81c8e6aa2ec4e78055b2deand normalised4c5bbc92792de5db2c8f1b89018fd45d4aea96fe43b7479e3c934db9069c6169. Both differ from the readings above.dist/index.mjsand CJSdist/index.jseach have 155 names at base and at head, the same list (the sha256 of the sorted names ised3ac8b902031d30…on all four).dist/on both sides, the same list. Onlyindex.js,index.mjs, their two.mapfiles and the two build-input hash stamps differ, as a move of code inside the bundle must.3. Tests and lint.
pnpm exec vitest run --maxWorkers=3inpackages/plugins/plugin-auth: baseTest Files 187 passed (187),Tests 3528 passed | 10 skipped (3538), VERDICT command-exit 0. Head: the same counts, VERDICT command-exit 0.pnpm --filter @objectstack/plugin-auth typecheck(bothtscprojects andcheck:test-typecheck): exit 0.eslint --no-inline-config(the rootlintscript's binary and flags) over the 2 files: exit 0.4. The census and the anchors, at base and at head.
node scripts/tenant-audit-census.mjs --json), compared per site with the new module's file name mapped back toauth-manager.ts: 237 sites on both sides, 0 per-site differences in object, verb, tenancy, context and elevation, 0 unresolved and 0 unledgered on both, and every total the same.dispatch-gatesdoes not derive the ADR one. All six exit 0 at both, with the same reading at both:check-adr-symbol-anchors: 2,384 anchors across 142 records, 466 symbol (440 declaration, 26 literal).check-adr-anchors: 80 anchored files; the decision-letter layer as above.check-scripts-symbol-anchors(4,061 anchors),check-spec-docblock-symbol-anchors(5,071 anchors, both read in the same built tree),check-system-context-census(148 elevation read sites) andcheck-platform-checklist(16 areas, 329 items).5. Gates.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 53 commands for the 2 paths. All 53 ran ateda6277866after the full build, and all 53 exited 0.--ran: 53 derived, 53 run, 0 NOT-MEASURED.check-governed-merges.mjs --branch HEAD: NOT governed, 291 changed lines.node scripts/docs-audit/affected-docs.mjs --json eca6b61922, exit 0): 0 pages name something this change touched (11 anchors: 7 symbol, 4 literal).auth-manager.ts, fix(plugin-auth): write the session last_activity_at heartbeat on every deployment #25398, in the docblock of theenforceSessionControlsstub, not a line of this cut. It has since merged, with feat(plugin-auth): Create User can grant a permission set in the same step, through the delegated-administration gate #25381, refactor(plugin-security): move the rest of the tenant write wall and the context prologue out of security-plugin.ts (#25223 cut 6) #25396 and feat(spec): a dataset dimension may name its null bucket withnullLabel#25397, andorigin/mainis7c68b10825. On that tree with this cut applied (no conflict):7c68b10825passes 3 of 3;check-tenant-audit-census,check-adr-anchors,check-adr-symbol-anchors,check-platform-checklist,check-system-context-census,check-auth-mount-ledgerandcheck-dispatcher-error-vocabularyexit 0;auth-manager.ts: fix(plugin-auth)!: a cloud-managed environment refuses local self-registration, invitations (resends included), user creation and import, and sets passwords only on local accounts #25399, insidecreateAuthInstanceandbuildPluginList, not a line of this cut.git merge-treeof this head with fix(plugin-auth)!: a cloud-managed environment refuses local self-registration, invitations (resends included), user creation and import, and sets passwords only on local accounts #25399's head is clean.Changeset
None:
skip-changeset, per ruling6348891160item 4. The published declarations and export lists are unchanged, and the bundle's bytes move only because code moved inside it.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr