Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
ae220d5
PDS: compare lexicon methods case-insensitively (#4643)
devinivy Feb 16, 2026
48aee6c
Add recIdStr to suggested follows by actor (#4644)
ds-boyce Feb 16, 2026
7e3eba6
Add action (#4646)
estrattonbailey Feb 17, 2026
978a99e
[ALG-58] Use existing `getOnboardingSuggestedUsersSkeleton` method na…
estrattonbailey Feb 17, 2026
2335075
Version packages (#4648)
github-actions[bot] Feb 17, 2026
39cf199
move feature gates to analytics folder (#4640)
rafaeleyng Feb 17, 2026
009c4af
Add test cases for `knownValues` strings (#4639)
matthieusieben Feb 18, 2026
0e8d4bb
Skip puppeteer download in build and verify steps (#4652)
matthieusieben Feb 18, 2026
cd9deb6
Fix conflicting identifier for generated type utilities (#4659)
matthieusieben Feb 19, 2026
39dea03
Lex SDK fixes and improvements (#4654)
matthieusieben Feb 19, 2026
ea5df64
Various Lex SDK improvements (#4660)
matthieusieben Feb 19, 2026
a23d132
OAuth client improvements (#4642)
matthieusieben Feb 19, 2026
7cf7d90
[APP-1796] Add identifier fallbacks (#4664)
estrattonbailey Feb 19, 2026
ce87b88
Ensure service entrypoints are copied into docker containers (#4666)
estrattonbailey Feb 19, 2026
caad882
Version packages (#4653)
github-actions[bot] Feb 20, 2026
66b7295
Fix `lex-cbor` warnin: import `with` (#4667)
matthieusieben Feb 20, 2026
dc9644b
Fix lint warning in `@atproto/identity` (#4669)
matthieusieben Feb 20, 2026
7229c03
Add changeset check workflow (#4670)
matthieusieben Feb 20, 2026
8603be6
Version packages (#4668)
github-actions[bot] Feb 20, 2026
1521b2e
Use workspace dictionary in vscode settings (#4650)
ds-boyce Feb 20, 2026
c4b0489
Attempt at fixing flaky BSKY test (#4673)
matthieusieben Feb 20, 2026
38852f0
QoL improvements for lex SDK (#4672)
matthieusieben Feb 20, 2026
450f085
docs: Updating app password based session example (#4631)
LotharieSlayer Feb 23, 2026
cb5f9bf
Version packages (#4674)
github-actions[bot] Feb 23, 2026
d69c81e
Fix invalid handle showing verification (#4634)
rafaeleyng Feb 23, 2026
1b83d84
Fix README typo
rafaeleyng Feb 23, 2026
52834ab
Lex SDK error handling improvements (#4688)
matthieusieben Mar 2, 2026
137065b
Add feed to sendInteractions input (#4704)
ds-boyce Mar 2, 2026
e8969b6
Add Lex SDK support to `@atproto/xrpc-server` (#4691)
matthieusieben Mar 3, 2026
138f0a0
Fix `@atproto/sync` test flakyness (#4705)
matthieusieben Mar 3, 2026
d5f4224
Update PDS's internal pipethough error handling (#4698)
matthieusieben Mar 3, 2026
1c473ab
Update `@atprto/lexicon-resolver` to use Lex SDK (#4697)
matthieusieben Mar 3, 2026
f7c2610
Increase type strictness in `@atproto/syntax` (#4689)
matthieusieben Mar 3, 2026
88326d2
Version packages (#4703)
github-actions[bot] Mar 3, 2026
6634140
Deprecate `recId` in favor of `recIdStr` (#4683)
ds-boyce Mar 4, 2026
0e5df95
Make sure to always trigger `refreshSession` when resuming sessions (…
matthieusieben Mar 4, 2026
82e7080
Version packages (#4715)
github-actions[bot] Mar 4, 2026
76ab6ea
Remove account status fallback to stale value (#4717)
estrattonbailey Mar 4, 2026
39ea120
Version packages (#4718)
github-actions[bot] Mar 4, 2026
383e157
bsky: remove format from img urls by default (#4712)
devinivy Mar 5, 2026
5a2f884
Improve lex-client error reporting (#4714)
matthieusieben Mar 5, 2026
7444295
use feature gate to remove image format from URL (#4721)
rafaeleyng Mar 5, 2026
b3f80b4
Fix typo in chat.bsky.authFullChatClient lexicon (#4719)
xenomachina Mar 5, 2026
7ed5704
bsky: switch default image format to jpeg temporarily (#4723)
devinivy Mar 5, 2026
c9004b4
disable tracking image rollout (#4726)
rafaeleyng Mar 5, 2026
3dc3791
Add Standard Schema compatibility (#4734)
matthieusieben Mar 10, 2026
192685f
Improve reading of car files (#4729)
matthieusieben Mar 11, 2026
5f73f8e
Improve reading of car files (#4740)
matthieusieben Mar 11, 2026
9f9f71a
:sparkles: Add purge event for age assurance data for compliance (#4709)
foysalit Mar 12, 2026
eaee3d4
Serialize pagerank (#4746)
estrattonbailey Mar 13, 2026
112b159
Export more string format type assertion utilities from `@atproto/lex`
matthieusieben Mar 16, 2026
67eb0c1
Improve efficiency of `AtUri` `did` getter and typing of `hostname` p…
matthieusieben Mar 16, 2026
139b294
bsky: remove feature gate for including format in image URLs (#4755)
devinivy Mar 16, 2026
266b563
add 'app.bsky.actor.getPreferences' to authViewAll (#4749)
bnewbold Mar 17, 2026
ff42a3a
Avoid Go zero dates (year 0001) in bsky views (#4753)
rafaeleyng Mar 17, 2026
bc69b03
improve checking for zero dates and test (#4762)
rafaeleyng Mar 17, 2026
c89225f
create chat declaration for dev-env users (#4763)
rafaeleyng Mar 17, 2026
3d60e1a
non-strict validation mode
matthieusieben Mar 18, 2026
cbbc03b
Revert "non-strict validation mode"
matthieusieben Mar 18, 2026
efb2b58
[APP-1926] Add bsync delete operations support (#4745)
estrattonbailey Mar 18, 2026
13b2aad
Version packages (#4720)
github-actions[bot] Mar 18, 2026
4ecde48
Add feature flag to enable social proof on getSuggestedFollowsByActor…
estrattonbailey Mar 18, 2026
3b41b81
Remove deprecated handling from getSuggestedFollowsByActor (#4747)
estrattonbailey Mar 18, 2026
6a88461
Allow processing of invalid lex data (#4761)
matthieusieben Mar 19, 2026
b2baf8d
Fix `Client.call` options for XRPC queries (#4772)
matthieusieben Mar 19, 2026
df8328c
Fix xrpc options (#4773)
matthieusieben Mar 19, 2026
737c046
Fix infinitely deep declaration (#4774)
matthieusieben Mar 19, 2026
8445e8a
Version packages (#4770)
github-actions[bot] Mar 19, 2026
db795be
Feature Gates Refactor (#4756)
estrattonbailey Mar 19, 2026
b86dbfc
Remove feature gate for new user onboarding (#4775)
ds-boyce Mar 19, 2026
57ca210
lexicons: add 'bot' wellKnown label value (#4777)
bnewbold Mar 19, 2026
527f5d4
Various lex SDK improvements (#4779)
matthieusieben Mar 23, 2026
e5e5bcf
Show invalid values in issue description (#4784)
matthieusieben Mar 23, 2026
c4df84c
Optimize counting of graphemes when validating strings (#4787)
matthieusieben Mar 23, 2026
d0c136c
Move PDS code over to `@atproto/lex` (#4408)
matthieusieben Mar 23, 2026
37b2f49
Ensure scoped checkGates merges outer user context (#4780)
estrattonbailey Mar 23, 2026
ae22362
Add A/A test to thread endpoint (#4790)
estrattonbailey Mar 23, 2026
3aa18f8
Add some missing codegen files (#4791)
estrattonbailey Mar 23, 2026
0dbea15
Make AT-uri validation faster (#4792)
matthieusieben Mar 24, 2026
a99dd58
Allow payload from XRPC endpoints with no `output` in their schema (#…
matthieusieben Mar 24, 2026
36e3019
Disable AppView response validation (#4797)
matthieusieben Mar 24, 2026
960a02b
Skip read-after-write for invalid records (#4798)
matthieusieben Mar 24, 2026
1479c87
Allow use of `foo[]=bar` syntax in query params (#4793)
matthieusieben Mar 24, 2026
fac213d
Fix error stating that the lex build folder could not be cleared (#4800)
matthieusieben Mar 24, 2026
c5c6c7d
Allow any encoding to match payload with no encoding specified in sch…
matthieusieben Mar 24, 2026
ac6bd18
Disable strict mode parsing of records (#4796)
matthieusieben Mar 24, 2026
6116d56
Allow `[]` query params syntax with `app.bsky.feed.getPostThread` end…
matthieusieben Mar 24, 2026
c224e1f
add missing paramsParseLoose (#4803)
matthieusieben Mar 24, 2026
4f11fc3
Add support for indexed query params syntax (#4804)
matthieusieben Mar 25, 2026
2203d82
Version packages (#4776)
github-actions[bot] Mar 26, 2026
34f9ae1
Show suggestions to logged out users (#4810)
ds-boyce Mar 26, 2026
2a5e2c2
Create new endpoints for suggested users (#4809)
ds-boyce Mar 27, 2026
3711454
Rewrite of aturi tests (#4815)
matthieusieben Mar 29, 2026
b6a9625
Version packages (#4811)
github-actions[bot] Apr 2, 2026
3b9af1b
Workaround missing dependency when installing npm@latest (#4821)
ds-boyce Apr 2, 2026
42232c7
Register new suggested user endpoints with the router (#4830)
estrattonbailey Apr 6, 2026
158daf6
Version packages (#4831)
github-actions[bot] Apr 7, 2026
9def38a
Fix legacy profile validation by using safeParse instead of ifMatches…
estrattonbailey Apr 7, 2026
284e6fb
Revert #4824 (#4834)
matthieusieben Apr 7, 2026
c62651d
Fix support for legacy blobs in lex SDK (#4828)
matthieusieben Apr 7, 2026
7000746
bsky: do not apply blocks viewed by trusted mod service (#4837)
devinivy Apr 7, 2026
f6f100c
Avoid alteration of record data (#4835)
matthieusieben Apr 8, 2026
b6b231f
Fix typing of `delete` options to omit `body` instead of `params`. (#…
matthieusieben Apr 8, 2026
aa1763d
[APP-1775] Hydrate labels for actor statuses (#4555)
estrattonbailey Apr 8, 2026
d8801e2
Increase max image upload size to 2mb from 1mb (#4823)
estrattonbailey Apr 8, 2026
83b7246
Version packages (#4833)
github-actions[bot] Apr 9, 2026
55c3986
Make sure to include did and isMe checks on decideStatus default deci…
estrattonbailey Apr 9, 2026
2a9221d
Version packages (#4842)
github-actions[bot] Apr 9, 2026
7c61f19
improve ctx typing (#4849)
rafaeleyng Apr 13, 2026
b704c52
Data Plane Caller Interceptor (#4852)
jcalabro Apr 13, 2026
b3ce11a
OAuth provider UI unification (#4820)
matthieusieben Apr 14, 2026
952354c
Improve performances of `lexParseJsonBytes` function (#4836)
matthieusieben Apr 14, 2026
61e75af
Improve error reporting (#4816)
matthieusieben Apr 14, 2026
55d06de
Fix `normalizeDatetime` inconsistencies (#4760)
matthieusieben Apr 14, 2026
26d793a
Improve `AtUriString` type an utilities (#4806)
matthieusieben Apr 14, 2026
0cfb16b
Disable COEP headers when hCaptcha is enabled (#4855)
matthieusieben Apr 15, 2026
ff9f84e
Version packages (#4851)
github-actions[bot] Apr 15, 2026
e36f6f5
Merge branch 'main' into merge-2026-04-15
nicolo-ribaudo Apr 15, 2026
5573ae2
Solve merge conflicts
nicolo-ribaudo Apr 15, 2026
c8b730b
Fix linting errors, disable changeset check
nicolo-ribaudo Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
904 changes: 904 additions & 0 deletions .claude/skills/backend-lexification/SKILL.md

Large diffs are not rendered by default.

247 changes: 247 additions & 0 deletions .claude/skills/vitest-patterns/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
---
name: vitest-patterns
description: Patterns and conventions for writing vitest tests in this project. This skill should be used when writing new tests, adding test cases to existing test files, or reviewing test code for correctness. Trigger whenever the user asks to write tests, add test coverage, create test files, or mentions vitest/testing.
---

# Vitest Test Patterns

Conventions and patterns for writing vitest tests in this codebase.

## Imports

Always import test utilities from `vitest`. Use named imports:

```ts
import { assert, describe, expect, it, vi } from 'vitest'
```

Only import what you use. Add `vi` only when using mock functions. Add `assert` only when narrowing types.

## Test Structure

### Describe labels

Pass the function/class under test directly as the `describe` label when possible. Use string labels for conceptual groupings:

```ts
// Function reference as label (preferred when testing a single export)
describe(parseCid, () => { ... })
describe(isLexValue, () => { ... })

// String label for conceptual groups or when testing multiple related behaviors
describe('roundtrip toBase64 <-> fromBase64', () => { ... })
describe('isObject', () => { ... })
```

### Grouping

Nest logical groupings inside the top-level describe. Common groupings:

```ts
describe(someFunction, () => {
describe('valid inputs', () => { ... })
describe('invalid inputs', () => { ... })
describe('edge cases', () => { ... })
})
```

For features with a default behavior and an override, cover both:

```ts
describe('validateResponse', () => {
it('rejects invalid response body by default', ...)
it('accepts invalid response body when disabled', ...)
it('succeeds with valid response body when enabled', ...)
})
```

For safety-critical code, group edge cases under a `'safety'` or `'edge cases'` describe:

```ts
describe('safety', () => {
it('handles cyclic structures without infinite loops', () => { ... })
it('handles deep structures without exceeding call stack', () => { ... })
})
```

## Parameterized Tests

Use `for...of` loops over test case arrays instead of `it.each`:

```ts
describe(isLexScalar, () => {
for (const { note, value, expected } of [
{ note: 'string', value: 'hello', expected: true },
{ note: 'boolean', value: true, expected: true },
{ note: 'null', value: null, expected: true },
{ note: 'number (float)', value: 3.14, expected: false },
{ note: 'undefined', value: undefined, expected: false },
]) {
it(note, () => {
expect(isLexScalar(value)).toBe(expected)
})
}
})
```

This also works for running the same test suite against multiple implementations:

```ts
for (const utf8Len of [utf8LenNode!, utf8LenCompute!] as const) {
describe(utf8Len, () => {
it('computes utf8 string length', () => {
expect(utf8Len('a')).toBe(1)
})
})
}
```

## Assertions

### Type narrowing with `assert`

Use `assert()` from vitest for type narrowing and boolean checks. **Always prefer `assert(result.success)` over `expect(result.success).toBe(true)`** — the `assert` provides type narrowing in TypeScript, which allows the rest of the test to access narrowed properties without additional type guards.

```ts
// Narrow to a specific type before further assertions
assert(err instanceof XrpcFetchError)
expect(err.cause).toBeInstanceOf(TypeError)

// Discriminated union checks - PREFERRED
assert(result.success)
expect(result.body).toEqual({ value: 'hello' })
// TypeScript now knows result.body exists

assert(!result.success)
expect(result).toBeInstanceOf(XrpcResponseError)
// TypeScript now knows result has error properties

// DON'T do this - it doesn't narrow types
expect(result.success).toBe(true) // ❌ No type narrowing
if (result.success) {
expect(result.body).toEqual({ value: 'hello' }) // Still need type guard
}
```

### Error assertions with `rejects.toSatisfy`

For thrown errors, prefer `rejects.toSatisfy()` over `rejects.toThrow()` when you need multiple detailed assertions:

```ts
await expect(
someAsyncFn(),
).rejects.toSatisfy((err) => {
assert(err instanceof SomeError)
expect(err.cause).toBeInstanceOf(TypeError)
expect(err.message).toContain('failed')
return true // must return true
})
```

Always `return true` at the end of `toSatisfy` callbacks.

For simple "it throws" checks, `toThrow()` is fine:

```ts
expect(() => parseCid(invalidStr)).toThrow()
expect(() => cidForRawHash(new Uint8Array(31))).toThrow('Invalid SHA-256 hash length')
```

## Mock Functions

### Use `vi.fn()` with type parameters

When you need to inspect how a function was called, use `vi.fn<Type>()`:

```ts
const fetchHandler = vi.fn<FetchHandler>(async () =>
Response.json({ value: 'ok' }),
)

await xrpc(fetchHandler, testQuery, { params: { limit: 25 } })

expect(fetchHandler).toHaveBeenCalledOnce()
const [path, init] = fetchHandler.mock.calls[0]
expect(path).toContain('/xrpc/io.example.testQuery')
```

When you don't need to inspect calls, use a plain typed function:

```ts
const fetchHandler: FetchHandler = async () => Response.json({ value: 'hello' })
```

## Test Fixtures

Define reusable fixtures at the top of the file, outside describe blocks:

```ts
const invalidCidStr = 'invalidcidstring'
const cborCidStr = 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'
const cborCid = parseCid(cborCidStr, { flavor: 'cbor' })
```

Keep fixtures minimal and focused on what the tests need.

## TypeScript in Tests

### Intentionally invalid arguments

Use `// @ts-expect-error` with a description:

```ts
await xrpc(fetchHandler, testQuery, {
// @ts-expect-error intentionally passing invalid params
params: { limit: 'not-a-number' },
validateRequest: true,
})
```

## Global Stubbing

When testing code that uses a global, temporarily replace it and restore in a `finally` block:

```ts
it('throws TypeError when fetch is not available', () => {
const originalFetch = globalThis.fetch
try {
// @ts-expect-error removing fetch to simulate missing environment
globalThis.fetch = undefined
expect(() => buildAgent({ service: 'https://example.com' })).toThrow(TypeError)
} finally {
globalThis.fetch = originalFetch
}
})
```

Use `try/finally` (not `beforeEach`/`afterEach`) when the stub is scoped to a single test.

## Roundtrip Tests

When testing encode/decode or serialize/deserialize pairs, add a dedicated roundtrip describe:

```ts
describe('roundtrip toBase64 <-> fromBase64', () => {
it('roundtrips empty array', () => {
const original = new Uint8Array(0)
expect(ui8Equals(fromBase64(toBase64(original)), original)).toBe(true)
})

it('roundtrips all byte values', () => {
const allBytes = new Uint8Array(256)
for (let i = 0; i < 256; i++) allBytes[i] = i
expect(ui8Equals(fromBase64(toBase64(allBytes)), allBytes)).toBe(true)
})
})
```

## Workflow

- Do not worry about code formatting or lint issues. The user will review changes in their editor, which applies formatting automatically on save.
- Do not commit test changes. Leave them as unstaged modifications for the user to review.

## Running Tests

```bash
pnpm exec vitest run path/to/file.test.ts
```
9 changes: 5 additions & 4 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ packages/bsync/src/proto

# codegen
packages/api/src/client
packages/lexicon-resolver/src/client
packages/bsky/src/lexicon
packages/pds/src/lexicon
packages/ozone/src/lexicon

# @atproto/lex
packages/lexicon-resolver/src/lexicons
packages/lex/*/src/lexicons
packages/lex/*/tests/lexicons
packages/oauth/oauth-client-browser-example/src/lexicons
packages/pds/src/lexicons
packages/bsky/src/lexicons
packages/sync/src/lexicons

# others
packages/api/src/moderation/const/labels.ts
packages/oauth/*/src/locales/*/messages.ts
packages/oauth/oauth-provider-frontend/src/routeTree.gen.ts
packages/oauth/oauth-client-expo/android/build
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"distinctGroup": true,
"alphabetize": { "order": "asc" },
"newlines-between": "never",
"pathGroups": [
{ "pattern": "#/**", "group": "parent", "position": "before" }
],
"groups": [
"builtin",
"external",
Expand Down
6 changes: 3 additions & 3 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ packages/bsync/src/proto/** linguist-generated=true

# codegen
packages/api/src/client/** linguist-generated=true
packages/lexicon-resolver/src/client/** linguist-generated=true
packages/bsky/src/lexicon/** linguist-generated=true
packages/pds/src/lexicon/** linguist-generated=true
packages/ozone/src/lexicon/** linguist-generated=true

# @atproto/lex
packages/lexicon-resolver/src/lexicons/** linguist-generated=true

# i18n
packages/oauth/oauth-provider-ui/src/locales/**/messages.po linguist-generated=true
2 changes: 2 additions & 0 deletions .github/workflows/build-and-push-bsky-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ on:
push:
branches:
- main
- msi/pds-lexification

env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-and-push-bsync-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main

env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-and-push-bsync-ghcr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main

env:
REGISTRY: ghcr.io
USERNAME: ${{ github.actor }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-and-push-ozone-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches:
- main
- divy/ozone-metadata

env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-and-push-ozone-ghcr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main

env:
REGISTRY: ghcr.io
USERNAME: ${{ github.actor }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build-and-push-pds-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ on:
push:
branches:
- main
- msi/pds-lexification

env:
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
Expand Down
Loading
Loading