Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 445f38c6be | |||
| f8a71d0aab | |||
| 49c8c9f124 |
@@ -1,904 +0,0 @@
|
|||||||
---
|
|
||||||
name: backend-lexification
|
|
||||||
description: >
|
|
||||||
Migrate backend apis from `@atproto/api`, `@atproto/lexicon`, `@atproto/xrpc`,
|
|
||||||
and `@atproto/lex-cli` to `@atproto/lex`. Use this skill whenever the user
|
|
||||||
wants to adopt `@atproto/lex` in a package that currently relies on
|
|
||||||
`@atproto/lex-cli` to perform code generation, replace AtpAgent with Client,
|
|
||||||
migrate generated lexicon code to the new `lex build` output, replace
|
|
||||||
`ids.XxxYyy` with namespace accessors, adopt branded string types, switch from
|
|
||||||
`jsonStringToLex` to `lexParse`, or any refactoring that moves code from the
|
|
||||||
old generated lexicon system to `@atproto/lex`. Also trigger when the user
|
|
||||||
mentions "lexification", "lex SDK", "lex migration", or asks about replacing
|
|
||||||
`@atproto/api` imports in service code.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Lexification: Migrating to `@atproto/lex`
|
|
||||||
|
|
||||||
This skill describes how to refactor an AT Protocol service package to replace:
|
|
||||||
|
|
||||||
- `@atproto/api` (the old high-level client)
|
|
||||||
- `@atproto/lexicon` (the old runtime lexicon library)
|
|
||||||
- `@atproto/xrpc` (the old XRPC client types)
|
|
||||||
- `@atproto/lex-cli` codegen (the old code generator that produced `src/lexicon/` directories)
|
|
||||||
|
|
||||||
...with `@atproto/lex`, which provides generated TypeScript schemas with type-safe validation, type guards, builders, and an XRPC client.
|
|
||||||
|
|
||||||
For the full `@atproto/lex` API reference, read `packages/lex/lex/README.md`.
|
|
||||||
|
|
||||||
## Guiding Principles
|
|
||||||
|
|
||||||
- **Minimize runtime changes.** Prefer type-level changes over runtime changes. If the old code works correctly at runtime, keep its logic and just improve the types around it.
|
|
||||||
- **Tests still use `@atproto/api` exclusively.** Do not migrate test files — they will be migrated in a separate phase. Tests that imported from the old `src/lexicon/` should switch to importing from `@atproto/api` instead.
|
|
||||||
|
|
||||||
## What `@atproto/lex` Provides
|
|
||||||
|
|
||||||
- **Generated TypeScript schemas** via `lex build` (replaces `lex-cli`'s `lex gen-server`)
|
|
||||||
- **Runtime utilities**: `lexParse`, `lexStringify`, `toDatetimeString`, `currentDatetimeString`
|
|
||||||
- **Branded string types**: `DidString`, `HandleString`, `AtIdentifierString`, `AtUriString`, `UriString`, `DatetimeString`
|
|
||||||
- **Type guards**: `isDidString`, `isHandleString`, `isAtIdentifierString`, `isAtUriString`
|
|
||||||
- **Data types**: `Cid`, `parseCid`, `BlobRef` (from `@atproto/lex-data` sub-export, also re-exported from `@atproto/lex`)
|
|
||||||
- **`Client` class**: replaces `AtpAgent` for service-to-service XRPC calls
|
|
||||||
- **`xrpc` / `xrpcSafe` functions**: standalone XRPC requests with structured error handling
|
|
||||||
- **`XrpcError`**: replaces `XRPCError` from `@atproto/xrpc`
|
|
||||||
- **Type-safe schema accessors**: `$type`, `$matches`, `$isTypeOf`, `$build`, `$safeParse`, `$lxm`, etc.
|
|
||||||
|
|
||||||
The generated schemas live in `src/lexicons/` (note the plural). They expose a namespace-based API (e.g., `app.bsky.feed.post`) with `$type`, `$build`, `$check`, `$matches`, `$isTypeOf`, `$parse`, `$validate`, `$safeParse`, `$lxm`, and other utilities.
|
|
||||||
|
|
||||||
## Overview of Changes
|
|
||||||
|
|
||||||
The refactor touches these areas (in recommended order):
|
|
||||||
|
|
||||||
1. **Project Configuration** - Update `package.json` dependencies, build scripts and git configuration
|
|
||||||
2. **Project Code Setup** - Replace server creation, endpoint registration, imports from old generated code, app context initialization (`AtpAgent` → `Client`), and type aliases with new patterns from `@atproto/lex`
|
|
||||||
3. **Endpoint Handlers Registration** - New `server.add()` pattern, handler return types, LXM references, and parameter/output/record type replacements
|
|
||||||
4. **XRPC Client Calls** - Replace `AtpAgent` API calls with `Client.xrpc()`, `Client.call()`, or `xrpcSafe()`
|
|
||||||
5. **Type Strictness** - Use branded types (`DidString`, `HandleString`, `AtUriString`, `Cid`, etc.)
|
|
||||||
6. **Data Utilities** - Replace old data manipulation functions (`jsonStringToLex` → `lexParse`, datetime helpers, `BlobRef`, etc.)
|
|
||||||
7. **Schema Validation and Type Guards** - Replace old `isX()` functions with `$matches()` / `$isTypeOf` / `$build()`
|
|
||||||
|
|
||||||
## 1. Project Configuration
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
Add `@atproto/lex` as a dependency.
|
|
||||||
|
|
||||||
Remove from dependencies (if present):
|
|
||||||
|
|
||||||
- `multiformats` (CID handling is now in `@atproto/lex-data`)
|
|
||||||
|
|
||||||
Remove from devDependencies:
|
|
||||||
|
|
||||||
- `@atproto/api` (unless still needed for tests in this phase)
|
|
||||||
- `@atproto/lexicon`
|
|
||||||
- `@atproto/lex-cli`
|
|
||||||
- `@atproto/xrpc`
|
|
||||||
|
|
||||||
### Lexicon installation
|
|
||||||
|
|
||||||
Remove the old `src/lexicon/` directory (singular) and any codegen scripts that referenced `lex gen-server`. We will also remove the (manually maintained) `./lexicons/` directory in order to manage installed lexicons with the new `lex install` commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
rm -rf ./src/lexicon
|
|
||||||
rm -rf ./lexicons
|
|
||||||
```
|
|
||||||
|
|
||||||
Install every lexicon NSID that the code uses. Run `lex install` once per NSID (or pass multiple NSIDs at once):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
lex install com.atproto.identity.resolveHandle app.bsky.feed.post
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Some systems (like MacOS) already have a `lex` command. If that is the case, use `npx lex`, `pnpm exec lex` or `yarn lex` to run the correct binary.
|
|
||||||
|
|
||||||
This creates a `manifest.json` and a local `./lexicons/` directory with the schema files the package depends on.
|
|
||||||
|
|
||||||
The `manifest.json` file and `./lexicons/` directory (schema inputs) should be committed to git.
|
|
||||||
|
|
||||||
### Code generation
|
|
||||||
|
|
||||||
Replace the old codegen script with `lex build` as a prebuild step:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- "codegen": "lex gen-server --yes ./src/lexicon ./lexicons/com/atproto/*/* ./lexicons/app/bsky/*/* ...",
|
|
||||||
+ "prebuild": "lex build --lexicons ./lexicons --clear --indexFile",
|
|
||||||
+ "postinstall": "lex install --ci",
|
|
||||||
```
|
|
||||||
|
|
||||||
The `--indexFile` flag generates an index file that re-exports all root-level namespaces, and `--clear` ensures a clean output directory on each build.
|
|
||||||
|
|
||||||
The `lex install --ci` command will ensure that the `manifest.json` is up to date with the installed lexicons. Using the `postinstall` hook ensures that the command runs after `npm install` or `yarn install`, which ensures lexicon integrity in CI environments.
|
|
||||||
|
|
||||||
The `./src/lexicons/` directory (generated output) should be gitignored since it is regenerated on every build:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
echo '/src/lexicons/' >> .gitignore
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Project Code Setup
|
|
||||||
|
|
||||||
After running `lex build`, the new generated code lives in `src/lexicons/` (plural). Import the namespace objects from the index file:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { app, com, chat } from '../lexicons/index.js'
|
|
||||||
```
|
|
||||||
|
|
||||||
Each namespace provides access to schemas through dot notation:
|
|
||||||
|
|
||||||
- `app.bsky.feed.post` - a record schema
|
|
||||||
- `app.bsky.feed.defs.postView` - an object definition
|
|
||||||
- `com.atproto.admin.defs.repoRef` - another object definition
|
|
||||||
- `app.bsky.feed.getAuthorFeed` - a query/procedure schema
|
|
||||||
|
|
||||||
The old codegen used `ids` for NSID string constants (e.g., `ids.AppBskyFeedPost`). These are replaced with `$type` or `$lxm` properties on the schema objects.
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> If the app's build process & bundler supports it, consider using path aliases to simplify imports from `src/lexicons/index.js` (e.g., `import { app } from '#lexicons'`).
|
|
||||||
|
|
||||||
### Server Creation
|
|
||||||
|
|
||||||
The `createServer` function now comes from `@atproto/xrpc-server` directly, not from generated code:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { createServer } from './lexicon'
|
|
||||||
+ import { createServer } from '@atproto/xrpc-server'
|
|
||||||
```
|
|
||||||
|
|
||||||
The call signature changes slightly:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- let server = createServer({
|
|
||||||
+ const server = createServer([], {
|
|
||||||
validateResponse: config.debugMode,
|
|
||||||
payload: { ... },
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Note the empty array `[]` as first argument.
|
|
||||||
|
|
||||||
The `Server` type used in handler files also changes:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { Server } from '../../../../lexicon'
|
|
||||||
+ import { Server } from '@atproto/xrpc-server'
|
|
||||||
```
|
|
||||||
|
|
||||||
The server's express router is accessed differently:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- app.use(server.xrpc.router)
|
|
||||||
+ app.use(server.router)
|
|
||||||
```
|
|
||||||
|
|
||||||
### App Context / Initialization
|
|
||||||
|
|
||||||
Replace `AtpAgent` with `Client` from `@atproto/lex`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { AtpAgent } from '@atproto/api'
|
|
||||||
+ import { Client } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
In context/config files, rename fields:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- searchAgent: AtpAgent | undefined
|
|
||||||
+ searchClient: Client | undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
Client instantiation:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- const myServiceAgent = config.serviceUrl
|
|
||||||
- ? new AtpAgent({ service: config.serviceUrl })
|
|
||||||
- : undefined
|
|
||||||
- if (myServiceAgent && config.serviceApiKey) {
|
|
||||||
- myServiceAgent.api.setHeader('authorization', `Bearer ${config.serviceApiKey}`)
|
|
||||||
- }
|
|
||||||
+ const myServiceClient = config.serviceUrl
|
|
||||||
+ ? new Client({
|
|
||||||
+ service: config.serviceUrl,
|
|
||||||
+ headers: config.serviceApiKey
|
|
||||||
+ ? { authorization: `Bearer ${config.serviceApiKey}` }
|
|
||||||
+ : undefined,
|
|
||||||
+ })
|
|
||||||
+ : undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
Headers are passed directly in the `Client` constructor options rather than being set imperatively after construction.
|
|
||||||
|
|
||||||
### Type Aliases File (if applicable)
|
|
||||||
|
|
||||||
The recommended pattern is to import from the generated code directly where needed, using import aliases. However, if the project contains a centralized types file that re-exports types from generated schemas, update it to import from the new generated code.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { app, chat, com } from '../lexicons/index.js'
|
|
||||||
|
|
||||||
// Type aliases
|
|
||||||
export type PostRecord = app.bsky.feed.post.Main
|
|
||||||
export type PostView = app.bsky.feed.defs.PostView
|
|
||||||
export type Label = com.atproto.label.defs.Label
|
|
||||||
export type StrongRef = com.atproto.repo.strongRef.Main
|
|
||||||
|
|
||||||
// Type guard aliases
|
|
||||||
export const isPostRecord = app.bsky.feed.post.$matches
|
|
||||||
export const isImagesEmbed = app.bsky.embed.images.$matches
|
|
||||||
|
|
||||||
// Validation aliases
|
|
||||||
export const parseStrongRef = com.atproto.repo.strongRef.$safeParse
|
|
||||||
```
|
|
||||||
|
|
||||||
The pattern is consistent:
|
|
||||||
|
|
||||||
- **Type**: `export type Foo = namespace.path.TypeName`
|
|
||||||
- **Type guard**: `export const isFoo = namespace.path.$matches`
|
|
||||||
- **Validation**: `export const parseFoo = namespace.path.$safeParse`
|
|
||||||
|
|
||||||
Types for the "main" definition of a record/object use `.Main`, sub-definitions use their specific name (e.g., `.ReplyRef`, `.ViewRecord`).
|
|
||||||
|
|
||||||
## 3. Endpoint Handlers Registration
|
|
||||||
|
|
||||||
The old pattern used method-chain registration on the server object. The new pattern uses `server.add()` with the schema object:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- server.app.bsky.feed.getAuthorFeed({
|
|
||||||
+ server.add(app.bsky.feed.getAuthorFeed, {
|
|
||||||
auth: ctx.authVerifier.optionalStandardOrRole,
|
|
||||||
handler: async ({ params, auth, req }) => { ... },
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- server.com.atproto.identity.resolveHandle(async ({ req, params }) => {
|
|
||||||
+ server.add(com.atproto.identity.resolveHandle, async ({ params }) => {
|
|
||||||
```
|
|
||||||
|
|
||||||
The schema object is always imported from the generated `lexicons/` directory.
|
|
||||||
|
|
||||||
### Handler Return Type Safety
|
|
||||||
|
|
||||||
When handlers return JSON responses, use `'application/json' as const` for the encoding field to satisfy the return type:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
return {
|
|
||||||
encoding: 'application/json' as const,
|
|
||||||
body: { preferences },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Without the `as const`, TypeScript widens the string literal type and the handler's return type won't match. This is a type-level change only.
|
|
||||||
|
|
||||||
Alternatively, a `satisfies` clause can be used to ensure the returned object matches the expected schema output type:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
return {
|
|
||||||
encoding: 'application/json',
|
|
||||||
body: { preferences },
|
|
||||||
} satisfies app.bsky.actor.getPreferences.$Output
|
|
||||||
```
|
|
||||||
|
|
||||||
### LXM References in Handlers
|
|
||||||
|
|
||||||
Inside handlers, access the `$lxm` property from the schema for auth/proxy computations:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const lxm = app.bsky.actor.getPreferences.$lxm
|
|
||||||
const aud = computeProxyTo(ctx, req, lxm)
|
|
||||||
permissions.assertRpc({ aud, lxm })
|
|
||||||
```
|
|
||||||
|
|
||||||
### Query/Procedure Parameter Types
|
|
||||||
|
|
||||||
Replace old codegen type imports with `$Params` on the schema:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { QueryParams } from '../../../../lexicon/types/app/bsky/feed/getAuthorFeed'
|
|
||||||
+ import { app } from '../../../../lexicons/index.js'
|
|
||||||
|
|
||||||
- type Params = QueryParams
|
|
||||||
+ type Params = app.bsky.feed.getAuthorFeed.$Params
|
|
||||||
```
|
|
||||||
|
|
||||||
### Output Types
|
|
||||||
|
|
||||||
Use `$Output` for response type annotations with `satisfies`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
return {
|
|
||||||
encoding: 'application/json' as const,
|
|
||||||
body: { actor, relationships },
|
|
||||||
} satisfies app.bsky.graph.getRelationships.$Output
|
|
||||||
```
|
|
||||||
|
|
||||||
### Record/Object Types
|
|
||||||
|
|
||||||
Replace individual type imports with namespace-based access:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { Record as PostRecord } from '../lexicon/types/app/bsky/feed/post'
|
|
||||||
+ // Use the types file or direct namespace access:
|
|
||||||
+ import { PostRecord } from './types.js'
|
|
||||||
+ // or: app.bsky.feed.post.Main
|
|
||||||
```
|
|
||||||
|
|
||||||
### Type Aliases for Defs
|
|
||||||
|
|
||||||
When code uses types from `defs` files, reference them through the namespace:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { StatusAttr } from '../../lexicon/types/com/atproto/admin/defs'
|
|
||||||
+ // Use namespace access:
|
|
||||||
+ type StatusAttr = com.atproto.admin.defs.StatusAttr
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- type CodeDetail = SomeCustomType
|
|
||||||
+ type CodeDetail = com.atproto.server.defs.InviteCode
|
|
||||||
+ type CodeUse = com.atproto.server.defs.InviteCodeUse
|
|
||||||
```
|
|
||||||
|
|
||||||
Whenever a new object with a `$type` needs to be constructed, use the `$build()` method on the schema:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const code = com.atproto.server.defs.inviteCode.$build({
|
|
||||||
code: invite.code,
|
|
||||||
available: invite.availableUses - invite.uses.length,
|
|
||||||
disabled: invite.disabled === 1,
|
|
||||||
forAccount: invite.forUser,
|
|
||||||
createdBy: invite.createdBy,
|
|
||||||
createdAt: invite.createdAt,
|
|
||||||
uses: invite.uses,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Otherwise, for plain data objects that don't require a `$type`, just use the namespace types for type annotations without changing the construction logic:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const code: com.atproto.server.defs.InviteCode = {
|
|
||||||
code: invite.code,
|
|
||||||
available: invite.availableUses - invite.uses.length,
|
|
||||||
disabled: invite.disabled === 1,
|
|
||||||
forAccount: invite.forUser,
|
|
||||||
createdBy: invite.createdBy,
|
|
||||||
createdAt: invite.createdAt,
|
|
||||||
uses: invite.uses,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token Values
|
|
||||||
|
|
||||||
For accessing lexicon "token" string constants:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { CURATELIST, MODLIST } from '../../../../lexicon/types/app/bsky/graph/defs'
|
|
||||||
+ import { app } from '../../../../lexicons/index.js'
|
|
||||||
+ const CURATELIST = app.bsky.graph.defs.curatelist.value
|
|
||||||
+ const MODLIST = app.bsky.graph.defs.modlist.value
|
|
||||||
```
|
|
||||||
|
|
||||||
### NSID String Constants
|
|
||||||
|
|
||||||
The old `ids` object (e.g., `ids.AppBskyFeedPost`) is replaced by `$type` on the schema:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { ids } from '../../../lexicon/lexicons'
|
|
||||||
- if (uri.collection === ids.AppBskyGraphList) {
|
|
||||||
+ import { app } from '../../../lexicons/index.js'
|
|
||||||
+ if (uri.collection === app.bsky.graph.list.$type) {
|
|
||||||
```
|
|
||||||
|
|
||||||
For LXM (lexicon method) checks in auth:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- method === ids.AppBskyFeedGetFeedSkeleton
|
|
||||||
+ method === app.bsky.feed.getFeedSkeleton.$lxm
|
|
||||||
```
|
|
||||||
|
|
||||||
For simple comparisons in utility code, plain string literals are also acceptable:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- if (uri.collection === ids.AppBskyFeedPost) {
|
|
||||||
+ if (uri.collection === 'app.bsky.feed.post') {
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. XRPC Client Calls
|
|
||||||
|
|
||||||
### Using `Client.xrpc()`
|
|
||||||
|
|
||||||
Replace `AtpAgent` API calls with `Client.xrpc()`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- const res = await ctx.suggestionsAgent.api.app.bsky.unspecced.getSuggestionsSkeleton(
|
|
||||||
- { viewer: params.hydrateCtx.viewer, relativeToDid },
|
|
||||||
- { headers: params.headers },
|
|
||||||
- )
|
|
||||||
- return {
|
|
||||||
- suggestedDids: res.data.actors.map((a) => a.did),
|
|
||||||
- headers: res.headers,
|
|
||||||
- }
|
|
||||||
+ const res = await ctx.suggestionsClient.xrpc(
|
|
||||||
+ app.bsky.unspecced.getSuggestionsSkeleton,
|
|
||||||
+ {
|
|
||||||
+ params: { viewer: params.hydrateCtx.viewer, relativeToDid },
|
|
||||||
+ headers: params.headers,
|
|
||||||
+ },
|
|
||||||
+ )
|
|
||||||
+ return {
|
|
||||||
+ suggestedDids: res.body.actors.map((a) => a.did),
|
|
||||||
+ contentLanguage: res.headers.get('content-language') ?? undefined,
|
|
||||||
+ }
|
|
||||||
```
|
|
||||||
|
|
||||||
Key differences:
|
|
||||||
|
|
||||||
- Response data is on `.body` (not `.data`)
|
|
||||||
- Response headers use the standard `Headers` API (`.get()`)
|
|
||||||
- Parameters go in a `params` sub-object
|
|
||||||
- Procedure input goes in an `input` sub-object (not used in this example)
|
|
||||||
|
|
||||||
### Using `Client.call()`
|
|
||||||
|
|
||||||
Calls that only need the response body, and are happy letting any error propagate, can use the simpler `call()` method:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const body = await ctx.suggestionsClient.call(
|
|
||||||
app.bsky.unspecced.getSuggestionsSkeleton,
|
|
||||||
// "params" for Queries, "input" for Procedures:
|
|
||||||
{ viewer: params.hydrateCtx.viewer, relativeToDid },
|
|
||||||
// Optional additional options (see API reference):
|
|
||||||
{
|
|
||||||
headers,
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using `xrpcSafe()` for Error Handling
|
|
||||||
|
|
||||||
For calls where you want to handle errors without exceptions, use `xrpcSafe()`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { AtpAgent } from '@atproto/api'
|
|
||||||
- import { ResponseType, XRPCError } from '@atproto/xrpc'
|
|
||||||
+ import { xrpcSafe } from '@atproto/lex'
|
|
||||||
|
|
||||||
- const agent = new AtpAgent({ service: fgEndpoint })
|
|
||||||
- try {
|
|
||||||
- const result = await agent.api.app.bsky.feed.getFeedSkeleton(
|
|
||||||
- { feed, limit, cursor },
|
|
||||||
- { headers },
|
|
||||||
- )
|
|
||||||
- skeleton = result.data
|
|
||||||
- } catch (err) {
|
|
||||||
- if (err instanceof AppBskyFeedGetFeedSkeleton.UnknownFeedError) { ... }
|
|
||||||
- if (err instanceof XRPCError) {
|
|
||||||
- if (err.status === ResponseType.Unknown) { ... }
|
|
||||||
- if (err.status === ResponseType.InvalidResponse) { ... }
|
|
||||||
- }
|
|
||||||
- throw err
|
|
||||||
- }
|
|
||||||
+ const result = await xrpcSafe(fgEndpoint, app.bsky.feed.getFeedSkeleton, {
|
|
||||||
+ headers,
|
|
||||||
+ params: { feed, limit, cursor },
|
|
||||||
+ })
|
|
||||||
+ if (!result.success) {
|
|
||||||
+ if (result.matchesSchemaErrors()) {
|
|
||||||
+ throw new InvalidRequestError(result.message, result.error)
|
|
||||||
+ }
|
|
||||||
+ if (result.error === 'InternalServerError') { ... }
|
|
||||||
+ if (result.error === 'UpstreamFailure') { ... }
|
|
||||||
+ throw result.reason
|
|
||||||
+ }
|
|
||||||
+ // result.body is the typed response
|
|
||||||
```
|
|
||||||
|
|
||||||
### XrpcError
|
|
||||||
|
|
||||||
Replace `XRPCError` from `@atproto/xrpc` with `XrpcError` from `@atproto/lex`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { XRPCError } from '@atproto/xrpc'
|
|
||||||
+ import { XrpcError } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Type Strictness (Branded Types)
|
|
||||||
|
|
||||||
`@atproto/lex` exports branded string types that improve type safety. Apply these at type boundaries — function signatures, interface fields, database schema types — while keeping runtime code unchanged where possible.
|
|
||||||
|
|
||||||
### `DidString`
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- did: string
|
|
||||||
+ did: DidString
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- iss: string
|
|
||||||
+ iss: DidString | `${DidString}#${string}`
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the type guard instead of string prefix checks:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- if (typeof iss !== 'string' || !iss.startsWith('did:')) {
|
|
||||||
+ if (typeof iss !== 'string' || !isDidString(iss)) {
|
|
||||||
```
|
|
||||||
|
|
||||||
Import from `@atproto/lex`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { DidString, isDidString } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
### `HandleString`
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- handle: string
|
|
||||||
+ handle: HandleString
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { HandleString, isHandleString } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
### `AtIdentifierString`
|
|
||||||
|
|
||||||
For parameters that accept either a DID or a handle:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- handleOrDid: string
|
|
||||||
+ handleOrDid: AtIdentifierString
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the guard before passing to functions that require a specific type:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { AtIdentifierString, isAtIdentifierString } from '@atproto/lex'
|
|
||||||
|
|
||||||
if (!isAtIdentifierString(actor)) {
|
|
||||||
throw new InvalidRequestError('Invalid actor identifier')
|
|
||||||
}
|
|
||||||
const account = await getAccount(actor)
|
|
||||||
```
|
|
||||||
|
|
||||||
### `AtUriString`
|
|
||||||
|
|
||||||
Apply to URI fields coming from data plane responses:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- post: { uri: item.uri, cid: item.cid || undefined },
|
|
||||||
+ post: { uri: item.uri as AtUriString, cid: item.cid || undefined },
|
|
||||||
```
|
|
||||||
|
|
||||||
### `Cid`
|
|
||||||
|
|
||||||
Replace `CID` from `multiformats` with `Cid` from `@atproto/lex-data`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { CID } from 'multiformats/cid'
|
|
||||||
+ import { Cid, parseCid } from '@atproto/lex-data'
|
|
||||||
```
|
|
||||||
|
|
||||||
Or import from `@atproto/lex` directly:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Cid } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
### `DatetimeString`
|
|
||||||
|
|
||||||
For datetime fields in database schemas and interfaces:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- indexedAt: string
|
|
||||||
+ indexedAt: DatetimeString
|
|
||||||
```
|
|
||||||
|
|
||||||
### Type Narrowing at Data Boundaries
|
|
||||||
|
|
||||||
When data comes from external sources (protobuf, data plane, Kysely queries), cast to branded types at the boundary:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- suggestedDids: dids,
|
|
||||||
+ suggestedDids: dids as DidString[],
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- qb.where('actor.did', '=', filter.sub!)
|
|
||||||
+ qb.where('actor.did', '=', filter.sub! as DidString)
|
|
||||||
```
|
|
||||||
|
|
||||||
This pattern is common when Kysely query builders need branded types that the query parameter doesn't naturally have.
|
|
||||||
|
|
||||||
### `HeadersMap`
|
|
||||||
|
|
||||||
Replace `Record<string, string>` headers with proper `Headers` type:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { HeadersMap } from '@atproto/xrpc'
|
|
||||||
+ import { Headers as HeadersMap } from '@atproto/xrpc-server'
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> `Headers` from `@atproto/xrpc-server` conflicts with the standard `Headers` type, so we alias it as `HeadersMap` to avoid confusion.
|
|
||||||
|
|
||||||
Response headers from `xrpc()` calls use the standard `Headers` API:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- result.headers['content-language']
|
|
||||||
+ result.headers.get('content-language')
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. Data Utilities
|
|
||||||
|
|
||||||
### JSON/Lex Parsing
|
|
||||||
|
|
||||||
Replace `jsonStringToLex` from `@atproto/lexicon` with `lexParse` from `@atproto/lex`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { jsonStringToLex } from '@atproto/lexicon'
|
|
||||||
+ import { lexParse } from '@atproto/lex'
|
|
||||||
|
|
||||||
- const parsed = jsonStringToLex(
|
|
||||||
- Buffer.from(payload).toString('utf8'),
|
|
||||||
- ) as SubjectActivitySubscription
|
|
||||||
+ const parsed = lexParse<app.bsky.notification.defs.SubjectActivitySubscription>(
|
|
||||||
+ Buffer.from(payload).toString('utf8'),
|
|
||||||
+ )
|
|
||||||
```
|
|
||||||
|
|
||||||
`lexParse` accepts a type parameter, eliminating the need for `as` casts.
|
|
||||||
|
|
||||||
### Datetime Strings
|
|
||||||
|
|
||||||
Replace `new Date().toISOString()` with branded datetime utilities for AT Protocol datetime fields:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- createdAt: new Date().toISOString(),
|
|
||||||
+ createdAt: currentDatetimeString(),
|
|
||||||
```
|
|
||||||
|
|
||||||
For converting an existing `Date` object:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- indexedAt: someDate.toISOString(),
|
|
||||||
+ indexedAt: toDatetimeString(someDate),
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { toDatetimeString, currentDatetimeString } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
### BlobRef
|
|
||||||
|
|
||||||
The old `BlobRef` class from `@atproto/lexicon` is replaced by a simple interface from `@atproto/lex-data`. It is no longer a class, so `instanceof` checks are not possible anymore. Instead, use type guards to check if an object is a `BlobRef`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { BlobRef } from '@atproto/lexicon'
|
|
||||||
+ import { BlobRef } from '@atproto/lex-data'
|
|
||||||
|
|
||||||
- export const cidFromBlobJson = (json: BlobRef) => {
|
|
||||||
- if (json instanceof BlobRef) {
|
|
||||||
- return json.ref.toString()
|
|
||||||
- }
|
|
||||||
- if (json['$type'] === 'blob') {
|
|
||||||
- return (json['ref']?.['$link'] ?? '') as string
|
|
||||||
- }
|
|
||||||
- return (json['cid'] ?? '') as string
|
|
||||||
- }
|
|
||||||
+ export const cidFromBlobJson = (json: BlobRef): string => {
|
|
||||||
+ return json.ref.toString()
|
|
||||||
+ }
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- if (value instanceof BlobRef) { ... }
|
|
||||||
+ if (isBlobRef(value)) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Legacy BlobRefs
|
|
||||||
|
|
||||||
Legacy blob references (`{ cid: string, mimeType: string }`) are automatically handled based on the **strict mode** setting. When `strict: false`, both standard and legacy blob formats are accepted. When `strict: true` (the default), only standard `TypedBlobRef` format is accepted.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
TypedBlobRef,
|
|
||||||
LegacyBlobRef,
|
|
||||||
isTypedBlobRef,
|
|
||||||
isLegacyBlobRef,
|
|
||||||
} from '@atproto/lex-data'
|
|
||||||
|
|
||||||
// Check for standard BlobRef
|
|
||||||
if (isTypedBlobRef(value)) {
|
|
||||||
console.log(value.ref.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for legacy format
|
|
||||||
if (isLegacyBlobRef(value)) {
|
|
||||||
console.log(value.cid)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
New utility functions are available for working with both formats:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
BlobRef,
|
|
||||||
getBlobCid,
|
|
||||||
getBlobCidString,
|
|
||||||
getBlobMime,
|
|
||||||
getBlobSize,
|
|
||||||
} from '@atproto/lex-data'
|
|
||||||
|
|
||||||
declare const blobRef: BlobRef // TypedBlobRef | LegacyBlobRef
|
|
||||||
|
|
||||||
const cid = getBlobCid(blobRef) // Returns Cid object
|
|
||||||
const cidString = getBlobCidString(blobRef) // Returns string (optimized)
|
|
||||||
const mimeType = getBlobMime(blobRef)
|
|
||||||
const size = getBlobSize(blobRef) // Returns number | undefined (legacy refs don't have size)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Strict Mode in Validation
|
|
||||||
|
|
||||||
All schema validation methods (`$parse`, `$safeParse`, `$validate`, `$safeValidate`) accept an optional `{ strict }` option that controls validation behavior uniformly across both parse and validate modes:
|
|
||||||
|
|
||||||
**Strict mode (`strict: true`, the default):**
|
|
||||||
|
|
||||||
- Datetime strings must have proper timezone information
|
|
||||||
- Blob MIME types and size constraints are enforced
|
|
||||||
- Only raw CIDs are allowed in blob references
|
|
||||||
- Legacy blob references are rejected
|
|
||||||
|
|
||||||
**Non-strict mode (`strict: false`):**
|
|
||||||
|
|
||||||
- Datetime strings without timezones are accepted
|
|
||||||
- Blob MIME type and size constraints are not enforced
|
|
||||||
- Any valid CID is allowed in blob references
|
|
||||||
- Legacy blob references are accepted
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Default strict validation
|
|
||||||
const result1 = schema.$safeParse(data) // strict: true by default
|
|
||||||
|
|
||||||
// Explicit strict validation
|
|
||||||
const result2 = schema.$safeParse(data, { strict: true })
|
|
||||||
|
|
||||||
// Non-strict validation (lenient)
|
|
||||||
const result3 = schema.$safeParse(data, { strict: false })
|
|
||||||
|
|
||||||
// Applies to all validation methods
|
|
||||||
schema.$validate(data, { strict: false })
|
|
||||||
schema.$parse(data, { strict: false })
|
|
||||||
schema.$safeValidate(data, { strict: false })
|
|
||||||
```
|
|
||||||
|
|
||||||
The `Client` class has a `strictResponseProcessing` option that controls the default strict mode for all XRPC calls:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const client = new Client(session, {
|
|
||||||
strictResponseProcessing: false, // Use non-strict mode for all calls
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
When `strictResponseProcessing: false`, response validation will use `strict: false`, which means legacy blobs and other lenient data formats are automatically accepted. Individual calls can override this with per-call options.
|
|
||||||
|
|
||||||
### Lex Stringify
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { stringifyLex } from '@atproto/lexicon'
|
|
||||||
+ import { lexStringify } from '@atproto/lex'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 7. Schema Validation and Type Guards
|
|
||||||
|
|
||||||
### `$matches()` — Validates and Narrows Unknown Data
|
|
||||||
|
|
||||||
Use `$matches()` when the data has not been pre-validated (e.g., it comes from an external source, or you need full runtime validation):
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { isRepoRef } from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
||||||
- if (isRepoRef(subject)) { ... }
|
|
||||||
+ if (com.atproto.admin.defs.repoRef.$matches(subject)) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- repost: isSkeletonReasonRepost(item.reason) ? ... : undefined,
|
|
||||||
+ repost: app.bsky.feed.defs.skeletonReasonRepost.$matches(item.reason) ? ... : undefined,
|
|
||||||
```
|
|
||||||
|
|
||||||
### `$isTypeOf` — Discriminates Pre-Validated Data by `$type`
|
|
||||||
|
|
||||||
Use `$isTypeOf` when the data is already validated and you only need to discriminate based on the `$type` property. This is faster than `$matches()` because it skips validation. Common in `.find()` and `.filter()` callbacks on arrays of already-parsed preference objects or union members:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- const personalDetailsPref = prefs.find(
|
|
||||||
- (pref) => pref.$type === 'app.bsky.actor.defs#personalDetailsPref'
|
|
||||||
- )
|
|
||||||
+ const personalDetailsPref = prefs.find(
|
|
||||||
+ app.bsky.actor.defs.personalDetailsPref.$isTypeOf,
|
|
||||||
+ )
|
|
||||||
```
|
|
||||||
|
|
||||||
`$isTypeOf` is a type predicate function, so TypeScript narrows the type automatically when used in conditionals or `.find()`.
|
|
||||||
|
|
||||||
### `$build()` — Constructs Typed Objects
|
|
||||||
|
|
||||||
Use `$build()` instead of manually setting `$type`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- return {
|
|
||||||
- $type: 'app.bsky.graph.defs#relationship',
|
|
||||||
- did,
|
|
||||||
- following: subject.following,
|
|
||||||
- }
|
|
||||||
+ return app.bsky.graph.defs.relationship.$build({
|
|
||||||
+ did,
|
|
||||||
+ following: subject.following,
|
|
||||||
+ })
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- prefs.push({
|
|
||||||
- $type: 'app.bsky.actor.defs#declaredAgePref',
|
|
||||||
- isOverAge13: age >= 13,
|
|
||||||
- isOverAge16: age >= 16,
|
|
||||||
- isOverAge18: age >= 18,
|
|
||||||
- })
|
|
||||||
+ prefs.push(
|
|
||||||
+ app.bsky.actor.defs.declaredAgePref.$build({
|
|
||||||
+ isOverAge13: age >= 13,
|
|
||||||
+ isOverAge16: age >= 16,
|
|
||||||
+ isOverAge18: age >= 18,
|
|
||||||
+ }),
|
|
||||||
+ )
|
|
||||||
```
|
|
||||||
|
|
||||||
`$build()` automatically sets the `$type` field and returns a properly typed object.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Tests still rely exclusively on `@atproto/api`. When tests previously imported from the old `src/lexicon/` directory, redirect those imports to `@atproto/api`:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- import { ids } from '../../src/lexicon/lexicons'
|
|
||||||
- import { RepoRef, isRepoRef } from '../../src/lexicon/types/com/atproto/admin/defs'
|
|
||||||
- import { $Typed } from '../../src/lexicon/util'
|
|
||||||
+ import { $Typed, AtpAgent, ComAtprotoAdminDefs, ids } from '@atproto/api'
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not change how tests make XRPC calls — they continue to use `AtpAgent` from `@atproto/api`. This allows to ensure that the refactor does not break existing functionality at runtime. Tests will be migrated to `@atproto/lex` in a separate phase after all service code has been lexified.
|
|
||||||
|
|
||||||
## Common Pitfalls
|
|
||||||
|
|
||||||
1. **`$type` vs `$lxm`**: Use `$type` for record/object type strings (e.g., `app.bsky.feed.post.$type` = `'app.bsky.feed.post'`). Use `$lxm` for XRPC method identifiers used in auth checks and proxy routing. `$nsid` is also available for the raw NSID string if needed, this is especially useful for lexicon defs that don't have a `main` type but still need to reference their NSID.
|
|
||||||
|
|
||||||
2. **`$matches` vs `$isTypeOf`**: Use `$matches()` when data needs validation (unknown input). Use `$isTypeOf` when data is already validated and you just need to check the `$type` tag (e.g., union discrimination, filtering an array of pre-parsed objects).
|
|
||||||
|
|
||||||
3. **Branded type casts at boundaries**: Data from protobuf/data plane/Kysely returns plain strings. Cast to branded types (`as DidString`, `as AtUriString`) at the point where data enters the typed domain. Avoid `assert()` — use `as` casts at known-safe boundaries instead.
|
|
||||||
|
|
||||||
4. **`'application/json' as const`**: Handler return values need `as const` on the encoding string literal to satisfy the return type. Without it, TypeScript widens the type.
|
|
||||||
|
|
||||||
5. **Response header changes**: Old `AtpAgent` returned headers as a plain object with string indexing. New `Client`/`xrpc` returns standard `Headers` objects requiring `.get()`.
|
|
||||||
|
|
||||||
6. **`@atproto/lex-data` sub-export**: `Cid`, `parseCid`, and `BlobRef` are available from `@atproto/lex-data` for files that only need data types without the full `@atproto/lex` package. Both import paths work.
|
|
||||||
|
|
||||||
7. **Prefer `@atproto/lex` imports** over `@atproto/syntax` when both export the same symbol (e.g., `DidString`, `AtUriString`).
|
|
||||||
|
|
||||||
8. **Avoid `assert()` calls.** Use type guards (`isDidString()`, `isHandleString()`) with conditional logic rather than assertions.
|
|
||||||
|
|
||||||
## Import Source Changes Summary
|
|
||||||
|
|
||||||
| Before | After |
|
|
||||||
| ------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
||||||
| `@atproto/api` (`AtpAgent`) | `@atproto/lex` (`Client`) |
|
|
||||||
| `@atproto/lexicon` (`jsonStringToLex`, `BlobRef`) | `@atproto/lex` (`lexParse`, `BlobRef`) |
|
|
||||||
| `@atproto/lexicon` (`stringifyLex`) | `@atproto/lex` (`lexStringify`) |
|
|
||||||
| `@atproto/xrpc` (`HeadersMap`, `XRPCError`) | `@atproto/xrpc-server` (`Headers`), `@atproto/lex` (`XrpcError`, `xrpcSafe`) |
|
|
||||||
| `multiformats/cid` (`CID`) | `@atproto/lex` (`Cid`, `parseCid`) |
|
|
||||||
| `@atproto/syntax` (`DidString`, etc.) | `@atproto/lex` (`DidString`, `HandleString`, etc.) — prefer `@atproto/lex` |
|
|
||||||
| `../lexicon` (`Server`, `createServer`) | `@atproto/xrpc-server` (`Server`, `createServer`) |
|
|
||||||
| `../lexicon/lexicons` (`ids`) | `../lexicons/index.js` (`app`, `com`, `chat`) |
|
|
||||||
| `../lexicon/types/...` (types, guards) | `../lexicons/index.js` (namespace-qualified access) |
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
||||||
```
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
**/dist
|
**/dist
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.git
|
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
|||||||
+2
-22
@@ -1,24 +1,4 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# buf
|
|
||||||
packages/bsky/src/proto
|
|
||||||
packages/bsync/src/proto
|
|
||||||
|
|
||||||
# codegen
|
|
||||||
packages/api/src/client
|
packages/api/src/client
|
||||||
|
packages/bsky/src/lexicon
|
||||||
|
packages/pds/src/lexicon
|
||||||
packages/ozone/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-client-expo/android/build
|
|
||||||
|
|||||||
@@ -1,58 +1,27 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
|
// parse TypeScript files
|
||||||
|
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
// configure eslint using options described at
|
||||||
|
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/base",
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
"plugin:prettier/recommended",
|
"plugin:prettier/recommended",
|
||||||
"plugin:import/recommended",
|
"prettier"
|
||||||
"plugin:import/typescript"
|
|
||||||
],
|
],
|
||||||
"plugins": ["n"],
|
|
||||||
"ignorePatterns": ["dist", "node_modules"],
|
"ignorePatterns": ["dist", "node_modules"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-var": "error",
|
"no-var": "error",
|
||||||
"prefer-const": "warn",
|
"prefer-const": "warn",
|
||||||
"no-misleading-character-class": "warn",
|
"no-misleading-character-class": "warn",
|
||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
"n/global-require": "error",
|
|
||||||
"n/no-extraneous-import": "error",
|
|
||||||
"n/prefer-node-protocol": "error",
|
|
||||||
"import/extensions": ["off", "ignorePackages"],
|
|
||||||
"import/export": "off",
|
|
||||||
"import/namespace": "off",
|
|
||||||
"import/no-deprecated": "off",
|
|
||||||
"import/no-absolute-path": "error",
|
|
||||||
"import/no-dynamic-require": "error",
|
|
||||||
"import/no-self-import": "error",
|
|
||||||
"import/order": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"named": true,
|
|
||||||
"distinctGroup": true,
|
|
||||||
"alphabetize": { "order": "asc" },
|
|
||||||
"newlines-between": "never",
|
|
||||||
"pathGroups": [
|
|
||||||
{ "pattern": "#/**", "group": "parent", "position": "before" }
|
|
||||||
],
|
|
||||||
"groups": [
|
|
||||||
"builtin",
|
|
||||||
"external",
|
|
||||||
"internal",
|
|
||||||
"parent",
|
|
||||||
["index", "sibling"],
|
|
||||||
"object"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
"@typescript-eslint/no-unused-vars": [
|
||||||
"error",
|
"warn",
|
||||||
{
|
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"ignoreRestSiblings": true
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/no-empty-interface": "off",
|
"@typescript-eslint/no-empty-interface": "off",
|
||||||
@@ -65,48 +34,15 @@
|
|||||||
"files": ["jest.config.js"],
|
"files": ["jest.config.js"],
|
||||||
"env": { "commonjs": true }
|
"env": { "commonjs": true }
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"files": ["vite.config.js", "vite.config.cjs", "vite.config.mjs"],
|
|
||||||
"env": { "node": true }
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files": ["jest.setup.js"],
|
"files": ["jest.setup.js"],
|
||||||
"env": { "jest": true }
|
"env": { "jest": true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.js", "*.cjs"],
|
"files": "*.js",
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-var-requires": "off"
|
"@typescript-eslint/no-var-requires": "off"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"files": ["**/*.test.ts", "**/tests/**/*.ts"],
|
|
||||||
"rules": {
|
|
||||||
"n/no-extraneous-import": [
|
|
||||||
"error",
|
|
||||||
{ "allowModules": ["@atproto/dev-env"] }
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"node": { "version": ">=18.7.0" },
|
|
||||||
"import/internal-regex": "^@atproto(?:-labs)?/",
|
|
||||||
"import/parsers": { "@typescript-eslint/parser": [".ts", ".tsx"] },
|
|
||||||
"import/resolver": {
|
|
||||||
"typescript": {
|
|
||||||
"project": [
|
|
||||||
"tsconfig.json",
|
|
||||||
"packages/lex/*/tsconfig.json",
|
|
||||||
"packages/oauth/*/tsconfig.json",
|
|
||||||
"packages/oauth/*/tsconfig.src.json",
|
|
||||||
"packages/internal/*/tsconfig.json",
|
|
||||||
"packages/*/tsconfig.json"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node": {
|
|
||||||
"extensions": [".js", ".jsx", ".json"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# buf
|
|
||||||
packages/bsky/src/proto/** linguist-generated=true
|
|
||||||
packages/bsync/src/proto/** linguist-generated=true
|
|
||||||
|
|
||||||
# codegen
|
|
||||||
packages/api/src/client/** 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
|
|
||||||
@@ -3,8 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- msi/pds-lexification
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
||||||
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
||||||
@@ -14,7 +12,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
bsky-container-aws:
|
bsky-container-aws:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- msi/pds-lexification
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
USERNAME: ${{ github.actor }}
|
USERNAME: ${{ github.actor }}
|
||||||
@@ -16,7 +14,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
bsky-container-ghcr:
|
bsky-container-ghcr:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
||||||
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
||||||
@@ -13,7 +12,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
bsync-container-aws:
|
bsync-container-aws:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
USERNAME: ${{ github.actor }}
|
USERNAME: ${{ github.actor }}
|
||||||
@@ -15,7 +14,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
bsync-container-ghcr:
|
bsync-container-ghcr:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- divy/ozone-passthru
|
||||||
env:
|
env:
|
||||||
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
||||||
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
||||||
@@ -13,7 +13,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
ozone-container-aws:
|
ozone-container-aws:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
USERNAME: ${{ github.actor }}
|
USERNAME: ${{ github.actor }}
|
||||||
@@ -15,7 +14,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
ozone-container-ghcr:
|
ozone-container-ghcr:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- msi/pds-lexification
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
REGISTRY: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_REGISTRY }}
|
||||||
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
USERNAME: ${{ secrets.AWS_ECR_REGISTRY_USEAST2_PACKAGES_USERNAME }}
|
||||||
@@ -14,7 +12,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
pds-container-aws:
|
pds-container-aws:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- msi/pds-lexification
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
USERNAME: ${{ github.actor }}
|
USERNAME: ${{ github.actor }}
|
||||||
@@ -16,7 +14,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
pds-container-ghcr:
|
pds-container-ghcr:
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
name: Claude Code
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
pull_request_review_comment:
|
|
||||||
types: [created]
|
|
||||||
issues:
|
|
||||||
types: [opened, assigned]
|
|
||||||
pull_request_review:
|
|
||||||
types: [submitted]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
claude:
|
|
||||||
if: |
|
|
||||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
||||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
||||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
||||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: read
|
|
||||||
issues: read
|
|
||||||
id-token: write
|
|
||||||
actions: read # Required for Claude to read CI results on PRs
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Run Claude Code
|
|
||||||
id: claude
|
|
||||||
uses: anthropics/claude-code-action@v1
|
|
||||||
with:
|
|
||||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
||||||
|
|
||||||
# This is an optional setting that allows Claude to read CI results on PRs
|
|
||||||
additional_permissions: |
|
|
||||||
actions: read
|
|
||||||
|
|
||||||
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
||||||
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
||||||
|
|
||||||
# Optional: Add claude_args to customize behavior and configuration
|
|
||||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
||||||
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
||||||
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
|
||||||
|
|
||||||
# NOTE(sfn): we can add a custom system prompt here
|
|
||||||
|
|
||||||
claude_args: |
|
|
||||||
--model claude-opus-4-5-20251101
|
|
||||||
@@ -5,40 +5,33 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Publish
|
name: Build & Publish
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
if: github.repository == 'bluesky-social/atproto'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: pnpm/action-setup@v4
|
- run: corepack enable && corepack prepare --activate
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: pnpm
|
node-version-file: package.json
|
||||||
node-version-file: '.nvmrc'
|
cache: 'pnpm'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
- run: pnpm i --frozen-lockfile
|
||||||
# Temporary workaround for an issue with Node.js v22
|
- run: pnpm build
|
||||||
# https://github.com/npm/cli/issues/9151
|
- run: pnpm verify
|
||||||
# https://github.com/npm/cli/pull/9152
|
|
||||||
- run: npm install --global npm@11.11.1
|
|
||||||
- run: npm install --global npm@latest
|
|
||||||
- run: pnpm install --frozen-lockfile
|
|
||||||
env:
|
|
||||||
PUPPETEER_SKIP_DOWNLOAD: true
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: changesets/action@v1
|
|
||||||
id: changesets
|
id: changesets
|
||||||
|
uses: changesets/action@v1
|
||||||
with:
|
with:
|
||||||
publish: pnpm release
|
publish: pnpm release
|
||||||
version: pnpm run version-packages
|
version: pnpm version-packages
|
||||||
commit: 'Version packages'
|
commit: 'Version packages'
|
||||||
title: 'Version packages'
|
title: 'Version packages'
|
||||||
|
|||||||
+36
-66
@@ -1,4 +1,4 @@
|
|||||||
name: Repository CI
|
name: Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -15,103 +15,73 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- run: corepack enable && corepack prepare --activate
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: pnpm
|
node-version-file: package.json
|
||||||
node-version-file: '.nvmrc'
|
cache: 'pnpm'
|
||||||
- run: pnpm install --frozen-lockfile
|
- name: Get current month
|
||||||
env:
|
run: echo "CURRENT_MONTH=$(date +'%Y-%m')" >> $GITHUB_ENV
|
||||||
PUPPETEER_SKIP_DOWNLOAD: true
|
- uses: actions/cache@v4
|
||||||
|
name: Cache Puppeteer browser binaries
|
||||||
|
with:
|
||||||
|
path: ~/.cache
|
||||||
|
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}
|
||||||
|
- run: pnpm i --frozen-lockfile
|
||||||
- run: pnpm build
|
- run: pnpm build
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
retention-days: 2
|
|
||||||
path: |
|
path: |
|
||||||
packages/*/dist
|
packages/*/dist
|
||||||
packages/*/*/dist
|
packages/*/*/dist
|
||||||
packages/lex/*/src/lexicons
|
retention-days: 1
|
||||||
packages/lex/*/tests/lexicons
|
|
||||||
packages/oauth/oauth-client-browser-example/src/lexicons
|
|
||||||
packages/oauth/*/src/locales/*/messages.ts
|
|
||||||
packages/api/src/moderation/const/labels.ts
|
|
||||||
packages/bsky/src/lexicons
|
|
||||||
packages/pds/src/lexicons
|
|
||||||
packages/sync/src/lexicons
|
|
||||||
|
|
||||||
changeset:
|
|
||||||
name: Changeset
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # needed for git diff against base branch
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
- uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
cache: pnpm
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
- run: pnpm install --frozen-lockfile
|
|
||||||
env:
|
|
||||||
PUPPETEER_SKIP_DOWNLOAD: true
|
|
||||||
- run: pnpm changeset status --since=origin/${{ github.base_ref }}
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
# Puppeteer does not work in recent Ubuntu versions without a workaround due
|
|
||||||
# to sandboxing issues. Using "ubuntu-latest" results in the following
|
|
||||||
# error:
|
|
||||||
#
|
|
||||||
# No usable sandbox! If you are running on Ubuntu 23.10+ or another Linux
|
|
||||||
# distro that has disabled unprivileged user namespaces with AppArmor, see
|
|
||||||
# https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md.
|
|
||||||
# Otherwise see
|
|
||||||
# https://chromium.googlesource.com/chromium/src/+/main/docs/linux/suid_sandbox_development.md
|
|
||||||
# for more information on developing with the (older) SUID sandbox. If you
|
|
||||||
# want to live dangerously and need an immediate workaround, you can try
|
|
||||||
# using --no-sandbox.
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
shard: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- run: corepack enable && corepack prepare --activate
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: pnpm
|
node-version-file: package.json
|
||||||
node-version-file: '.nvmrc'
|
cache: 'pnpm'
|
||||||
- run: echo "CURRENT_MONTH=$(date +'%Y-%m')" >> $GITHUB_ENV
|
- name: Get current month
|
||||||
|
run: echo "CURRENT_MONTH=$(date +'%Y-%m')" >> $GITHUB_ENV
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
name: Cache Puppeteer browser binaries
|
name: Cache Puppeteer browser binaries
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/puppeteer
|
path: ~/.cache
|
||||||
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}-${{ runner.arch }}
|
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm i --frozen-lockfile
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
- run: pnpm test:withFlags --maxWorkers=1 --shard=${{ matrix.shard }} --passWithNoTests
|
- run: pnpm test:withFlags --maxWorkers=1 --shard=${{ matrix.shard }} --passWithNoTests
|
||||||
|
|
||||||
verify:
|
verify:
|
||||||
name: Verify
|
name: Verify
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- run: corepack enable && corepack prepare --activate
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
cache: pnpm
|
node-version-file: package.json
|
||||||
node-version-file: '.nvmrc'
|
cache: 'pnpm'
|
||||||
- run: pnpm install --frozen-lockfile
|
- name: Get current month
|
||||||
env:
|
run: echo "CURRENT_MONTH=$(date +'%Y-%m')" >> $GITHUB_ENV
|
||||||
PUPPETEER_SKIP_DOWNLOAD: true
|
- uses: actions/cache@v4
|
||||||
|
name: Cache Puppeteer browser binaries
|
||||||
|
with:
|
||||||
|
path: ~/.cache
|
||||||
|
key: ${{ env.CURRENT_MONTH }}-${{ runner.os }}
|
||||||
|
- run: pnpm i --frozen-lockfile
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
name: Sync to internal repo
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
if: github.repository == 'bluesky-social/atproto'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout public repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Generate GitHub App Token
|
|
||||||
id: app-token
|
|
||||||
uses: actions/create-github-app-token@v1
|
|
||||||
with:
|
|
||||||
app-id: ${{ vars.SYNC_INTERNAL_APP_ID }}
|
|
||||||
private-key: ${{ secrets.SYNC_INTERNAL_PK }}
|
|
||||||
repositories: atproto-internal
|
|
||||||
- name: Push to internal repo
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ steps.app-token.outputs.token }}
|
|
||||||
run: |
|
|
||||||
git config user.name "github-actions"
|
|
||||||
git config user.email "test@users.noreply.github.com"
|
|
||||||
git config --unset-all http.https://github.com/.extraheader
|
|
||||||
git remote add internal https://x-access-token:${TOKEN}@github.com/bluesky-social/atproto-internal.git
|
|
||||||
git push internal main --force
|
|
||||||
+1
-3
@@ -5,15 +5,13 @@ yarn-error.log
|
|||||||
packages/**/dist
|
packages/**/dist
|
||||||
.idea
|
.idea
|
||||||
packages/*/coverage
|
packages/*/coverage
|
||||||
|
.vscode/
|
||||||
test.sqlite
|
test.sqlite
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.log
|
*.log
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.*.env
|
.*.env
|
||||||
.env.*
|
|
||||||
.env
|
.env
|
||||||
\#*\#
|
\#*\#
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
.claude/
|
|
||||||
coverage
|
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
enable-pre-post-scripts = true
|
enable-pre-post-scripts = true
|
||||||
include-workspace-root = true
|
|
||||||
|
|||||||
+5
-23
@@ -1,30 +1,12 @@
|
|||||||
node_modules
|
node_modules
|
||||||
interop-test-files
|
interop-test-files
|
||||||
__snapshots__
|
|
||||||
dist
|
dist
|
||||||
|
build
|
||||||
|
.nyc_output
|
||||||
|
coverage
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
.pnpm*
|
.pnpm*
|
||||||
.changeset
|
.changeset
|
||||||
|
*.d.ts
|
||||||
|
packages/bsky/src/data-plane/gen
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
|
||||||
# buf
|
|
||||||
packages/bsky/src/proto
|
|
||||||
packages/bsync/src/proto
|
|
||||||
|
|
||||||
# codegen
|
|
||||||
packages/api/src/client
|
|
||||||
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-client-expo/android/build
|
|
||||||
|
|||||||
@@ -3,20 +3,12 @@
|
|||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"plugins": ["prettier-plugin-tailwindcss"],
|
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": "*.hbs",
|
"files": "*.hbs",
|
||||||
"options": {
|
"options": {
|
||||||
"singleQuote": false
|
"singleQuote": false
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": [".eslintrc"],
|
|
||||||
"options": {
|
|
||||||
"parser": "json",
|
|
||||||
"trailingComma": "none"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-9
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"wengerk.highlight-bad-chars",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"streetsidesoftware.code-spell-checker",
|
|
||||||
"vitest.explorer"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Vendored
-53
@@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"cSpell.language": "en,en-US",
|
|
||||||
"cSpell.words": [
|
|
||||||
"algs",
|
|
||||||
"appview",
|
|
||||||
"atproto",
|
|
||||||
"blockstore",
|
|
||||||
"bluesky",
|
|
||||||
"bsky",
|
|
||||||
"bsync",
|
|
||||||
"cbor",
|
|
||||||
"clsx",
|
|
||||||
"consolas",
|
|
||||||
"dpop",
|
|
||||||
"googleusercontent",
|
|
||||||
"hcaptcha",
|
|
||||||
"hexeditor",
|
|
||||||
"ingester",
|
|
||||||
"insertable",
|
|
||||||
"ipld",
|
|
||||||
"jwks",
|
|
||||||
"keypair",
|
|
||||||
"kysely",
|
|
||||||
"merkle",
|
|
||||||
"msid",
|
|
||||||
"multibase",
|
|
||||||
"multiformats",
|
|
||||||
"nameserver",
|
|
||||||
"oidc",
|
|
||||||
"pkce",
|
|
||||||
"ponyfill",
|
|
||||||
"proxied",
|
|
||||||
"ssrf",
|
|
||||||
"undici",
|
|
||||||
"webcrypto",
|
|
||||||
"whatwg",
|
|
||||||
"xrpc"
|
|
||||||
],
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": "explicit",
|
|
||||||
"source.sortImports": "never"
|
|
||||||
},
|
|
||||||
"files.associations": {
|
|
||||||
"**/tsconfig/*.json": "jsonc"
|
|
||||||
},
|
|
||||||
"files.defaultLanguage": "ts",
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
"files.trimTrailingWhitespace": true,
|
|
||||||
"prettier.semi": false,
|
|
||||||
"prettier.singleQuote": true,
|
|
||||||
"prettier.trailingComma": "es5",
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
|
||||||
}
|
|
||||||
@@ -19,9 +19,3 @@ ATProto receives so many contributions that we could never list everyone who des
|
|||||||
#### [goeo\_](https://bsky.app/profile/did:web:genco.me), Security disclosure, May 2024
|
#### [goeo\_](https://bsky.app/profile/did:web:genco.me), Security disclosure, May 2024
|
||||||
|
|
||||||
#### [DavidBuchanan314](https://github.com/DavidBuchanan314), Security disclosure, November 2024
|
#### [DavidBuchanan314](https://github.com/DavidBuchanan314), Security disclosure, November 2024
|
||||||
|
|
||||||
#### [daniel](https://hackerone.com/daniel), Security disclosure, November 2024
|
|
||||||
|
|
||||||
#### [imax](https://github.com/imax9000), Security disclosure, January 2025
|
|
||||||
|
|
||||||
#### [avivkeller](https://github.com/avivkeller), Security disclosure, December 2025
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
Dual MIT/Apache-2.0 License
|
Dual MIT/Apache-2.0 License
|
||||||
|
|
||||||
Copyright (c) 2022-2026 Bluesky Social PBC, and Contributors
|
Copyright (c) 2022-2024 Bluesky PBC, and Contributors
|
||||||
|
|
||||||
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ help: ## Print info about all commands
|
|||||||
@echo "NOTE: dependencies between commands are not automatic. Eg, you must run 'deps' and 'build' first, and after any changes"
|
@echo "NOTE: dependencies between commands are not automatic. Eg, you must run 'deps' and 'build' first, and after any changes"
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: codegen ## Compile all modules
|
build: ## Compile all modules
|
||||||
pnpm build
|
pnpm build
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
@@ -28,7 +28,12 @@ run-dev-env-logged: ## Run a "development environment" shell (with logging)
|
|||||||
|
|
||||||
.PHONY: codegen
|
.PHONY: codegen
|
||||||
codegen: ## Re-generate packages from lexicon/ files
|
codegen: ## Re-generate packages from lexicon/ files
|
||||||
pnpm codegen
|
cd packages/api; pnpm run codegen
|
||||||
|
cd packages/pds; pnpm run codegen
|
||||||
|
cd packages/bsky; pnpm run codegen
|
||||||
|
cd packages/ozone; pnpm run codegen
|
||||||
|
# clean up codegen output
|
||||||
|
pnpm format
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: ## Run style checks and verify syntax
|
lint: ## Run style checks and verify syntax
|
||||||
@@ -38,18 +43,10 @@ lint: ## Run style checks and verify syntax
|
|||||||
fmt: ## Run syntax re-formatting
|
fmt: ## Run syntax re-formatting
|
||||||
pnpm format
|
pnpm format
|
||||||
|
|
||||||
.PHONY: fmt-lexicons
|
|
||||||
fmt-lexicons: ## Run syntax re-formatting, just on .json files
|
|
||||||
pnpm exec eslint ./lexicons/ --ext .json --fix
|
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: deps
|
||||||
deps: ## Installs dependent libs using 'pnpm install'
|
deps: ## Installs dependent libs using 'pnpm install'
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean: ## Deletes all 'dist' and 'node_package' directories (including nested)
|
|
||||||
rm -rf **/dist **/node_packages
|
|
||||||
|
|
||||||
.PHONY: nvm-setup
|
.PHONY: nvm-setup
|
||||||
nvm-setup: ## Use NVM to install and activate node+pnpm
|
nvm-setup: ## Use NVM to install and activate node+pnpm
|
||||||
nvm install 18
|
nvm install 18
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ make help
|
|||||||
|
|
||||||
## About AT Protocol
|
## About AT Protocol
|
||||||
|
|
||||||
The Authenticated Transfer Protocol ("ATP" or "atproto") is a decentralized social media protocol, developed by [Bluesky Social PBC](https://bsky.social). Learn more at:
|
The Authenticated Transfer Protocol ("ATP" or "atproto") is a decentralized social media protocol, developed by [Bluesky PBC](https://bsky.social). Learn more at:
|
||||||
|
|
||||||
- [Overview and Guides](https://atproto.com/guides/overview) 👈 Best starting point
|
- [Overview and Guides](https://atproto.com/guides/overview) 👈 Best starting point
|
||||||
- [Github Discussions](https://github.com/bluesky-social/atproto/discussions) 👈 Great place to ask questions
|
- [Github Discussions](https://github.com/bluesky-social/atproto/discussions) 👈 Great place to ask questions
|
||||||
@@ -113,5 +113,3 @@ This project is dual-licensed under MIT and Apache 2.0 terms:
|
|||||||
- Apache License, Version 2.0, ([LICENSE-APACHE.txt](https://github.com/bluesky-social/atproto/blob/main/LICENSE-APACHE.txt) or http://www.apache.org/licenses/LICENSE-2.0)
|
- Apache License, Version 2.0, ([LICENSE-APACHE.txt](https://github.com/bluesky-social/atproto/blob/main/LICENSE-APACHE.txt) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
|
Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
|
||||||
|
|
||||||
Bluesky Social PBC has committed to a software patent non-aggression pledge. For details see [the original announcement](https://bsky.social/about/blog/10-01-2025-patent-pledge).
|
|
||||||
|
|||||||
@@ -32,4 +32,3 @@ at://did:abc:123/io.nsid.someFunc/-
|
|||||||
at://did:abc:123/io.nsid.someFunc/_
|
at://did:abc:123/io.nsid.someFunc/_
|
||||||
at://did:abc:123/io.nsid.someFunc/~
|
at://did:abc:123/io.nsid.someFunc/~
|
||||||
at://did:abc:123/io.nsid.someFunc/...
|
at://did:abc:123/io.nsid.someFunc/...
|
||||||
at://did:plc:asdf123/com.atproto.feed.postV2
|
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ com.oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.foo
|
|||||||
com.example.oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
com.example.oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
||||||
com.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.foo
|
com.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.foo
|
||||||
|
|
||||||
# invalid examples
|
# invliad examples
|
||||||
com.example.foo.*
|
com.example.foo.*
|
||||||
com.example.foo.blah*
|
com.example.foo.blah*
|
||||||
com.example.foo.*blah
|
com.example.foo.*blah
|
||||||
|
com.example.f00
|
||||||
com.exa💩ple.thing
|
com.exa💩ple.thing
|
||||||
a-0.b-1.c-3
|
a-0.b-1.c-3
|
||||||
a-0.b-1.c-o
|
a-0.b-1.c-o
|
||||||
|
a0.b1.c3
|
||||||
1.0.0.127.record
|
1.0.0.127.record
|
||||||
0two.example.foo
|
0two.example.foo
|
||||||
example.com
|
example.com
|
||||||
@@ -27,4 +29,4 @@ com.atproto.feed.p*st
|
|||||||
com.atproto.feed.po#t
|
com.atproto.feed.po#t
|
||||||
com.atproto.feed.p!ot
|
com.atproto.feed.p!ot
|
||||||
com.example-.foo
|
com.example-.foo
|
||||||
com.example.fooBar.2
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ com.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle.middle
|
|||||||
|
|
||||||
# valid examples
|
# valid examples
|
||||||
com.example.fooBar
|
com.example.fooBar
|
||||||
com.example.fooBarV2
|
|
||||||
net.users.bob.ping
|
net.users.bob.ping
|
||||||
a.b.c
|
a.b.c
|
||||||
m.xn--masekowski-d0b.pl
|
m.xn--masekowski-d0b.pl
|
||||||
@@ -19,8 +18,6 @@ test.12345.record
|
|||||||
a01.thing.record
|
a01.thing.record
|
||||||
a.0.c
|
a.0.c
|
||||||
xn--fiqs8s.xn--fiqa61au8b7zsevnm8ak20mc4a87e.record.two
|
xn--fiqs8s.xn--fiqa61au8b7zsevnm8ak20mc4a87e.record.two
|
||||||
a0.b1.c3
|
|
||||||
com.example.f00
|
|
||||||
|
|
||||||
# allows onion (Tor) NSIDs
|
# allows onion (Tor) NSIDs
|
||||||
onion.expyuzz4wqqyqhjn.spec.getThing
|
onion.expyuzz4wqqyqhjn.spec.getThing
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"maxGraphemes": 64,
|
"maxGraphemes": 64,
|
||||||
"maxLength": 640
|
"maxLength": 640
|
||||||
},
|
},
|
||||||
"pronouns": { "type": "string" },
|
|
||||||
"avatar": { "type": "string", "format": "uri" },
|
"avatar": { "type": "string", "format": "uri" },
|
||||||
"associated": {
|
"associated": {
|
||||||
"type": "ref",
|
"type": "ref",
|
||||||
@@ -24,19 +23,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
||||||
},
|
},
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
"verification": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#verificationState"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#statusView"
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"type": "unknown",
|
|
||||||
"description": "Debug information for internal development"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profileView": {
|
"profileView": {
|
||||||
@@ -50,7 +37,6 @@
|
|||||||
"maxGraphemes": 64,
|
"maxGraphemes": 64,
|
||||||
"maxLength": 640
|
"maxLength": 640
|
||||||
},
|
},
|
||||||
"pronouns": { "type": "string" },
|
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"maxGraphemes": 256,
|
"maxGraphemes": 256,
|
||||||
@@ -67,18 +53,6 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
||||||
},
|
|
||||||
"verification": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#verificationState"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#statusView"
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"type": "unknown",
|
|
||||||
"description": "Debug information for internal development"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -98,8 +72,6 @@
|
|||||||
"maxGraphemes": 256,
|
"maxGraphemes": 256,
|
||||||
"maxLength": 2560
|
"maxLength": 2560
|
||||||
},
|
},
|
||||||
"pronouns": { "type": "string" },
|
|
||||||
"website": { "type": "string", "format": "uri" },
|
|
||||||
"avatar": { "type": "string", "format": "uri" },
|
"avatar": { "type": "string", "format": "uri" },
|
||||||
"banner": { "type": "string", "format": "uri" },
|
"banner": { "type": "string", "format": "uri" },
|
||||||
"followersCount": { "type": "integer" },
|
"followersCount": { "type": "integer" },
|
||||||
@@ -123,18 +95,6 @@
|
|||||||
"pinnedPost": {
|
"pinnedPost": {
|
||||||
"type": "ref",
|
"type": "ref",
|
||||||
"ref": "com.atproto.repo.strongRef"
|
"ref": "com.atproto.repo.strongRef"
|
||||||
},
|
|
||||||
"verification": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#verificationState"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#statusView"
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"type": "unknown",
|
|
||||||
"description": "Debug information for internal development"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -145,12 +105,7 @@
|
|||||||
"feedgens": { "type": "integer" },
|
"feedgens": { "type": "integer" },
|
||||||
"starterPacks": { "type": "integer" },
|
"starterPacks": { "type": "integer" },
|
||||||
"labeler": { "type": "boolean" },
|
"labeler": { "type": "boolean" },
|
||||||
"chat": { "type": "ref", "ref": "#profileAssociatedChat" },
|
"chat": { "type": "ref", "ref": "#profileAssociatedChat" }
|
||||||
"activitySubscription": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#profileAssociatedActivitySubscription"
|
|
||||||
},
|
|
||||||
"germ": { "type": "ref", "ref": "#profileAssociatedGerm" }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profileAssociatedChat": {
|
"profileAssociatedChat": {
|
||||||
@@ -160,34 +115,6 @@
|
|||||||
"allowIncoming": {
|
"allowIncoming": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"knownValues": ["all", "none", "following"]
|
"knownValues": ["all", "none", "following"]
|
||||||
},
|
|
||||||
"allowGroupInvites": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["all", "none", "following"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profileAssociatedGerm": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["showButtonTo", "messageMeUrl"],
|
|
||||||
"properties": {
|
|
||||||
"messageMeUrl": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
"showButtonTo": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["usersIFollow", "everyone"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profileAssociatedActivitySubscription": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["allowSubscriptions"],
|
|
||||||
"properties": {
|
|
||||||
"allowSubscriptions": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["followers", "mutuals", "none"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -209,14 +136,8 @@
|
|||||||
"following": { "type": "string", "format": "at-uri" },
|
"following": { "type": "string", "format": "at-uri" },
|
||||||
"followedBy": { "type": "string", "format": "at-uri" },
|
"followedBy": { "type": "string", "format": "at-uri" },
|
||||||
"knownFollowers": {
|
"knownFollowers": {
|
||||||
"description": "This property is present only in selected cases, as an optimization.",
|
|
||||||
"type": "ref",
|
"type": "ref",
|
||||||
"ref": "#knownFollowers"
|
"ref": "#knownFollowers"
|
||||||
},
|
|
||||||
"activitySubscription": {
|
|
||||||
"description": "This property is present only in selected cases, as an optimization.",
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#activitySubscription"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -237,54 +158,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"verificationState": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Represents the verification information about the user this object is attached to.",
|
|
||||||
"required": ["verifications", "verifiedStatus", "trustedVerifierStatus"],
|
|
||||||
"properties": {
|
|
||||||
"verifications": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included.",
|
|
||||||
"items": { "type": "ref", "ref": "#verificationView" }
|
|
||||||
},
|
|
||||||
"verifiedStatus": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user's status as a verified account.",
|
|
||||||
"knownValues": ["valid", "invalid", "none"]
|
|
||||||
},
|
|
||||||
"trustedVerifierStatus": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user's status as a trusted verifier.",
|
|
||||||
"knownValues": ["valid", "invalid", "none"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"verificationView": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "An individual verification for an associated subject.",
|
|
||||||
"required": ["issuer", "uri", "isValid", "createdAt"],
|
|
||||||
"properties": {
|
|
||||||
"issuer": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user who issued this verification.",
|
|
||||||
"format": "did"
|
|
||||||
},
|
|
||||||
"uri": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The AT-URI of the verification record.",
|
|
||||||
"format": "at-uri"
|
|
||||||
},
|
|
||||||
"isValid": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "True if the verification passes validation, otherwise false."
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Timestamp when the verification was created.",
|
|
||||||
"format": "datetime"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -295,17 +168,13 @@
|
|||||||
"#savedFeedsPref",
|
"#savedFeedsPref",
|
||||||
"#savedFeedsPrefV2",
|
"#savedFeedsPrefV2",
|
||||||
"#personalDetailsPref",
|
"#personalDetailsPref",
|
||||||
"#declaredAgePref",
|
|
||||||
"#feedViewPref",
|
"#feedViewPref",
|
||||||
"#threadViewPref",
|
"#threadViewPref",
|
||||||
"#interestsPref",
|
"#interestsPref",
|
||||||
"#mutedWordsPref",
|
"#mutedWordsPref",
|
||||||
"#hiddenPostsPref",
|
"#hiddenPostsPref",
|
||||||
"#bskyAppStatePref",
|
"#bskyAppStatePref",
|
||||||
"#labelersPref",
|
"#labelersPref"
|
||||||
"#postInteractionSettingsPref",
|
|
||||||
"#verificationPrefs",
|
|
||||||
"#liveEventPreferences"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -397,24 +266,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"declaredAgePref": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Read-only preference containing value(s) inferred from the user's declared birthdate. Absence of this preference object in the response indicates that the user has not made a declaration.",
|
|
||||||
"properties": {
|
|
||||||
"isOverAge13": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates if the user has declared that they are over 13 years of age."
|
|
||||||
},
|
|
||||||
"isOverAge16": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates if the user has declared that they are over 16 years of age."
|
|
||||||
},
|
|
||||||
"isOverAge18": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates if the user has declared that they are over 18 years of age."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"feedViewPref": {
|
"feedViewPref": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["feed"],
|
"required": ["feed"],
|
||||||
@@ -453,6 +304,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Sorting mode for threads.",
|
"description": "Sorting mode for threads.",
|
||||||
"knownValues": ["oldest", "newest", "most-likes", "random", "hotness"]
|
"knownValues": ["oldest", "newest", "most-likes", "random", "hotness"]
|
||||||
|
},
|
||||||
|
"prioritizeFollowedUsers": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Show followed users at the top of all replies."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -613,100 +468,6 @@
|
|||||||
"description": "The date and time at which the NUX will expire and should be considered completed."
|
"description": "The date and time at which the NUX will expire and should be considered completed."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"verificationPrefs": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Preferences for how verified accounts appear in the app.",
|
|
||||||
"required": [],
|
|
||||||
"properties": {
|
|
||||||
"hideBadges": {
|
|
||||||
"description": "Hide the blue check badges for verified accounts and trusted verifiers.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"liveEventPreferences": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Preferences for live events.",
|
|
||||||
"properties": {
|
|
||||||
"hiddenFeedIds": {
|
|
||||||
"description": "A list of feed IDs that the user has hidden from live events.",
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "string" }
|
|
||||||
},
|
|
||||||
"hideAllFeeds": {
|
|
||||||
"description": "Whether to hide all feeds from live events.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"postInteractionSettingsPref": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Default post interaction settings for the account. These values should be applied as default values when creating new posts. These refs should mirror the threadgate and postgate records exactly.",
|
|
||||||
"required": [],
|
|
||||||
"properties": {
|
|
||||||
"threadgateAllowRules": {
|
|
||||||
"description": "Matches threadgate record. List of rules defining who can reply to this users posts. If value is an empty array, no one can reply. If value is undefined, anyone can reply.",
|
|
||||||
"type": "array",
|
|
||||||
"maxLength": 5,
|
|
||||||
"items": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": [
|
|
||||||
"app.bsky.feed.threadgate#mentionRule",
|
|
||||||
"app.bsky.feed.threadgate#followerRule",
|
|
||||||
"app.bsky.feed.threadgate#followingRule",
|
|
||||||
"app.bsky.feed.threadgate#listRule"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"postgateEmbeddingRules": {
|
|
||||||
"description": "Matches postgate record. List of rules defining who can embed this users posts. If value is an empty array or is undefined, no particular rules apply and anyone can embed.",
|
|
||||||
"type": "array",
|
|
||||||
"maxLength": 5,
|
|
||||||
"items": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": ["app.bsky.feed.postgate#disableRule"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"statusView": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["status", "record"],
|
|
||||||
"properties": {
|
|
||||||
"uri": { "type": "string", "format": "at-uri" },
|
|
||||||
"cid": { "type": "string", "format": "cid" },
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status for the account.",
|
|
||||||
"knownValues": ["app.bsky.actor.status#live"]
|
|
||||||
},
|
|
||||||
"record": { "type": "unknown" },
|
|
||||||
"embed": {
|
|
||||||
"type": "union",
|
|
||||||
"description": "An optional embed associated with the status.",
|
|
||||||
"refs": ["app.bsky.embed.external#view"]
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
|
||||||
},
|
|
||||||
"expiresAt": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The date when this status will expire. The application might choose to no longer return the status after expiration.",
|
|
||||||
"format": "datetime"
|
|
||||||
},
|
|
||||||
"isActive": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "True if the status is not expired, false if it is expired. Only present if expiration was set."
|
|
||||||
},
|
|
||||||
"isDisabled": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "True if the user's go-live access has been disabled by a moderator, false otherwise."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,6 @@
|
|||||||
},
|
},
|
||||||
"recId": {
|
"recId": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "DEPRECATED: use recIdStr instead."
|
|
||||||
},
|
|
||||||
"recIdStr": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Snowflake for this recommendation, use when submitting recommendation events."
|
"description": "Snowflake for this recommendation, use when submitting recommendation events."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,6 @@
|
|||||||
"maxGraphemes": 256,
|
"maxGraphemes": 256,
|
||||||
"maxLength": 2560
|
"maxLength": 2560
|
||||||
},
|
},
|
||||||
"pronouns": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Free-form pronouns text.",
|
|
||||||
"maxGraphemes": 20,
|
|
||||||
"maxLength": 200
|
|
||||||
},
|
|
||||||
"website": { "type": "string", "format": "uri" },
|
|
||||||
"avatar": {
|
"avatar": {
|
||||||
"type": "blob",
|
"type": "blob",
|
||||||
"description": "Small image to be displayed next to posts from account. AKA, 'profile picture'",
|
"description": "Small image to be displayed next to posts from account. AKA, 'profile picture'",
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.actor.status",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "record",
|
|
||||||
"description": "A declaration of a Bluesky account status.",
|
|
||||||
"key": "literal:self",
|
|
||||||
"record": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["status", "createdAt"],
|
|
||||||
"properties": {
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status for the account.",
|
|
||||||
"knownValues": ["app.bsky.actor.status#live"]
|
|
||||||
},
|
|
||||||
"embed": {
|
|
||||||
"type": "union",
|
|
||||||
"description": "An optional embed associated with the status.",
|
|
||||||
"refs": ["app.bsky.embed.external"]
|
|
||||||
},
|
|
||||||
"durationMinutes": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The duration of the status in minutes. Applications can choose to impose minimum and maximum limits.",
|
|
||||||
"minimum": 1
|
|
||||||
},
|
|
||||||
"createdAt": { "type": "string", "format": "datetime" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"live": {
|
|
||||||
"type": "token",
|
|
||||||
"description": "Advertises an account as currently offering live content."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.ageassurance.begin",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Initiate Age Assurance for an account.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["email", "language", "countryCode"],
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user's email address to receive Age Assurance instructions."
|
|
||||||
},
|
|
||||||
"language": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user's preferred language for communication during the Age Assurance process."
|
|
||||||
},
|
|
||||||
"countryCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "An ISO 3166-1 alpha-2 code of the user's location."
|
|
||||||
},
|
|
||||||
"regionCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "An optional ISO 3166-2 code of the user's region or state within the country."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#state"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{ "name": "InvalidEmail" },
|
|
||||||
{ "name": "DidTooLong" },
|
|
||||||
{ "name": "InvalidInitiation" },
|
|
||||||
{ "name": "RegionNotSupported" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.ageassurance.defs",
|
|
||||||
"defs": {
|
|
||||||
"access": {
|
|
||||||
"description": "The access level granted based on Age Assurance data we've processed.",
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["unknown", "none", "safe", "full"]
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status of the Age Assurance process.",
|
|
||||||
"knownValues": ["unknown", "pending", "assured", "blocked"]
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The user's computed Age Assurance state.",
|
|
||||||
"required": ["status", "access"],
|
|
||||||
"properties": {
|
|
||||||
"lastInitiatedAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The timestamp when this state was last updated."
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#status"
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stateMetadata": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Additional metadata needed to compute Age Assurance state client-side.",
|
|
||||||
"required": [],
|
|
||||||
"properties": {
|
|
||||||
"accountCreatedAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The account creation timestamp."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "",
|
|
||||||
"required": ["regions"],
|
|
||||||
"properties": {
|
|
||||||
"regions": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "The per-region Age Assurance configuration.",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#configRegion"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegion": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The Age Assurance configuration for a specific region.",
|
|
||||||
"required": ["countryCode", "minAccessAge", "rules"],
|
|
||||||
"properties": {
|
|
||||||
"countryCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ISO 3166-1 alpha-2 country code this configuration applies to."
|
|
||||||
},
|
|
||||||
"regionCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ISO 3166-2 region code this configuration applies to. If omitted, the configuration applies to the entire country."
|
|
||||||
},
|
|
||||||
"minAccessAge": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The minimum age (as a whole integer) required to use Bluesky in this region."
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "The ordered list of Age Assurance rules that apply to this region. Rules should be applied in order, and the first matching rule determines the access level granted. The rules array should always include a default rule as the last item.",
|
|
||||||
"items": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": [
|
|
||||||
"#configRegionRuleDefault",
|
|
||||||
"#configRegionRuleIfDeclaredOverAge",
|
|
||||||
"#configRegionRuleIfDeclaredUnderAge",
|
|
||||||
"#configRegionRuleIfAssuredOverAge",
|
|
||||||
"#configRegionRuleIfAssuredUnderAge",
|
|
||||||
"#configRegionRuleIfAccountNewerThan",
|
|
||||||
"#configRegionRuleIfAccountOlderThan"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleDefault": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies by default.",
|
|
||||||
"required": ["access"],
|
|
||||||
"properties": {
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfDeclaredOverAge": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the user has declared themselves equal-to or over a certain age.",
|
|
||||||
"required": ["age", "access"],
|
|
||||||
"properties": {
|
|
||||||
"age": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The age threshold as a whole integer."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfDeclaredUnderAge": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the user has declared themselves under a certain age.",
|
|
||||||
"required": ["age", "access"],
|
|
||||||
"properties": {
|
|
||||||
"age": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The age threshold as a whole integer."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfAssuredOverAge": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the user has been assured to be equal-to or over a certain age.",
|
|
||||||
"required": ["age", "access"],
|
|
||||||
"properties": {
|
|
||||||
"age": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The age threshold as a whole integer."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfAssuredUnderAge": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the user has been assured to be under a certain age.",
|
|
||||||
"required": ["age", "access"],
|
|
||||||
"properties": {
|
|
||||||
"age": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The age threshold as a whole integer."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfAccountNewerThan": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the account is equal-to or newer than a certain date.",
|
|
||||||
"required": ["date", "access"],
|
|
||||||
"properties": {
|
|
||||||
"date": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The date threshold as a datetime string."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"configRegionRuleIfAccountOlderThan": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Age Assurance rule that applies if the account is older than a certain date.",
|
|
||||||
"required": ["date", "access"],
|
|
||||||
"properties": {
|
|
||||||
"date": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The date threshold as a datetime string."
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#access"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"event": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Object used to store Age Assurance data in stash.",
|
|
||||||
"required": ["createdAt", "status", "access", "attemptId", "countryCode"],
|
|
||||||
"properties": {
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The date and time of this write operation."
|
|
||||||
},
|
|
||||||
"attemptId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The unique identifier for this instance of the Age Assurance flow, in UUID format."
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status of the Age Assurance process.",
|
|
||||||
"knownValues": ["unknown", "pending", "assured", "blocked"]
|
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"description": "The access level granted based on Age Assurance data we've processed.",
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["unknown", "none", "safe", "full"]
|
|
||||||
},
|
|
||||||
"countryCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ISO 3166-1 alpha-2 country code provided when beginning the Age Assurance flow."
|
|
||||||
},
|
|
||||||
"regionCode": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ISO 3166-2 region code provided when beginning the Age Assurance flow."
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The email used for Age Assurance."
|
|
||||||
},
|
|
||||||
"initIp": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The IP address used when initiating the Age Assurance flow."
|
|
||||||
},
|
|
||||||
"initUa": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user agent used when initiating the Age Assurance flow."
|
|
||||||
},
|
|
||||||
"completeIp": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The IP address used when completing the Age Assurance flow."
|
|
||||||
},
|
|
||||||
"completeUa": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user agent used when completing the Age Assurance flow."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.ageassurance.getConfig",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Returns Age Assurance configuration for use on the client.",
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#config"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.ageassurance.getState",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Returns server-computed Age Assurance state, if available, and any additional metadata needed to compute Age Assurance state client-side.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"required": ["countryCode"],
|
|
||||||
"properties": {
|
|
||||||
"countryCode": { "type": "string" },
|
|
||||||
"regionCode": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["state", "metadata"],
|
|
||||||
"properties": {
|
|
||||||
"state": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#state"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.ageassurance.defs#stateMetadata"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authCreatePosts",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Create Bluesky Posts",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Can not update or delete posts.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "rpc",
|
|
||||||
"inheritAud": true,
|
|
||||||
"lxm": [
|
|
||||||
"app.bsky.video.uploadVideo",
|
|
||||||
"app.bsky.video.getJobStatus",
|
|
||||||
"app.bsky.video.getUploadLimits"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create"],
|
|
||||||
"collection": [
|
|
||||||
"app.bsky.feed.post",
|
|
||||||
"app.bsky.feed.postgate",
|
|
||||||
"app.bsky.feed.threadgate"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authDeleteContent",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Delete Bluesky Content",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Clean up public account history: posts, reposts, and likes.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["delete"],
|
|
||||||
"collection": [
|
|
||||||
"app.bsky.feed.like",
|
|
||||||
"app.bsky.feed.post",
|
|
||||||
"app.bsky.feed.postgate",
|
|
||||||
"app.bsky.feed.repost",
|
|
||||||
"app.bsky.feed.threadgate"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authFullApp",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Full Bluesky Social App Permissions",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Manage all public content and interactions, private preferences and subscriptions, and other Bluesky-specific app features and data.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "rpc",
|
|
||||||
"inheritAud": true,
|
|
||||||
"lxm": [
|
|
||||||
"app.bsky.actor.getPreferences",
|
|
||||||
"app.bsky.actor.getProfile",
|
|
||||||
"app.bsky.actor.getProfiles",
|
|
||||||
"app.bsky.actor.getSuggestions",
|
|
||||||
"app.bsky.actor.putPreferences",
|
|
||||||
"app.bsky.actor.searchActors",
|
|
||||||
"app.bsky.actor.searchActorsTypeahead",
|
|
||||||
"app.bsky.bookmark.createBookmark",
|
|
||||||
"app.bsky.bookmark.deleteBookmark",
|
|
||||||
"app.bsky.bookmark.getBookmarks",
|
|
||||||
"app.bsky.contact.dismissMatch",
|
|
||||||
"app.bsky.contact.getMatches",
|
|
||||||
"app.bsky.contact.getSyncStatus",
|
|
||||||
"app.bsky.contact.importContacts",
|
|
||||||
"app.bsky.contact.removeData",
|
|
||||||
"app.bsky.contact.startPhoneVerification",
|
|
||||||
"app.bsky.contact.verifyPhone",
|
|
||||||
"app.bsky.feed.describeFeedGenerator",
|
|
||||||
"app.bsky.feed.getActorFeeds",
|
|
||||||
"app.bsky.feed.getActorLikes",
|
|
||||||
"app.bsky.feed.getAuthorFeed",
|
|
||||||
"app.bsky.feed.getFeed",
|
|
||||||
"app.bsky.feed.getFeedGenerator",
|
|
||||||
"app.bsky.feed.getFeedGenerators",
|
|
||||||
"app.bsky.feed.getFeedSkeleton",
|
|
||||||
"app.bsky.feed.getLikes",
|
|
||||||
"app.bsky.feed.getListFeed",
|
|
||||||
"app.bsky.feed.getPostThread",
|
|
||||||
"app.bsky.feed.getPosts",
|
|
||||||
"app.bsky.feed.getQuotes",
|
|
||||||
"app.bsky.feed.getRepostedBy",
|
|
||||||
"app.bsky.feed.getSuggestedFeeds",
|
|
||||||
"app.bsky.feed.getTimeline",
|
|
||||||
"app.bsky.feed.searchPosts",
|
|
||||||
"app.bsky.feed.sendInteractions",
|
|
||||||
"app.bsky.graph.getActorStarterPacks",
|
|
||||||
"app.bsky.graph.getBlocks",
|
|
||||||
"app.bsky.graph.getFollowers",
|
|
||||||
"app.bsky.graph.getFollows",
|
|
||||||
"app.bsky.graph.getKnownFollowers",
|
|
||||||
"app.bsky.graph.getList",
|
|
||||||
"app.bsky.graph.getListBlocks",
|
|
||||||
"app.bsky.graph.getListMutes",
|
|
||||||
"app.bsky.graph.getLists",
|
|
||||||
"app.bsky.graph.getListsWithMembership",
|
|
||||||
"app.bsky.graph.getMutes",
|
|
||||||
"app.bsky.graph.getRelationships",
|
|
||||||
"app.bsky.graph.getStarterPack",
|
|
||||||
"app.bsky.graph.getStarterPacks",
|
|
||||||
"app.bsky.graph.getStarterPacksWithMembership",
|
|
||||||
"app.bsky.graph.getSuggestedFollowsByActor",
|
|
||||||
"app.bsky.graph.muteActor",
|
|
||||||
"app.bsky.graph.muteActorList",
|
|
||||||
"app.bsky.graph.muteThread",
|
|
||||||
"app.bsky.graph.searchStarterPacks",
|
|
||||||
"app.bsky.graph.unmuteActor",
|
|
||||||
"app.bsky.graph.unmuteActorList",
|
|
||||||
"app.bsky.graph.unmuteThread",
|
|
||||||
"app.bsky.labeler.getServices",
|
|
||||||
"app.bsky.notification.getPreferences",
|
|
||||||
"app.bsky.notification.getUnreadCount",
|
|
||||||
"app.bsky.notification.listActivitySubscriptions",
|
|
||||||
"app.bsky.notification.listNotifications",
|
|
||||||
"app.bsky.notification.putActivitySubscription",
|
|
||||||
"app.bsky.notification.putPreferences",
|
|
||||||
"app.bsky.notification.putPreferencesV2",
|
|
||||||
"app.bsky.notification.registerPush",
|
|
||||||
"app.bsky.notification.unregisterPush",
|
|
||||||
"app.bsky.notification.updateSeen",
|
|
||||||
"app.bsky.unspecced.getAgeAssuranceState",
|
|
||||||
"app.bsky.unspecced.getConfig",
|
|
||||||
"app.bsky.unspecced.getOnboardingSuggestedStarterPacks",
|
|
||||||
"app.bsky.unspecced.getPopularFeedGenerators",
|
|
||||||
"app.bsky.unspecced.getPostThreadOtherV2",
|
|
||||||
"app.bsky.unspecced.getPostThreadV2",
|
|
||||||
"app.bsky.unspecced.getSuggestedFeeds",
|
|
||||||
"app.bsky.unspecced.getSuggestedFeedsSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestedStarterPacks",
|
|
||||||
"app.bsky.unspecced.getSuggestedStarterPacksSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestedUsers",
|
|
||||||
"app.bsky.unspecced.getSuggestedUsersSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestionsSkeleton",
|
|
||||||
"app.bsky.unspecced.getTaggedSuggestions",
|
|
||||||
"app.bsky.unspecced.getTrendingTopics",
|
|
||||||
"app.bsky.unspecced.getTrends",
|
|
||||||
"app.bsky.unspecced.getTrendsSkeleton",
|
|
||||||
"app.bsky.unspecced.initAgeAssurance",
|
|
||||||
"app.bsky.unspecced.searchActorsSkeleton",
|
|
||||||
"app.bsky.unspecced.searchPostsSkeleton",
|
|
||||||
"app.bsky.unspecced.searchStarterPacksSkeleton",
|
|
||||||
"app.bsky.video.getJobStatus",
|
|
||||||
"app.bsky.video.getUploadLimits",
|
|
||||||
"app.bsky.video.uploadVideo"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create", "update", "delete"],
|
|
||||||
"collection": [
|
|
||||||
"app.bsky.actor.profile",
|
|
||||||
"app.bsky.actor.status",
|
|
||||||
"app.bsky.feed.like",
|
|
||||||
"app.bsky.feed.post",
|
|
||||||
"app.bsky.feed.postgate",
|
|
||||||
"app.bsky.feed.repost",
|
|
||||||
"app.bsky.feed.threadgate",
|
|
||||||
"app.bsky.graph.block",
|
|
||||||
"app.bsky.graph.follow",
|
|
||||||
"app.bsky.graph.list",
|
|
||||||
"app.bsky.graph.listblock",
|
|
||||||
"app.bsky.graph.listitem",
|
|
||||||
"app.bsky.graph.starterpack",
|
|
||||||
"app.bsky.notification.declaration"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authManageFeedDeclarations",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Manage Hosted Feeds",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Configure feed generator declaration records.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create", "update", "delete"],
|
|
||||||
"collection": ["app.bsky.feed.generator"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authManageLabelerService",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Manage Hosted Labeling Service",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Configure labeler declaration records.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create", "update", "delete"],
|
|
||||||
"collection": ["app.bsky.labeler.service"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authManageModeration",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Manage Personal Moderation",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Control over blocks, mutes, mod lists, mod services, and preferences.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "rpc",
|
|
||||||
"inheritAud": true,
|
|
||||||
"lxm": [
|
|
||||||
"app.bsky.actor.getPreferences",
|
|
||||||
"app.bsky.actor.putPreferences",
|
|
||||||
"app.bsky.graph.muteActor",
|
|
||||||
"app.bsky.graph.muteActorList",
|
|
||||||
"app.bsky.graph.muteThread",
|
|
||||||
"app.bsky.graph.unmuteActor",
|
|
||||||
"app.bsky.graph.unmuteActorList",
|
|
||||||
"app.bsky.graph.unmuteThread"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create", "update", "delete"],
|
|
||||||
"collection": ["app.bsky.graph.block", "app.bsky.graph.listblock"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authManageNotifications",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Manage Bluesky Notifications",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "View and configure notifications for the Bluesky app.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "rpc",
|
|
||||||
"inheritAud": true,
|
|
||||||
"lxm": [
|
|
||||||
"app.bsky.notification.getPreferences",
|
|
||||||
"app.bsky.notification.getUnreadCount",
|
|
||||||
"app.bsky.notification.listActivitySubscriptions",
|
|
||||||
"app.bsky.notification.listNotifications",
|
|
||||||
"app.bsky.notification.putActivitySubscription",
|
|
||||||
"app.bsky.notification.putPreferences",
|
|
||||||
"app.bsky.notification.putPreferencesV2",
|
|
||||||
"app.bsky.notification.registerPush",
|
|
||||||
"app.bsky.notification.unregisterPush",
|
|
||||||
"app.bsky.notification.updateSeen"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authManageProfile",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Manage Bluesky Profile",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "Update profile data, as well as status and public chat visibility.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "repo",
|
|
||||||
"action": ["create", "update", "delete"],
|
|
||||||
"collection": [
|
|
||||||
"app.bsky.actor.profile",
|
|
||||||
"app.bsky.actor.status",
|
|
||||||
"app.bsky.notification.declaration"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.authViewAll",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "permission-set",
|
|
||||||
"title": "Read-only access to all content",
|
|
||||||
"title:lang": {},
|
|
||||||
"detail": "View Bluesky network content from account perspective, and read all notifications and preferences.",
|
|
||||||
"detail:lang": {},
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"type": "permission",
|
|
||||||
"resource": "rpc",
|
|
||||||
"inheritAud": true,
|
|
||||||
"lxm": [
|
|
||||||
"app.bsky.actor.getPreferences",
|
|
||||||
"app.bsky.actor.getProfile",
|
|
||||||
"app.bsky.actor.getProfiles",
|
|
||||||
"app.bsky.actor.getSuggestions",
|
|
||||||
"app.bsky.actor.searchActors",
|
|
||||||
"app.bsky.actor.searchActorsTypeahead",
|
|
||||||
"app.bsky.bookmark.getBookmarks",
|
|
||||||
"app.bsky.feed.describeFeedGenerator",
|
|
||||||
"app.bsky.feed.getActorFeeds",
|
|
||||||
"app.bsky.feed.getActorLikes",
|
|
||||||
"app.bsky.feed.getAuthorFeed",
|
|
||||||
"app.bsky.feed.getFeed",
|
|
||||||
"app.bsky.feed.getFeedGenerator",
|
|
||||||
"app.bsky.feed.getFeedGenerators",
|
|
||||||
"app.bsky.feed.getFeedSkeleton",
|
|
||||||
"app.bsky.feed.getLikes",
|
|
||||||
"app.bsky.feed.getListFeed",
|
|
||||||
"app.bsky.feed.getPostThread",
|
|
||||||
"app.bsky.feed.getPosts",
|
|
||||||
"app.bsky.feed.getQuotes",
|
|
||||||
"app.bsky.feed.getRepostedBy",
|
|
||||||
"app.bsky.feed.getSuggestedFeeds",
|
|
||||||
"app.bsky.feed.getTimeline",
|
|
||||||
"app.bsky.feed.searchPosts",
|
|
||||||
"app.bsky.graph.getActorStarterPacks",
|
|
||||||
"app.bsky.graph.getBlocks",
|
|
||||||
"app.bsky.graph.getFollowers",
|
|
||||||
"app.bsky.graph.getFollows",
|
|
||||||
"app.bsky.graph.getKnownFollowers",
|
|
||||||
"app.bsky.graph.getListBlocks",
|
|
||||||
"app.bsky.graph.getListMutes",
|
|
||||||
"app.bsky.graph.getLists",
|
|
||||||
"app.bsky.graph.getListsWithMembership",
|
|
||||||
"app.bsky.graph.getMutes",
|
|
||||||
"app.bsky.graph.getRelationships",
|
|
||||||
"app.bsky.graph.getStarterPack",
|
|
||||||
"app.bsky.graph.getStarterPacks",
|
|
||||||
"app.bsky.graph.getStarterPacksWithMembership",
|
|
||||||
"app.bsky.graph.getSuggestedFollowsByActor",
|
|
||||||
"app.bsky.graph.searchStarterPacks",
|
|
||||||
"app.bsky.labeler.getServices",
|
|
||||||
"app.bsky.notification.getPreferences",
|
|
||||||
"app.bsky.notification.getUnreadCount",
|
|
||||||
"app.bsky.notification.listActivitySubscriptions",
|
|
||||||
"app.bsky.notification.listNotifications",
|
|
||||||
"app.bsky.notification.updateSeen",
|
|
||||||
"app.bsky.unspecced.getAgeAssuranceState",
|
|
||||||
"app.bsky.unspecced.getConfig",
|
|
||||||
"app.bsky.unspecced.getOnboardingSuggestedStarterPacks",
|
|
||||||
"app.bsky.unspecced.getPopularFeedGenerators",
|
|
||||||
"app.bsky.unspecced.getPostThreadOtherV2",
|
|
||||||
"app.bsky.unspecced.getPostThreadV2",
|
|
||||||
"app.bsky.unspecced.getSuggestedFeeds",
|
|
||||||
"app.bsky.unspecced.getSuggestedFeedsSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestedStarterPacks",
|
|
||||||
"app.bsky.unspecced.getSuggestedStarterPacksSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestedUsers",
|
|
||||||
"app.bsky.unspecced.getSuggestedUsersSkeleton",
|
|
||||||
"app.bsky.unspecced.getSuggestionsSkeleton",
|
|
||||||
"app.bsky.unspecced.getTaggedSuggestions",
|
|
||||||
"app.bsky.unspecced.getTrendingTopics",
|
|
||||||
"app.bsky.unspecced.getTrends",
|
|
||||||
"app.bsky.unspecced.getTrendsSkeleton",
|
|
||||||
"app.bsky.unspecced.searchActorsSkeleton",
|
|
||||||
"app.bsky.unspecced.searchPostsSkeleton",
|
|
||||||
"app.bsky.unspecced.searchStarterPacksSkeleton",
|
|
||||||
"app.bsky.video.getUploadLimits"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.bookmark.createBookmark",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Creates a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["uri", "cid"],
|
|
||||||
"properties": {
|
|
||||||
"uri": { "type": "string", "format": "at-uri" },
|
|
||||||
"cid": { "type": "string", "format": "cid" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "UnsupportedCollection",
|
|
||||||
"description": "The URI to be bookmarked is for an unsupported collection."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.bookmark.defs",
|
|
||||||
"defs": {
|
|
||||||
"bookmark": {
|
|
||||||
"description": "Object used to store bookmark data in stash.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject"],
|
|
||||||
"properties": {
|
|
||||||
"subject": {
|
|
||||||
"description": "A strong ref to the record to be bookmarked. Currently, only `app.bsky.feed.post` records are supported.",
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.repo.strongRef"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bookmarkView": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject", "item"],
|
|
||||||
"properties": {
|
|
||||||
"subject": {
|
|
||||||
"description": "A strong ref to the bookmarked record.",
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.repo.strongRef"
|
|
||||||
},
|
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
|
||||||
"item": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": [
|
|
||||||
"app.bsky.feed.defs#blockedPost",
|
|
||||||
"app.bsky.feed.defs#notFoundPost",
|
|
||||||
"app.bsky.feed.defs#postView"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.bookmark.deleteBookmark",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Deletes a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["uri"],
|
|
||||||
"properties": {
|
|
||||||
"uri": { "type": "string", "format": "at-uri" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "UnsupportedCollection",
|
|
||||||
"description": "The URI to be bookmarked is for an unsupported collection."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.bookmark.getBookmarks",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Gets views of records bookmarked by the authenticated user. Requires authentication.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["bookmarks"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"bookmarks": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.bookmark.defs#bookmarkView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.defs",
|
|
||||||
"defs": {
|
|
||||||
"matchAndContactIndex": {
|
|
||||||
"description": "Associates a profile with the positional index of the contact import input in the call to `app.bsky.contact.importContacts`, so clients can know which phone caused a particular match.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["match", "contactIndex"],
|
|
||||||
"properties": {
|
|
||||||
"match": {
|
|
||||||
"description": "Profile of the matched user.",
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.actor.defs#profileView"
|
|
||||||
},
|
|
||||||
"contactIndex": {
|
|
||||||
"description": "The index of this match in the import contact input.",
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"syncStatus": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["syncedAt", "matchesCount"],
|
|
||||||
"properties": {
|
|
||||||
"syncedAt": {
|
|
||||||
"description": "Last date when contacts where imported.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
},
|
|
||||||
"matchesCount": {
|
|
||||||
"description": "Number of existing contact matches resulting of the user imports and of their imported contacts having imported the user. Matches stop being counted when the user either follows the matched contact or dismisses the match.",
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification": {
|
|
||||||
"description": "A stash object to be sent via bsync representing a notification to be created.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["from", "to"],
|
|
||||||
"properties": {
|
|
||||||
"from": {
|
|
||||||
"description": "The DID of who this notification comes from.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"description": "The DID of who this notification should go to.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.dismissMatch",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Removes a match that was found via contact import. It shouldn't appear again if the same contact is re-imported. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject"],
|
|
||||||
"properties": {
|
|
||||||
"subject": {
|
|
||||||
"description": "The subject's DID to dismiss the match with.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.getMatches",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Returns the matched contacts (contacts that were mutually imported). Excludes dismissed matches. Requires authentication.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["matches"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"matches": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.actor.defs#profileView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidLimit"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidCursor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.getSyncStatus",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Gets the user's current contact import status. Requires authentication.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"syncStatus": {
|
|
||||||
"description": "If present, indicates the user has imported their contacts. If not present, indicates the user never used the feature or called `app.bsky.contact.removeData` and didn't import again since.",
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.contact.defs#syncStatus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.importContacts",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Import contacts for securely matching with other users. This follows the protocol explained in https://docs.bsky.app/blog/contact-import-rfc. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["token", "contacts"],
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"description": "JWT to authenticate the call. Use the JWT received as a response to the call to `app.bsky.contact.verifyPhone`.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"contacts": {
|
|
||||||
"description": "List of phone numbers in global E.164 format (e.g., '+12125550123'). Phone numbers that cannot be normalized into a valid phone number will be discarded. Should not repeat the 'phone' input used in `app.bsky.contact.verifyPhone`.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"minLength": 1,
|
|
||||||
"maxLength": 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["matchesAndContactIndexes"],
|
|
||||||
"properties": {
|
|
||||||
"matchesAndContactIndexes": {
|
|
||||||
"description": "The users that matched during import and their indexes on the input contacts, so the client can correlate with its local list.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.contact.defs#matchAndContactIndex"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidContacts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TooManyContacts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidToken"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.removeData",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Removes all stored hashes used for contact matching, existing matches, and sync status. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.sendNotification",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "System endpoint to send notifications related to contact imports. Requires role authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["from", "to"],
|
|
||||||
"properties": {
|
|
||||||
"from": {
|
|
||||||
"description": "The DID of who this notification comes from.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"description": "The DID of who this notification should go to.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.startPhoneVerification",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Starts a phone verification flow. The phone passed will receive a code via SMS that should be passed to `app.bsky.contact.verifyPhone`. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["phone"],
|
|
||||||
"properties": {
|
|
||||||
"phone": {
|
|
||||||
"description": "The phone number to receive the code via SMS.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "RateLimitExceeded"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidPhone"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.contact.verifyPhone",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Verifies control over a phone number with a code received via SMS and starts a contact import session. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["phone", "code"],
|
|
||||||
"properties": {
|
|
||||||
"phone": {
|
|
||||||
"description": "The phone number to verify. Should be the same as the one passed to `app.bsky.contact.startPhoneVerification`.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"code": {
|
|
||||||
"description": "The code received via SMS as a result of the call to `app.bsky.contact.startPhoneVerification`.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["token"],
|
|
||||||
"properties": {
|
|
||||||
"token": {
|
|
||||||
"description": "JWT to be used in a call to `app.bsky.contact.importContacts`. It is only valid for a single call.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "RateLimitExceeded"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidDid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidPhone"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InvalidCode"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "InternalError"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.draft.createDraft",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Inserts a draft using private storage (stash). An upper limit of drafts might be enforced. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["draft"],
|
|
||||||
"properties": {
|
|
||||||
"draft": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.draft.defs#draft"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["id"],
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ID of the created draft."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"name": "DraftLimitReached",
|
|
||||||
"description": "Trying to insert a new draft when the limit was already reached."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.draft.defs",
|
|
||||||
"defs": {
|
|
||||||
"draftWithId": {
|
|
||||||
"description": "A draft with an identifier, used to store drafts in private storage (stash).",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["id", "draft"],
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"description": "A TID to be used as a draft identifier.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "tid"
|
|
||||||
},
|
|
||||||
"draft": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draft"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draft": {
|
|
||||||
"description": "A draft containing an array of draft posts.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["posts"],
|
|
||||||
"properties": {
|
|
||||||
"deviceId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "UUIDv4 identifier of the device that created this draft.",
|
|
||||||
"maxLength": 100
|
|
||||||
},
|
|
||||||
"deviceName": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The device and/or platform on which the draft was created.",
|
|
||||||
"maxLength": 100
|
|
||||||
},
|
|
||||||
"posts": {
|
|
||||||
"description": "Array of draft posts that compose this draft.",
|
|
||||||
"type": "array",
|
|
||||||
"minLength": 1,
|
|
||||||
"maxLength": 100,
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draftPost"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"langs": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Indicates human language of posts primary text content.",
|
|
||||||
"maxLength": 3,
|
|
||||||
"items": { "type": "string", "format": "language" }
|
|
||||||
},
|
|
||||||
"postgateEmbeddingRules": {
|
|
||||||
"description": "Embedding rules for the postgates to be created when this draft is published.",
|
|
||||||
"type": "array",
|
|
||||||
"maxLength": 5,
|
|
||||||
"items": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": ["app.bsky.feed.postgate#disableRule"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"threadgateAllow": {
|
|
||||||
"description": "Allow-rules for the threadgate to be created when this draft is published.",
|
|
||||||
"type": "array",
|
|
||||||
"maxLength": 5,
|
|
||||||
"items": {
|
|
||||||
"type": "union",
|
|
||||||
"refs": [
|
|
||||||
"app.bsky.feed.threadgate#mentionRule",
|
|
||||||
"app.bsky.feed.threadgate#followerRule",
|
|
||||||
"app.bsky.feed.threadgate#followingRule",
|
|
||||||
"app.bsky.feed.threadgate#listRule"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draftPost": {
|
|
||||||
"description": "One of the posts that compose a draft.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["text"],
|
|
||||||
"properties": {
|
|
||||||
"text": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 10000,
|
|
||||||
"maxGraphemes": 1000,
|
|
||||||
"description": "The primary post content. It has a higher limit than post contents to allow storing a larger text that can later be refined into smaller posts."
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"type": "union",
|
|
||||||
"description": "Self-label values for this post. Effectively content warnings.",
|
|
||||||
"refs": ["com.atproto.label.defs#selfLabels"]
|
|
||||||
},
|
|
||||||
"embedImages": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#draftEmbedImage" },
|
|
||||||
"maxLength": 4
|
|
||||||
},
|
|
||||||
"embedVideos": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#draftEmbedVideo" },
|
|
||||||
"maxLength": 1
|
|
||||||
},
|
|
||||||
"embedExternals": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#draftEmbedExternal" },
|
|
||||||
"maxLength": 1
|
|
||||||
},
|
|
||||||
"embedRecords": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#draftEmbedRecord" },
|
|
||||||
"maxLength": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"draftView": {
|
|
||||||
"description": "View to present drafts data to users.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["id", "draft", "createdAt", "updatedAt"],
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"description": "A TID to be used as a draft identifier.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "tid"
|
|
||||||
},
|
|
||||||
"draft": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draft"
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"description": "The time the draft was created.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
},
|
|
||||||
"updatedAt": {
|
|
||||||
"description": "The time the draft was last updated.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"draftEmbedLocalRef": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["path"],
|
|
||||||
"properties": {
|
|
||||||
"path": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Local, on-device ref to file to be embedded. Embeds are currently device-bound for drafts.",
|
|
||||||
"minLength": 1,
|
|
||||||
"maxLength": 1024
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draftEmbedCaption": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["lang", "content"],
|
|
||||||
"properties": {
|
|
||||||
"lang": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "language"
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 10000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"draftEmbedImage": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["localRef"],
|
|
||||||
"properties": {
|
|
||||||
"localRef": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draftEmbedLocalRef"
|
|
||||||
},
|
|
||||||
"alt": {
|
|
||||||
"type": "string",
|
|
||||||
"maxGraphemes": 2000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draftEmbedVideo": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["localRef"],
|
|
||||||
"properties": {
|
|
||||||
"localRef": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draftEmbedLocalRef"
|
|
||||||
},
|
|
||||||
"alt": {
|
|
||||||
"type": "string",
|
|
||||||
"maxGraphemes": 2000
|
|
||||||
},
|
|
||||||
"captions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#draftEmbedCaption"
|
|
||||||
},
|
|
||||||
"maxLength": 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draftEmbedExternal": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["uri"],
|
|
||||||
"properties": {
|
|
||||||
"uri": { "type": "string", "format": "uri" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draftEmbedRecord": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["record"],
|
|
||||||
"properties": {
|
|
||||||
"record": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.draft.deleteDraft",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Deletes a draft by ID. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["id"],
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "tid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.draft.getDrafts",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Gets views of user drafts. Requires authentication.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["drafts"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"drafts": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.draft.defs#draftView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.draft.updateDraft",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Updates a draft using private storage (stash). If the draft ID points to a non-existing ID, the update will be silently ignored. This is done because updates don't enforce draft limit, so it accepts all writes, but will ignore invalid ones. Requires authentication.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["draft"],
|
|
||||||
"properties": {
|
|
||||||
"draft": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.draft.defs#draftWithId"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,9 +20,8 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"image": {
|
"image": {
|
||||||
"type": "blob",
|
"type": "blob",
|
||||||
"description": "The raw image file. May be up to 2 MB, formerly limited to 1 MB.",
|
|
||||||
"accept": ["image/*"],
|
"accept": ["image/*"],
|
||||||
"maxSize": 2000000
|
"maxSize": 1000000
|
||||||
},
|
},
|
||||||
"alt": {
|
"alt": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -9,9 +9,8 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"video": {
|
"video": {
|
||||||
"type": "blob",
|
"type": "blob",
|
||||||
"description": "The mp4 video file. May be up to 100mb, formerly limited to 50mb.",
|
|
||||||
"accept": ["video/mp4"],
|
"accept": ["video/mp4"],
|
||||||
"maxSize": 100000000
|
"maxSize": 50000000
|
||||||
},
|
},
|
||||||
"captions": {
|
"captions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -27,11 +26,6 @@
|
|||||||
"aspectRatio": {
|
"aspectRatio": {
|
||||||
"type": "ref",
|
"type": "ref",
|
||||||
"ref": "app.bsky.embed.defs#aspectRatio"
|
"ref": "app.bsky.embed.defs#aspectRatio"
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A hint to the client about how to present the video.",
|
|
||||||
"knownValues": ["default", "gif"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -65,11 +59,6 @@
|
|||||||
"aspectRatio": {
|
"aspectRatio": {
|
||||||
"type": "ref",
|
"type": "ref",
|
||||||
"ref": "app.bsky.embed.defs#aspectRatio"
|
"ref": "app.bsky.embed.defs#aspectRatio"
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A hint to the client about how to present the video.",
|
|
||||||
"knownValues": ["default", "gif"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"app.bsky.embed.recordWithMedia#view"
|
"app.bsky.embed.recordWithMedia#view"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"bookmarkCount": { "type": "integer" },
|
|
||||||
"replyCount": { "type": "integer" },
|
"replyCount": { "type": "integer" },
|
||||||
"repostCount": { "type": "integer" },
|
"repostCount": { "type": "integer" },
|
||||||
"likeCount": { "type": "integer" },
|
"likeCount": { "type": "integer" },
|
||||||
@@ -34,11 +33,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
||||||
},
|
},
|
||||||
"threadgate": { "type": "ref", "ref": "#threadgateView" },
|
"threadgate": { "type": "ref", "ref": "#threadgateView" }
|
||||||
"debug": {
|
|
||||||
"type": "unknown",
|
|
||||||
"description": "Debug information for internal development"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"viewerState": {
|
"viewerState": {
|
||||||
@@ -47,20 +42,12 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"repost": { "type": "string", "format": "at-uri" },
|
"repost": { "type": "string", "format": "at-uri" },
|
||||||
"like": { "type": "string", "format": "at-uri" },
|
"like": { "type": "string", "format": "at-uri" },
|
||||||
"bookmarked": { "type": "boolean" },
|
|
||||||
"threadMuted": { "type": "boolean" },
|
"threadMuted": { "type": "boolean" },
|
||||||
"replyDisabled": { "type": "boolean" },
|
"replyDisabled": { "type": "boolean" },
|
||||||
"embeddingDisabled": { "type": "boolean" },
|
"embeddingDisabled": { "type": "boolean" },
|
||||||
"pinned": { "type": "boolean" }
|
"pinned": { "type": "boolean" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"threadContext": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Metadata about this post within the context of the thread it is in.",
|
|
||||||
"properties": {
|
|
||||||
"rootAuthorLike": { "type": "string", "format": "at-uri" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"feedViewPost": {
|
"feedViewPost": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["post"],
|
"required": ["post"],
|
||||||
@@ -72,11 +59,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Context provided by feed generator that may be passed back alongside interactions.",
|
"description": "Context provided by feed generator that may be passed back alongside interactions.",
|
||||||
"maxLength": 2000
|
"maxLength": 2000
|
||||||
},
|
|
||||||
"reqId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Unique identifier per request that may be passed back alongside interactions.",
|
|
||||||
"maxLength": 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -104,8 +86,6 @@
|
|||||||
"required": ["by", "indexedAt"],
|
"required": ["by", "indexedAt"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"by": { "type": "ref", "ref": "app.bsky.actor.defs#profileViewBasic" },
|
"by": { "type": "ref", "ref": "app.bsky.actor.defs#profileViewBasic" },
|
||||||
"uri": { "type": "string", "format": "at-uri" },
|
|
||||||
"cid": { "type": "string", "format": "cid" },
|
|
||||||
"indexedAt": { "type": "string", "format": "datetime" }
|
"indexedAt": { "type": "string", "format": "datetime" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -128,8 +108,7 @@
|
|||||||
"type": "union",
|
"type": "union",
|
||||||
"refs": ["#threadViewPost", "#notFoundPost", "#blockedPost"]
|
"refs": ["#threadViewPost", "#notFoundPost", "#blockedPost"]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"threadContext": { "type": "ref", "ref": "#threadContext" }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notFoundPost": {
|
"notFoundPost": {
|
||||||
@@ -183,13 +162,6 @@
|
|||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
||||||
},
|
},
|
||||||
"viewer": { "type": "ref", "ref": "#generatorViewerState" },
|
"viewer": { "type": "ref", "ref": "#generatorViewerState" },
|
||||||
"contentMode": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": [
|
|
||||||
"app.bsky.feed.defs#contentModeUnspecified",
|
|
||||||
"app.bsky.feed.defs#contentModeVideo"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"indexedAt": { "type": "string", "format": "datetime" }
|
"indexedAt": { "type": "string", "format": "datetime" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -263,11 +235,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton.",
|
"description": "Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton.",
|
||||||
"maxLength": 2000
|
"maxLength": 2000
|
||||||
},
|
|
||||||
"reqId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Unique identifier per request that may be passed back alongside interactions.",
|
|
||||||
"maxLength": 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -295,14 +262,6 @@
|
|||||||
"type": "token",
|
"type": "token",
|
||||||
"description": "User clicked through to the embedded content of the feed item"
|
"description": "User clicked through to the embedded content of the feed item"
|
||||||
},
|
},
|
||||||
"contentModeUnspecified": {
|
|
||||||
"type": "token",
|
|
||||||
"description": "Declares the feed generator returns any types of posts."
|
|
||||||
},
|
|
||||||
"contentModeVideo": {
|
|
||||||
"type": "token",
|
|
||||||
"description": "Declares the feed generator returns posts containing app.bsky.embed.video embeds."
|
|
||||||
},
|
|
||||||
"interactionSeen": {
|
"interactionSeen": {
|
||||||
"type": "token",
|
"type": "token",
|
||||||
"description": "Feed item was seen by user"
|
"description": "Feed item was seen by user"
|
||||||
|
|||||||
@@ -39,13 +39,6 @@
|
|||||||
"description": "Self-label values",
|
"description": "Self-label values",
|
||||||
"refs": ["com.atproto.label.defs#selfLabels"]
|
"refs": ["com.atproto.label.defs#selfLabels"]
|
||||||
},
|
},
|
||||||
"contentMode": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": [
|
|
||||||
"app.bsky.feed.defs#contentModeUnspecified",
|
|
||||||
"app.bsky.feed.defs#contentModeVideo"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"createdAt": { "type": "string", "format": "datetime" }
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@
|
|||||||
"posts_with_replies",
|
"posts_with_replies",
|
||||||
"posts_no_replies",
|
"posts_no_replies",
|
||||||
"posts_with_media",
|
"posts_with_media",
|
||||||
"posts_and_author_threads",
|
"posts_and_author_threads"
|
||||||
"posts_with_video"
|
|
||||||
],
|
],
|
||||||
"default": "posts_with_replies"
|
"default": "posts_with_replies"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,11 +36,6 @@
|
|||||||
"type": "ref",
|
"type": "ref",
|
||||||
"ref": "app.bsky.feed.defs#skeletonFeedPost"
|
"ref": "app.bsky.feed.defs#skeletonFeedPost"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"reqId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Unique identifier per request that may be passed back alongside interactions.",
|
|
||||||
"maxLength": 100
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
"required": ["subject", "createdAt"],
|
"required": ["subject", "createdAt"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
"via": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
"description": "List of AT-URIs embedding this post that the author has detached from."
|
"description": "List of AT-URIs embedding this post that the author has detached from."
|
||||||
},
|
},
|
||||||
"embeddingRules": {
|
"embeddingRules": {
|
||||||
"description": "List of rules defining who can embed this post. If value is an empty array or is undefined, no particular rules apply and anyone can embed.",
|
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"maxLength": 5,
|
"maxLength": 5,
|
||||||
"items": {
|
"items": {
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
"required": ["subject", "createdAt"],
|
"required": ["subject", "createdAt"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
"subject": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
"via": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"defs": {
|
"defs": {
|
||||||
"main": {
|
"main": {
|
||||||
"type": "query",
|
"type": "query",
|
||||||
"description": "Find posts matching search criteria, returning views of those posts. Note that this API endpoint may require authentication (eg, not public) for some service providers and implementations.",
|
"description": "Find posts matching search criteria, returning views of those posts.",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "params",
|
"type": "params",
|
||||||
"required": ["q"],
|
"required": ["q"],
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["interactions"],
|
"required": ["interactions"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"feed": { "type": "string", "format": "at-uri" },
|
|
||||||
"interactions": {
|
"interactions": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
|||||||
@@ -16,23 +16,17 @@
|
|||||||
"description": "Reference (AT-URI) to the post record."
|
"description": "Reference (AT-URI) to the post record."
|
||||||
},
|
},
|
||||||
"allow": {
|
"allow": {
|
||||||
"description": "List of rules defining who can reply to this post. If value is an empty array, no one can reply. If value is undefined, anyone can reply.",
|
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"maxLength": 5,
|
"maxLength": 5,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "union",
|
"type": "union",
|
||||||
"refs": [
|
"refs": ["#mentionRule", "#followingRule", "#listRule"]
|
||||||
"#mentionRule",
|
|
||||||
"#followerRule",
|
|
||||||
"#followingRule",
|
|
||||||
"#listRule"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" },
|
||||||
"hiddenReplies": {
|
"hiddenReplies": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"maxLength": 300,
|
"maxLength": 50,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "at-uri"
|
"format": "at-uri"
|
||||||
@@ -47,11 +41,6 @@
|
|||||||
"description": "Allow replies from actors mentioned in your post.",
|
"description": "Allow replies from actors mentioned in your post.",
|
||||||
"properties": {}
|
"properties": {}
|
||||||
},
|
},
|
||||||
"followerRule": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Allow replies from actors who follow you.",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"followingRule": {
|
"followingRule": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Allow replies from actors you follow.",
|
"description": "Allow replies from actors you follow.",
|
||||||
|
|||||||
@@ -159,26 +159,6 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "at-uri",
|
"format": "at-uri",
|
||||||
"description": "if the actor is followed by this DID, contains the AT-URI of the follow record"
|
"description": "if the actor is followed by this DID, contains the AT-URI of the follow record"
|
||||||
},
|
|
||||||
"blocking": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-uri",
|
|
||||||
"description": "if the actor blocks this DID, this is the AT-URI of the block record"
|
|
||||||
},
|
|
||||||
"blockedBy": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-uri",
|
|
||||||
"description": "if the actor is blocked by this DID, contains the AT-URI of the block record"
|
|
||||||
},
|
|
||||||
"blockingByList": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-uri",
|
|
||||||
"description": "if the actor blocks this DID via a block list, this is the AT-URI of the listblock record"
|
|
||||||
},
|
|
||||||
"blockedByList": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-uri",
|
|
||||||
"description": "if the actor is blocked by this DID via a block list, contains the AT-URI of the listblock record"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
"required": ["subject", "createdAt"],
|
"required": ["subject", "createdAt"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"subject": { "type": "string", "format": "did" },
|
"subject": { "type": "string", "format": "did" },
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
"via": { "type": "ref", "ref": "com.atproto.repo.strongRef" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,7 @@
|
|||||||
"maximum": 100,
|
"maximum": 100,
|
||||||
"default": 50
|
"default": 50
|
||||||
},
|
},
|
||||||
"cursor": { "type": "string" },
|
"cursor": { "type": "string" }
|
||||||
"purposes": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Optional filter by list purpose. If not specified, all supported types are returned.",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["modlist", "curatelist"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.graph.getListsWithMembership",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Enumerates the lists created by the session user, and includes membership information about `actor` in those lists. Only supports curation and moderation lists (no reference lists, used in starter packs). Requires auth.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"required": ["actor"],
|
|
||||||
"properties": {
|
|
||||||
"actor": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-identifier",
|
|
||||||
"description": "The account (actor) to check for membership."
|
|
||||||
},
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"purposes": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Optional filter by list purpose. If not specified, all supported types are returned.",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["modlist", "curatelist"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["listsWithMembership"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"listsWithMembership": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#listWithMembership" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"listWithMembership": {
|
|
||||||
"description": "A list and an optional list item indicating membership of a target user to that list.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["list"],
|
|
||||||
"properties": {
|
|
||||||
"list": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.graph.defs#listView"
|
|
||||||
},
|
|
||||||
"listItem": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.graph.defs#listItemView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.graph.getStarterPacksWithMembership",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Enumerates the starter packs created by the session user, and includes membership information about `actor` in those starter packs. Requires auth.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"required": ["actor"],
|
|
||||||
"properties": {
|
|
||||||
"actor": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "at-identifier",
|
|
||||||
"description": "The account (actor) to check for membership."
|
|
||||||
},
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["starterPacksWithMembership"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"starterPacksWithMembership": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#starterPackWithMembership" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"starterPackWithMembership": {
|
|
||||||
"description": "A starter pack and an optional list item indicating membership of a target user to that starter pack.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["starterPack"],
|
|
||||||
"properties": {
|
|
||||||
"starterPack": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.graph.defs#starterPackView"
|
|
||||||
},
|
|
||||||
"listItem": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.graph.defs#listItemView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,18 +25,14 @@
|
|||||||
"ref": "app.bsky.actor.defs#profileView"
|
"ref": "app.bsky.actor.defs#profileView"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recIdStr": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Snowflake for this recommendation, use when submitting recommendation events."
|
|
||||||
},
|
|
||||||
"isFallback": {
|
"isFallback": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "DEPRECATED, unused. Previously: if true, response has fallen-back to generic results, and is not scoped using relativeToDid",
|
"description": "If true, response has fallen-back to generic results, and is not scoped using relativeToDid",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"recId": {
|
"recId": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "DEPRECATED: use recIdStr instead."
|
"description": "Snowflake for this recommendation, use when submitting recommendation events."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.graph.verification",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "record",
|
|
||||||
"description": "Record declaring a verification relationship between two accounts. Verifications are only considered valid by an app if issued by an account the app considers trusted.",
|
|
||||||
"key": "tid",
|
|
||||||
"record": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject", "handle", "displayName", "createdAt"],
|
|
||||||
"properties": {
|
|
||||||
"subject": {
|
|
||||||
"description": "DID of the subject the verification applies to.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
},
|
|
||||||
"handle": {
|
|
||||||
"description": "Handle of the subject the verification applies to at the moment of verifying, which might not be the same at the time of viewing. The verification is only valid if the current handle matches the one at the time of verifying.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "handle"
|
|
||||||
},
|
|
||||||
"displayName": {
|
|
||||||
"description": "Display name of the subject the verification applies to at the moment of verifying, which might not be the same at the time of viewing. The verification is only valid if the current displayName matches the one at the time of verifying.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"description": "Date of when the verification was created.",
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,27 +35,6 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
|
||||||
},
|
|
||||||
"reasonTypes": {
|
|
||||||
"description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.moderation.defs#reasonType"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subjectTypes": {
|
|
||||||
"description": "The set of subject types (account, record, etc) this service accepts reports on.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.moderation.defs#subjectType"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subjectCollections": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type.",
|
|
||||||
"items": { "type": "string", "format": "nsid" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,28 +18,7 @@
|
|||||||
"type": "union",
|
"type": "union",
|
||||||
"refs": ["com.atproto.label.defs#selfLabels"]
|
"refs": ["com.atproto.label.defs#selfLabels"]
|
||||||
},
|
},
|
||||||
"createdAt": { "type": "string", "format": "datetime" },
|
"createdAt": { "type": "string", "format": "datetime" }
|
||||||
"reasonTypes": {
|
|
||||||
"description": "The set of report reason 'codes' which are in-scope for this service to review and action. These usually align to policy categories. If not defined (distinct from empty array), all reason types are allowed.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.moderation.defs#reasonType"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subjectTypes": {
|
|
||||||
"description": "The set of subject types (account, record, etc) this service accepts reports on.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "com.atproto.moderation.defs#subjectType"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subjectCollections": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "Set of record types (collection NSIDs) which can be reported to this service. If not defined (distinct from empty array), default is any record type.",
|
|
||||||
"items": { "type": "string", "format": "nsid" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.declaration",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "record",
|
|
||||||
"description": "A declaration of the user's choices related to notifications that can be produced by them.",
|
|
||||||
"key": "literal:self",
|
|
||||||
"record": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["allowSubscriptions"],
|
|
||||||
"properties": {
|
|
||||||
"allowSubscriptions": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A declaration of the user's preference for allowing activity subscriptions from other users. Absence of a record implies 'followers'.",
|
|
||||||
"knownValues": ["followers", "mutuals", "none"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.defs",
|
|
||||||
"defs": {
|
|
||||||
"recordDeleted": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"chatPreference": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["include", "push"],
|
|
||||||
"properties": {
|
|
||||||
"include": { "type": "string", "knownValues": ["all", "accepted"] },
|
|
||||||
"push": { "type": "boolean" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"filterablePreference": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["include", "list", "push"],
|
|
||||||
"properties": {
|
|
||||||
"include": { "type": "string", "knownValues": ["all", "follows"] },
|
|
||||||
"list": { "type": "boolean" },
|
|
||||||
"push": { "type": "boolean" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preference": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["list", "push"],
|
|
||||||
"properties": {
|
|
||||||
"list": { "type": "boolean" },
|
|
||||||
"push": { "type": "boolean" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"preferences": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"chat",
|
|
||||||
"follow",
|
|
||||||
"like",
|
|
||||||
"likeViaRepost",
|
|
||||||
"mention",
|
|
||||||
"quote",
|
|
||||||
"reply",
|
|
||||||
"repost",
|
|
||||||
"repostViaRepost",
|
|
||||||
"starterpackJoined",
|
|
||||||
"subscribedPost",
|
|
||||||
"unverified",
|
|
||||||
"verified"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"chat": { "type": "ref", "ref": "#chatPreference" },
|
|
||||||
"follow": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"like": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"likeViaRepost": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"mention": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"quote": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"reply": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"repost": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"repostViaRepost": { "type": "ref", "ref": "#filterablePreference" },
|
|
||||||
"starterpackJoined": { "type": "ref", "ref": "#preference" },
|
|
||||||
"subscribedPost": { "type": "ref", "ref": "#preference" },
|
|
||||||
"unverified": { "type": "ref", "ref": "#preference" },
|
|
||||||
"verified": { "type": "ref", "ref": "#preference" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"activitySubscription": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["post", "reply"],
|
|
||||||
"properties": {
|
|
||||||
"post": { "type": "boolean" },
|
|
||||||
"reply": { "type": "boolean" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subjectActivitySubscription": {
|
|
||||||
"description": "Object used to store activity subscription data in stash.",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject", "activitySubscription"],
|
|
||||||
"properties": {
|
|
||||||
"subject": { "type": "string", "format": "did" },
|
|
||||||
"activitySubscription": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "#activitySubscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.getPreferences",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Get notification-related preferences for an account. Requires auth.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["preferences"],
|
|
||||||
"properties": {
|
|
||||||
"preferences": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preferences"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.listActivitySubscriptions",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Enumerate all accounts to which the requesting account is subscribed to receive notifications for. Requires auth.",
|
|
||||||
"parameters": {
|
|
||||||
"type": "params",
|
|
||||||
"properties": {
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 100,
|
|
||||||
"default": 50
|
|
||||||
},
|
|
||||||
"cursor": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subscriptions"],
|
|
||||||
"properties": {
|
|
||||||
"cursor": { "type": "string" },
|
|
||||||
"subscriptions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.actor.defs#profileView"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"author": { "type": "ref", "ref": "app.bsky.actor.defs#profileView" },
|
"author": { "type": "ref", "ref": "app.bsky.actor.defs#profileView" },
|
||||||
"reason": {
|
"reason": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The reason why this notification was delivered - e.g. your post was liked, or you received a new follower.",
|
"description": "Expected values are 'like', 'repost', 'follow', 'mention', 'reply', 'quote', and 'starterpack-joined'.",
|
||||||
"knownValues": [
|
"knownValues": [
|
||||||
"like",
|
"like",
|
||||||
"repost",
|
"repost",
|
||||||
@@ -69,13 +69,7 @@
|
|||||||
"mention",
|
"mention",
|
||||||
"reply",
|
"reply",
|
||||||
"quote",
|
"quote",
|
||||||
"starterpack-joined",
|
"starterpack-joined"
|
||||||
"verified",
|
|
||||||
"unverified",
|
|
||||||
"like-via-repost",
|
|
||||||
"repost-via-repost",
|
|
||||||
"subscribed-post",
|
|
||||||
"contact-match"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"reasonSubject": { "type": "string", "format": "at-uri" },
|
"reasonSubject": { "type": "string", "format": "at-uri" },
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.putActivitySubscription",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Puts an activity subscription entry. The key should be omitted for creation and provided for updates. Requires auth.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject", "activitySubscription"],
|
|
||||||
"properties": {
|
|
||||||
"subject": { "type": "string", "format": "did" },
|
|
||||||
"activitySubscription": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#activitySubscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["subject"],
|
|
||||||
"properties": {
|
|
||||||
"subject": { "type": "string", "format": "did" },
|
|
||||||
"activitySubscription": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#activitySubscription"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.putPreferencesV2",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "Set notification-related preferences for an account. Requires auth.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"chat": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#chatPreference"
|
|
||||||
},
|
|
||||||
"follow": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"like": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"likeViaRepost": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"mention": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"quote": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"reply": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"repost": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"repostViaRepost": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#filterablePreference"
|
|
||||||
},
|
|
||||||
"starterpackJoined": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preference"
|
|
||||||
},
|
|
||||||
"subscribedPost": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preference"
|
|
||||||
},
|
|
||||||
"unverified": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preference"
|
|
||||||
},
|
|
||||||
"verified": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preference"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["preferences"],
|
|
||||||
"properties": {
|
|
||||||
"preferences": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.notification.defs#preferences"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,11 +17,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"knownValues": ["ios", "android", "web"]
|
"knownValues": ["ios", "android", "web"]
|
||||||
},
|
},
|
||||||
"appId": { "type": "string" },
|
"appId": { "type": "string" }
|
||||||
"ageRestricted": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Set to true when the actor is age restricted"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.notification.unregisterPush",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "procedure",
|
|
||||||
"description": "The inverse of registerPush - inform a specified service that push notifications should no longer be sent to the given token for the requesting account. Requires auth.",
|
|
||||||
"input": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["serviceDid", "token", "platform", "appId"],
|
|
||||||
"properties": {
|
|
||||||
"serviceDid": { "type": "string", "format": "did" },
|
|
||||||
"token": { "type": "string" },
|
|
||||||
"platform": {
|
|
||||||
"type": "string",
|
|
||||||
"knownValues": ["ios", "android", "web"]
|
|
||||||
},
|
|
||||||
"appId": { "type": "string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,167 +32,6 @@
|
|||||||
"description": { "type": "string" },
|
"description": { "type": "string" },
|
||||||
"link": { "type": "string" }
|
"link": { "type": "string" }
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"skeletonTrend": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"topic",
|
|
||||||
"displayName",
|
|
||||||
"link",
|
|
||||||
"startedAt",
|
|
||||||
"postCount",
|
|
||||||
"dids"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"topic": { "type": "string" },
|
|
||||||
"displayName": { "type": "string" },
|
|
||||||
"link": { "type": "string" },
|
|
||||||
"startedAt": { "type": "string", "format": "datetime" },
|
|
||||||
"postCount": { "type": "integer" },
|
|
||||||
"status": { "type": "string", "knownValues": ["hot"] },
|
|
||||||
"category": { "type": "string" },
|
|
||||||
"dids": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "did"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"trendView": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"topic",
|
|
||||||
"displayName",
|
|
||||||
"link",
|
|
||||||
"startedAt",
|
|
||||||
"postCount",
|
|
||||||
"actors"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"topic": { "type": "string" },
|
|
||||||
"displayName": { "type": "string" },
|
|
||||||
"link": { "type": "string" },
|
|
||||||
"startedAt": { "type": "string", "format": "datetime" },
|
|
||||||
"postCount": { "type": "integer" },
|
|
||||||
"status": { "type": "string", "knownValues": ["hot"] },
|
|
||||||
"category": { "type": "string" },
|
|
||||||
"actors": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.actor.defs#profileViewBasic"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"threadItemPost": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"post",
|
|
||||||
"moreParents",
|
|
||||||
"moreReplies",
|
|
||||||
"opThread",
|
|
||||||
"hiddenByThreadgate",
|
|
||||||
"mutedByViewer"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"post": { "type": "ref", "ref": "app.bsky.feed.defs#postView" },
|
|
||||||
"moreParents": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "This post has more parents that were not present in the response. This is just a boolean, without the number of parents."
|
|
||||||
},
|
|
||||||
"moreReplies": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "This post has more replies that were not present in the response. This is a numeric value, which is best-effort and might not be accurate."
|
|
||||||
},
|
|
||||||
"opThread": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "This post is part of a contiguous thread by the OP from the thread root. Many different OP threads can happen in the same thread."
|
|
||||||
},
|
|
||||||
"hiddenByThreadgate": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread."
|
|
||||||
},
|
|
||||||
"mutedByViewer": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "This is by an account muted by the viewer requesting it."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"threadItemNoUnauthenticated": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"threadItemNotFound": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"threadItemBlocked": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["author"],
|
|
||||||
"properties": {
|
|
||||||
"author": { "type": "ref", "ref": "app.bsky.feed.defs#blockedAuthor" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ageAssuranceState": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The computed state of the age assurance process, returned to the user in question on certain authenticated requests.",
|
|
||||||
"required": ["status"],
|
|
||||||
"properties": {
|
|
||||||
"lastInitiatedAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The timestamp when this state was last updated."
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status of the age assurance process.",
|
|
||||||
"knownValues": ["unknown", "pending", "assured", "blocked"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ageAssuranceEvent": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Object used to store age assurance data in stash.",
|
|
||||||
"required": ["createdAt", "status", "attemptId"],
|
|
||||||
"properties": {
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime",
|
|
||||||
"description": "The date and time of this write operation."
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The status of the age assurance process.",
|
|
||||||
"knownValues": ["unknown", "pending", "assured"]
|
|
||||||
},
|
|
||||||
"attemptId": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The unique identifier for this instance of the age assurance flow, in UUID format."
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The email used for AA."
|
|
||||||
},
|
|
||||||
"initIp": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The IP address used when initiating the AA flow."
|
|
||||||
},
|
|
||||||
"initUa": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user agent used when initiating the AA flow."
|
|
||||||
},
|
|
||||||
"completeIp": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The IP address used when completing the AA flow."
|
|
||||||
},
|
|
||||||
"completeUa": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user agent used when completing the AA flow."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"lexicon": 1,
|
|
||||||
"id": "app.bsky.unspecced.getAgeAssuranceState",
|
|
||||||
"defs": {
|
|
||||||
"main": {
|
|
||||||
"type": "query",
|
|
||||||
"description": "Returns the current state of the age assurance process for an account. This is used to check if the user has completed age assurance or if further action is required.",
|
|
||||||
"output": {
|
|
||||||
"encoding": "application/json",
|
|
||||||
"schema": {
|
|
||||||
"type": "ref",
|
|
||||||
"ref": "app.bsky.unspecced.defs#ageAssuranceState"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,24 +11,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [],
|
"required": [],
|
||||||
"properties": {
|
"properties": {
|
||||||
"checkEmailConfirmed": { "type": "boolean" },
|
"checkEmailConfirmed": { "type": "boolean" }
|
||||||
"liveNow": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "ref", "ref": "#liveNowConfig" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"liveNowConfig": {
|
|
||||||
"type": "object",
|
|
||||||
"required": ["did", "domains"],
|
|
||||||
"properties": {
|
|
||||||
"did": { "type": "string", "format": "did" },
|
|
||||||
"domains": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user