Skip to content

[HYPER-109] feat: custom CSS injection for trusted OAuth clients#9

Open
aspiers wants to merge 11 commits intomainfrom
css-injection-trusted-clients
Open

[HYPER-109] feat: custom CSS injection for trusted OAuth clients#9
aspiers wants to merge 11 commits intomainfrom
css-injection-trusted-clients

Conversation

@aspiers
Copy link
Contributor

@aspiers aspiers commented Mar 3, 2026

Summary

  • Extract resolveClientMetadata and ClientMetadata type into @certified-app/shared so both pds-core and auth-service can use them
  • Add branding.css support to ClientMetadata with escapeCss() (prevents </style> injection) and getClientCss() (trust-gated helper)
  • Auth-service: inject trusted client CSS into login and consent pages via <style> tag (CSP already allows 'unsafe-inline')
  • PDS-core: middleware intercepts /oauth/authorize responses — wraps res.setHeader/res.end to inject <style> tag and append SHA256 hash to CSP style-src directive
  • Demo app: add sample branding CSS (gradient buttons, CSS custom properties) to client metadata
  • Add PDS_OAUTH_TRUSTED_CLIENTS env var (comma-separated client_id URLs) to all env examples

How it works

  1. Trusted clients list their CSS in client-metadata.json under branding.css
  2. Both pds-core and auth-service read PDS_OAUTH_TRUSTED_CLIENTS (must be set identically)
  3. When a trusted client initiates OAuth, the CSS is fetched, escaped, and injected into authorization pages
  4. PDS side: CSP hash is computed and appended to style-src since the stock oauth-provider uses strict hash-based CSP
  5. Auth-service side: no CSP changes needed ('unsafe-inline' already allowed)

Test plan

  • All 173 existing tests pass
  • TypeScript builds cleanly for shared, auth-service, and pds-core
  • Manual: start with PDS_OAUTH_TRUSTED_CLIENTS set to demo's client_id, verify CSS appears on login/consent pages
  • Manual: remove from trusted list, verify CSS no longer injected
  • Manual: verify CSP header includes CSS hash on PDS authorization page

Ref: atproto-9fa

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Trusted OAuth clients can be configured to inject client-specific CSS (branding appears on consent and login pages).
    • OTP UI is customizable: length and format options drive input attributes and user-facing text.
  • Chores

    • .env examples expanded with commented vars for trusted clients and OTP configuration.

aspiers and others added 5 commits March 3, 2026 21:53
… support

Move resolveClientMetadata, resolveClientName, and ClientMetadata type
from auth-service into @certified-app/shared so both pds-core and
auth-service can use them. Add branding.css to ClientMetadata interface,
escapeCss() for safe HTML embedding, and getClientCss() trust-gated helper.

Auth-service's client-metadata.ts becomes a re-export shim.

Ref: bd-20x.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ages

Add trustedClients config (from PDS_OAUTH_TRUSTED_CLIENTS env var) to
AuthServiceConfig. Login page and consent page now resolve full client
metadata, call getClientCss() for trusted clients, and inject a <style>
tag before </head>. Auth-service CSP already allows 'unsafe-inline' so
no header changes needed.

Ref: bd-20x.2, bd-20x.3, bd-20x.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
For GET /oauth/authorize, intercept responses from the stock
@atproto/oauth-provider to inject custom CSS from trusted clients.
Wraps res.setHeader to append SHA256 hash to CSP style-src directive,
and wraps res.end to inject <style> tag before </head>.

Uses same Express stack insertion technique as AS metadata override.

Ref: bd-20x.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add gradient button styling and CSS custom properties to demonstrate
the custom CSS injection feature for trusted OAuth clients.

Ref: bd-20x.6

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the comma-separated list of OAuth client_id URLs trusted for
CSS branding injection. Must be set identically in pds-core and
auth-service.

Ref: bd-20x.7

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
epds-demo Ready Ready Preview, Comment Mar 4, 2026 6:39pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Warning

Rate limit exceeded

@aspiers has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb885253-96d2-4c22-ba34-902c1e4949f2

📥 Commits

Reviewing files that changed from the base of the PR and between 97877a3 and ac359bd.

📒 Files selected for processing (12)
  • .beads/backup/backup_state.json
  • .beads/backup/comments.jsonl
  • .beads/backup/config.jsonl
  • .beads/backup/dependencies.jsonl
  • .beads/backup/events.jsonl
  • .beads/backup/issues.jsonl
  • .beads/backup/labels.jsonl
  • .beads/issues.jsonl
  • packages/auth-service/src/routes/consent.ts
  • packages/demo/src/app/client-metadata.json/route.ts
  • packages/pds-core/src/index.ts
  • packages/shared/src/otp-config.ts
📝 Walkthrough

Walkthrough

Adds trusted-client configuration and client branding support: new shared client-metadata utilities, re-exports in shared, auth-service and pds-core updated to resolve client metadata and inject client-specific CSS into rendered OAuth/login/consent pages; OTP UI configuration options and env docs added.

Changes

Cohort / File(s) Summary
Environment examples
\.env.example, packages/auth-service/.env.example, packages/pds-core/.env.example
Inserted PDS_OAUTH_TRUSTED_CLIENTS and commented OTP configuration (OTP_LENGTH, OTP_FORMAT) into shared and package-specific env examples.
Auth service config & tests
packages/auth-service/src/context.ts, packages/auth-service/src/index.ts, packages/auth-service/src/__tests__/consent.test.ts
Added trustedClients: string[] to AuthServiceConfig and initialized it from PDS_OAUTH_TRUSTED_CLIENTS; updated tests to include trustedClients: [] in mocks.
Shared client metadata & OTP exports
packages/shared/src/client-metadata.ts, packages/shared/src/index.ts
New client-metadata module (resolveClientMetadata, resolveClientName, escapeCss, getClientCss, types) with caching/fetch/fallback; OTP-related utilities (otpConfig, generateOtp, otpHtmlAttrs, otpDescriptionText, types) added to shared exports; removed generateOtpCode export.
Auth service client metadata refactor
packages/auth-service/src/lib/client-metadata.ts
Replaced local client-metadata implementation with re-exports from @certified-app/shared (delegation to shared module).
Auth routes: login & consent
packages/auth-service/src/routes/login-page.ts, packages/auth-service/src/routes/consent.ts
Resolve client metadata and customCss (via getClientCss) for client_id; pass customCss into renderers; login route also uses otpHtmlAttrs and otpDescriptionText to parameterize OTP UI and client-side script.
PDS core CSS injection middleware
packages/pds-core/src/index.ts
Added middleware on GET /oauth/authorize that, for trusted client_id values, resolves metadata, fetches client CSS, computes CSP sha256 hash, injects a <style> block and augments CSP headers; imports shared metadata helpers. (Note: similar injection block appears duplicated.)
Demo metadata
packages/demo/src/app/client-metadata.json/route.ts
Added branding field with CSS rules (joined array) to the demo client metadata response.
Project task list
.beads/issues.jsonl
Many issue entries added/removed reflecting OTP, branding, tests, demo, and migration work.

Sequence Diagram(s)

sequenceDiagram
    participant Client as OAuth Client
    participant PDS as PDS Core
    participant Shared as Shared Metadata
    participant Auth as Auth Service
    participant Browser as Browser/User

    Client->>PDS: GET /oauth/authorize?client_id=...
    PDS->>PDS: Check client_id against trustedClients
    alt trusted
        PDS->>Shared: resolveClientMetadata(client_id)
        Shared-->>PDS: ClientMetadata (including branding.css)
        PDS->>Shared: getClientCss(client_id, metadata, trustedClients)
        Shared-->>PDS: Escaped CSS
        PDS->>PDS: Compute SHA-256(CSS) and augment CSP
        PDS->>PDS: Inject <style> into HTML head
    end
    PDS-->>Browser: HTML + CSP headers
    Browser->>Auth: User interacts / submits login/consent
    Auth->>Shared: resolveClientMetadata(client_id)
    Shared-->>Auth: ClientMetadata
    Auth->>Shared: getClientCss(client_id, metadata, trustedClients)
    Shared-->>Auth: Escaped CSS
    Auth->>Auth: Include customCss in render output
    Auth-->>Browser: Branded HTML
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 New styles hop in on tiptoe light,
Trusted clients gleam in CSS bright,
Metadata fetched, cached with care,
OTPs shaped to fit and share,
A rabbit cheers: branding takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main feature: custom CSS injection for trusted OAuth clients, which aligns with the core objective of adding branding.css support and middleware injection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch css-injection-trusted-clients

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aspiers aspiers force-pushed the css-injection-trusted-clients branch from 1f498b1 to 97877a3 Compare March 3, 2026 21:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/shared/src/client-metadata.ts (1)

118-127: Consider case-insensitive comparison for trusted client IDs.

The trustedClients.includes(clientId) check is case-sensitive. While OAuth client IDs are typically URLs and should be compared exactly, ensure that the configuration in PDS_OAUTH_TRUSTED_CLIENTS matches the exact client_id values used in requests (including trailing slashes, protocol, etc.).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/client-metadata.ts` around lines 118 - 127, The trusted
client check in getClientCss is currently case-sensitive; update the logic to
perform a case-insensitive comparison (e.g., compare clientId.toLowerCase()
against trustedClients entries normalized with .map(s => s.toLowerCase())) and
consider normalizing trivial URL differences (trim and optionally remove a
trailing slash) before comparing so trusted client matching is robust; modify
getClientCss to use the normalized comparison when evaluating
trustedClients.includes(...).
packages/pds-core/src/index.ts (1)

348-401: Async middleware without explicit error handling for next().

The middleware is async but Express doesn't natively handle promise rejections in middleware. While there's a try-catch around the main logic that calls next() on error, if resolveClientMetadata throws after next() is somehow not reached, Express won't catch it. The current implementation looks correct, but consider wrapping the entire function body or using an async middleware wrapper for robustness.

This is a minor concern given the existing try-catch structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/pds-core/src/index.ts` around lines 348 - 401, The
cssInjectionMiddleware is declared async but currently swallows errors by
calling next() in the catch; change error handling to forward exceptions to
Express by calling next(err) inside the catch so unhandled rejections reach the
error handler, or wrap the middleware with an async wrapper (e.g.,
express-async-handler) and remove the try/catch; specifically update
cssInjectionMiddleware (and where resolveClientMetadata is awaited) to call
next(err) instead of next() on failure, or export/replace the async function
with a wrapper that catches promise rejections and forwards them to next.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/auth-service/src/routes/login-page.ts`:
- Around line 228-229: The imports otpHtmlAttrs and otpDescriptionText
referenced by login-page.ts are missing because packages/shared lacks
otp-config.ts and index.ts exports ./otp-config.js point to a non-existent
module; fix by either (A) creating packages/shared/src/otp-config.ts that
exports otpHtmlAttrs (returns the OTP input HTML attributes such as
maxlength="8" and pattern="[0-9]{8}") and otpDescriptionText (returns the
8-digit OTP description string), and update the package/shared index.ts to
export these symbols, or (B) change the import in login-page.ts to the correct
existing module that provides these utilities; ensure the exported function
names otpHtmlAttrs and otpDescriptionText are used exactly as in login-page.ts.

In `@packages/pds-core/src/index.ts`:
- Around line 383-394: The overridden response end uses untyped any and a loose
rest param; update types to remove ESLint errors and match Node signatures by
typing origEnd as typeof res.end, and change the override to res.end = (chunk?:
string | Buffer | null, encoding?: BufferEncoding | ((err?: Error) => void) |
undefined, callback?: (() => void) | undefined) => { ... } (handle encoding
being a callback), check Buffer.isBuffer and typeof chunk === 'string' as
before, and call origEnd(chunk as any, encoding as any, callback as any) to
satisfy typing while preserving behavior; reference origEnd, res.end, and
styleTag when editing.

In `@packages/shared/src/index.ts`:
- Around line 27-33: The re-export block in index.ts references a non-existent
module './otp-config.js' causing build failures; either add a new module that
exports otpConfig, generateOtp, otpHtmlAttrs, otpDescriptionText and the types
OtpFormat and OtpConfig (implementing their expected signatures), or remove
these re-exports from the file if OTP functionality isn’t part of this
change—update the import paths in any callers to point to the new module name if
you create it, and ensure the exported symbol names (otpConfig, generateOtp,
otpHtmlAttrs, otpDescriptionText, OtpFormat, OtpConfig) exactly match what's
declared.

---

Nitpick comments:
In `@packages/pds-core/src/index.ts`:
- Around line 348-401: The cssInjectionMiddleware is declared async but
currently swallows errors by calling next() in the catch; change error handling
to forward exceptions to Express by calling next(err) inside the catch so
unhandled rejections reach the error handler, or wrap the middleware with an
async wrapper (e.g., express-async-handler) and remove the try/catch;
specifically update cssInjectionMiddleware (and where resolveClientMetadata is
awaited) to call next(err) instead of next() on failure, or export/replace the
async function with a wrapper that catches promise rejections and forwards them
to next.

In `@packages/shared/src/client-metadata.ts`:
- Around line 118-127: The trusted client check in getClientCss is currently
case-sensitive; update the logic to perform a case-insensitive comparison (e.g.,
compare clientId.toLowerCase() against trustedClients entries normalized with
.map(s => s.toLowerCase())) and consider normalizing trivial URL differences
(trim and optionally remove a trailing slash) before comparing so trusted client
matching is robust; modify getClientCss to use the normalized comparison when
evaluating trustedClients.includes(...).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9351243 and 1f498b1.

📒 Files selected for processing (14)
  • .beads/issues.jsonl
  • .env.example
  • packages/auth-service/.env.example
  • packages/auth-service/src/__tests__/consent.test.ts
  • packages/auth-service/src/context.ts
  • packages/auth-service/src/index.ts
  • packages/auth-service/src/lib/client-metadata.ts
  • packages/auth-service/src/routes/consent.ts
  • packages/auth-service/src/routes/login-page.ts
  • packages/demo/src/app/client-metadata.json/route.ts
  • packages/pds-core/.env.example
  • packages/pds-core/src/index.ts
  • packages/shared/src/client-metadata.ts
  • packages/shared/src/index.ts

Comment on lines +383 to +394
const origEnd = res.end.bind(res)
res.end = (chunk: any, ...args: any[]) => {
if (typeof chunk === 'string' && chunk.includes('</head>')) {
chunk = chunk.replace('</head>', `${styleTag}</head>`)
} else if (Buffer.isBuffer(chunk)) {
const str = chunk.toString('utf-8')
if (str.includes('</head>')) {
chunk = str.replace('</head>', `${styleTag}</head>`)
}
}
return origEnd(chunk, ...args)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add type annotations to fix ESLint errors and improve type safety.

The any types on line 384 trigger ESLint errors. While this is internal Express plumbing, adding minimal type annotations satisfies the linter and documents intent.

Also, the res.end signature in Node.js can accept (chunk, encoding, callback). The current spread may work, but being explicit is safer.

Proposed fix
         // Wrap res.end to inject the <style> tag before </head>
         const origEnd = res.end.bind(res)
-        res.end = (chunk: any, ...args: any[]) => {
+        res.end = (chunk?: Buffer | string, encoding?: BufferEncoding | (() => void), cb?: () => void) => {
           if (typeof chunk === 'string' && chunk.includes('</head>')) {
             chunk = chunk.replace('</head>', `${styleTag}</head>`)
           } else if (Buffer.isBuffer(chunk)) {
             const str = chunk.toString('utf-8')
             if (str.includes('</head>')) {
               chunk = str.replace('</head>', `${styleTag}</head>`)
             }
           }
-          return origEnd(chunk, ...args)
+          return origEnd(chunk, encoding as BufferEncoding, cb)
         }
🧰 Tools
🪛 ESLint

[error] 384-384: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


[error] 384-384: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🪛 GitHub Check: lint

[failure] 384-384:
Unexpected any. Specify a different type


[failure] 384-384:
Unexpected any. Specify a different type

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/pds-core/src/index.ts` around lines 383 - 394, The overridden
response end uses untyped any and a loose rest param; update types to remove
ESLint errors and match Node signatures by typing origEnd as typeof res.end, and
change the override to res.end = (chunk?: string | Buffer | null, encoding?:
BufferEncoding | ((err?: Error) => void) | undefined, callback?: (() => void) |
undefined) => { ... } (handle encoding being a callback), check Buffer.isBuffer
and typeof chunk === 'string' as before, and call origEnd(chunk as any, encoding
as any, callback as any) to satisfy typing while preserving behavior; reference
origEnd, res.end, and styleTag when editing.

Comment on lines +27 to +33
export {
otpConfig,
generateOtp,
otpHtmlAttrs,
otpDescriptionText,
} from './otp-config.js'
export type { OtpFormat, OtpConfig } from './otp-config.js'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if otp-config.ts/js exists in the shared package
echo "=== Searching for otp-config files ==="
fd -t f 'otp-config' packages/shared/

echo -e "\n=== Listing all .ts files in packages/shared/src ==="
ls -la packages/shared/src/*.ts 2>/dev/null || echo "No .ts files found"

echo -e "\n=== Checking if client-metadata.ts exists ==="
fd -t f 'client-metadata' packages/shared/

echo -e "\n=== Checking the index.ts file at lines 25-35 ==="
sed -n '25,35p' packages/shared/src/index.ts

Repository: hypercerts-org/ePDS

Length of output: 1140


Build failure: Missing ./otp-config.js module.

The exports on lines 27-33 reference ./otp-config.js, which does not exist in packages/shared/src/. No otp-config.ts or compiled otp-config.js file is present. This will cause TypeScript compilation to fail for the shared package and any dependents. Either the file needs to be created with the exported functions (otpConfig, generateOtp, otpHtmlAttrs, otpDescriptionText) and types (OtpFormat, OtpConfig), or the exports must be removed if this module is not part of this PR.

🧰 Tools
🪛 GitHub Actions: CI

[error] 32-32: Cannot find module './otp-config.js' or its corresponding type declarations. (TS2307) During 'pnpm --recursive build' step.

🪛 GitHub Check: typecheck

[failure] 33-33:
Cannot find module './otp-config.js' or its corresponding type declarations.


[failure] 32-32:
Cannot find module './otp-config.js' or its corresponding type declarations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/index.ts` around lines 27 - 33, The re-export block in
index.ts references a non-existent module './otp-config.js' causing build
failures; either add a new module that exports otpConfig, generateOtp,
otpHtmlAttrs, otpDescriptionText and the types OtpFormat and OtpConfig
(implementing their expected signatures), or remove these re-exports from the
file if OTP functionality isn’t part of this change—update the import paths in
any callers to point to the new module name if you create it, and ensure the
exported symbol names (otpConfig, generateOtp, otpHtmlAttrs, otpDescriptionText,
OtpFormat, OtpConfig) exactly match what's declared.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.beads/issues.jsonl:
- Around line 19-21: The issue is that the atproto-9fa parent and its child
tasks (atproto-9fa.1, .2, etc.) have inconsistent statuses and inverted
dependency directions; fix by updating the JSON entries so the parent
atproto-9fa remains open until all child tasks are closed (set "status":"open"
for the parent if children are still open) and correct the dependency edges:
make the shared extraction task atproto-9fa.1 an upstream dependency (other
tasks should have depends_on entries pointing to "atproto-9fa.1" and/or the
parent "atproto-9fa") instead of using inverted "blocks" relationships, and
ensure each dependency object uses the proper "depends_on_id" value and "type"
(use "depends_on" for normal dependency rather than "blocks" where appropriate);
update the "dependencies" arrays for atproto-9fa, atproto-9fa.1, and
atproto-9fa.2 to reflect the correct upstream/downstream ordering and set
statuses consistently.
- Line 17: The default OTP length should remain 8 (not 6): update the shared OTP
config module to read OTP_LENGTH (default 8) and OTP_FORMAT (default "numeric"),
ensure the exported config and generateOTP logic use that default, update any
wiring in better-auth.ts (otpLength + generateOTP callback) and .env.example to
reflect OTP_LENGTH=8, and adjust any tests/HTML defaults (login-page.ts,
recovery.ts, account-login.ts) that assumed 6 so they now use the configured
default of 8.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f498b1 and 97877a3.

📒 Files selected for processing (1)
  • .beads/issues.jsonl

@aspiers aspiers changed the title feat: custom CSS injection for trusted OAuth clients [HYPER-109] feat: custom CSS injection for trusted OAuth clients Mar 3, 2026
aspiers and others added 2 commits March 3, 2026 23:31
Replace unused CSS custom properties with a full dark theme override
while keeping the gradient button styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file was created on a previous branch but never committed,
causing CI typecheck failures since shared/src/index.ts re-exports it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add eslint-disable for res.end wrapper's any types (complex Node
overloads). Run prettier on consent.ts and pds-core/index.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
.1 (shared extraction) now blocks .2 and .5 (not the reverse).
.2 (auth config) now blocks .3 and .4 (not the reverse).
Also closes .1 and .2 which were left open.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant