Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/olive-pandas-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@factiii/auth': patch
---

Build against zod 4, so the emitted types match the declared peer range.

`peerDependencies` has always said `zod >=4.3.6 <5`, but a `zod: 3.25.76`
override in the workspace forced zod 3 into the build. The package compiled and
tested green while emitting zod 3 shapes — `z.ZodObject<..., "strip",
z.ZodTypeAny, ...>` — into the published `.d.ts`, which do not typecheck for a
consumer on zod 4.

`validators.ts`, `types/hooks.ts` and `procedures/passkey.ts` imported
`AnyZodObject`, which zod 3 exported and zod 4 removed. `src/types/zod.ts` now
defines the zod 4 equivalent, the override is gone, and auth builds against zod
4.5.4 with a clean `.d.ts`.

Consumers already on the declared zod 4 get a fix. A consumer whose `zod`
resolves to v3 was outside the peer range already and needs to move to zod 4 —
`createAuthRouter` throws `merging._def.shape is not a function` at startup when
it merges `schemaExtensions` built by a different zod major.

Also drops the unused `better-sqlite3` dependency, its adapter and its types,
which removes a native build step from install.
19 changes: 19 additions & 0 deletions .changeset/quiet-moons-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@factiii/stack': patch
---

Load the AWS SDK only when an AWS code path runs.

`aws-helpers.ts` imported all nine `@aws-sdk/client-*` packages at module
scope. Every AWS scanfix file imports that helper, and `factiii/index.ts`
imports those scanfix files to build its `fixes` array, so requiring any CLI
command pulled the whole SDK in. That cost about 100 ms on every invocation,
including `--help`, `ops`, `db` and `backup`, which never touch AWS.

The SDK types are now `import type` — TypeScript erases those — and nine
`*Sdk()` accessors require the real packages on first use and cache them. Call
sites construct commands through the accessor, so the SDK loads only when a fix
actually provisions infrastructure.

`node bin/stack --help` drops from 140 ms to 50 ms, and the AWS SDK module
count at startup drops from 24 to 0.
14 changes: 4 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,15 @@
"release": "pnpm build && changeset publish"
},
"devDependencies": {
"@changesets/cli": "^2.30.0",
"@changesets/cli": "^3.0.2",
"typescript": "^5.9.3"
},
"packageManager": "pnpm@10.32.1",
"pnpm": {
"overrides": {
"minimatch": ">=10.2.1",
"diff": ">=4.0.4",
"hono": ">=4.12.7",
"@hono/node-server": ">=1.19.10",
"lodash": ">=4.17.23",
"flatted": ">=3.4.0",
"rollup": ">=4.59.0",
"ajv": "^6.14.0",
"zod": "3.25.76"
"deepmerge-ts": ">=8.0.2",
"esbuild": ">=0.28.1",
"mysql2": ">=3.24.3"
}
}
}
64 changes: 30 additions & 34 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@
"e2e:db:down": "docker compose -f e2e/docker-compose.yml down"
},
"dependencies": {
"@simplewebauthn/server": "^13.3.2",
"@trpc/server": "^11.8.0",
"apple-signin-auth": "^2.0.0",
"@simplewebauthn/server": "^13.3.3",
"@trpc/server": "^11.18.0",
"apple-signin-auth": "^2.1.0",
"bcryptjs": "^2.4.3",
"google-auth-library": "^10.5.0",
"jsonwebtoken": "^9.0.2",
"totp-generator": "^2.0.0"
"google-auth-library": "^10.9.1",
"jsonwebtoken": "^9.0.3",
"totp-generator": "^2.0.1"
},
"peerDependencies": {
"@prisma/client": ">=5.0.0",
Expand All @@ -116,37 +116,33 @@
}
},
"devDependencies": {
"@playwright/test": "^1.58.1",
"@prisma/adapter-better-sqlite3": "^7.3.0",
"@prisma/adapter-pg": "^7.3.0",
"@prisma/client": "^7.1.0",
"@trpc/client": "^11.8.0",
"@trpc/server": "^11.8.0",
"@playwright/test": "^1.62.1",
"@prisma/adapter-pg": "^7.10.0",
"@prisma/client": "^7.10.0",
"@trpc/client": "^11.18.0",
"@types/bcryptjs": "^2.4.6",
"@types/better-sqlite3": "^7.6.13",
"@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.19.0",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.11",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vitejs/plugin-react": "^5.1.3",
"better-sqlite3": "^12.6.2",
"drizzle-orm": "^0.44.0",
"eslint": "^8.57.0",
"pg": "^8.18.0",
"prettier": "^3.8.1",
"prisma": "^7.1.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.2.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^20.19.43",
"@types/pg": "^8.23.1",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.7",
"@typescript-eslint/eslint-plugin": "^8.69.0",
"@typescript-eslint/parser": "^8.69.0",
"@vitejs/plugin-react": "^5.2.0",
"drizzle-orm": "^0.45.2",
"eslint": "^8.57.1",
"pg": "^8.23.0",
"prettier": "^3.9.6",
"prisma": "^7.10.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-router-dom": "^7.18.3",
"superjson": "^1.13.3",
"tsup": "^8.3.5",
"tsx": "^4.21.0",
"tsup": "^8.5.1",
"tsx": "^4.23.13",
"typescript": "5.9.3",
"vite": "^7.3.1",
"vitest": "^4.1.0",
"vite": "^7.3.6",
"vitest": "^4.1.11",
"zod": "^4.3.6"
},
"engines": {
Expand Down
18 changes: 14 additions & 4 deletions packages/auth/src/adapters/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ export interface DatabaseAdapter {
create(data: CreateUserData): Promise<AuthUser>;
update(id: number, data: Partial<Omit<AuthUser, 'id'>>): Promise<AuthUser>;
/** Read just the standard-mode 2FA secret + backup codes for a user. */
findTwoFaSecret(id: number): Promise<{ twoFaSecret: string | null; twoFaBackupCodes: string[] }>;
findTwoFaSecret(
id: number
): Promise<{ twoFaSecret: string | null; twoFaBackupCodes: string[] }>;
/** Persist the standard-mode 2FA secret and backup codes. Non-null secret == 2FA on. */
setTwoFaSecret(id: number, secret: string, backupCodes: string[]): Promise<void>;
/** Replace the backup codes without touching the TOTP secret. */
Expand All @@ -118,13 +120,21 @@ export interface DatabaseAdapter {
/** Find session by ID with user status and verifiedHumanAt joined. */
findById(id: number): Promise<SessionWithUser | null>;
create(data: CreateSessionData): Promise<AuthSession>;
update(id: number, data: Partial<Pick<AuthSession, 'revokedAt' | 'lastUsed'>>): Promise<AuthSession>;
update(
id: number,
data: Partial<Pick<AuthSession, 'revokedAt' | 'lastUsed'>>
): Promise<AuthSession>;
/** Update lastUsed and return session with user's verifiedHumanAt and updatedAt. */
updateLastUsed(id: number): Promise<AuthSession & { user: { verifiedHumanAt: Date | null; updatedAt: Date } }>;
updateLastUsed(
id: number
): Promise<AuthSession & { user: { verifiedHumanAt: Date | null; updatedAt: Date } }>;
/** Set revokedAt on a single session. */
revoke(id: number): Promise<void>;
/** Find active (non-revoked) sessions for a user, optionally excluding one. */
findActiveByUserId(userId: number, excludeSessionId?: number): Promise<Pick<AuthSession, 'id' | 'socketId' | 'userId'>[]>;
findActiveByUserId(
userId: number,
excludeSessionId?: number
): Promise<Pick<AuthSession, 'id' | 'socketId' | 'userId'>[]>;
/** Revoke all active sessions for a user, optionally excluding one. */
revokeAllByUserId(userId: number, excludeSessionId?: number): Promise<void>;

Expand Down
Loading
Loading