Skip to content

Commit 19d3720

Browse files
refactor(plugin-auth): move the better-auth global before-hook into auth-manager-before-hook.ts (#25224 cut 13) (#25548)
<!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuMjbPDrsigsyqoKsV4WZQMz)_ Part of #25224 Clause-②: no Metadata-protocol: none Ruling-ref: 6348891160, 6364006498 The basis for the `Clause-②: no` line: this is a move plus two path-reader migrations. No accept set, route, refusal, code or answer changes, and the published surface is measured unchanged: - `@objectstack/plugin-auth`'s `dist/index.d.ts` and `dist/index.d.mts` are raw byte-identical to base (0 diff lines, so the union-order normalisation has nothing to do). The positive control is the one ruling `6348891160` asks for. - Its ESM and CJS entries export the same 159 names at base and at head. The `.d.ts` bar is ruling `6348891160` item 1 (on #25222, covering this card). Ruling `6364006498` is Sales's ruling A on this card. This is the fifth cut it applies to. Cut 13 of the `auth-manager.ts` split: **the global before-hook of the better-auth instance**. It is the second of the three `createAuthInstance` slices the seat set (`hooks.after`, then `hooks.before`, then the rest of the method). #25224 stays open for the later cuts. This follows the amended claim `6376168919`, which amends `6374301142` on the file cut 12 left (#25536, landed note `6375260860`). The shape is cut 12's: #23050's, applied to a closure. The seat ruled that in scope at 19:56Z, with the bars listed below. Branch: `claude/project-thread-lg3wrj`, the harness-pinned branch for this session, restarted from `origin/main` `93a6ba4136` after #25536 merged. It names no issue, so the card relation is this body's first line. `auth-manager.ts` goes from 8,362 to 7,800 lines. The cut is +734 / −597 over 4 files: 1,331 changed lines, under the 3,000-line line. Two of the four files are path readers outside the move, so `check-governed-merges.mjs --test` ran on the final file list before the claim: NOT governed (0 of 4 paths hit the register), and `--branch HEAD` agrees. One commit: `3ec293d86a` `refactor(plugin-auth): move the better-auth global before-hook into auth-manager-before-hook.ts`. ## The move | moved | base lines | proof lines | tokens | members it reads | |---|---|---:|---:|---:| | the `hooks.before` arrow body in `createAuthInstance` | `:2246`–`:2827` | 584 | 1,326 | 18 methods, 1 field | The lines are base `93a6ba4136`. The 584 proof lines are the body with its braces (`:2245`'s `{` to `:2828`'s `}`). - **What it is:** `createAuthInstance` (`:1695`, private, annotated `Promise<Auth<any>>`) builds one config literal for better-auth. Its `hooks.before` is `createAuthMiddleware(async (ctx: any) => {BODY})`: the before-hook every better-auth endpoint's request passes through before the endpoint runs. - **What BODY does, in order** (all of it moves, with its comments): the actor resolver handed to the attribution scope, the refresh skip for a minted principal session, the per-number OTP send guard, the password complexity and reuse checks, the change-email capture, the remove-member, organization-delete and no-record revoke refusals, the held-caller refusal on the admin plugin's role-scalar actions, the no-record admin revoke refusal, the held-caller refusal on rewriting or deleting an identity provider, the identity-provider registration gate, the cloud-as-IdP authorization gate, the break-glass last-local-credential refusal, the audience posture and uniqueness refusal on password sign-up, the cloud-managed invite-member refusal, the account lockout gate, and the first-user `disableSignUp` flip. - **Where it goes:** a new module, `packages/plugins/plugin-auth/src/auth-manager-before-hook.ts` (696 lines). BODY becomes the body of `export async function globalBeforeHook(this: BeforeHookHost, ctx: any, setShouldSkipSessionRefresh: typeof import('better-auth/api').setShouldSkipSessionRefresh)`, re-indented eight spaces (four levels) and otherwise unedited. The module header gives the reasoning, in the form the twelve earlier `auth-manager-*.ts` modules use. - **What stays:** `createAuthInstance` keeps its place, docblock and signature, and `hooks.before` keeps its place in the literal. Its arrow is now: ```ts // [#25224 cut 13] The body moved to `auth-manager-before-hook.ts`; this // arrow hands it the members it reads (`BeforeHookHost`) and the one // local it read, `setShouldSkipSessionRefresh`. before: createAuthMiddleware((ctx: any) => { const readConfig = () => this.config; const host: BeforeHookHost = { assertAccountNotLocked: (...a) => this.assertAccountNotLocked(...a), // … one closure per method, alphabetical, 18 in all, and: get config() { return readConfig(); }, }; return globalBeforeHook.call(host, ctx, setShouldSkipSessionRefresh); }), ``` Nothing else in `createAuthInstance` changes. ## The seat's bars **Body.** - BODY is the same TypeScript token sequence at base and in `globalBeforeHook`: 584 lines, 1,326 tokens. Every line is the base line minus exactly eight spaces. The first parameter (`ctx: any`) is the same text; the second is the captured local, below. Neither side has a return annotation. - **`async` moves from the arrow to the function.** The head arrow is not `async`, and it returns `globalBeforeHook.call(host, ctx, setShouldSkipSessionRefresh)`, the function's own promise, unchanged. better-auth's `createAuthMiddleware` is better-call 1.4.4's `createMiddleware`, whose handler wrapper runs `const response = await _handler(internalContext);` inside its `try` (`dist/middleware.mjs:15`). So it awaits the same promise BODY settles, with the same value or rejection, and its `catch` sees the same error. The reader closure, the host literal and the `.call` cannot throw before that promise exists. - **No `arguments`, `new.target` or `super`** (0 of each), where an arrow and a function differ. There is no bare `this`: BODY's only `this` uses are 28 member reads, `this.X`. **One captured local, now a parameter.** Unlike cut 12's body, this one read a `createAuthInstance` local: `setShouldSkipSessionRefresh`, once (`:2273`, `if (carriesPrincipalSession(ctx)) await setShouldSkipSessionRefresh(true);`). - The method binds it once, `const { createAuthMiddleware, setShouldSkipSessionRefresh } = await import('better-auth/api');` at `:1697`, before it builds the literal, and never reassigns it. - It becomes the function's second parameter, under the same name, typed `typeof import('better-auth/api').setShouldSkipSessionRefresh`. The head arrow passes the same binding on every call. The base arrow read that `const` when the hook ran, and the head arrow reads it at the same moment and hands it on, so BODY calls the same function object. - None of the method's other locals declared before the literal (`betterAuth`, `createAuthMiddleware`, `plugins`, `passwordHasher`) is read in BODY. The binding proof below finds no other identifier in BODY bound in the method. **Receiver.** - BODY's `this` was the `AuthManager` instance, the arrow's lexical receiver. It is now the host. - `BeforeHookHost` names exactly the 19 members BODY reads, no more, each typed as the class member (`AuthManager['x']`). - 18 are methods, so each goes as a late-bound closure, `(...a) => this.x(...a)`. The method runs on the manager and is looked up when BODY calls it, so a spy or double set on the manager is still the one BODY reaches: `assertAccountNotLocked`, `assertActorNotHeld`, `assertAdminRevokeUserSessionIdentifiesRecord`, `assertOrganizationDeletable`, `assertPasswordComplexity`, `assertPasswordNotReused`, `assertPhoneOtpSendAllowed`, `assertRemoveMemberPermitted`, `assertRevokeSessionIdentifiesRecord`, `existingAddressSignUpAnswersSuccess`, `hasExistingUserFor`, `isAuthGateActive`, `isBootstrapCreation`, `isOperatorProvisioning`, `isPlatformAdminUserId`, `resolveActor`, `resolvePasswordChangeUserId` and `validateAudienceAdmission`. - One is a field: `config`. The class reassigns it after construction (`setRuntimeBaseUrl` at `:4549`, `applyConfigPatch` at `:4630`), so it goes as a getter over a reader closure (`const readConfig = () => this.config;` and `get config() { return readConfig(); }`), the shape cut 6's stubs use. BODY reads the manager's current object each time. It is `readonly` on the interface, and BODY never writes it. - **A function-valued member, disclosed** (the way #25516 disclosed `resolveKernelService`): BODY calls `this.config.oidcAuthorizeGate({…})` (`:2546`, after the presence check at `:2536`), an optional function on the config object (`AuthManagerOptions`, `:789`). Its receiver is `this.config`, which the getter returns as the manager's current object, so the gate is looked up and called on the same object, at the same moment, with the same `this`, as before. - `hasExistingUserFor` and `isBootstrapCreation` read private statics. They stay in the class, per ruling A, and BODY reaches them as members. **One static read, a value import.** BODY reads `AuthManager.OPERATOR_PROVISIONING_TICKET_FIELD` once (base `:2820`, module `:688`), a public `static readonly` string (`:4844`). The module imports the class by value from `./auth-manager.js`, as cut 8's `auth-manager-membership-admission.ts` does. The one read is inside the function, at call time, so the cycle is never read during evaluation (below). **Host lifetime.** The host is built inside the arrow, once per run of the hook: each time better-auth runs `hooks.before` on a request. This is what every method stub in this series does. Nothing BODY reads is captured early: the 18 closures look each method up on the manager at call time, and the getter reads `config` when BODY reads it. **Readers.** The decision letters are read on the merge base and on the head below: 10 drop, as expected. No anchor entry is owed. Tenant census and symbol-anchor output are byte-identical at both ends, and the full plugin-auth suite passes at both ends. The two path readers that read the moved lines are migrated in this commit. ## Imports and evaluation order Nine names leave `auth-manager.ts`, because BODY was their last reader there: - `setAuthActorResolver` (its import line goes); - `SIGN_IN_EMAIL_PATH` (cut 12 left BODY its last reader here; the block keeps `type AuthEventAuditSurface`); - `VENDOR_ROLE_SCALAR_ADMIN_PATHS`, `carriesPrincipalSession` and `ORGANIZATION_DELETE_PATH` (each block keeps its other names); - the whole `last-local-credential.js` block: `isLastLocalCredentialHolder`, `LAST_LOCAL_CREDENTIAL_CODE` and `LAST_LOCAL_CREDENTIAL_MESSAGE`. `isCloudManaged`, `CLOUD_MANAGED_REFUSAL_CODE` and `CLOUD_MANAGED_REFUSAL_MESSAGES` have other readers in `auth-manager.ts`, so both files import them. One import line is added after cut 12's, with a comment in the same form: `import { globalBeforeHook, type BeforeHookHost } from './auth-manager-before-hook.js'`. esbuild drops a type-only import, so a module whose last value import leaves `auth-manager.ts` can now be evaluated later. The order was measured rather than argued: - **The ES modules, at run time.** A loader hook logged each ES module as its body ran while `dist/index.mjs` loaded, at base and at head. 171 modules evaluate at both, in the same order, except one: `@better-auth/core/dist/utils/ip.mjs` now runs just after `@objectstack/platform-objects`' `identity` entry instead of just before it. Its own dependencies (`env/env-impl.mjs`, `zod`) run much earlier at both, unchanged. Its top level is two `zod` schema objects (`z.ipv4()`, `z.ipv6()`), constants and function declarations, and it registers nothing. - **The CommonJS entry.** A `Module._load` log for `dist/index.js` gives 127 first loads at both, the same apart from that one module, which now loads last. - **plugin-auth's own modules.** A walk of the bundle's top level and its eager `init_*` calls finds two that run later: `auth-session-audit.ts`, whose one value import in `auth-manager.ts` was `SIGN_IN_EMAIL_PATH`, and `last-local-credential.ts`. Their top levels are string and regex constants and function declarations, with no side effect. `auth-session-audit.ts`'s one import is `getIP` from that `ip` module, which is why the `ip` module moves with it. No importer reads either module's bindings at load time: all 24 references in the 5 importing files are inside functions, and `index.ts` re-exports both. - `auth-actor-attribution.ts`, whose `setAuthActorResolver` leaves, is an `__esm`-wrapped module that another importer initialises earlier, at both, so it does not move. The other modules keep a value import in `auth-manager.ts`. - The new module's own top level is imports, one interface and one function declaration. Its value import of `auth-manager.ts`, for the static, and `auth-manager.ts`'s import of it form a cycle with nothing read at load time. ## The decision-letter layer: −10, as expected `check-adr-anchors`, measured on the merge base and on the head. The gate exits 0 at both. | point | anchored files | citations | files | decision letters | |---|---:|---:|---:|---:| | merge base `93a6ba4136` | 84 | 52,274 | 6,681 | 1,955 | | head `3ec293d86a` | 84 | 52,274 | 6,682 | 1,945 | - The ten letters are in BODY's comments: ADR-0095 D3 (`:2258`), ADR-0069 D1 (`:2292`, `:2308`), ADR-0069 D3 (`:2406`, `:2445`, `:2492`), ADR-0135 D6 and ADR-0068 D4 (`:2463`), ADR-0068 D4 (`:2481`) and ADR-0069 D2 (`:2787`). They leave `auth-manager.ts`, which is anchored, for a module that is not. Their citations still resolve, so the citation count does not move. - **No entry is owed.** The `auth-manager.ts` entry has two paragraphs: ADR-0108 (better-auth's organization roles map) and ADR-0093 D2 (`settleMembership` and its two seams, `user.create.after` and `session.create.before`, which are database hooks, not `hooks.before`). Neither describes this hook, and no other entry names it. So, per the seat's bar, no entry is added for the new module. ## Path readers Two readers outside the move read the moved lines by path, and both follow BODY in this commit: - **`api-error-code.pin.test.ts`**: `authManagerFiles` gains `'auth-manager-before-hook.ts'`. BODY carries 5 of the auth manager's `new APIError(` sites (`:2505`, `:2552`, `:2651`, `:2691`, `:2781`). Without the entry the floor would still pass, silently, at 25 against `≥ 24`. With it the count stays 30, as at base. - **`scripts/check-dispatcher-error-vocabulary.mjs`**: `APIERROR_ARGUMENT_POSITION_CENSUS.liveMemberFile` now names `packages/plugins/plugin-auth/src/auth-manager-before-hook.ts`, where `BASE_ERROR_CODES.USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL` (`APIError.from`, base `:2752`) now lives, with a two-line comment that the counts stay the reading at `measuredOn`. The three comments that name the file the idiom lives in say the new module too. No count, pattern or verdict changes. The gate exits 0 at both: 56 sites found and 56 classified, and only its scanned-file count moves (3,424 → 3,425). `--self-test` passes (11 shapes, 396 assertions). Read, and not edited: - **The tenant-audit census.** No census site moves: BODY has no engine write (its one `adapter.findOne` is in a comment, `:2802`), no `this.X.delete()` and no elevation read. `check-tenant-audit-census` exits 0 at both with no `--write`, and its output is byte-identical. The `--json` document has 238 sites at both and 0 per-site differences with line numbers ignored. 2 sites are in `auth-manager.ts` at both (each 562 lines earlier at head, the cut's net), and 0 are in the new module. Only `scannedSources` (747 → 748) and `measuredAt` differ, so neither census document is edited. - **`scripts/measure-durability-swallow-family.mjs`** (a measurement, not a gate): 77 members and 84 quiet rows at both. The members are the same with line numbers ignored (one member's write line, in `observeFirstMemberships`, which stays, moves 5,739 → 5,177), and the quiet rows are identical. Only the scanned file count moves (3,474 → 3,475, the new module). `tryStatements` (3,640), `droppedNotAwaited` (235) and every other statistic are the same: BODY's `try` blocks move whole. - **`check-auth-mount-ledger.mjs`** reads only the `basePath` declaration in `auth-manager.ts`, which stays. **`auth-manager-base-path.test.ts`** reads `auth-manager.ts` for code lines carrying `'/api/v1/auth'`; BODY has none. - **The plugin-factory scans** in `better-auth-schema-parity.test.ts` and `managed-extension-fields.test.ts` match `const { X } = await import('better-auth/plugins…' | '@better-auth/…')`. BODY's dynamic imports are `better-auth/api` and `@better-auth/core/error`, and `@better-auth/core` is excluded, so none is a factory. Both suites pass at head. - **`rate-limit-storage-isolation.test.ts`** walks the import graph from `rate-limit-storage.ts`, which reaches neither file. It passes at head. - **The system-context census** reads 149 elevation read sites at both, with byte-identical output. - **Every symbol anchor into the file.** Before the cut, every `auth-manager.ts#symbol` anchor in the tracked tree outside changelogs was mapped with `symbolSegmentResolution` from `scripts/symbol-anchors.mjs`. There are 29, and all 29 are `declaration`-class at base, at base with BODY blanked, and at head. Two of their symbols occur as a token on a line of BODY: `AuthManager` (ADR-0135 `:104`; BODY `:2820`, the static read) and `isBootstrapCreation` (the identity-auth checklist area `:1810`; BODY `:2823`, a member call). Each resolves to its declaration, which stays, at all three readings. ## Proof Builds and suites ran through `os-verify-lock`, and every exit code was captured before any pipe. Base `93a6ba4136`. A full `turbo run build` ran first. Then `plugin-auth` was built and its suite run at base, and built and run again at head, on the same dependency dists. **1. The moved text.** - **Token identity:** the base arrow body and the module function body are the same TypeScript token sequence (scanner tokens, kind and text, trivia skipped, each template literal re-scanned at its substitutions as the parser does): 584 lines and 1,326 tokens, each line the base line minus eight spaces. - **The head arrow's shape**, checked by the same run: not `async`, the same first parameter, the reader closure for `config`, one host literal whose keys are exactly `BeforeHookHost`'s, whose 18 method values are each `(...a) => this.<key>(...a)` and whose `config` is the getter over the reader, then `return globalBeforeHook.call(host, ctx, setShouldSkipSessionRefresh)`, and nothing else. - **Binding identity:** a second proof walks all 356 identifiers of BODY at base and in the module, in order, and resolves each with the TypeScript checker (the module's `./auth-manager.js` resolved to the base file). All 356 resolve the same way at both ends: - 69 to a parameter: `ctx`, and the captured local, which the proof is told about; - 119 to a declaration inside BODY, the same declaring identifier by position; - 29 to the class's members: the 28 receiver reads (`AuthManager` at base, `BeforeHookHost` at head) and the one static; - 12 to imports with the same specifier and imported name, the class itself among them; - 2 to the same declaration in the base file: `oidcAuthorizeGate` on `AuthManagerOptions`, twice; - 125 to no declaration: globals and property names on untyped values. None resolves to any other `createAuthInstance` local or to an unexported top-level declaration of either file. - **Negative controls:** each proof reads the module text, and each control changes it in memory only; the file is untouched. - Token proof: `setShouldSkipSessionRefresh(true)` changed to `(false)` fails the token and line checks, and the run exits 1. - Binding proof: the module's `last-local-credential` import pointed at another specifier gives 3 differences, and the run exits 1. Run without being told about the captured local, it reports that local, and exits 1. - **An independent reading:** `git diff --color-moved=plain --color-moved-ws=allow-indentation-change 93a6ba4 3ec293d` over the two source files marks 588 of the 593 deleted lines and 596 of the 727 added lines as moved. - The 5 unmoved deletions are four import-name lines (`SIGN_IN_EMAIL_PATH,`, `VENDOR_ROLE_SCALAR_ADMIN_PATHS,`, `carriesPrincipalSession,` and `ORGANIZATION_DELETE_PATH,`, each now on its own line in the module) and the `before: createAuthMiddleware(async (ctx: any) => {` line (now without `async`). - The other added lines are the module header, its import lines, the host interface and its docblock, the function signature and docblock, the arrow's comment, reader, host literal and `return`, and the new import comment and line. **2. `.d.ts` and exports (ruling `6348891160` items 1 and 2).** | file | base and head | raw diff lines | |---|---|---:| | `dist/index.d.ts` raw | `1b3a48357d137155de33104277667a922aab62df73b418b03894c56faaacd0ba` | 0 | | `dist/index.d.ts` normalised | `cee7303ccdfe72794efd588c85b628419d4253d730b5a0c5a3f1cdb8dddb4443` | | | `dist/index.d.mts` raw | `b5e8dc982b634f0a5108b1b533626a68767f2da5a56f72dba801059b149e2872` | 0 | | `dist/index.d.mts` normalised | `562d0a4ce656f97aeb937541482cdf531b61d7c75a005918784bf0a52d032dfc` | | - The normaliser, verbatim (the ruling's one-liner): `node -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`. - The raw files are byte-identical, so no union-order swap occurs: items 1(b) and 1(d) hold with nothing to account for. - **Positive control:** the head `dist/index.d.ts` with one private member made public (`private createAuthInstance;` → `createAuthInstance;`, line 1712, a one-line change) hashes raw `35b18682171cd394898c9e56bc8d5272ccf20aeff0d1966fc568cb5e566a08df` and normalised `e69c1b0157b45e48bbc16bd64eb939abf9be33ccb31553485df93ef05aac6fc3`. Both differ from the readings above. - **Export lists:** ESM `dist/index.mjs` and CJS `dist/index.js` each have 159 names at base and at head, the same list (the sha256 of the sorted names is `3abb126f5babb701…` on all four). `globalBeforeHook` and `BeforeHookHost` are not among them, and `dist/index.d.ts` names neither. - **The bundle:** the same 18 files in `dist/` at both, plus the two `.build-input-hash` stamps the build writes from its inputs. Only `index.js`, `index.mjs` and their two `.map` files differ, as a move of code inside the bundle must. `tsup` bundles the new module into the root entry (`splitting: false`). **3. Tests and lint.** - `pnpm exec vitest run --maxWorkers=3` in `packages/plugins/plugin-auth`: base `Test Files 191 passed (191)`, `Tests 3607 passed | 10 skipped (3617)`, exit 0. Head: the same counts, exit 0. The pin test's `authManagerFiles` sum is 30 at both. - `pnpm --filter @objectstack/plugin-auth typecheck` (both `tsc` projects and `check:test-typecheck`): exit 0. `pnpm --filter @objectstack/runtime typecheck`: exit 0. - `eslint --no-inline-config` (the root `lint` script's binary and flags) over the 4 files: exit 0. **4. The censuses, the measurement and the anchors, at base and at head.** - The tenant-audit census, as above: byte-identical gate output, 238 sites at both, 0 per-site differences line-blind, 0 unresolved and 0 unledgered at both. - The durability measurement, as above. - These checkers were run by hand at base and at head, because `dispatch-gates` does not derive the ADR one. All six exit 0 at both. Five print byte-identical output at both (`cmp` clean): - `check-adr-symbol-anchors`: 2,384 anchors across 142 records, 466 symbol (440 declaration, 26 literal); - `check-scripts-symbol-anchors`: 4,070 anchors; - `check-spec-docblock-symbol-anchors`: 5,076 anchors; - `check-system-context-census`: 149 elevation read sites; - `check-platform-checklist`: 16 areas, 329 items. The sixth, `check-adr-anchors`, differs only in the file count and the decision letters, as in the table above. **5. Gates.** - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 80 commands for the 4 paths. All 80 ran at head after the full build, and all 80 exited 0. - `--ran` over the record with each exit code: 80 derived, 80 run, 0 NOT-MEASURED, a derived zero. `--tier`: no path-derived mandate. - `check-governed-merges.mjs`: `--test` on the final file list before the claim and `--branch HEAD` after the commit both read NOT governed (0 of 4 paths hit the register), 1,331 changed lines. - **Docs drift** (`node scripts/docs-audit/affected-docs.mjs --json 93a6ba4`, exit 0): 2 pages, both name matches only. `content/docs/kernel/contracts/auth-service.mdx:39` is the auth service contract's `isAuthGateActive?()`, which the class implements unchanged; `BeforeHookHost` only names the member. `content/docs/permissions/system-context.mdx:172` cites `plugin-approvals`' `approval-service.ts#resolveActor`, another symbol. 0 release-owned pages, so no doc edit is owed. - **Serial constraints** (in the claim): 11 PRs are open. One, #25541 (another account's, on a base before cut 12), touches `auth-manager.ts`: a comment inside the `hooks.after` body, which cut 12 already moved out, and one property in `externalDirectoryDoorDeps`, which stays. Neither is in BODY or in an import line this cut edits, and it touches no reader named above. The Version Packages PR #24876 touches `plugin-auth`'s `package.json`, its `CHANGELOG.md` and changesets only. `origin/main` has moved 4 merges past this base, to `4df174bcc4` (#25539, #25538, #25528, #25543); none touches `plugin-auth`, `scripts/adr-anchors/` or a reader named above. ## Changeset None: `skip-changeset`, per ruling `6348891160` item 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](https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr Co-authored-by: Claude <noreply@anthropic.com>
1 parent 51e804c commit 19d3720

4 files changed

Lines changed: 734 additions & 597 deletions

File tree

‎packages/plugins/plugin-auth/src/api-error-code.pin.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ describe('every new APIError( in plugin-auth/src carries an ADR-0112 code (#2424
181181
'auth-manager-sign-in-lockout.ts',
182182
'auth-manager-password-session-policy.ts',
183183
'auth-manager-email-sms-delivery.ts',
184+
'auth-manager-before-hook.ts',
184185
];
185186
expect(sites.length).toBeGreaterThanOrEqual(30);
186187
expect(new Set(sites.map((s) => s.file)).size).toBeGreaterThanOrEqual(4);

0 commit comments

Comments
 (0)