An experimental TypeSpec syntax for Lexicon

Compare changes

Choose any two refs to compare.

Changed files
+7767 -3813
.tangled
workflows
packages
cli
emitter
lib
src
test
integration
atproto
input
app
bsky
actor
bookmark
embed
feed
graph
labeler
notification
richtext
unspecced
video
chat
com
atproto
admin
identity
label
lexicon
moderation
repo
server
sync
temp
tools
output
app
bsky
richtext
tools
ozone
moderation
team
lexicon-examples
input
com
atproto
admin
identity
label
lexicon
moderation
repo
server
sync
temp
pub
sh
output
spec
example
playground
website
public
src
pages
+23 -38
.tangled/workflows/deploy.yaml
··· 1 - name: Deploy 1 + engine: nixery 2 + when: 3 + - event: ["push", "pull_request"] 4 + branch: ["main"] 2 5 3 - on: 4 - push: 5 - branches: [main] 6 + dependencies: 7 + nixpkgs: 8 + - nodejs 9 + - gnused 6 10 7 - jobs: 8 - deploy: 9 - runs-on: ubuntu-latest 10 - steps: 11 - - uses: actions/checkout@v4 12 - 13 - - uses: pnpm/action-setup@v4 14 - with: 15 - version: 9 16 - 17 - - uses: actions/setup-node@v4 18 - with: 19 - node-version: '20' 20 - cache: 'pnpm' 21 - 22 - - name: Install dependencies 23 - run: pnpm install 24 - 25 - - name: Build website 26 - run: pnpm run build 27 - working-directory: ./packages/website 28 - 29 - - name: Build playground 30 - run: pnpm run build 31 - working-directory: ./packages/playground 32 - 33 - Deployment commands (commented out - uncomment when ready to deploy): 34 - - name: Deploy playground 35 - run: npx --yes wrangler pages deploy --branch main --project-name typelex-playground ./dist/ 36 - working-directory: ./packages/playground 37 - 38 - - name: Deploy website 39 - run: npx --yes wrangler pages deploy --branch main --project-name typelex ./dist/ 40 - working-directory: ./packages/website 11 + steps: 12 + - name: install and build 13 + command: | 14 + npx pnpm install 15 + cd packages/emitter && npx pnpm run build 16 + cd ../website && npx pnpm run build 17 + cd ../playground && npx pnpm run build 18 + - name: deploy playground 19 + command: | 20 + cd packages/playground 21 + npx --yes wrangler pages deploy --branch main --project-name typelex-playground ./dist/ 22 + - name: deploy website 23 + command: | 24 + cd packages/website 25 + npx --yes wrangler pages deploy --branch main --project-name typelex ./dist/
+23
CHANGELOG.md
··· 1 + ### 0.2.0 2 + 3 + - Add `@external` support 4 + 5 + ### 0.1.6 6 + 7 + - Rebuild 8 + 9 + ### 0.1.5 10 + 11 + - Allow tokens in string unions 12 + 13 + ### 0.1.4 14 + 15 + - Fix some namespaces failing to emit 16 + 17 + ### 0.1.3 18 + 19 + - Fix some namespaces failing to emit 20 + 21 + ### 0.1.2 22 + 23 + - Disallow mixing `@required` with `?:`
+11 -34
CLAUDE.md
··· 1 - you're working on a TypeSpec emitter for atproto lexicons. you have these commands: `pnpm run build` builds the example (you can inspect the generated lexicons in the `packages/example/lexicon`) and `pnpm test` runs tests (they are in `packages/emitter/test/`). 1 + you're working on a TypeSpec emitter for atproto Lexicons. the goal is to make a more comfortable language for writing lexicons, kind of like "coffeescript for lexicons" (however terrible that may be). one may imagine it getting supplanted by a proper atproto IDL someday. however, we're staying within what TypeSpec offers, which means we want to stay idiomatic both to atproto and TypeSpec, or rather, to try to translate atproto idioms into TypeSpec idioms as closely as we can. 2 2 3 - what i want you to do is to gradually keep adding features to typelex so that in the end it's possible to express all Lexicon code with that language. you have the following resources: 3 + you have the following resources you're encouraged to explore on your own whenever they seem relevant to the task: 4 4 5 - - read `DOCS.md`!!! it's the main guideline 5 + - `DOCS.md` is a must-read. it gives you an intro to typelex and how its conventions map to atproto. it also gives you a sense of the project philosophy. 6 + - `../atproto-website/` has a Lexicon spec (look for `lexicon/en.mdx`) which you'll want to consult 7 + - `../typespec/` is the TypeSpec repo. you can consult the source code of other emitters (in `packages`, e.g. `packages/protobuf` and `packages/json-schema`) and `website` folder for documentation of TypeSpec syntax and idioms. 6 8 7 - - `../typespec/packages` has a bunch of other emitters (so you can check how they're implemented and common patterns) 9 + as a part of your workflow, you will: 8 10 9 - - `../typespec/website/src/content` has typescpec docs (which you might find very useful) 11 + - run `pnpm test` whenever you make changes to verify no regressions. note a few test suites: an `atproto` test suite with definitions from upstream (`.tsp` files produce expected `.json` files), `lexicon-examples` suite with third party lexicons, and `spec` with a more focused suite. use checked-in `*.tsp` files liberally to learn typelex conventions. we have a LOT of them! 10 12 11 - - `../atproto-website/src/app/[locale]/specs/lexicon` contains lexicon spec (where we want to reach feature parity), and most importantly 13 + - if you're working on `packages/example`, use `pnpm run build` to run it and observe the output. you may also want to look at codegen output (it's hooked up to atproto codegen). 12 14 13 - - `test/scenarios/atproto/output` contains a TON of lexicon definitions which i want you to translate to typespec one by one. for each atproto lexicon you're porting, your job is to create the corresponding "`input`s" for them (and fix bugs or add missing features along the way). 15 + - we also have a playground in `packages/playground` and website in `packages/website`. 14 16 15 - the goal is to end up with a language that feels nice and concise, not some weird interop thing. so try to do things "typespec" way as you go through each lexicon. and try to respect atproto too. this should feel like a *language for atproto*. consider whether your design is elegant. and make sure to look at other typespec emitters for design ideas! 16 - 17 - remember NOT to change the output to fit the input -- your job is to make the input MATCH the output. if some lexicon is too hard, you can give up on it and try another one. good luck! 18 - 19 - the workflow is: pick some new lexicons, create the input files, figure out missing features / bugs, and try to get tests to pass. repeat. when you make nontrivial progress, you'll want to prepare a commit. 20 - 21 - before you want to make a commit, make sure `pnpm test` and `pnpm run build` passes and, ideally, inspect the `example` app output (and maybe add the features you've just implemented there for parity). then stop and notify me. 22 - 23 - ps. be mindful of the code style. we only put decorators on same line if it's `@required` alone; otherwise, put each on newline and add a newline after. see existing files for a hint. 17 + you'll approach the project thoughtfully. while `playground` and `website` are more vibey and can be garbage code, it's essential to keep the `emitter` making sense. this doesn't just mean always verifying the tests pass (that's a given), but also making each decision *based on the spec* and a good understanding of atproto semantics. always think: is this the simplest solution? does it still make sense if you forget the code that exists now and think from first principles? don't hesitate to pause and ask or rethink if something actually doesn't make sense. 24 18 25 19 good luck! i believe in you 26 20 27 - 28 - --- 29 - 30 - whenever you get stuck (try `npm test`) because you don't have a convention for how to define some pattern yetm i want you to read these sources 31 - 32 - - `../typespec/packages` has a bunch of other emitters (so you can check how they're implemented and common patterns) 33 - 34 - - `../typespec/website/src/content` has typescpec docs (which you might find very useful) 35 - 36 - - `../atproto-website/src/app/[locale]/specs/lexicon` contains lexicon spec 37 - 38 - and ultrathink about what's the most appropriate way to define defs like you want would be in this typespec translation of atproto. you can research these sources for what feels most idiomatic to define that syntax, and how to make 39 - the formatter understand that convention and emit the JSON we want. 40 - 41 - also note that you can always look at generated atproto lexicons in ../atproto/lexicons -- use these!! look at the TS types there for corresponding 42 - models to inspire how you represent things in tsp syntax. 43 - 44 - - read `DOCS.md`!!! it's the main guideline 21 + and read the `DOCS.md` (and `*.tsp` files in this repo)!!! they're your most important sources of information.
+711 -395
DOCS.md
··· 1 - # Typelex Docs 1 + # typelex docs 2 + 3 + Typelex is a [TypeSpec](https://typespec.io/) emitter that outputs [AT Lexicon](https://atproto.com/specs/lexicon) JSON files. 4 + 5 + ## Introduction 6 + 7 + ### What's Lexicon? 8 + 9 + [Lexicon](https://atproto.com/specs/lexicon) is a schema format used by [AT](https://atproto.com/) applications. Here's a small example: 10 + 11 + ```json 12 + { 13 + "lexicon": 1, 14 + "id": "app.bsky.bookmark.defs", 15 + "defs": { 16 + "listItemView": { 17 + "type": "object", 18 + "properties": { 19 + "uri": { "type": "string", "format": "at-uri" } 20 + }, 21 + "required": ["uri"] 22 + } 23 + } 24 + } 25 + ``` 2 26 3 - This maps [atproto Lexicon](https://atproto.com/specs/lexicon) JSON syntax to typelex (which is a [TypeSpec](https://typespec.io/) emitter). It assumes you're familiar with Lexicon and want to understand how to express it in TypeSpec. Consult [TypeSpec docs](https://typespec.io/) on details of TypeSpec syntax. 27 + This schema is then used to generate code for parsing of these objects, their validation, and their types. 4 28 5 - This page was mostly written by Claude based on the test fixtures from this repo (which are [deployed in the playground](https://playground.typelex.org/)). I hope it's mostly correct and comprehensible. When in doubt, refer to those fixtures. 29 + ### What's TypeSpec? 6 30 7 - ## Playground 31 + [TypeSpec](https://typespec.io/) is a TypeScript-like language for writing schemas for data and API calls. It offers flexible syntax and tooling (like LSP), but doesn't specify output formatโ€”that's what *emitters* do. For example, there's a [JSON Schema emitter](https://typespec.io/docs/emitters/json-schema/reference/) and a [Protobuf emitter](https://typespec.io/docs/emitters/protobuf/reference/). 8 32 9 - Go to https://playground.typelex.org/ to play with a bunch of lexicons. 33 + ### What's typelex? 34 + 35 + Typelex is a TypeSpec emitter targeting Lexicon. Here's the same schema in TypeSpec: 36 + 37 + ```typescript 38 + import "@typelex/emitter"; 39 + 40 + namespace app.bsky.bookmark.defs { 41 + model ListItemView { 42 + @required uri: atUri; 43 + } 44 + } 45 + ``` 46 + 47 + Run the compiler, and it generates Lexicon JSON for you. 48 + 49 + The JSON is what you'll publishโ€”typelex just makes authoring easier. Think of it as "CoffeeScript for Lexicon" (however terrible that may be). 50 + 51 + Typelex tries to stay faithful to both TypeSpec idioms and Lexicon concepts, which is a tricky balance. Since we can't add keywords to TypeSpec, decorators fill the gapsโ€”you'll write `@procedure op` instead of `procedure`, or `model` for what Lexicon calls a "def". One downside of this approach is you'll need to learn both Lexicon *and* TypeSpec to know what you're doing. Scan the [TypeSpec language overview](https://typespec.io/docs/language-basics/overview/) to get a feel for it. 52 + 53 + Personally, I find JSON Lexicons hard to read and author, so the tradeoff works for me. 54 + 55 + ### Playground 56 + 57 + [Open Playground](https://playground.typelex.org/) to play with a bunch of lexicons and to see how typelex code translates to Lexicon JSON. 58 + 59 + If you already know Lexicon, you can learn the syntax by just opening the Lexicons you're familiar with. 10 60 11 61 ## Quick Start 12 62 13 - Every TypeSpec file starts with an import and namespace: 63 + ### Namespaces 64 + 65 + A namespace corresponds to a Lexicon file: 14 66 15 67 ```typescript 16 68 import "@typelex/emitter"; 17 69 18 - /** Common definitions used by other lexicons */ 19 - namespace com.example.defs { 20 - // definitions here 70 + namespace app.bsky.feed.defs { 71 + model PostView { 72 + // ... 73 + } 21 74 } 22 75 ``` 23 76 24 - **Maps to:** 77 + This emits `app/bsky/feed/defs.json`: 78 + 25 79 ```json 26 80 { 27 81 "lexicon": 1, 28 - "id": "com.example.defs", 29 - "description": "Common definitions used by other lexicons", 82 + "id": "app.bsky.feed.defs", 30 83 "defs": { ... } 31 84 } 32 85 ``` 33 86 34 - Use `/** */` doc comments for descriptions (or `@doc()` decorator as alternative). 87 + [Try it in the playground](https://playground.typelex.org/?c=aW1wb3J0ICJAdHlwZWxleC9lbWl0dGVyIjsKCm5hbWVzcGFjZSBhcHAuYnNreS5mZWVkLmRlZnMgewogIG1vZGVsIFBvc3RWaWV3xRMgIHJlcGx5Q291bnQ%2FOiBpbnRlZ2VyO8gab3N01RtsaWtl0xl9Cn0K&e=%40typelex%2Femitter&options=%7B%7D&vs=%7B%7D). 35 88 36 - ## Top-Level Lexicon Types 89 + If TypeSpec complains about reserved words in namespaces, use backticks: 90 + 91 + ```typescript 92 + import "@typelex/emitter"; 93 + 94 + namespace app.bsky.feed.post.`record` { } 95 + namespace `pub`.blocks.blockquote { } 96 + ``` 37 97 38 - ### Query (XRPC Query) 98 + You can define multiple namespaces in one file: 39 99 40 100 ```typescript 41 - namespace com.example.getRecord { 42 - /** Retrieve a record by ID */ 43 - @query 44 - op main( 45 - /** The record identifier */ 46 - @required id: string 47 - ): { 48 - @required record: com.example.record.Main; 49 - }; 101 + import "@typelex/emitter"; 102 + 103 + namespace com.example.foo { 104 + model Main { /* ... */ } 105 + } 106 + 107 + namespace com.example.bar { 108 + model Main { /* ... */ } 50 109 } 51 110 ``` 52 111 53 - **Maps to:** `{"type": "query", ...}` with `parameters` and `output` 112 + This emits two files: `com/example/foo.json` and `com/example/bar.json`. 113 + 114 + You can `import` other `.tsp` files to reuse definitions. Output structure is determined solely by namespaces, not by source file organization. 54 115 55 - ### Procedure (XRPC Procedure) 116 + ## Models 117 + 118 + By default, **every `model` becomes a Lexicon definition**: 56 119 57 120 ```typescript 58 - namespace com.example.createRecord { 59 - /** Create a new record */ 60 - @procedure 61 - op main(input: { 62 - @required text: string; 63 - }): { 64 - @required uri: atUri; 65 - @required cid: cid; 66 - }; 121 + import "@typelex/emitter"; 122 + 123 + namespace app.bsky.feed.defs { 124 + model PostView { /* ... */ } 125 + model ViewerState { /* ... */ } 126 + } 127 + ``` 128 + 129 + Model names convert PascalCase โ†’ camelCase. For example, `PostView` becomes `postView`: 130 + 131 + ```json 132 + { 133 + "id": "app.bsky.feed.defs", 134 + "defs": { 135 + "postView": { /* ... */ }, 136 + "viewerState": { /* ... */ } 137 + } 138 + // ... 67 139 } 68 140 ``` 69 141 70 - **Maps to:** `{"type": "procedure", ...}` with `input` and `output` 142 + Models in the same namespace can be in separate `namespace` blocks or even different files (via [`import`](https://typespec.io/docs/language-basics/imports/)). TypeSpec bundles them all into one Lexicon file per namespace. 143 + 144 + ### Namespace Conventions: `.defs` vs `Main` 71 145 72 - ### Subscription (XRPC Subscription) 146 + By convention, a namespace must either end with `.defs` or have a `Main` model. 147 + 148 + Use `.defs` for a grabbag of reusable definitions: 73 149 74 150 ```typescript 75 - namespace com.example.subscribeRecords { 76 - /** Subscribe to record updates */ 77 - @subscription 78 - op main(cursor?: integer): (Record | Delete); 151 + import "@typelex/emitter"; 152 + 153 + namespace app.bsky.feed.defs { 154 + model PostView { /* ... */ } 155 + model ViewerState { /* ... */ } 156 + } 157 + ``` 158 + 159 + For a Lexicon about one main concept, add a `Main` model instead: 79 160 80 - model Record { 81 - @required uri: atUri; 82 - @required record: com.example.record.Main; 83 - } 161 + ```typescript 162 + import "@typelex/emitter"; 84 163 85 - model Delete { 86 - @required uri: atUri; 87 - } 164 + namespace app.bsky.embed.video { 165 + model Main { /* ... */ } 166 + model Caption { /* ... */ } 88 167 } 89 168 ``` 90 169 91 - **Maps to:** `{"type": "subscription", ...}` with `message` containing union 170 + Pick one or the otherโ€”the compiler will error if you don't. 171 + 172 + ### References 92 173 93 - ### Record 174 + Models can reference other models: 94 175 95 176 ```typescript 96 - namespace com.example.post { 97 - @rec("tid") 98 - /** A post record */ 177 + import "@typelex/emitter"; 178 + 179 + namespace app.bsky.embed.video { 99 180 model Main { 100 - @required text: string; 101 - @required createdAt: datetime; 181 + captions?: Caption[]; 102 182 } 183 + model Caption { /* ... */ } 103 184 } 104 185 ``` 105 186 106 - **Maps to:** `{"type": "record", "key": "tid", "record": {...}}` 187 + This becomes a `ref` to the `caption` definition in the same file: 107 188 108 - **Record key types:** `@rec("tid")`, `@rec("any")`, `@rec("nsid")` 189 + ```json 190 + // ... 191 + "defs": { 192 + "main": { 193 + // ... 194 + "properties": { 195 + "captions": { 196 + // ... 197 + "items": { "type": "ref", "ref": "#caption" } 198 + } 199 + } 200 + }, 201 + "caption": { 202 + "type": "object", 203 + "properties": {} 204 + } 205 + // ... 206 + ``` 109 207 110 - ### Object (Plain Definition) 208 + You can also reference models from other namespaces: 111 209 112 210 ```typescript 113 - namespace com.example.defs { 114 - /** User metadata */ 115 - model Metadata { 116 - version?: integer = 1; 117 - tags?: string[]; 211 + import "@typelex/emitter"; 212 + 213 + namespace app.bsky.actor.profile { 214 + model Main { 215 + labels?: (com.atproto.label.defs.SelfLabels | unknown); 118 216 } 119 217 } 218 + 219 + namespace com.atproto.label.defs { 220 + model SelfLabels { /* ... */ } 221 + } 120 222 ``` 121 223 122 - **Maps to:** `{"type": "object", "properties": {...}}` 224 + This becomes a fully qualified reference to another Lexicon: 123 225 124 - ## Reserved Keywords 226 + ```json 227 + // ... 228 + "labels": { 229 + "type": "union", 230 + "refs": ["com.atproto.label.defs#selfLabels"] 231 + } 232 + // ... 233 + ``` 125 234 126 - Use backticks for TypeScript/TypeSpec reserved words: 235 + ([See it in the Playground](https://playground.typelex.org/?c=aW1wb3J0ICJAdHlwZWxleC9lbWl0dGVyIjsKCm5hbWVzcGFjZSBhcHAuYnNreS5hY3Rvci5wcm9maWxlIHsKICBAcmVjKCJsaXRlcmFsOnNlbGYiKQogIG1vZGVsIE1haW7FJiAgZGlzcGxheU5hbWU%2FOiBzdHJpbmc7xRpsYWJlbHM%2FOiAoY29tLmF0cHJvdG8uxRYuZGVmcy5TZWxmTMUlIHwgdW5rbm93binEPH0KfewAptY%2F5QCA5gCPy0nEFSAgLy8gLi4uxko%3D&e=%40typelex%2Femitter&options=%7B%7D&vs=%7B%7D).) 127 236 128 - ```typescript 129 - namespace app.bsky.feed.post.`record` { ... } 130 - namespace `pub`.leaflet.subscription { ... } 131 - ``` 237 + This works across files tooโ€”just remember to `import` the file with the definition. 132 238 133 - ## Inline vs Definitions 239 + ### External Stubs 134 240 135 - **By default, models become separate defs.** Use `@inline` to prevent this: 241 + If you don't have TypeSpec definitions for external Lexicons, you can stub them out using the `@external` decorator: 136 242 137 243 ```typescript 138 - // Without @inline - becomes separate def "statusEnum" 139 - union StatusEnum { 140 - "active", 141 - "inactive", 244 + import "@typelex/emitter"; 245 + 246 + namespace app.bsky.actor.profile { 247 + model Main { 248 + labels?: (com.atproto.label.defs.SelfLabels | unknown); 249 + } 142 250 } 143 251 144 - // With @inline - inlined where used 145 - @inline 146 - union StatusEnum { 147 - "active", 148 - "inactive", 252 + // Empty stub (like .d.ts in TypeScript) 253 + @external 254 + namespace com.atproto.label.defs { 255 + model SelfLabels { } 149 256 } 150 257 ``` 151 258 152 - Use `@inline` when you want the type directly embedded rather than referenced. 259 + The `@external` decorator tells the emitter to skip JSON output for that namespace. This is useful when referencing definitions from other Lexicons that you don't want to re-emit. 260 + 261 + You could collect external stubs in one file and import them: 262 + 263 + ```typescript 264 + import "@typelex/emitter"; 265 + import "../atproto-stubs.tsp"; 153 266 154 - ## Optional vs Required Fields 267 + namespace app.bsky.actor.profile { 268 + model Main { 269 + labels?: (com.atproto.label.defs.SelfLabels | unknown); 270 + } 271 + } 272 + ``` 155 273 156 - **In lexicons, optional fields are the norm.** Required fields are discouraged and need explicit `@required`: 274 + Then in `atproto-stubs.tsp`: 157 275 158 276 ```typescript 159 - model Post { 160 - text?: string; // optional (common) 161 - @required createdAt: datetime; // required (discouraged, needs decorator) 277 + import "@typelex/emitter"; 278 + 279 + @external 280 + namespace com.atproto.label.defs { 281 + model SelfLabels { } 162 282 } 163 - ``` 164 283 165 - **Maps to:** 166 - ```json 167 - { 168 - "type": "object", 169 - "required": ["createdAt"], 170 - "properties": { 171 - "text": {"type": "string"}, 172 - "createdAt": {"type": "string", "format": "datetime"} 173 - } 284 + @external 285 + namespace com.atproto.repo.defs { 286 + model StrongRef { } 287 + @token model SomeToken { } // Note: Tokens still need @token 174 288 } 289 + // ... more stubs 175 290 ``` 176 291 177 - ## Primitive Types 292 + You'll want to ensure the real JSON for external Lexicons is available before running codegen. 178 293 179 - | TypeSpec | Lexicon JSON | 180 - |----------|--------------| 181 - | `boolean` | `{"type": "boolean"}` | 182 - | `integer` | `{"type": "integer"}` | 183 - | `string` | `{"type": "string"}` | 184 - | `bytes` | `{"type": "bytes"}` | 185 - | `cidLink` | `{"type": "cid-link"}` | 186 - | `unknown` | `{"type": "unknown"}` | 294 + ### Inline Models 187 295 188 - ## Format Types 296 + By default, every `model` becomes a top-level def: 189 297 190 - Specialized string formats: 191 - 192 - | TypeSpec | Lexicon Format | 193 - |----------|----------------| 194 - | `atIdentifier` | `at-identifier` - Handle or DID | 195 - | `atUri` | `at-uri` - AT Protocol URI | 196 - | `cid` | `cid` - Content ID | 197 - | `datetime` | `datetime` - ISO 8601 datetime | 198 - | `did` | `did` - DID identifier | 199 - | `handle` | `handle` - Handle identifier | 200 - | `nsid` | `nsid` - Namespaced ID | 201 - | `tid` | `tid` - Timestamp ID | 202 - | `recordKey` | `record-key` - Record key | 203 - | `uri` | `uri` - Generic URI | 204 - | `language` | `language` - Language tag | 298 + ```typescript 299 + import "@typelex/emitter"; 205 300 206 - ## Unions 301 + namespace app.bsky.embed.video { 302 + model Main { 303 + captions?: Caption[]; 304 + } 305 + model Caption { /* ... */ } 306 + } 307 + ``` 207 308 208 - ### Open Unions (Common Pattern) 309 + This creates two defs: `main` and `caption`. 209 310 210 - **Open unions are the default and preferred in lexicons.** Add `unknown` to mark as open: 311 + Use `@inline` to expand a model inline instead: 211 312 212 313 ```typescript 213 - model Main { 214 - /** Can be any of these types or future additions */ 215 - @required item: TypeA | TypeB | TypeC | unknown; 216 - } 314 + import "@typelex/emitter"; 217 315 218 - model TypeA { 219 - @readOnly @required kind: string = "a"; 220 - @required valueA: string; 316 + namespace app.bsky.embed.video { 317 + model Main { 318 + captions?: Caption[]; 319 + } 320 + 321 + @inline 322 + model Caption { 323 + text?: string 324 + } 221 325 } 222 326 ``` 223 327 224 - **Maps to:** 328 + Now `Caption` is expanded inline: 329 + 225 330 ```json 226 - { 227 - "properties": { 228 - "item": { 229 - "type": "union", 230 - "refs": ["#typeA", "#typeB", "#typeC"] 231 - } 331 + // ... 332 + "captions": { 333 + "type": "array", 334 + "items": { 335 + "type": "object", 336 + "properties": { "text": { "type": "string" } } 232 337 } 233 338 } 339 + // ... 234 340 ``` 235 341 236 - The `unknown` makes it open but doesn't appear in refs. 342 + Note that `Caption` won't exist as a separate defโ€”the abstraction is erased in the output. 343 + 344 + ## Top-Level Lexicon Types 345 + 346 + TypeSpec uses `model` for almost everything. Decorators specify what kind of Lexicon type it becomes. 237 347 238 - ### Known Values (Open String Enum) 348 + ### Objects 239 349 240 - Suggest values but allow others: 350 + A plain `model` becomes a Lexicon object: 241 351 242 352 ```typescript 243 - model Main { 244 - /** Language - suggests common values but allows any */ 245 - lang?: "en" | "es" | "fr" | string; 353 + import "@typelex/emitter"; 354 + 355 + namespace com.example.post { 356 + model Main { /* ... */ } 246 357 } 247 358 ``` 248 359 249 - **Maps to:** 360 + Output: 361 + 250 362 ```json 251 - { 252 - "properties": { 253 - "lang": { 254 - "type": "string", 255 - "knownValues": ["en", "es", "fr"] 256 - } 257 - } 363 + // ... 364 + "main": { 365 + "type": "object", 366 + "properties": { /* ... */ } 258 367 } 368 + // ... 259 369 ``` 260 370 261 - ### Closed Unions (Discouraged) 371 + ### Records 262 372 263 - **โš ๏ธ Closed unions are discouraged in lexicons** as they prevent future additions. Use only when absolutely necessary: 373 + Use `@rec` to make a model a Lexicon record: 264 374 265 375 ```typescript 266 - @closed 267 - @inline 268 - union Action { 269 - Create, 270 - Update, 271 - Delete, 272 - } 376 + import "@typelex/emitter"; 273 377 274 - model Main { 275 - @required action: Action; 378 + namespace com.example.post { 379 + @rec("tid") 380 + model Main { /* ... */ } 276 381 } 277 382 ``` 278 383 279 - **Maps to:** 384 + Output: 385 + 280 386 ```json 281 - { 282 - "properties": { 283 - "action": { 284 - "type": "union", 285 - "refs": ["#create", "#update", "#delete"], 286 - "closed": true 287 - } 288 - } 387 + // ... 388 + "main": { 389 + "type": "record", 390 + "key": "tid", 391 + "record": { "type": "object", "properties": { /* ... */ } } 289 392 } 393 + // ... 290 394 ``` 291 395 292 - ### Closed Enums (Discouraged) 396 + You can pass any [Record Key Type](https://atproto.com/specs/record-key): `@rec("tid")`, `@rec("nsid")`, `@rec("literal:self")`, etc. 397 + 398 + (It's `@rec` not `@record` because "record" is reserved in TypeSpec.) 399 + 400 + ### Queries 293 401 294 - **โš ๏ธ Closed enums are also discouraged.** Use `@closed @inline union` for fixed value sets: 402 + In TypeSpec, use [`op`](https://typespec.io/docs/language-basics/operations/) for functions. Mark with `@query` for queries: 295 403 296 404 ```typescript 297 - @closed 298 - @inline 299 - union Status { 300 - "active", 301 - "inactive", 302 - "pending", 405 + import "@typelex/emitter"; 406 + 407 + namespace com.atproto.repo.getRecord { 408 + @query 409 + op main( 410 + @required repo: atIdentifier, 411 + @required collection: nsid, 412 + @required rkey: recordKey, 413 + cid?: cid 414 + ): { 415 + @required uri: atUri; 416 + cid?: cid; 417 + }; 303 418 } 304 419 ``` 305 420 306 - **Maps to:** 421 + Arguments become `parameters`, return type becomes `output`: 422 + 307 423 ```json 308 - { 309 - "type": "string", 310 - "enum": ["active", "inactive", "pending"] 424 + // ... 425 + "main": { 426 + "type": "query", 427 + "parameters": { 428 + "type": "params", 429 + "properties": { 430 + "repo": { /* ... */ }, 431 + "collection": { /* ... */ }, 432 + // ... 433 + }, 434 + "required": ["repo", "collection", "rkey"] 435 + }, 436 + "output": { 437 + "encoding": "application/json", 438 + "schema": { 439 + "type": "object", 440 + "properties": { 441 + "uri": { /* ... */ }, 442 + "cid": { /* ... */ } 443 + }, 444 + "required": ["uri"] 445 + } 446 + } 311 447 } 448 + // ... 312 449 ``` 313 450 314 - Integer enums work the same way: 451 + `encoding` defaults to `"application/json"`. Override with `@encoding("foo/bar")` on the `op`. 452 + 453 + Declare errors with `@errors`: 315 454 316 455 ```typescript 317 - @closed 318 - @inline 319 - union Fibonacci { 320 - 1, 2, 3, 5, 8, 456 + import "@typelex/emitter"; 457 + 458 + namespace com.atproto.repo.getRecord { 459 + @query 460 + @errors(FooError, BarError) 461 + op main(/* ... */): { /* ... */ }; 462 + 463 + model FooError {} 464 + model BarError {} 321 465 } 322 466 ``` 323 467 324 - ## Arrays 468 + You can extract the `{ /* ... */ }` output type to a separate `@inline` model if you prefer. 469 + 470 + ### Procedures 325 471 326 - Use `[]` suffix: 472 + Use `@procedure` for procedures. The first argument must be called `input`: 327 473 328 474 ```typescript 329 - model Main { 330 - /** Array of strings */ 331 - stringArray?: string[]; 475 + import "@typelex/emitter"; 332 476 333 - /** Array with size constraints */ 334 - @minItems(1) 335 - @maxItems(10) 336 - limitedArray?: integer[]; 477 + namespace com.example.createRecord { 478 + @procedure 479 + op main(input: { 480 + @required text: string; 481 + }): { 482 + @required uri: atUri; 483 + @required cid: cid; 484 + }; 485 + } 486 + ``` 337 487 338 - /** Array of references */ 339 - items?: Item[]; 488 + Output: 340 489 341 - /** Array of union types */ 342 - mixed?: (TypeA | TypeB | unknown)[]; 490 + ```json 491 + // ... 492 + "main": { 493 + "type": "procedure", 494 + "input": { 495 + "encoding": "application/json", 496 + "schema": { 497 + "type": "object", 498 + "properties": { "text": { "type": "string" } }, 499 + "required": ["text"] 500 + } 501 + }, 502 + "output": { 503 + "encoding": "application/json", 504 + "schema": { 505 + "type": "object", 506 + "properties": { 507 + "uri": { /* ... */ }, 508 + "cid": { /* ... */ } 509 + }, 510 + "required": ["uri", "cid"] 511 + } 512 + } 343 513 } 514 + // ... 344 515 ``` 345 516 346 - **Maps to:** `{"type": "array", "items": {...}}` 517 + Procedures can also receive parameters (rarely used): `op main(input: {}, parameters: {})`. 518 + 519 + Use `: void` for no output, or `: never` with `@encoding()` on the `op` for output with encoding but no schema. 347 520 348 - **Note:** `@minItems`/`@maxItems` in TypeSpec map to `minLength`/`maxLength` in JSON. 521 + ### Subscriptions 349 522 350 - ## Blobs 523 + Use `@subscription` for subscriptions: 351 524 352 525 ```typescript 353 - model Main { 354 - /** Basic blob */ 355 - file?: Blob; 526 + import "@typelex/emitter"; 356 527 357 - /** Image up to 5MB */ 358 - image?: Blob<#["image/*"], 5000000>; 528 + namespace com.atproto.sync.subscribeRepos { 529 + @subscription 530 + @errors(FutureCursor, ConsumerTooSlow) 531 + op main(cursor?: integer): Commit | Sync | unknown; 359 532 360 - /** Specific types up to 2MB */ 361 - photo?: Blob<#["image/png", "image/jpeg"], 2000000>; 533 + model Commit { /* ... */ } 534 + model Sync { /* ... */ } 535 + model FutureCursor {} 536 + model ConsumerTooSlow {} 362 537 } 363 538 ``` 364 539 365 - **Maps to:** 540 + Output: 541 + 366 542 ```json 367 - { 368 - "file": {"type": "blob"}, 369 - "image": { 370 - "type": "blob", 371 - "accept": ["image/*"], 372 - "maxSize": 5000000 373 - } 543 + // ... 544 + "main": { 545 + "type": "subscription", 546 + "parameters": { 547 + "type": "params", 548 + "properties": { "cursor": { /* ... */ } } 549 + }, 550 + "message": { 551 + "schema": { 552 + "type": "union", 553 + "refs": ["#commit", "#sync"] 554 + } 555 + }, 556 + "errors": [{ "name": "FutureCursor" }, { "name": "ConsumerTooSlow" }] 374 557 } 558 + // ... 375 559 ``` 376 560 377 - ## References 561 + ### Tokens 378 562 379 - ### Local References 563 + Use `@token` for empty token models: 380 564 381 - Same namespace, uses `#`: 565 + ```typescript 566 + namespace com.example.moderation.defs { 567 + @token 568 + model ReasonSpam {} 382 569 383 - ```typescript 384 - model Main { 385 - metadata?: Metadata; 570 + @token 571 + model ReasonViolation {} 572 + 573 + model Report { 574 + @required reason: (ReasonSpam | ReasonViolation | unknown); 575 + } 386 576 } 577 + ``` 387 578 388 - model Metadata { 389 - @required key: string; 579 + Output: 580 + 581 + ```json 582 + // ... 583 + "reasonSpam": { "type": "token" }, 584 + "reasonViolation": { "type": "token" }, 585 + "report": { 586 + "type": "object", 587 + "properties": { 588 + "reason": { 589 + "type": "union", 590 + "refs": ["#reasonSpam", "#reasonViolation"] 591 + } 592 + }, 593 + "required": ["reason"] 390 594 } 595 + // ... 391 596 ``` 392 597 393 - **Maps to:** `{"type": "ref", "ref": "#metadata"}` 598 + ## Data Types 599 + 600 + All [Lexicon data types](https://atproto.com/specs/lexicon#overview-of-types) are supported. 601 + 602 + ### Primitive Types 603 + 604 + | TypeSpec | Lexicon JSON | 605 + |----------|--------------| 606 + | `boolean` | `{"type": "boolean"}` | 607 + | `integer` | `{"type": "integer"}` | 608 + | `string` | `{"type": "string"}` | 609 + | `bytes` | `{"type": "bytes"}` | 610 + | `cidLink` | `{"type": "cid-link"}` | 611 + | `unknown` | `{"type": "unknown"}` | 612 + 613 + ### Format Types 614 + 615 + Specialized string formats: 616 + 617 + | TypeSpec | Lexicon Format | 618 + |----------|----------------| 619 + | `atIdentifier` | `at-identifier` - Handle or DID | 620 + | `atUri` | `at-uri` - AT Protocol URI | 621 + | `cid` | `cid` - Content ID | 622 + | `datetime` | `datetime` - ISO 8601 datetime | 623 + | `did` | `did` - DID identifier | 624 + | `handle` | `handle` - Handle identifier | 625 + | `nsid` | `nsid` - Namespaced ID | 626 + | `tid` | `tid` - Timestamp ID | 627 + | `recordKey` | `record-key` - Record key | 628 + | `uri` | `uri` - Generic URI | 629 + | `language` | `language` - Language tag | 394 630 395 - ### External References 631 + ### Arrays 396 632 397 - Different namespace to specific def: 633 + Use `[]` suffix: 398 634 399 635 ```typescript 400 - model Main { 401 - externalRef?: com.example.defs.Metadata; 402 - } 403 - ``` 636 + import "@typelex/emitter"; 404 637 405 - **Maps to:** `{"type": "ref", "ref": "com.example.defs#metadata"}` 638 + namespace com.example.arrays { 639 + model Main { 640 + stringArray?: string[]; 406 641 407 - Different namespace to main def (no fragment): 642 + @minItems(1) 643 + @maxItems(10) 644 + limitedArray?: integer[]; 408 645 409 - ```typescript 410 - model Main { 411 - mainRef?: com.example.post.Main; 646 + items?: Item[]; 647 + mixed?: (TypeA | TypeB | unknown)[]; 648 + } 649 + // ... 412 650 } 413 651 ``` 414 652 415 - **Maps to:** `{"type": "ref", "ref": "com.example.post"}` 653 + Output: `{ "type": "array", "items": {...} }`. 416 654 417 - ## Tokens 655 + Note: `@minItems`/`@maxItems` map to `minLength`/`maxLength` in JSON. 418 656 419 - Empty models marked with `@token`: 657 + ### Blobs 420 658 421 659 ```typescript 422 - /** Indicates spam content */ 423 - @token 424 - model ReasonSpam {} 660 + import "@typelex/emitter"; 425 661 426 - /** Indicates policy violation */ 427 - @token 428 - model ReasonViolation {} 429 - 430 - model Report { 431 - @required reason: (ReasonSpam | ReasonViolation | unknown); 662 + namespace com.example.blobs { 663 + model Main { 664 + file?: Blob; 665 + image?: Blob<#["image/*"], 5000000>; 666 + photo?: Blob<#["image/png", "image/jpeg"], 2000000>; 667 + } 432 668 } 433 669 ``` 434 670 435 - **Maps to:** 671 + Output: 672 + 436 673 ```json 437 - { 438 - "report": { 439 - "properties": { 440 - "reason": { 441 - "type": "union", 442 - "refs": ["#reasonSpam", "#reasonViolation"] 443 - } 444 - } 445 - }, 446 - "reasonSpam": { 447 - "type": "token", 448 - "description": "Indicates spam content" 449 - } 674 + // ... 675 + "image": { 676 + "type": "blob", 677 + "accept": ["image/*"], 678 + "maxSize": 5000000 450 679 } 680 + // ... 451 681 ``` 452 682 453 - ## Operation Details 683 + ## Required and Optional Fields 454 684 455 - ### Query Parameters 685 + In Lexicon, fields are optional by default. Use `?:`: 456 686 457 687 ```typescript 458 - @query 459 - op main( 460 - @required search: string, 461 - limit?: integer = 50, 462 - tags?: string[] 463 - ): { ... }; 688 + import "@typelex/emitter"; 689 + 690 + namespace tools.ozone.moderation.defs { 691 + model SubjectStatusView { 692 + subjectRepoHandle?: string; 693 + } 694 + } 464 695 ``` 465 696 466 - Parameters can be inline with decorators before each. 697 + **Think thrice before adding required fields**โ€”you can't make them optional later. 467 698 468 - ### Procedure with Input and Parameters 699 + This is why `@required` is explicit: 469 700 470 701 ```typescript 471 - @procedure 472 - op main( 473 - input: { 474 - @required data: string; 475 - }, 476 - parameters: { 477 - @required repo: atIdentifier; 478 - validate?: boolean = true; 702 + import "@typelex/emitter"; 703 + 704 + namespace tools.ozone.moderation.defs { 705 + model SubjectStatusView { 706 + subjectRepoHandle?: string; 707 + @required createdAt: datetime; 479 708 } 480 - ): { ... }; 709 + } 481 710 ``` 482 711 483 - Use `input:` for body, `parameters:` for query params. 712 + Output: 484 713 485 - ### No Output 486 - 487 - ```typescript 488 - @procedure 489 - op main(input: { 490 - @required uri: atUri; 491 - }): void; 714 + ```json 715 + // ... 716 + "required": ["createdAt"] 717 + // ... 492 718 ``` 493 719 494 - Use `: void` for procedures with no output. 720 + ## Unions 721 + 722 + ### Open Unions (Recommended) 495 723 496 - ### Output Without Schema 724 + Unions default to being *open*โ€”allowing you to add more options later. Write `| unknown`: 497 725 498 726 ```typescript 499 - @query 500 - @encoding("application/json") 501 - op main(id?: string): never; 727 + import "@typelex/emitter"; 728 + 729 + namespace app.bsky.feed.post { 730 + model Main { 731 + embed?: Images | Video | unknown; 732 + } 733 + 734 + model Images { /* ... */ } 735 + model Video { /* ... */ } 736 + } 502 737 ``` 503 738 504 - Use `: never` with `@encoding()` for output with encoding but no schema. 739 + Output: 505 740 506 - ### Errors 741 + ```json 742 + // ... 743 + "embed": { 744 + "type": "union", 745 + "refs": ["#images", "#video"] 746 + } 747 + // ... 748 + ``` 749 + 750 + You can also use the `union` syntax to give it a name: 507 751 508 752 ```typescript 509 - /** The provided text is invalid */ 510 - model InvalidText {} 753 + import "@typelex/emitter"; 754 + 755 + namespace app.bsky.feed.post { 756 + model Main { 757 + embed?: EmbedType; 758 + } 511 759 512 - /** User not found */ 513 - model NotFound {} 760 + @inline union EmbedType { Images, Video, unknown } 514 761 515 - @procedure 516 - @errors(InvalidText, NotFound) 517 - op main(...): ...; 762 + model Images { /* ... */ } 763 + model Video { /* ... */ } 764 + } 518 765 ``` 519 766 520 - Empty models with descriptions become error definitions. 767 + The `@inline` prevents it from becoming a separate def in the output. 521 768 522 - ## Constraints 769 + ### Known Values (Open Enums) 523 770 524 - ### String Constraints 771 + Suggest common values but allow others with `| string`: 525 772 526 773 ```typescript 527 - model Main { 528 - /** Byte length constraints */ 529 - @minLength(1) 530 - @maxLength(100) 531 - text?: string; 774 + import "@typelex/emitter"; 532 775 533 - /** Grapheme cluster length constraints */ 534 - @minGraphemes(1) 535 - @maxGraphemes(50) 536 - displayName?: string; 776 + namespace com.example { 777 + model Main { 778 + lang?: "en" | "es" | "fr" | string; 779 + } 537 780 } 538 781 ``` 539 782 540 - **Maps to:** `minLength`/`maxLength`, `minGraphemes`/`maxGraphemes` 783 + The `union` syntax works here too: 784 + 785 + ```typescript 786 + import "@typelex/emitter"; 541 787 542 - ### Integer Constraints 788 + namespace com.example { 789 + model Main { 790 + lang?: Languages; 791 + } 543 792 544 - ```typescript 545 - model Main { 546 - @minValue(1) 547 - @maxValue(100) 548 - score?: integer; 793 + @inline union Languages { "en", "es", "fr", string } 549 794 } 550 795 ``` 551 796 552 - **Maps to:** `minimum`/`maximum` 797 + You can remove `@inline` to make it a reusable `def` accessible from other Lexicons. 798 + 799 + ### Closed Unions and Enums (Discouraged) 800 + 801 + **Heavily discouraged** in Lexicon. 553 802 554 - ### Bytes Constraints 803 + Marking a `union` as `@closed` lets you remove `unknown` from the list of options: 555 804 556 805 ```typescript 557 - model Main { 558 - @minBytes(1) 559 - @maxBytes(1024) 560 - data?: bytes; 806 + import "@typelex/emitter"; 807 + 808 + namespace com.atproto.repo.applyWrites { 809 + model Main { 810 + @required writes: WriteAction[]; 811 + } 812 + 813 + @closed // Discouraged! 814 + @inline 815 + union WriteAction { Create, Update, Delete } 816 + 817 + model Create { /* ... */ } 818 + model Update { /* ... */ } 819 + model Delete { /* ... */ } 561 820 } 562 821 ``` 563 822 564 - **Maps to:** `minLength`/`maxLength` 823 + Output: 565 824 566 - **Note:** Use `@minBytes`/`@maxBytes` in TypeSpec, but they map to `minLength`/`maxLength` in JSON. 825 + ```json 826 + // ... 827 + "writes": { 828 + "type": "array", 829 + "items": { 830 + "type": "union", 831 + "refs": ["#create", "#update", "#delete"], 832 + "closed": true 833 + } 834 + } 835 + // ... 836 + ``` 567 837 568 - ### Array Constraints 838 + With strings or numbers, this becomes a closed `enum`: 569 839 570 840 ```typescript 571 - model Main { 572 - @minItems(1) 573 - @maxItems(10) 574 - items?: string[]; 841 + import "@typelex/emitter"; 842 + 843 + namespace com.atproto.repo.applyWrites { 844 + model Main { 845 + @required action: WriteAction; 846 + } 847 + 848 + @closed // Discouraged! 849 + @inline 850 + union WriteAction { "create", "update", "delete" } 575 851 } 576 852 ``` 577 853 578 - **Maps to:** `minLength`/`maxLength` 854 + Output: 579 855 580 - **Note:** Use `@minItems`/`@maxItems` in TypeSpec, but they map to `minLength`/`maxLength` in JSON. 856 + ```json 857 + // ... 858 + "type": "string", 859 + "enum": ["create", "update", "delete"] 860 + // ... 861 + ``` 581 862 582 - ## Default and Constant Values 863 + Avoid closed unions/enums when possible. 583 864 584 - ### Defaults 865 + ## Constraints 866 + 867 + ### Strings 585 868 586 869 ```typescript 587 - model Main { 588 - version?: integer = 1; 589 - lang?: string = "en"; 870 + import "@typelex/emitter"; 871 + 872 + namespace com.example { 873 + model Main { 874 + @minLength(1) 875 + @maxLength(100) 876 + text?: string; 877 + 878 + @minGraphemes(1) 879 + @maxGraphemes(50) 880 + displayName?: string; 881 + } 590 882 } 591 883 ``` 592 884 593 - **Maps to:** `{"default": 1}`, `{"default": "en"}` 885 + Maps to: `minLength`/`maxLength`, `minGraphemes`/`maxGraphemes` 594 886 595 - ### Constants 887 + ### Integers 596 888 597 - Use `@readOnly` with default value: 889 + ```typescript 890 + import "@typelex/emitter"; 598 891 599 - ```typescript 600 - model Main { 601 - @readOnly status?: string = "active"; 892 + namespace com.example { 893 + model Main { 894 + @minValue(1) 895 + @maxValue(100) 896 + score?: integer; 897 + } 602 898 } 603 899 ``` 604 900 605 - **Maps to:** `{"const": "active"}` 901 + Maps to: `minimum`/`maximum` 606 902 607 - ## Nullable Fields 903 + ### Bytes 608 904 609 - Use `| null` for nullable fields: 905 + ```typescript 906 + import "@typelex/emitter"; 610 907 611 - ```typescript 612 - model Main { 613 - @required createdAt: datetime; 614 - updatedAt?: datetime | null; // can be omitted or null 615 - deletedAt?: datetime; // can only be omitted 908 + namespace com.example { 909 + model Main { 910 + @minBytes(1) 911 + @maxBytes(1024) 912 + data?: bytes; 913 + } 616 914 } 617 915 ``` 618 916 619 - **Maps to:** 620 - ```json 621 - { 622 - "required": ["createdAt"], 623 - "nullable": ["updatedAt"], 624 - "properties": { ... } 917 + Maps to: `minLength`/`maxLength` 918 + 919 + ### Arrays 920 + 921 + ```typescript 922 + import "@typelex/emitter"; 923 + 924 + namespace com.example { 925 + model Main { 926 + @minItems(1) 927 + @maxItems(10) 928 + items?: string[]; 929 + } 625 930 } 626 931 ``` 627 932 628 - ## Common Patterns 933 + Maps to: `minLength`/`maxLength` 629 934 630 - ### Discriminated Unions 935 + ## Defaults and Constants 631 936 632 - Use `@readOnly` with const for discriminator: 937 + ### Defaults 633 938 634 939 ```typescript 635 - model Create { 636 - @readOnly @required type: string = "create"; 637 - @required data: string; 638 - } 940 + import "@typelex/emitter"; 639 941 640 - model Update { 641 - @readOnly @required type: string = "update"; 642 - @required id: string; 942 + namespace com.example { 943 + model Main { 944 + version?: integer = 1; 945 + lang?: string = "en"; 946 + } 643 947 } 644 948 ``` 645 949 646 - ### Nested Unions 950 + Maps to: `{"default": 1}`, `{"default": "en"}` 951 + 952 + ### Constants 953 + 954 + Use `@readOnly` with a default: 647 955 648 956 ```typescript 649 - model Container { 650 - @required id: string; 651 - @required payload: (PayloadA | PayloadB | unknown); 957 + import "@typelex/emitter"; 958 + 959 + namespace com.example { 960 + model Main { 961 + @readOnly status?: string = "active"; 962 + } 652 963 } 653 964 ``` 654 965 655 - Unions can be nested in objects and arrays. 966 + Maps to: `{"const": "active"}` 656 967 657 - ## Naming Conventions 968 + ## Nullable Fields 658 969 659 - Model names convert from PascalCase to camelCase in defs: 970 + Use `| null` for nullable fields: 660 971 661 972 ```typescript 662 - model StatusEnum { ... } // becomes "statusEnum" 663 - model UserMetadata { ... } // becomes "userMetadata" 664 - model Main { ... } // becomes "main" 973 + import "@typelex/emitter"; 974 + 975 + namespace com.example { 976 + model Main { 977 + @required createdAt: datetime; 978 + updatedAt?: datetime | null; // can be omitted or null 979 + deletedAt?: datetime; // can only be omitted 980 + } 981 + } 665 982 ``` 666 983 667 - ## Decorator Style 984 + Output: 668 985 669 - - Single `@required` goes on same line: `@required text: string` 670 - - Multiple decorators go on separate lines with blank line after: 671 - ```typescript 672 - @minLength(1) 673 - @maxLength(100) 674 - text?: string; 675 - ``` 986 + ```json 987 + // ... 988 + "required": ["createdAt"], 989 + "nullable": ["updatedAt"] 990 + // ... 991 + ```
+6 -4
README.md
··· 2 2 3 3 An experimental [TypeSpec](https://typespec.io/) syntax for [Lexicon](https://atproto.com/specs/lexicon). 4 4 5 - See https://typelex.pages.dev/ 6 - 7 - **This is a hobby project but maybe somebody will find it helpful.** 5 + See https://typelex.org/ 8 6 9 7 Design is not final and might change. Ideas welcome. 10 8 ··· 14 12 15 13 ## Documentation 16 14 17 - No "proper" docs yet. See [DOCS.md](./DOCS.md) for now. 15 + See [DOCS.md](./DOCS.md). 16 + 17 + ## Alternatives 18 + 19 + * [MFL](https://mlf.lol/) 18 20 19 21 ## License 20 22
+2 -1
package.json
··· 10 10 "example": "pnpm --filter @typelex/example build", 11 11 "playground": "pnpm --filter @typelex/playground dev", 12 12 "validate": "pnpm build && pnpm run validate-lexicons && pnpm test", 13 - "validate-lexicons": "node scripts/validate-lexicons.js" 13 + "validate-lexicons": "node scripts/validate-lexicons.js", 14 + "cli": "pnpm --filter @typelex/cli" 14 15 }, 15 16 "repository": { 16 17 "type": "git",
+3
packages/cli/.gitignore
··· 1 + dist 2 + node_modules 3 + *.log
+66
packages/cli/README.md
··· 1 + # @typelex/cli 2 + 3 + Experimental CLI for typelex 4 + 5 + ## Installation 6 + 7 + ```bash 8 + pnpm add -D @typelex/cli @typelex/emitter 9 + ``` 10 + 11 + ## Usage 12 + 13 + ```bash 14 + typelex compile xyz.statusphere.* 15 + ``` 16 + 17 + This command: 18 + 1. Scans `lexicons/` for all external lexicons (not matching `xyz.statusphere`) 19 + 2. Generates `typelex/externals.tsp` with `@external` stubs 20 + 3. Compiles `typelex/main.tsp` to `lexicons/` (or custom output via `--out`) 21 + 22 + Fixed paths: 23 + - Entry point: `typelex/main.tsp` 24 + - Externals: `typelex/externals.tsp` 25 + 26 + ## Example 27 + 28 + ```typescript 29 + // typelex/main.tsp 30 + import "@typelex/emitter"; 31 + import "./externals.tsp"; 32 + 33 + namespace xyz.statusphere.defs { 34 + model StatusView { 35 + @required uri: atUri; 36 + @required status: string; 37 + @required profile: app.bsky.actor.defs.ProfileView; 38 + } 39 + } 40 + ``` 41 + 42 + ```bash 43 + typelex compile 'xyz.statusphere.*' 44 + ``` 45 + 46 + The CLI scans `lexicons/` for external types and auto-generates `typelex/externals.tsp` with stubs 47 + 48 + ### Integration 49 + 50 + ```json 51 + { 52 + "scripts": { 53 + "build:lexicons": "typelex compile 'xyz.statusphere.*'", 54 + "build:codegen": "lex gen-server --yes ./src lexicons/xyz/statusphere/*.json" 55 + } 56 + } 57 + ``` 58 + 59 + ## Options 60 + 61 + - `--out <directory>` - Output directory for generated Lexicon files (default: `./lexicons`) 62 + - `--watch` - Watch mode for continuous compilation 63 + 64 + ## License 65 + 66 + MIT
+40
packages/cli/package.json
··· 1 + { 2 + "name": "@typelex/cli", 3 + "version": "0.2.0", 4 + "description": "CLI for typelex - TypeSpec-based IDL for ATProto Lexicons", 5 + "main": "dist/index.js", 6 + "type": "module", 7 + "bin": { 8 + "typelex": "dist/cli.js" 9 + }, 10 + "files": [ 11 + "dist", 12 + "src" 13 + ], 14 + "scripts": { 15 + "build": "tsc", 16 + "clean": "rm -rf dist", 17 + "watch": "tsc --watch", 18 + "prepublishOnly": "npm run build" 19 + }, 20 + "keywords": [ 21 + "typespec", 22 + "atproto", 23 + "lexicon", 24 + "cli" 25 + ], 26 + "author": "Dan Abramov <dan.abramov@gmail.com>", 27 + "license": "MIT", 28 + "dependencies": { 29 + "@typespec/compiler": "^1.4.0", 30 + "yargs": "^18.0.0" 31 + }, 32 + "devDependencies": { 33 + "@types/node": "^20.0.0", 34 + "@types/yargs": "^17.0.33", 35 + "typescript": "^5.0.0" 36 + }, 37 + "peerDependencies": { 38 + "@typelex/emitter": "^0.2.0" 39 + } 40 + }
+64
packages/cli/src/cli.ts
··· 1 + #!/usr/bin/env node 2 + import yargs from "yargs"; 3 + import { hideBin } from "yargs/helpers"; 4 + import { compileCommand } from "./commands/compile.js"; 5 + 6 + async function main() { 7 + await yargs(hideBin(process.argv)) 8 + .scriptName("typelex") 9 + .usage("$0 compile <namespace>") 10 + .command( 11 + "compile <namespace>", 12 + "Compile TypeSpec files to Lexicon JSON", 13 + (yargs) => { 14 + return yargs 15 + .positional("namespace", { 16 + describe: "Primary namespace pattern (e.g., app.bsky.*)", 17 + type: "string", 18 + demandOption: true, 19 + }) 20 + .option("out", { 21 + describe: "Output directory for generated Lexicon files (relative to cwd)", 22 + type: "string", 23 + default: "./lexicons", 24 + }); 25 + }, 26 + async (argv) => { 27 + const options: Record<string, unknown> = {}; 28 + if (argv.watch) { 29 + options.watch = true; 30 + } 31 + if (argv.out) { 32 + options.out = argv.out; 33 + } 34 + await compileCommand(argv.namespace, options); 35 + } 36 + ) 37 + .option("watch", { 38 + describe: "Watch mode", 39 + type: "boolean", 40 + default: false, 41 + }) 42 + .demandCommand(1, "You must specify a command") 43 + .help() 44 + .version() 45 + .fail((msg, err) => { 46 + if (err) { 47 + console.error(err); 48 + } else { 49 + console.error(msg); 50 + } 51 + process.exit(1); 52 + }).argv; 53 + } 54 + 55 + process.on("unhandledRejection", (error: unknown) => { 56 + console.error("Unhandled promise rejection!"); 57 + console.error(error); 58 + process.exit(1); 59 + }); 60 + 61 + main().catch((error) => { 62 + console.error(error); 63 + process.exit(1); 64 + });
+68
packages/cli/src/commands/compile.ts
··· 1 + import { resolve } from "path"; 2 + import { spawn } from "child_process"; 3 + import { generateExternalsFile } from "../utils/externals-generator.js"; 4 + import { ensureMainImports } from "../utils/ensure-imports.js"; 5 + 6 + /** 7 + * Compile TypeSpec files to Lexicon JSON 8 + * 9 + * @param namespace - Primary namespace pattern (e.g., "app.bsky.*") 10 + * @param options - Additional compiler options 11 + */ 12 + export async function compileCommand( 13 + namespace: string, 14 + options: Record<string, unknown> = {} 15 + ): Promise<void> { 16 + const cwd = process.cwd(); 17 + const outDir = (options.out as string) || "./lexicons"; 18 + 19 + // Validate that output directory ends with 'lexicons' 20 + const normalizedPath = outDir.replace(/\\/g, '/').replace(/\/+$/, ''); 21 + if (!normalizedPath.endsWith('/lexicons') && normalizedPath !== 'lexicons' && normalizedPath !== './lexicons') { 22 + console.error(`Error: Output directory must end with 'lexicons'`); 23 + console.error(`Got: ${outDir}`); 24 + console.error(`Valid examples: ./lexicons, ../../lexicons, /path/to/lexicons`); 25 + process.exit(1); 26 + } 27 + 28 + // Generate externals first (scans the output directory for external lexicons) 29 + await generateExternalsFile(namespace, cwd, outDir); 30 + 31 + // Ensure required imports are present in main.tsp 32 + await ensureMainImports(cwd); 33 + 34 + // Compile TypeSpec using the TypeSpec CLI 35 + const entrypoint = resolve(cwd, "typelex/main.tsp"); 36 + const args = [ 37 + "compile", 38 + entrypoint, 39 + "--emit", 40 + "@typelex/emitter", 41 + "--option", 42 + `@typelex/emitter.emitter-output-dir={project-root}/${outDir}`, 43 + ]; 44 + 45 + if (options.watch) { 46 + args.push("--watch"); 47 + } 48 + 49 + return new Promise((resolve, reject) => { 50 + const tsp = spawn("tsp", args, { 51 + cwd, 52 + stdio: "inherit", 53 + }); 54 + 55 + tsp.on("close", (code) => { 56 + if (code === 0) { 57 + resolve(); 58 + } else { 59 + process.exit(code ?? 1); 60 + } 61 + }); 62 + 63 + tsp.on("error", (err) => { 64 + console.error("Failed to start TypeSpec compiler:", err); 65 + reject(err); 66 + }); 67 + }); 68 + }
+1
packages/cli/src/index.ts
··· 1 + export { compileCommand } from "./commands/compile.js";
+38
packages/cli/src/utils/ensure-imports.ts
··· 1 + import { readFile } from "fs/promises"; 2 + import { resolve } from "path"; 3 + 4 + const REQUIRED_FIRST_LINE = 'import "@typelex/emitter";'; 5 + const REQUIRED_SECOND_LINE = 'import "./externals.tsp";'; 6 + 7 + /** 8 + * Validates that main.tsp starts with the required imports. 9 + * Fails the build if the first two lines are not exactly as expected. 10 + * 11 + * @param cwd - Current working directory 12 + */ 13 + export async function ensureMainImports(cwd: string): Promise<void> { 14 + const mainPath = resolve(cwd, "typelex/main.tsp"); 15 + 16 + try { 17 + const content = await readFile(mainPath, "utf-8"); 18 + const lines = content.split("\n"); 19 + 20 + if (lines[0]?.trim() !== REQUIRED_FIRST_LINE) { 21 + console.error(`Error: main.tsp must start with: ${REQUIRED_FIRST_LINE}`); 22 + console.error(`Found: ${lines[0] || "(empty line)"}`); 23 + process.exit(1); 24 + } 25 + 26 + if (lines[1]?.trim() !== REQUIRED_SECOND_LINE) { 27 + console.error(`Error: Line 2 of main.tsp must be: ${REQUIRED_SECOND_LINE}`); 28 + console.error(`Found: ${lines[1] || "(empty line)"}`); 29 + process.exit(1); 30 + } 31 + } catch (err) { 32 + if ((err as NodeJS.ErrnoException).code === "ENOENT") { 33 + console.error("Error: typelex/main.tsp not found"); 34 + process.exit(1); 35 + } 36 + throw err; 37 + } 38 + }
+105
packages/cli/src/utils/externals-generator.ts
··· 1 + import { resolve } from "path"; 2 + import { writeFile, mkdir } from "fs/promises"; 3 + import { findExternalLexicons, LexiconDoc, isTokenDef, isModelDef } from "./lexicon.js"; 4 + 5 + /** 6 + * Convert camelCase to PascalCase 7 + */ 8 + function toPascalCase(str: string): string { 9 + return str.charAt(0).toUpperCase() + str.slice(1); 10 + } 11 + 12 + /** 13 + * Extract namespace prefix from pattern (e.g., "app.bsky.*" -> "app.bsky") 14 + */ 15 + function getNamespacePrefix(pattern: string): string { 16 + if (!pattern.endsWith(".*")) { 17 + throw new Error(`Namespace pattern must end with .*: ${pattern}`); 18 + } 19 + return pattern.slice(0, -2); 20 + } 21 + 22 + /** 23 + * Generate TypeSpec external definitions from lexicon documents 24 + */ 25 + function generateExternalsCode(lexicons: Map<string, LexiconDoc>): string { 26 + const lines: string[] = []; 27 + 28 + lines.push('import "@typelex/emitter";'); 29 + lines.push(""); 30 + lines.push("// Generated by typelex"); 31 + lines.push("// This file is auto-generated. Do not edit manually."); 32 + lines.push(""); 33 + 34 + // Sort namespaces for consistent output 35 + const sortedNamespaces = Array.from(lexicons.entries()).sort(([a], [b]) => 36 + a.localeCompare(b) 37 + ); 38 + 39 + for (const [nsid, lexicon] of sortedNamespaces) { 40 + lines.push("@external"); 41 + // Escape reserved keywords in namespace (like 'record') 42 + const escapedNsid = nsid.replace(/\b(record|union|enum|interface|namespace|model|op|import|using|extends|is|scalar|alias|if|else|return|void|never|unknown|any|true|false|null)\b/g, '`$1`'); 43 + lines.push(`namespace ${escapedNsid} {`); 44 + 45 + // Sort definitions for consistent output 46 + const sortedDefs = Object.entries(lexicon.defs).sort(([a], [b]) => 47 + a.localeCompare(b) 48 + ); 49 + 50 + for (const [defName, def] of sortedDefs) { 51 + if (!isModelDef(def)) { 52 + continue; 53 + } 54 + 55 + const modelName = toPascalCase(defName); 56 + const isToken = isTokenDef(def); 57 + 58 + if (isToken) { 59 + lines.push(` @token model ${modelName} { }`); 60 + } else { 61 + lines.push(` model ${modelName} { }`); 62 + } 63 + } 64 + 65 + lines.push("}"); 66 + lines.push(""); 67 + } 68 + 69 + return lines.join("\n"); 70 + } 71 + 72 + /** 73 + * Generate externals.tsp file for the given namespace pattern 74 + */ 75 + export async function generateExternalsFile( 76 + namespacePattern: string, 77 + cwd: string, 78 + outDir: string = "./lexicons" 79 + ): Promise<void> { 80 + try { 81 + const prefix = getNamespacePrefix(namespacePattern); 82 + const lexiconsDir = resolve(cwd, outDir); 83 + const outputFile = resolve(cwd, "typelex/externals.tsp"); 84 + 85 + const externals = await findExternalLexicons(lexiconsDir, prefix); 86 + 87 + if (externals.size === 0) { 88 + // No externals, create empty file 89 + await mkdir(resolve(cwd, "typelex"), { recursive: true }); 90 + await writeFile( 91 + outputFile, 92 + 'import "@typelex/emitter";\n\n// Generated by typelex\n// No external lexicons found\n', 93 + "utf-8" 94 + ); 95 + return; 96 + } 97 + 98 + const code = generateExternalsCode(externals); 99 + await mkdir(resolve(cwd, "typelex"), { recursive: true }); 100 + await writeFile(outputFile, code, "utf-8"); 101 + } catch (error) { 102 + // Re-throw with better context 103 + throw new Error(`Failed to generate externals: ${error instanceof Error ? error.message : String(error)}`); 104 + } 105 + }
+78
packages/cli/src/utils/lexicon.ts
··· 1 + import { readFile } from "fs/promises"; 2 + import { resolve } from "path"; 3 + import { globby } from "globby"; 4 + 5 + export interface LexiconDef { 6 + type: string; 7 + [key: string]: unknown; 8 + } 9 + 10 + export interface LexiconDoc { 11 + lexicon: number; 12 + id: string; 13 + defs: Record<string, LexiconDef>; 14 + } 15 + 16 + /** 17 + * Read and parse a lexicon JSON file 18 + */ 19 + export async function readLexicon(path: string): Promise<LexiconDoc> { 20 + const content = await readFile(path, "utf-8"); 21 + return JSON.parse(content); 22 + } 23 + 24 + /** 25 + * Find all lexicon files in a directory 26 + */ 27 + export async function findLexicons(dir: string): Promise<string[]> { 28 + try { 29 + const pattern = resolve(dir, "**/*.json"); 30 + return await globby(pattern); 31 + } catch { 32 + // If directory doesn't exist, return empty array 33 + return []; 34 + } 35 + } 36 + 37 + /** 38 + * Extract external lexicons that don't match the given namespace 39 + */ 40 + export async function findExternalLexicons( 41 + lexiconsDir: string, 42 + primaryNamespace: string 43 + ): Promise<Map<string, LexiconDoc>> { 44 + const files = await findLexicons(lexiconsDir); 45 + const externals = new Map<string, LexiconDoc>(); 46 + 47 + for (const file of files) { 48 + const lexicon = await readLexicon(file); 49 + if (!lexicon.id.startsWith(primaryNamespace)) { 50 + externals.set(lexicon.id, lexicon); 51 + } 52 + } 53 + 54 + return externals; 55 + } 56 + 57 + /** 58 + * Check if a definition is a token type 59 + */ 60 + export function isTokenDef(def: LexiconDef): boolean { 61 + return def.type === "token"; 62 + } 63 + 64 + /** 65 + * Check if a definition should become a model in TypeSpec 66 + */ 67 + export function isModelDef(def: LexiconDef): boolean { 68 + const type = def.type; 69 + return ( 70 + type === "object" || 71 + type === "token" || 72 + type === "record" || 73 + type === "union" || 74 + type === "string" || 75 + type === "bytes" || 76 + type === "cid-link" 77 + ); 78 + }
+20
packages/cli/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "Node16", 5 + "moduleResolution": "Node16", 6 + "lib": ["ES2022"], 7 + "outDir": "dist", 8 + "rootDir": "src", 9 + "declaration": true, 10 + "declarationMap": true, 11 + "sourceMap": true, 12 + "strict": true, 13 + "esModuleInterop": true, 14 + "skipLibCheck": true, 15 + "forceConsistentCasingInFileNames": true, 16 + "resolveJsonModule": true 17 + }, 18 + "include": ["src/**/*"], 19 + "exclude": ["node_modules", "dist"] 20 + }
+4
packages/cli/typelex/externals.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + // Generated by typelex 4 + // No external lexicons found
+15
packages/emitter/lib/decorators.tsp
··· 1 1 import "../dist/tsp-index.js"; 2 2 3 + using TypeSpec.Reflection; 4 + 3 5 /** 4 6 * Specifies the maximum number of graphemes (user-perceived characters) allowed. 5 7 * Used alongside maxLength for proper Unicode text handling. ··· 159 161 * ``` 160 162 */ 161 163 extern dec errors(target: unknown, ...errors: unknown[]); 164 + 165 + /** 166 + * Marks a namespace as external, preventing it from emitting JSON output. 167 + * This decorator can only be applied to namespaces. 168 + * Useful for importing definitions from other lexicons without re-emitting them. 169 + * 170 + * @example 171 + * ```typespec 172 + * @external 173 + * namespace com.atproto.repo.defs; 174 + * ``` 175 + */ 176 + extern dec external(target: Namespace);
+5 -3
packages/emitter/package.json
··· 1 1 { 2 2 "name": "@typelex/emitter", 3 - "version": "0.1.0", 3 + "version": "0.2.0", 4 4 "description": "TypeSpec emitter for ATProto Lexicon definitions", 5 5 "main": "dist/index.js", 6 6 "type": "module", 7 7 "files": [ 8 8 "lib", 9 - "src" 9 + "src", 10 + "dist" 10 11 ], 11 12 "exports": { 12 13 ".": { ··· 26 27 "test:ci": "npm run build && vitest run", 27 28 "test:watch": "tsc-watch --onSuccess \"vitest run\"", 28 29 "clean": "rm -rf dist", 29 - "watch": "tsc --watch" 30 + "watch": "tsc --watch", 31 + "prepublishOnly": "npm test" 30 32 }, 31 33 "keywords": [ 32 34 "typespec",
+23
packages/emitter/src/decorators.ts
··· 24 24 const inlineKey = Symbol("inline"); 25 25 const maxBytesKey = Symbol("maxBytes"); 26 26 const minBytesKey = Symbol("minBytes"); 27 + const externalKey = Symbol("external"); 27 28 28 29 /** 29 30 * @maxBytes decorator for maximum length of bytes type ··· 294 295 export function isReadOnly(program: Program, target: Type): boolean { 295 296 return program.stateSet(readOnlyKey).has(target); 296 297 } 298 + 299 + /** 300 + * @external decorator for marking a namespace as external 301 + * External namespaces are skipped during emission and don't produce JSON files 302 + */ 303 + export function $external(context: DecoratorContext, target: Type) { 304 + if (target.kind !== "Namespace") { 305 + context.program.reportDiagnostic({ 306 + code: "external-not-on-namespace", 307 + severity: "error", 308 + message: "@external decorator can only be applied to namespaces", 309 + target: target, 310 + }); 311 + return; 312 + } 313 + 314 + context.program.stateSet(externalKey).add(target); 315 + } 316 + 317 + export function isExternal(program: Program, target: Type): boolean { 318 + return program.stateSet(externalKey).has(target); 319 + }
+164 -29
packages/emitter/src/emitter.ts
··· 39 39 LexObjectProperty, 40 40 LexArrayItem, 41 41 LexXrpcParameterProperty, 42 - LexInteger, 43 - LexString, 44 42 LexRefUnion, 45 43 LexUserType, 46 44 LexRecord, ··· 48 46 LexXrpcParameters, 49 47 LexBytes, 50 48 LexCidLink, 51 - LexRef, 52 49 LexRefVariant, 50 + LexToken, 53 51 } from "./types.js"; 54 52 55 53 import { ··· 69 67 isInline, 70 68 getMaxBytes, 71 69 getMinBytes, 70 + isExternal, 72 71 } from "./decorators.js"; 73 72 74 73 export interface EmitterOptions { ··· 123 122 return; 124 123 } 125 124 125 + // Skip external namespaces - they don't emit JSON files 126 + if (isExternal(this.program, ns)) { 127 + // Validate that all models in external namespaces are empty (stub-only) 128 + for (const [_, model] of ns.models) { 129 + if (model.properties && model.properties.size > 0) { 130 + this.program.reportDiagnostic({ 131 + code: "external-model-not-empty", 132 + severity: "error", 133 + message: `Models in @external namespaces must be empty stubs. Model '${model.name}' in namespace '${fullName}' has properties.`, 134 + target: model, 135 + }); 136 + } 137 + } 138 + return; 139 + } 140 + 126 141 // Check for TypeSpec enum syntax and throw error 127 142 if (ns.enums && ns.enums.size > 0) { 128 143 for (const [_, enumType] of ns.enums) { ··· 166 181 const hasScalars = ns.scalars.size > 0; 167 182 const hasUnions = ns.unions?.size > 0; 168 183 const hasOperations = ns.operations?.size > 0; 169 - const hasChildNamespaces = ns.namespaces.size > 0; 170 184 const hasContent = hasModels || hasScalars || hasUnions; 171 185 172 186 if (hasOperations) { 173 187 return "operation"; 174 188 } 175 189 176 - if (hasContent && hasChildNamespaces) { 177 - return "defs"; 178 - } 179 - 180 - if (hasContent && !hasChildNamespaces) { 190 + if (hasContent) { 181 191 return "content"; 182 192 } 183 193 ··· 267 277 }; 268 278 } 269 279 270 - private createMainDef(mainModel: Model): LexRecord | LexObject { 280 + private createMainDef(mainModel: Model): LexRecord | LexObject | LexToken { 271 281 const modelDescription = getDoc(this.program, mainModel); 282 + 283 + // Check if this is a token type 284 + if (isToken(this.program, mainModel)) { 285 + return { 286 + type: "token", 287 + description: modelDescription, 288 + }; 289 + } 290 + 272 291 const recordKey = getRecordKey(this.program, mainModel); 273 292 const modelDef = this.modelToLexiconObject(mainModel, !!modelDescription); 274 293 ··· 367 386 return; 368 387 } 369 388 370 - // Only string enums can be added as defs 389 + // Only string enums (including token refs) can be added as defs 371 390 // Union refs (type: "union") must be inlined at usage sites 372 391 if (unionDef.type === "string" && (unionDef.knownValues || unionDef.enum)) { 373 392 const defName = name.charAt(0).toLowerCase() + name.slice(1); ··· 378 397 code: "union-refs-not-allowed-as-def", 379 398 severity: "error", 380 399 message: 381 - `Named unions of model references cannot be defined as standalone defs. ` + 382 - `Use @inline to inline them at usage sites, or use string enums instead.`, 400 + `Named unions of non-token model references cannot be defined as standalone defs. ` + 401 + `Use @inline to inline them at usage sites, use @token models for known values, or use string literals.`, 383 402 target: union, 384 403 }); 385 404 } ··· 459 478 private unionToLexiconProperty( 460 479 unionType: Union, 461 480 prop?: ModelProperty, 481 + isDefining?: boolean, 462 482 ): LexObjectProperty | null { 463 483 const variants = this.parseUnionVariants(unionType); 464 484 ··· 501 521 (variants.stringLiterals.length > 0 && 502 522 !variants.hasStringType && 503 523 variants.unionRefs.length === 0 && 524 + variants.knownValueRefs.length === 0 && 504 525 isClosed(this.program, unionType)) 505 526 ) { 506 527 const isClosedUnion = isClosed(this.program, unionType); ··· 512 533 const minLength = getMinLength(this.program, unionType); 513 534 const maxGraphemes = getMaxGraphemes(this.program, unionType); 514 535 const minGraphemes = getMinGraphemes(this.program, unionType); 536 + 537 + // Combine string literals and token refs for known values 538 + const allKnownValues = [ 539 + ...variants.stringLiterals, 540 + ...variants.knownValueRefs, 541 + ]; 542 + 515 543 return { 516 544 type: "string", 517 - [isClosedUnion ? "enum" : "knownValues"]: variants.stringLiterals, 545 + [isClosedUnion ? "enum" : "knownValues"]: allKnownValues, 518 546 ...(propDesc && { description: propDesc }), 519 547 ...(defaultValue !== undefined && 520 548 typeof defaultValue === "string" && { default: defaultValue }), ··· 527 555 528 556 // Model reference union (including empty union with unknown) 529 557 if (variants.unionRefs.length > 0 || variants.hasUnknown) { 530 - if (variants.stringLiterals.length > 0) { 558 + if ( 559 + variants.stringLiterals.length > 0 || 560 + variants.knownValueRefs.length > 0 561 + ) { 531 562 this.program.reportDiagnostic({ 532 563 code: "union-mixed-refs-literals", 533 564 severity: "error", 534 565 message: 535 - `Union contains both model references and string literals. Lexicon unions must be either: ` + 536 - `(1) model references only (type: "union"), ` + 537 - `(2) string literals + string type (type: "string" with knownValues), or ` + 566 + `Union contains both non-token model references and string literals/token refs. Lexicon unions must be either: ` + 567 + `(1) non-token model references only (type: "union"), ` + 568 + `(2) token refs + string literals + string type (type: "string" with knownValues), or ` + 538 569 `(3) integer literals + integer type (type: "integer" with knownValues). ` + 539 570 `Separate these into distinct fields or nested unions.`, 540 571 target: unionType, ··· 607 638 const stringLiterals: string[] = []; 608 639 const numericLiterals: number[] = []; 609 640 const booleanLiterals: boolean[] = []; 641 + const tokenModels: Model[] = []; 610 642 let hasStringType = false; 611 643 let hasUnknown = false; 612 644 613 645 for (const variant of unionType.variants.values()) { 614 646 switch (variant.type.kind) { 615 647 case "Model": 616 - const ref = this.getModelReference(variant.type as Model); 617 - if (ref) unionRefs.push(ref); 648 + const model = variant.type as Model; 649 + // Collect token models separately - they're treated differently based on hasStringType 650 + if (isToken(this.program, model)) { 651 + tokenModels.push(model); 652 + } else { 653 + const ref = this.getModelReference(model); 654 + if (ref) unionRefs.push(ref); 655 + } 618 656 break; 619 657 case "String": 620 658 stringLiterals.push((variant.type as StringLiteral).value); ··· 639 677 } 640 678 } 641 679 680 + // Validate: tokens must appear with | string 681 + // Per Lexicon spec line 240: "unions can not reference token" 682 + if (tokenModels.length > 0 && !hasStringType) { 683 + this.program.reportDiagnostic({ 684 + code: "tokens-require-string", 685 + severity: "error", 686 + message: 687 + "Tokens must be used with | string. Per Lexicon spec, tokens encode as string values and cannot appear in union refs.", 688 + target: unionType, 689 + }); 690 + } 691 + 692 + // Token models become "known values" (always fully qualified refs) 693 + const knownValueRefs = tokenModels 694 + .map((m) => this.getModelReference(m, true)) 695 + .filter((ref): ref is string => ref !== null); 696 + 642 697 const isStringEnum = 643 - stringLiterals.length > 0 && hasStringType && unionRefs.length === 0; 698 + (stringLiterals.length > 0 || knownValueRefs.length > 0) && 699 + hasStringType && 700 + unionRefs.length === 0; 644 701 645 702 return { 646 703 unionRefs, 647 704 stringLiterals, 648 705 numericLiterals, 649 706 booleanLiterals, 707 + knownValueRefs, 650 708 hasStringType, 651 709 hasUnknown, 652 710 isStringEnum, ··· 705 763 const required: string[] = []; 706 764 707 765 for (const [paramName, param] of operation.parameters.properties) { 766 + // Check for conflicting @required on optional property 767 + if (param.optional && isRequired(this.program, param)) { 768 + this.program.reportDiagnostic({ 769 + code: "required-on-optional", 770 + message: 771 + `Parameter "${paramName}" has conflicting markers: @required decorator with optional "?". ` + 772 + `Either remove @required to make it optional (preferred), or remove the "?".`, 773 + target: param, 774 + severity: "error", 775 + }); 776 + } 777 + 778 + if (!param.optional) { 779 + if (!isRequired(this.program, param)) { 780 + this.program.reportDiagnostic({ 781 + code: "parameter-missing-required", 782 + message: 783 + `Required parameter "${paramName}" must be explicitly marked with @required decorator. ` + 784 + `In atproto, required fields are discouraged and must be intentional. ` + 785 + `Either add @required to the parameter or make it optional with "?".`, 786 + target: param, 787 + severity: "error", 788 + }); 789 + } 790 + required.push(paramName); 791 + } 792 + 708 793 const paramDef = this.typeToLexiconDefinition(param.type, param); 709 794 if (paramDef && this.isXrpcParameterProperty(paramDef)) { 710 795 properties[paramName] = paramDef; 711 - if (!param.optional) required.push(paramName); 712 796 } 713 797 } 714 798 ··· 844 928 const required: string[] = []; 845 929 846 930 for (const [propName, prop] of parametersModel.properties) { 931 + // Check for conflicting @required on optional property 932 + if (prop.optional && isRequired(this.program, prop)) { 933 + this.program.reportDiagnostic({ 934 + code: "required-on-optional", 935 + message: 936 + `Parameter "${propName}" has conflicting markers: @required decorator with optional "?". ` + 937 + `Either remove @required to make it optional (preferred), or remove the "?".`, 938 + target: prop, 939 + severity: "error", 940 + }); 941 + } 942 + 943 + if (!prop.optional) { 944 + if (!isRequired(this.program, prop)) { 945 + this.program.reportDiagnostic({ 946 + code: "parameter-missing-required", 947 + message: 948 + `Required parameter "${propName}" must be explicitly marked with @required decorator. ` + 949 + `In atproto, required fields are discouraged and must be intentional. ` + 950 + `Either add @required to the parameter or make it optional with "?".`, 951 + target: prop, 952 + severity: "error", 953 + }); 954 + } 955 + required.push(propName); 956 + } 957 + 847 958 const propDef = this.typeToLexiconDefinition(prop.type, prop); 848 959 if (propDef && this.isXrpcParameterProperty(propDef)) { 849 960 properties[propName] = propDef; 850 - if (!prop.optional) { 851 - required.push(propName); 852 - } 853 961 } 854 962 } 855 963 ··· 947 1055 const properties: Record<string, LexObjectProperty> = {}; 948 1056 949 1057 for (const [name, prop] of model.properties) { 1058 + // Check for conflicting @required on optional property 1059 + if (prop.optional && isRequired(this.program, prop)) { 1060 + this.program.reportDiagnostic({ 1061 + code: "required-on-optional", 1062 + message: 1063 + `Property "${name}" has conflicting markers: @required decorator with optional "?". ` + 1064 + `Either remove @required to make it optional (preferred), or remove the "?".`, 1065 + target: prop, 1066 + severity: "error", 1067 + }); 1068 + } 1069 + 950 1070 if (!prop.optional) { 951 1071 if (!isRequired(this.program, prop)) { 952 1072 this.program.reportDiagnostic({ ··· 1130 1250 } 1131 1251 } 1132 1252 1133 - const unionDef = this.unionToLexiconProperty(unionType, prop); 1253 + const unionDef = this.unionToLexiconProperty(unionType, prop, isDefining); 1134 1254 if (!unionDef) return null; 1135 1255 1136 1256 // Inherit description from union if no prop description and union is @inline ··· 1331 1451 entity: Model | Union, 1332 1452 name: string | undefined, 1333 1453 namespace: Namespace | undefined, 1454 + fullyQualified = false, 1334 1455 ): string | null { 1335 1456 if (!name || !namespace || namespace.name === "TypeSpec") return null; 1336 1457 ··· 1350 1471 }); 1351 1472 } 1352 1473 1353 - // Local reference (same namespace) 1474 + // For knownValues (fullyQualified=true), always use fully qualified refs 1475 + if (fullyQualified) { 1476 + return `${namespaceName}#${defName}`; 1477 + } 1478 + 1479 + // Local reference (same namespace) - use short ref 1354 1480 if ( 1355 1481 this.currentLexiconId === namespaceName || 1356 1482 this.currentLexiconId === `${namespaceName}.defs` ··· 1363 1489 return namespaceName; 1364 1490 } 1365 1491 1492 + // All other refs use fully qualified format 1366 1493 return `${namespaceName}#${defName}`; 1367 1494 } 1368 1495 1369 - private getModelReference(model: Model): string | null { 1370 - return this.getReference(model, model.name, model.namespace); 1496 + private getModelReference( 1497 + model: Model, 1498 + fullyQualified = false, 1499 + ): string | null { 1500 + return this.getReference( 1501 + model, 1502 + model.name, 1503 + model.namespace, 1504 + fullyQualified, 1505 + ); 1371 1506 } 1372 1507 1373 1508 private getUnionReference(union: Union): string | null {
+2
packages/emitter/src/tsp-index.ts
··· 14 14 $inline, 15 15 $maxBytes, 16 16 $minBytes, 17 + $external, 17 18 } from "./decorators.js"; 18 19 19 20 /** @internal */ ··· 34 35 inline: $inline, 35 36 maxBytes: $maxBytes, 36 37 minBytes: $minBytes, 38 + external: $external, 37 39 }, 38 40 };
+94 -48
packages/emitter/test/integration/atproto/input/app/bsky/actor/defs.tsp
··· 90 90 @required allowSubscriptions: "followers" | "mutuals" | "none" | string; 91 91 } 92 92 93 - @doc("Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests.") 93 + /** Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests. */ 94 94 model ViewerState { 95 95 muted?: boolean; 96 96 mutedByList?: app.bsky.graph.defs.ListViewBasic; ··· 100 100 following?: atUri; 101 101 followedBy?: atUri; 102 102 103 - @doc("This property is present only in selected cases, as an optimization.") 103 + /** This property is present only in selected cases, as an optimization. */ 104 104 knownFollowers?: KnownFollowers; 105 105 106 - @doc("This property is present only in selected cases, as an optimization.") 106 + /** This property is present only in selected cases, as an optimization. */ 107 107 activitySubscription?: app.bsky.notification.defs.ActivitySubscription; 108 108 } 109 109 110 - @doc("The subject's followers whom you also follow") 110 + /** The subject's followers whom you also follow */ 111 111 model KnownFollowers { 112 112 @required count: integer; 113 113 ··· 117 117 followers: ProfileViewBasic[]; 118 118 } 119 119 120 - @doc("Represents the verification information about the user this object is attached to.") 120 + /** Represents the verification information about the user this object is attached to. */ 121 121 model VerificationState { 122 - @doc("All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included.") 122 + /** All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included. */ 123 123 @required 124 124 verifications: VerificationView[]; 125 125 126 - @doc("The user's status as a verified account.") 126 + /** The user's status as a verified account. */ 127 127 @required 128 128 verifiedStatus: "valid" | "invalid" | "none" | string; 129 129 130 - @doc("The user's status as a trusted verifier.") 130 + /** The user's status as a trusted verifier. */ 131 131 @required 132 132 trustedVerifierStatus: "valid" | "invalid" | "none" | string; 133 133 } 134 134 135 - @doc("An individual verification for an associated subject.") 135 + /** An individual verification for an associated subject. */ 136 136 model VerificationView { 137 - @doc("The user who issued this verification.") 137 + /** The user who issued this verification. */ 138 138 @required 139 139 issuer: did; 140 140 141 - @doc("The AT-URI of the verification record.") 141 + /** The AT-URI of the verification record. */ 142 142 @required 143 143 uri: atUri; 144 144 145 - @doc("True if the verification passes validation, otherwise false.") 145 + /** True if the verification passes validation, otherwise false. */ 146 146 @required 147 147 isValid: boolean; 148 148 149 - @doc("Timestamp when the verification was created.") 149 + /** Timestamp when the verification was created. */ 150 150 @required 151 151 createdAt: datetime; 152 152 } ··· 174 174 } 175 175 176 176 model ContentLabelPref { 177 - @doc("Which labeler does this preference apply to? If undefined, applies globally.") 177 + /** Which labeler does this preference apply to? If undefined, applies globally. */ 178 178 labelerDid?: did; 179 179 180 180 @required label: string; ··· 199 199 } 200 200 201 201 model PersonalDetailsPref { 202 - @doc("The birth date of account owner.") 202 + /** The birth date of account owner. */ 203 203 birthDate?: datetime; 204 204 } 205 205 206 206 model FeedViewPref { 207 - @doc("The URI of the feed, or an identifier which describes the feed.") 207 + /** The URI of the feed, or an identifier which describes the feed. */ 208 208 @required 209 209 feed: string; 210 210 211 - @doc("Hide replies in the feed.") 211 + /** Hide replies in the feed. */ 212 212 hideReplies?: boolean; 213 213 214 - @doc("Hide replies in the feed if they are not by followed users.") 214 + /** Hide replies in the feed if they are not by followed users. */ 215 215 hideRepliesByUnfollowed?: boolean = true; 216 216 217 - @doc("Hide replies in the feed if they do not have this number of likes.") 217 + /** Hide replies in the feed if they do not have this number of likes. */ 218 218 hideRepliesByLikeCount?: integer; 219 219 220 - @doc("Hide reposts in the feed.") 220 + /** Hide reposts in the feed. */ 221 221 hideReposts?: boolean; 222 222 223 - @doc("Hide quote posts in the feed.") 223 + /** Hide quote posts in the feed. */ 224 224 hideQuotePosts?: boolean; 225 225 } 226 226 227 227 model ThreadViewPref { 228 - @doc("Sorting mode for threads.") 228 + /** Sorting mode for threads. */ 229 229 sort?: "oldest" | "newest" | "most-likes" | "random" | "hotness" | string; 230 230 231 - @doc("Show followed users at the top of all replies.") 231 + /** Show followed users at the top of all replies. */ 232 232 prioritizeFollowedUsers?: boolean; 233 233 } 234 234 ··· 237 237 scalar InterestTag extends string; 238 238 239 239 model InterestsPref { 240 - @doc("A list of tags which describe the account owner's interests gathered during onboarding.") 240 + /** A list of tags which describe the account owner's interests gathered during onboarding. */ 241 241 @maxItems(100) 242 242 @required 243 243 tags: InterestTag[]; ··· 251 251 string, 252 252 } 253 253 254 - @doc("A word that the account owner has muted.") 254 + /** A word that the account owner has muted. */ 255 255 model MutedWord { 256 256 id?: string; 257 257 258 - @doc("The muted word itself.") 258 + /** The muted word itself. */ 259 259 @maxGraphemes(1000) 260 260 @maxLength(10000) 261 261 @required 262 262 value: string; 263 263 264 - @doc("The intended targets of the muted word.") 264 + /** The intended targets of the muted word. */ 265 265 @required 266 266 targets: MutedWordTarget[]; 267 267 268 - @doc("Groups of users to apply the muted word to. If undefined, applies to all users.") 268 + /** Groups of users to apply the muted word to. If undefined, applies to all users. */ 269 269 actorTarget?: "all" | "exclude-following" | string = "all"; 270 270 271 - @doc("The date and time at which the muted word will expire and no longer be applied.") 271 + /** The date and time at which the muted word will expire and no longer be applied. */ 272 272 expiresAt?: datetime; 273 273 } 274 274 275 275 model MutedWordsPref { 276 - @doc("A list of words the account owner has muted.") 276 + /** A list of words the account owner has muted. */ 277 277 @required 278 278 items: MutedWord[]; 279 279 } 280 280 281 281 model HiddenPostsPref { 282 - @doc("A list of URIs of posts the account owner has hidden.") 282 + /** A list of URIs of posts the account owner has hidden. */ 283 283 @required 284 284 items: atUri[]; 285 285 } ··· 295 295 @maxLength(100) 296 296 scalar NudgeToken extends string; 297 297 298 - @doc("A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this.") 298 + /** A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this. */ 299 299 model BskyAppStatePref { 300 300 activeProgressGuide?: BskyAppProgressGuide; 301 301 302 - @doc("An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user.") 302 + /** An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user. */ 303 303 @maxItems(1000) 304 304 queuedNudges?: NudgeToken[]; 305 305 306 - @doc("Storage for NUXs the user has encountered.") 306 + /** Storage for NUXs the user has encountered. */ 307 307 @maxItems(100) 308 308 nuxs?: Nux[]; 309 309 } 310 310 311 - @doc("If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress.") 311 + /** If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress. */ 312 312 model BskyAppProgressGuide { 313 313 @maxLength(100) 314 314 @required 315 315 guide: string; 316 316 } 317 317 318 - @doc("A new user experiences (NUX) storage object") 318 + /** A new user experiences (NUX) storage object */ 319 319 model Nux { 320 320 @maxLength(100) 321 321 @required ··· 323 323 324 324 @required completed: boolean = false; 325 325 326 - @doc("Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters.") 326 + /** Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters. */ 327 327 @maxGraphemes(300) 328 328 @maxLength(3000) 329 329 data?: string; 330 330 331 - @doc("The date and time at which the NUX will expire and should be considered completed.") 331 + /** The date and time at which the NUX will expire and should be considered completed. */ 332 332 expiresAt?: datetime; 333 333 } 334 334 335 - @doc("Preferences for how verified accounts appear in the app.") 335 + /** Preferences for how verified accounts appear in the app. */ 336 336 model VerificationPrefs { 337 - @doc("Hide the blue check badges for verified accounts and trusted verifiers.") 337 + /** Hide the blue check badges for verified accounts and trusted verifiers. */ 338 338 hideBadges?: boolean = false; 339 339 } 340 340 341 - @doc("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.") 341 + /** 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. */ 342 342 model PostInteractionSettingsPref { 343 - @doc("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.") 343 + /** 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. */ 344 344 @maxItems(5) 345 345 threadgateAllowRules?: ( 346 346 | app.bsky.feed.threadgate.MentionRule ··· 350 350 | unknown 351 351 )[]; 352 352 353 - @doc("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.") 353 + /** 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. */ 354 354 @maxItems(5) 355 355 postgateEmbeddingRules?: (app.bsky.feed.postgate.DisableRule | unknown)[]; 356 356 } 357 357 358 358 model StatusView { 359 - @doc("The status for the account.") 359 + /** The status for the account. */ 360 360 @required 361 - status: "app.bsky.actor.status#live" | string; 361 + status: app.bsky.actor.status.Live | string; 362 362 363 363 @required record: unknown; 364 364 365 - @doc("An optional embed associated with the status.") 365 + /** An optional embed associated with the status. */ 366 366 embed?: app.bsky.embed.external.View; 367 367 368 - @doc("The date when this status will expire. The application might choose to no longer return the status after expiration.") 368 + /** The date when this status will expire. The application might choose to no longer return the status after expiration. */ 369 369 expiresAt?: datetime; 370 370 371 - @doc("True if the status is not expired, false if it is expired. Only present if expiration was set.") 371 + /** True if the status is not expired, false if it is expired. Only present if expiration was set. */ 372 372 isActive?: boolean; 373 373 } 374 374 } 375 + 376 + // --- Externals --- 377 + 378 + @external 379 + namespace com.atproto.label.defs { 380 + model Label { } 381 + } 382 + 383 + @external 384 + namespace app.bsky.graph.defs { 385 + model StarterPackViewBasic { } 386 + model ListViewBasic { } 387 + } 388 + 389 + @external 390 + namespace com.atproto.repo.strongRef { 391 + model Main { } 392 + } 393 + 394 + @external 395 + namespace app.bsky.notification.defs { 396 + model ActivitySubscription { } 397 + } 398 + 399 + @external 400 + namespace app.bsky.feed.threadgate { 401 + model MentionRule { } 402 + model FollowerRule { } 403 + model FollowingRule { } 404 + model ListRule { } 405 + } 406 + 407 + @external 408 + namespace app.bsky.feed.postgate { 409 + model DisableRule { } 410 + } 411 + 412 + @external 413 + namespace app.bsky.actor.status { 414 + @token model Live { } 415 + } 416 + 417 + @external 418 + namespace app.bsky.embed.external { 419 + model View { } 420 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/actor/getPreferences.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.getPreferences { 4 - @doc("Get private preferences attached to the current account. Expected use is synchronization between multiple devices, and import/export during account migration. Requires auth.") 4 + /** Get private preferences attached to the current account. Expected use is synchronization between multiple devices, and import/export during account migration. Requires auth. */ 5 5 @query 6 6 op main(): { 7 7 @required preferences: app.bsky.actor.defs.Preferences; 8 8 }; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace app.bsky.actor.defs { 15 + model Preferences { } 16 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfile.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.getProfile { 4 - @doc("Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth.") 4 + /** Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth. */ 5 5 @query 6 6 op main( 7 - @doc("Handle or DID of account to fetch profile of.") 8 - actor: atIdentifier 7 + /** Handle or DID of account to fetch profile of. */ 8 + @required actor: atIdentifier 9 9 ): app.bsky.actor.defs.ProfileViewDetailed; 10 10 } 11 + 12 + // --- Externals --- 13 + 14 + @external 15 + namespace app.bsky.actor.defs { 16 + model ProfileViewDetailed { } 17 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfiles.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.getProfiles { 4 - @doc("Get detailed profile views of multiple actors.") 4 + /** Get detailed profile views of multiple actors. */ 5 5 @query 6 6 op main( 7 7 @maxItems(25) 8 - actors: atIdentifier[] 8 + @required actors: atIdentifier[] 9 9 ): { 10 10 @required profiles: app.bsky.actor.defs.ProfileViewDetailed[]; 11 11 }; 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace app.bsky.actor.defs { 18 + model ProfileViewDetailed { } 19 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/actor/getSuggestions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.getSuggestions { 4 - @doc("Get a list of suggested actors. Expected use is discovery of accounts to follow during new account onboarding.") 4 + /** Get a list of suggested actors. Expected use is discovery of accounts to follow during new account onboarding. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 15 15 @required 16 16 actors: app.bsky.actor.defs.ProfileView[]; 17 17 18 - @doc("Snowflake for this recommendation, use when submitting recommendation events.") 18 + /** Snowflake for this recommendation, use when submitting recommendation events. */ 19 19 recId?: int32; 20 20 }; 21 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace app.bsky.actor.defs { 27 + model ProfileView { } 28 + }
+18 -6
packages/emitter/test/integration/atproto/input/app/bsky/actor/profile.tsp
··· 2 2 3 3 namespace app.bsky.actor.profile { 4 4 @rec("literal:self") 5 - @doc("A declaration of a Bluesky account profile.") 5 + /** A declaration of a Bluesky account profile. */ 6 6 model Main { 7 7 @maxGraphemes(64) 8 8 @maxLength(640) 9 9 displayName?: string; 10 10 11 - @doc("Free-form profile description text.") 11 + /** Free-form profile description text. */ 12 12 @maxGraphemes(256) 13 13 @maxLength(2560) 14 14 description?: string; 15 15 16 - @doc("Free-form pronouns text.") 16 + /** Free-form pronouns text. */ 17 17 @maxGraphemes(20) 18 18 @maxLength(200) 19 19 pronouns?: string; 20 20 21 21 website?: uri; 22 22 23 - @doc("Small image to be displayed next to posts from account. AKA, 'profile picture'") 23 + /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ 24 24 avatar?: Blob<#["image/png", "image/jpeg"], 1000000>; 25 25 26 - @doc("Larger horizontal image to display behind profile view.") 26 + /** Larger horizontal image to display behind profile view. */ 27 27 banner?: Blob<#["image/png", "image/jpeg"], 1000000>; 28 28 29 - @doc("Self-label values, specific to the Bluesky application, on the overall account.") 29 + /** Self-label values, specific to the Bluesky application, on the overall account. */ 30 30 labels?: (com.atproto.label.defs.SelfLabels | unknown); 31 31 32 32 joinedViaStarterPack?: com.atproto.repo.strongRef.Main; ··· 34 34 createdAt?: datetime; 35 35 } 36 36 } 37 + 38 + // --- Externals --- 39 + 40 + @external 41 + namespace com.atproto.label.defs { 42 + model SelfLabels { } 43 + } 44 + 45 + @external 46 + namespace com.atproto.repo.strongRef { 47 + model Main { } 48 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/actor/putPreferences.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.putPreferences { 4 - @doc("Set the private preferences attached to the account.") 4 + /** Set the private preferences attached to the account. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required preferences: app.bsky.actor.defs.Preferences; 8 8 }): void; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace app.bsky.actor.defs { 15 + model Preferences { } 16 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActors.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.searchActors { 4 - @doc("Find actors (profiles) matching search criteria. Does not require auth.") 4 + /** Find actors (profiles) matching search criteria. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("DEPRECATED: use 'q' instead.") 7 + /** DEPRECATED: use 'q' instead. */ 8 8 term?: string, 9 9 10 - @doc("Search query string. Syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended.") 10 + /** Search query string. Syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. */ 11 11 q?: string, 12 12 13 13 @minValue(1) ··· 20 20 @required actors: app.bsky.actor.defs.ProfileView[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.actor.defs { 28 + model ProfileView { } 29 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActorsTypeahead.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.searchActorsTypeahead { 4 - @doc("Find actor suggestions for a prefix search term. Expected use is for auto-completion during text field entry. Does not require auth.") 4 + /** Find actor suggestions for a prefix search term. Expected use is for auto-completion during text field entry. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("DEPRECATED: use 'q' instead.") 7 + /** DEPRECATED: use 'q' instead. */ 8 8 term?: string, 9 9 10 - @doc("Search query prefix; not a full query string.") 10 + /** Search query prefix; not a full query string. */ 11 11 q?: string, 12 12 13 13 @minValue(1) ··· 17 17 @required actors: app.bsky.actor.defs.ProfileViewBasic[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace app.bsky.actor.defs { 25 + model ProfileViewBasic { } 26 + }
+13 -6
packages/emitter/test/integration/atproto/input/app/bsky/actor/status.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.actor.status { 4 - @doc("A declaration of a Bluesky account status.") 4 + /** A declaration of a Bluesky account status. */ 5 5 @rec("literal:self") 6 6 model Main { 7 - @doc("The status for the account.") 7 + /** The status for the account. */ 8 8 @required 9 - status: "app.bsky.actor.status#live" | string; 9 + status: Live | string; 10 10 11 - @doc("An optional embed associated with the status.") 11 + /** An optional embed associated with the status. */ 12 12 embed?: (app.bsky.embed.external.Main | unknown); 13 13 14 - @doc("The duration of the status in minutes. Applications can choose to impose minimum and maximum limits.") 14 + /** The duration of the status in minutes. Applications can choose to impose minimum and maximum limits. */ 15 15 @minValue(1) 16 16 durationMinutes?: integer; 17 17 18 18 @required createdAt: datetime; 19 19 } 20 20 21 - @doc("Advertises an account as currently offering live content.") 21 + /** Advertises an account as currently offering live content. */ 22 22 @token 23 23 model Live {} 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace app.bsky.embed.external { 30 + model Main { } 31 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/createBookmark.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.bookmark.createBookmark { 4 - @doc("Creates a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication.") 4 + /** Creates a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication. */ 5 5 @errors(UnsupportedCollection) 6 6 @procedure 7 7 op main(input: { ··· 9 9 @required cid: cid; 10 10 }): void; 11 11 12 - @doc("The URI to be bookmarked is for an unsupported collection.") 12 + /** The URI to be bookmarked is for an unsupported collection. */ 13 13 model UnsupportedCollection {} 14 14 }
+17 -3
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.bookmark.defs { 4 - @doc("Object used to store bookmark data in stash.") 4 + /** Object used to store bookmark data in stash. */ 5 5 model Bookmark { 6 - @doc("A strong ref to the record to be bookmarked. Currently, only `app.bsky.feed.post` records are supported.") 6 + /** A strong ref to the record to be bookmarked. Currently, only `app.bsky.feed.post` records are supported. */ 7 7 @required 8 8 subject: com.atproto.repo.strongRef.Main; 9 9 } 10 10 11 11 model BookmarkView { 12 - @doc("A strong ref to the bookmarked record.") 12 + /** A strong ref to the bookmarked record. */ 13 13 @required 14 14 subject: com.atproto.repo.strongRef.Main; 15 15 ··· 24 24 ); 25 25 } 26 26 } 27 + 28 + // --- Externals --- 29 + 30 + @external 31 + namespace com.atproto.repo.strongRef { 32 + model Main { } 33 + } 34 + 35 + @external 36 + namespace app.bsky.feed.defs { 37 + model BlockedPost { } 38 + model NotFoundPost { } 39 + model PostView { } 40 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/deleteBookmark.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.bookmark.deleteBookmark { 4 - @doc("Deletes a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication.") 4 + /** Deletes a private bookmark for the specified record. Currently, only `app.bsky.feed.post` records are supported. Requires authentication. */ 5 5 @errors(UnsupportedCollection) 6 6 @procedure 7 7 op main(input: { 8 8 @required uri: atUri; 9 9 }): void; 10 10 11 - @doc("The URI to be bookmarked is for an unsupported collection.") 11 + /** The URI to be bookmarked is for an unsupported collection. */ 12 12 model UnsupportedCollection {} 13 13 }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/getBookmarks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.bookmark.getBookmarks { 4 - @doc("Gets views of records bookmarked by the authenticated user. Requires authentication.") 4 + /** Gets views of records bookmarked by the authenticated user. Requires authentication. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required bookmarks: app.bsky.bookmark.defs.BookmarkView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.bookmark.defs { 22 + model BookmarkView { } 23 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/embed/defs.tsp
··· 2 2 3 3 namespace app.bsky.embed.defs { 4 4 // Description goes on the model for defs, unlike standalone lexicons where it goes at lexicon level 5 - @doc("width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit.") 5 + /** width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit. */ 6 6 model AspectRatio { 7 7 @minValue(1) 8 8 @required
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/embed/external.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.embed.external { 4 - @doc("A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post).") 4 + /** A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post). */ 5 5 model Main { 6 6 @required external: External; 7 7 }
+12 -5
packages/emitter/test/integration/atproto/input/app/bsky/embed/images.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A set of images embedded in a Bluesky record (eg, a post).") 3 + /** A set of images embedded in a Bluesky record (eg, a post). */ 4 4 namespace app.bsky.embed.images { 5 5 model Main { 6 6 @maxItems(4) ··· 11 11 model Image { 12 12 @required image: Blob<#["image/*"], 1000000>; 13 13 14 - @doc("Alt text description of the image, for accessibility.") 14 + /** Alt text description of the image, for accessibility. */ 15 15 @required 16 16 alt: string; 17 17 ··· 25 25 } 26 26 27 27 model ViewImage { 28 - @doc("Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View.") 28 + /** Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View. */ 29 29 @required 30 30 thumb: uri; 31 31 32 - @doc("Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View.") 32 + /** Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View. */ 33 33 @required 34 34 fullsize: uri; 35 35 36 - @doc("Alt text description of the image, for accessibility.") 36 + /** Alt text description of the image, for accessibility. */ 37 37 @required 38 38 alt: string; 39 39 40 40 aspectRatio?: app.bsky.embed.defs.AspectRatio; 41 41 } 42 42 } 43 + 44 + // --- Externals --- 45 + 46 + @external 47 + namespace app.bsky.embed.defs { 48 + model AspectRatio { } 49 + }
+56 -2
packages/emitter/test/integration/atproto/input/app/bsky/embed/record.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A representation of a record embedded in a Bluesky record (eg, a post). For example, a quote-post, or sharing a feed generator record.") 3 + /** A representation of a record embedded in a Bluesky record (eg, a post). For example, a quote-post, or sharing a feed generator record. */ 4 4 namespace app.bsky.embed.`record` { 5 5 model Main { 6 6 @required record: com.atproto.repo.strongRef.Main; ··· 26 26 @required cid: cid; 27 27 @required author: app.bsky.actor.defs.ProfileViewBasic; 28 28 29 - @doc("The record data itself.") 29 + /** The record data itself. */ 30 30 @required 31 31 value: unknown; 32 32 ··· 74 74 detached: boolean = true; 75 75 } 76 76 } 77 + 78 + // --- Externals --- 79 + 80 + @external 81 + namespace com.atproto.repo.strongRef { 82 + model Main { } 83 + } 84 + 85 + @external 86 + namespace app.bsky.feed.defs { 87 + model GeneratorView { } 88 + model BlockedAuthor { } 89 + } 90 + 91 + @external 92 + namespace app.bsky.graph.defs { 93 + model ListView { } 94 + model StarterPackViewBasic { } 95 + } 96 + 97 + @external 98 + namespace app.bsky.labeler.defs { 99 + model LabelerView { } 100 + } 101 + 102 + @external 103 + namespace app.bsky.actor.defs { 104 + model ProfileViewBasic { } 105 + } 106 + 107 + @external 108 + namespace com.atproto.label.defs { 109 + model Label { } 110 + } 111 + 112 + @external 113 + namespace app.bsky.embed.images { 114 + model View { } 115 + } 116 + 117 + @external 118 + namespace app.bsky.embed.video { 119 + model View { } 120 + } 121 + 122 + @external 123 + namespace app.bsky.embed.external { 124 + model View { } 125 + } 126 + 127 + @external 128 + namespace app.bsky.embed.recordWithMedia { 129 + model View { } 130 + }
+27 -1
packages/emitter/test/integration/atproto/input/app/bsky/embed/recordWithMedia.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A representation of a record embedded in a Bluesky record (eg, a post), alongside other compatible embeds. For example, a quote post and image, or a quote post and external URL card.") 3 + /** A representation of a record embedded in a Bluesky record (eg, a post), alongside other compatible embeds. For example, a quote post and image, or a quote post and external URL card. */ 4 4 namespace app.bsky.embed.recordWithMedia { 5 5 model Main { 6 6 @required record: app.bsky.embed.record.Main; ··· 26 26 ); 27 27 } 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.embed.`record` { 34 + model Main { } 35 + model View { } 36 + } 37 + 38 + @external 39 + namespace app.bsky.embed.images { 40 + model Main { } 41 + model View { } 42 + } 43 + 44 + @external 45 + namespace app.bsky.embed.video { 46 + model Main { } 47 + model View { } 48 + } 49 + 50 + @external 51 + namespace app.bsky.embed.external { 52 + model Main { } 53 + model View { } 54 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/embed/video.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A video embedded in a Bluesky record (eg, a post).") 3 + /** A video embedded in a Bluesky record (eg, a post). */ 4 4 namespace app.bsky.embed.video { 5 5 model Main { 6 - @doc("The mp4 video file. May be up to 100mb, formerly limited to 50mb.") 6 + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ 7 7 @required 8 8 video: Blob<#["video/mp4"], 100000000>; 9 9 10 10 @maxItems(20) 11 11 captions?: Caption[]; 12 12 13 - @doc("Alt text description of the video, for accessibility.") 13 + /** Alt text description of the video, for accessibility. */ 14 14 @maxGraphemes(1000) 15 15 @maxLength(10000) 16 16 alt?: string; ··· 36 36 aspectRatio?: app.bsky.embed.defs.AspectRatio; 37 37 } 38 38 } 39 + 40 + // --- Externals --- 41 + 42 + @external 43 + namespace app.bsky.embed.defs { 44 + model AspectRatio { } 45 + }
+84 -35
packages/emitter/test/integration/atproto/input/app/bsky/feed/defs.tsp
··· 29 29 threadgate?: ThreadgateView; 30 30 } 31 31 32 - @doc("Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests.") 32 + /** Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests. */ 33 33 model ViewerState { 34 34 repost?: atUri; 35 35 like?: atUri; ··· 40 40 pinned?: boolean; 41 41 } 42 42 43 - @doc("Metadata about this post within the context of the thread it is in.") 43 + /** Metadata about this post within the context of the thread it is in. */ 44 44 model ThreadContext { 45 45 rootAuthorLike?: atUri; 46 46 } ··· 51 51 reply?: ReplyRef; 52 52 reason?: (ReasonRepost | ReasonPin | unknown); 53 53 54 - @doc("Context provided by feed generator that may be passed back alongside interactions.") 54 + /** Context provided by feed generator that may be passed back alongside interactions. */ 55 55 @maxLength(2000) 56 56 feedContext?: string; 57 57 58 - @doc("Unique identifier per request that may be passed back alongside interactions.") 58 + /** Unique identifier per request that may be passed back alongside interactions. */ 59 59 @maxLength(100) 60 60 reqId?: string; 61 61 } ··· 64 64 @required root: (PostView | NotFoundPost | BlockedPost | unknown); 65 65 @required parent: (PostView | NotFoundPost | BlockedPost | unknown); 66 66 67 - @doc("When parent is a reply to another post, this is the author of that post.") 67 + /** When parent is a reply to another post, this is the author of that post. */ 68 68 grandparentAuthor?: app.bsky.actor.defs.ProfileViewBasic; 69 69 } 70 70 ··· 133 133 acceptsInteractions?: boolean; 134 134 labels?: com.atproto.label.defs.Label[]; 135 135 viewer?: GeneratorViewerState; 136 - contentMode?: "app.bsky.feed.defs#contentModeUnspecified" | "app.bsky.feed.defs#contentModeVideo" | string; 136 + contentMode?: ContentModeUnspecified | ContentModeVideo | string; 137 137 138 138 @required indexedAt: datetime; 139 139 } ··· 147 147 148 148 reason?: (SkeletonReasonRepost | SkeletonReasonPin | unknown); 149 149 150 - @doc("Context that will be passed through to client and may be passed to feed generator back alongside interactions.") 150 + /** Context that will be passed through to client and may be passed to feed generator back alongside interactions. */ 151 151 @maxLength(2000) 152 152 feedContext?: string; 153 153 } ··· 167 167 168 168 model Interaction { 169 169 item?: atUri; 170 - event?: "app.bsky.feed.defs#requestLess" 171 - | "app.bsky.feed.defs#requestMore" 172 - | "app.bsky.feed.defs#clickthroughItem" 173 - | "app.bsky.feed.defs#clickthroughAuthor" 174 - | "app.bsky.feed.defs#clickthroughReposter" 175 - | "app.bsky.feed.defs#clickthroughEmbed" 176 - | "app.bsky.feed.defs#interactionSeen" 177 - | "app.bsky.feed.defs#interactionLike" 178 - | "app.bsky.feed.defs#interactionRepost" 179 - | "app.bsky.feed.defs#interactionReply" 180 - | "app.bsky.feed.defs#interactionQuote" 181 - | "app.bsky.feed.defs#interactionShare" 170 + event?: RequestLess 171 + | RequestMore 172 + | ClickthroughItem 173 + | ClickthroughAuthor 174 + | ClickthroughReposter 175 + | ClickthroughEmbed 176 + | InteractionSeen 177 + | InteractionLike 178 + | InteractionRepost 179 + | InteractionReply 180 + | InteractionQuote 181 + | InteractionShare 182 182 | string; 183 183 184 - @doc("Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton.") 184 + /** Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton. */ 185 185 @maxLength(2000) 186 186 feedContext?: string; 187 187 188 - @doc("Unique identifier per request that may be passed back alongside interactions.") 188 + /** Unique identifier per request that may be passed back alongside interactions. */ 189 189 @maxLength(100) 190 190 reqId?: string; 191 191 } 192 192 193 - @doc("Request that less content like the given feed item be shown in the feed") 193 + /** Request that less content like the given feed item be shown in the feed */ 194 194 @token 195 195 model RequestLess {} 196 196 197 - @doc("Request that more content like the given feed item be shown in the feed") 197 + /** Request that more content like the given feed item be shown in the feed */ 198 198 @token 199 199 model RequestMore {} 200 200 201 - @doc("User clicked through to the feed item") 201 + /** User clicked through to the feed item */ 202 202 @token 203 203 model ClickthroughItem {} 204 204 205 - @doc("User clicked through to the author of the feed item") 205 + /** User clicked through to the author of the feed item */ 206 206 @token 207 207 model ClickthroughAuthor {} 208 208 209 - @doc("User clicked through to the reposter of the feed item") 209 + /** User clicked through to the reposter of the feed item */ 210 210 @token 211 211 model ClickthroughReposter {} 212 212 213 - @doc("User clicked through to the embedded content of the feed item") 213 + /** User clicked through to the embedded content of the feed item */ 214 214 @token 215 215 model ClickthroughEmbed {} 216 216 217 - @doc("Declares the feed generator returns any types of posts.") 217 + /** Declares the feed generator returns any types of posts. */ 218 218 @token 219 219 model ContentModeUnspecified {} 220 220 221 - @doc("Declares the feed generator returns posts containing app.bsky.embed.video embeds.") 221 + /** Declares the feed generator returns posts containing app.bsky.embed.video embeds. */ 222 222 @token 223 223 model ContentModeVideo {} 224 224 225 - @doc("Feed item was seen by user") 225 + /** Feed item was seen by user */ 226 226 @token 227 227 model InteractionSeen {} 228 228 229 - @doc("User liked the feed item") 229 + /** User liked the feed item */ 230 230 @token 231 231 model InteractionLike {} 232 232 233 - @doc("User reposted the feed item") 233 + /** User reposted the feed item */ 234 234 @token 235 235 model InteractionRepost {} 236 236 237 - @doc("User replied to the feed item") 237 + /** User replied to the feed item */ 238 238 @token 239 239 model InteractionReply {} 240 240 241 - @doc("User quoted the feed item") 241 + /** User quoted the feed item */ 242 242 @token 243 243 model InteractionQuote {} 244 244 245 - @doc("User shared the feed item") 245 + /** User shared the feed item */ 246 246 @token 247 247 model InteractionShare {} 248 248 } 249 + 250 + // --- Externals --- 251 + 252 + @external 253 + namespace app.bsky.actor.defs { 254 + model ProfileViewBasic { } 255 + model ViewerState { } 256 + model ProfileView { } 257 + } 258 + 259 + @external 260 + namespace app.bsky.embed.images { 261 + model View { } 262 + } 263 + 264 + @external 265 + namespace app.bsky.embed.video { 266 + model View { } 267 + } 268 + 269 + @external 270 + namespace app.bsky.embed.external { 271 + model View { } 272 + } 273 + 274 + @external 275 + namespace app.bsky.embed.`record` { 276 + model View { } 277 + } 278 + 279 + @external 280 + namespace app.bsky.embed.recordWithMedia { 281 + model View { } 282 + } 283 + 284 + @external 285 + namespace com.atproto.label.defs { 286 + model Label { } 287 + } 288 + 289 + @external 290 + namespace app.bsky.richtext.facet { 291 + model Main { } 292 + } 293 + 294 + @external 295 + namespace app.bsky.graph.defs { 296 + model ListViewBasic { } 297 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/feed/describeFeedGenerator.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.describeFeedGenerator { 4 - @doc("Get information about a feed generator, including policies and offered feed URIs. Does not require auth; implemented by Feed Generator services (not App View).") 4 + /** Get information about a feed generator, including policies and offered feed URIs. Does not require auth; implemented by Feed Generator services (not App View). */ 5 5 @query 6 6 op main(): { 7 7 @required did: did;
+22 -4
packages/emitter/test/integration/atproto/input/app/bsky/feed/generator.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.generator { 4 - @doc("Record declaring of the existence of a feed generator, and containing metadata about it. The record can exist in any repository.") 4 + /** Record declaring of the existence of a feed generator, and containing metadata about it. The record can exist in any repository. */ 5 5 @rec("any") 6 6 model Main { 7 7 @required did: did; ··· 19 19 20 20 avatar?: Blob<#["image/png", "image/jpeg"], 1000000>; 21 21 22 - @doc("Declaration that a feed accepts feedback interactions from a client through app.bsky.feed.sendInteractions") 22 + /** Declaration that a feed accepts feedback interactions from a client through app.bsky.feed.sendInteractions */ 23 23 acceptsInteractions?: boolean; 24 24 25 - @doc("Self-label values") 25 + /** Self-label values */ 26 26 labels?: (com.atproto.label.defs.SelfLabels | unknown); 27 27 28 - contentMode?: "app.bsky.feed.defs#contentModeUnspecified" | "app.bsky.feed.defs#contentModeVideo" | string; 28 + contentMode?: app.bsky.feed.defs.ContentModeUnspecified | app.bsky.feed.defs.ContentModeVideo | string; 29 29 30 30 @required createdAt: datetime; 31 31 } 32 32 } 33 + 34 + // --- Externals --- 35 + 36 + @external 37 + namespace app.bsky.richtext.facet { 38 + model Main { } 39 + } 40 + 41 + @external 42 + namespace com.atproto.label.defs { 43 + model SelfLabels { } 44 + } 45 + 46 + @external 47 + namespace app.bsky.feed.defs { 48 + @token model ContentModeUnspecified { } 49 + @token model ContentModeVideo { } 50 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorFeeds.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getActorFeeds { 4 - @doc("Get a list of feeds (feed generator records) created by the actor (in the actor's repo).") 4 + /** Get a list of feeds (feed generator records) created by the actor (in the actor's repo). */ 5 5 @query 6 6 op main( 7 - actor: atIdentifier, 7 + @required actor: atIdentifier, 8 8 9 9 @minValue(1) 10 10 @maxValue(100) ··· 16 16 @required feeds: app.bsky.feed.defs.GeneratorView[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace app.bsky.feed.defs { 24 + model GeneratorView { } 25 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorLikes.tsp
··· 4 4 model BlockedActor {} 5 5 model BlockedByActor {} 6 6 7 - @doc("Get a list of posts liked by an actor. Requires auth, actor must be the requesting account.") 7 + /** Get a list of posts liked by an actor. Requires auth, actor must be the requesting account. */ 8 8 @query 9 9 @errors(BlockedActor, BlockedByActor) 10 10 op main( 11 - actor: atIdentifier, 11 + @required actor: atIdentifier, 12 12 13 13 @minValue(1) 14 14 @maxValue(100) ··· 20 20 @required feed: app.bsky.feed.defs.FeedViewPost[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.feed.defs { 28 + model FeedViewPost { } 29 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/feed/getAuthorFeed.tsp
··· 4 4 model BlockedActor {} 5 5 model BlockedByActor {} 6 6 7 - @doc("Get a view of an actor's 'author feed' (post and reposts by the author). Does not require auth.") 7 + /** Get a view of an actor's 'author feed' (post and reposts by the author). Does not require auth. */ 8 8 @query 9 9 @errors(BlockedActor, BlockedByActor) 10 10 op main( 11 - actor: atIdentifier, 11 + @required actor: atIdentifier, 12 12 13 13 @minValue(1) 14 14 @maxValue(100) ··· 16 16 17 17 cursor?: string, 18 18 19 - @doc("Combinations of post/repost types to include in response.") 19 + /** Combinations of post/repost types to include in response. */ 20 20 filter?: "posts_with_replies" | "posts_no_replies" | "posts_with_media" | "posts_and_author_threads" | "posts_with_video" | string = "posts_with_replies", 21 21 22 22 includePins?: boolean = false ··· 25 25 @required feed: app.bsky.feed.defs.FeedViewPost[]; 26 26 }; 27 27 } 28 + 29 + // --- Externals --- 30 + 31 + @external 32 + namespace app.bsky.feed.defs { 33 + model FeedViewPost { } 34 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeed.tsp
··· 3 3 namespace app.bsky.feed.getFeed { 4 4 model UnknownFeed {} 5 5 6 - @doc("Get a hydrated feed from an actor's selected feed generator. Implemented by App View.") 6 + /** Get a hydrated feed from an actor's selected feed generator. Implemented by App View. */ 7 7 @query 8 8 @errors(UnknownFeed) 9 9 op main( 10 - feed: atUri, 10 + @required feed: atUri, 11 11 12 12 @minValue(1) 13 13 @maxValue(100) ··· 19 19 @required feed: app.bsky.feed.defs.FeedViewPost[]; 20 20 }; 21 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace app.bsky.feed.defs { 27 + model FeedViewPost { } 28 + }
+12 -5
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerator.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getFeedGenerator { 4 - @doc("Get information about a feed generator. Implemented by AppView.") 4 + /** Get information about a feed generator. Implemented by AppView. */ 5 5 @query 6 6 op main( 7 - @doc("AT-URI of the feed generator record.") 8 - feed: atUri 7 + /** AT-URI of the feed generator record. */ 8 + @required feed: atUri 9 9 ): { 10 10 @required view: app.bsky.feed.defs.GeneratorView; 11 11 12 - @doc("Indicates whether the feed generator service has been online recently, or else seems to be inactive.") 12 + /** Indicates whether the feed generator service has been online recently, or else seems to be inactive. */ 13 13 @required 14 14 isOnline: boolean; 15 15 16 - @doc("Indicates whether the feed generator service is compatible with the record declaration.") 16 + /** Indicates whether the feed generator service is compatible with the record declaration. */ 17 17 @required 18 18 isValid: boolean; 19 19 }; 20 20 } 21 + 22 + // --- Externals --- 23 + 24 + @external 25 + namespace app.bsky.feed.defs { 26 + model GeneratorView { } 27 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerators.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getFeedGenerators { 4 - @doc("Get information about a list of feed generators.") 4 + /** Get information about a list of feed generators. */ 5 5 @query 6 6 op main( 7 - feeds: atUri[] 7 + @required feeds: atUri[] 8 8 ): { 9 9 @required feeds: app.bsky.feed.defs.GeneratorView[]; 10 10 }; 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace app.bsky.feed.defs { 17 + model GeneratorView { } 18 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedSkeleton.tsp
··· 3 3 namespace app.bsky.feed.getFeedSkeleton { 4 4 model UnknownFeed {} 5 5 6 - @doc("Get a skeleton of a feed provided by a feed generator. Auth is optional, depending on provider requirements, and provides the DID of the requester. Implemented by Feed Generator Service.") 6 + /** Get a skeleton of a feed provided by a feed generator. Auth is optional, depending on provider requirements, and provides the DID of the requester. Implemented by Feed Generator Service. */ 7 7 @query 8 8 @errors(UnknownFeed) 9 9 op main( 10 - @doc("Reference to feed generator record describing the specific feed being requested.") 11 - feed: atUri, 10 + /** Reference to feed generator record describing the specific feed being requested. */ 11 + @required feed: atUri, 12 12 13 13 @minValue(1) 14 14 @maxValue(100) ··· 21 21 @required 22 22 feed: app.bsky.feed.defs.SkeletonFeedPost[]; 23 23 24 - @doc("Unique identifier per request that may be passed back alongside interactions.") 24 + /** Unique identifier per request that may be passed back alongside interactions. */ 25 25 @maxLength(100) 26 26 reqId?: string; 27 27 }; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.feed.defs { 34 + model SkeletonFeedPost { } 35 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/feed/getLikes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getLikes { 4 - @doc("Get like records which reference a subject (by AT-URI and CID).") 4 + /** Get like records which reference a subject (by AT-URI and CID). */ 5 5 @query 6 6 op main( 7 - @doc("AT-URI of the subject (eg, a post record).") 8 - uri: atUri, 7 + /** AT-URI of the subject (eg, a post record). */ 8 + @required uri: atUri, 9 9 10 - @doc("CID of the subject record (aka, specific version of record), to filter likes.") 10 + /** CID of the subject record (aka, specific version of record), to filter likes. */ 11 11 cid?: cid, 12 12 13 13 @minValue(1) ··· 28 28 @required actor: app.bsky.actor.defs.ProfileView; 29 29 } 30 30 } 31 + 32 + // --- Externals --- 33 + 34 + @external 35 + namespace app.bsky.actor.defs { 36 + model ProfileView { } 37 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/feed/getListFeed.tsp
··· 3 3 namespace app.bsky.feed.getListFeed { 4 4 model UnknownList {} 5 5 6 - @doc("Get a feed of recent posts from a list (posts and reposts from any actors on the list). Does not require auth.") 6 + /** Get a feed of recent posts from a list (posts and reposts from any actors on the list). Does not require auth. */ 7 7 @query 8 8 @errors(UnknownList) 9 9 op main( 10 - @doc("Reference (AT-URI) to the list record.") 11 - list: atUri, 10 + /** Reference (AT-URI) to the list record. */ 11 + @required list: atUri, 12 12 13 13 @minValue(1) 14 14 @maxValue(100) ··· 20 20 @required feed: app.bsky.feed.defs.FeedViewPost[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.feed.defs { 28 + model FeedViewPost { } 29 + }
+15 -5
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPostThread.tsp
··· 3 3 namespace app.bsky.feed.getPostThread { 4 4 model NotFound {} 5 5 6 - @doc("Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests.") 6 + /** Get posts in a thread. Does not require auth, but additional metadata and filtering will be applied for authed requests. */ 7 7 @query 8 8 @errors(NotFound) 9 9 op main( 10 - @doc("Reference (AT-URI) to post record.") 11 - uri: atUri, 10 + /** Reference (AT-URI) to post record. */ 11 + @required uri: atUri, 12 12 13 - @doc("How many levels of reply depth should be included in response.") 13 + /** How many levels of reply depth should be included in response. */ 14 14 @minValue(0) 15 15 @maxValue(1000) 16 16 depth?: int32 = 6, 17 17 18 - @doc("How many levels of parent (and grandparent, etc) post to include.") 18 + /** How many levels of parent (and grandparent, etc) post to include. */ 19 19 @minValue(0) 20 20 @maxValue(1000) 21 21 parentHeight?: int32 = 80 ··· 26 26 threadgate?: app.bsky.feed.defs.ThreadgateView; 27 27 }; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.feed.defs { 34 + model ThreadViewPost { } 35 + model NotFoundPost { } 36 + model BlockedPost { } 37 + model ThreadgateView { } 38 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPosts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getPosts { 4 - @doc("Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'.") 4 + /** Gets post views for a specified list of posts (by AT-URI). This is sometimes referred to as 'hydrating' a 'feed skeleton'. */ 5 5 @query 6 6 op main( 7 - @doc("List of post AT-URIs to return hydrated views for.") 7 + /** List of post AT-URIs to return hydrated views for. */ 8 8 @maxItems(25) 9 - uris: atUri[] 9 + @required uris: atUri[] 10 10 ): { 11 11 @required posts: app.bsky.feed.defs.PostView[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.feed.defs { 19 + model PostView { } 20 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/feed/getQuotes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getQuotes { 4 - @doc("Get a list of quotes for a given post.") 4 + /** Get a list of quotes for a given post. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) of post record") 8 - uri: atUri, 7 + /** Reference (AT-URI) of post record */ 8 + @required uri: atUri, 9 9 10 - @doc("If supplied, filters to quotes of specific version (by CID) of the post record.") 10 + /** If supplied, filters to quotes of specific version (by CID) of the post record. */ 11 11 cid?: cid, 12 12 13 13 @minValue(1) ··· 22 22 @required posts: app.bsky.feed.defs.PostView[]; 23 23 }; 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace app.bsky.feed.defs { 30 + model PostView { } 31 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/feed/getRepostedBy.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getRepostedBy { 4 - @doc("Get a list of reposts for a given post.") 4 + /** Get a list of reposts for a given post. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) of post record") 8 - uri: atUri, 7 + /** Reference (AT-URI) of post record */ 8 + @required uri: atUri, 9 9 10 - @doc("If supplied, filters to reposts of specific version (by CID) of the post record.") 10 + /** If supplied, filters to reposts of specific version (by CID) of the post record. */ 11 11 cid?: cid, 12 12 13 13 @minValue(1) ··· 22 22 @required repostedBy: app.bsky.actor.defs.ProfileView[]; 23 23 }; 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace app.bsky.actor.defs { 30 + model ProfileView { } 31 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/feed/getSuggestedFeeds.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getSuggestedFeeds { 4 - @doc("Get a list of suggested feeds (feed generators) for the requesting account.") 4 + /** Get a list of suggested feeds (feed generators) for the requesting account. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required feeds: app.bsky.feed.defs.GeneratorView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.feed.defs { 22 + model GeneratorView { } 23 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/feed/getTimeline.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.getTimeline { 4 - @doc("Get a view of the requesting account's home timeline. This is expected to be some form of reverse-chronological feed.") 4 + /** Get a view of the requesting account's home timeline. This is expected to be some form of reverse-chronological feed. */ 5 5 @query 6 6 op main( 7 - @doc("Variant 'algorithm' for timeline. Implementation-specific. NOTE: most feed flexibility has been moved to feed generator mechanism.") 7 + /** Variant 'algorithm' for timeline. Implementation-specific. NOTE: most feed flexibility has been moved to feed generator mechanism. */ 8 8 algorithm?: string, 9 9 10 10 @minValue(1) ··· 17 17 @required feed: app.bsky.feed.defs.FeedViewPost[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace app.bsky.feed.defs { 25 + model FeedViewPost { } 26 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/feed/like.tsp
··· 2 2 3 3 namespace app.bsky.feed.like { 4 4 @rec("tid") 5 - @doc("Record declaring a 'like' of a piece of subject content.") 5 + /** Record declaring a 'like' of a piece of subject content. */ 6 6 model Main { 7 7 @required 8 8 subject: com.atproto.repo.strongRef.Main; ··· 13 13 via?: com.atproto.repo.strongRef.Main; 14 14 } 15 15 } 16 + 17 + // --- Externals --- 18 + 19 + @external 20 + namespace com.atproto.repo.strongRef { 21 + model Main { } 22 + }
+53 -11
packages/emitter/test/integration/atproto/input/app/bsky/feed/post.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.post { 4 - @doc("Record containing a Bluesky post.") 4 + /** Record containing a Bluesky post. */ 5 5 @rec("tid") 6 6 model Main { 7 - @doc("The primary post content. May be an empty string, if there are embeds.") 7 + /** The primary post content. May be an empty string, if there are embeds. */ 8 8 @maxGraphemes(300) 9 9 @maxLength(3000) 10 10 @required 11 11 text: string; 12 12 13 - @doc("DEPRECATED: replaced by app.bsky.richtext.facet.") 13 + /** DEPRECATED: replaced by app.bsky.richtext.facet. */ 14 14 entities?: Entity[]; 15 15 16 - @doc("Annotations of text (mentions, URLs, hashtags, etc)") 16 + /** Annotations of text (mentions, URLs, hashtags, etc) */ 17 17 facets?: app.bsky.richtext.facet.Main[]; 18 18 19 19 reply?: ReplyRef; ··· 27 27 | unknown 28 28 ); 29 29 30 - @doc("Indicates human language of post primary text content.") 30 + /** Indicates human language of post primary text content. */ 31 31 @maxItems(3) 32 32 langs?: language[]; 33 33 34 - @doc("Self-label values for this post. Effectively content warnings.") 34 + /** Self-label values for this post. Effectively content warnings. */ 35 35 labels?: (com.atproto.label.defs.SelfLabels | unknown); 36 36 37 - @doc("Additional hashtags, in addition to any included in post text and facets.") 37 + /** Additional hashtags, in addition to any included in post text and facets. */ 38 38 @maxItems(8) 39 39 tags?: PostTag[]; 40 40 41 - @doc("Client-declared timestamp when this post was originally created.") 41 + /** Client-declared timestamp when this post was originally created. */ 42 42 @required 43 43 createdAt: datetime; 44 44 } ··· 48 48 @required parent: com.atproto.repo.strongRef.Main; 49 49 } 50 50 51 - @doc("Deprecated: use facets instead.") 51 + /** Deprecated: use facets instead. */ 52 52 model Entity { 53 53 @required index: TextSlice; 54 54 55 - @doc("Expected values are 'mention' and 'link'.") 55 + /** Expected values are 'mention' and 'link'. */ 56 56 @required 57 57 type: string; 58 58 59 59 @required value: string; 60 60 } 61 61 62 - @doc("Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings.") 62 + /** Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings. */ 63 63 model TextSlice { 64 64 @minValue(0) 65 65 @required ··· 74 74 @maxGraphemes(64) 75 75 @maxLength(640) 76 76 scalar PostTag extends string; 77 + 78 + // --- Externals --- 79 + 80 + @external 81 + namespace app.bsky.richtext.facet { 82 + model Main { } 83 + } 84 + 85 + @external 86 + namespace app.bsky.embed.images { 87 + model Main { } 88 + } 89 + 90 + @external 91 + namespace app.bsky.embed.video { 92 + model Main { } 93 + } 94 + 95 + @external 96 + namespace app.bsky.embed.external { 97 + model Main { } 98 + } 99 + 100 + @external 101 + namespace app.bsky.embed.`record` { 102 + model Main { } 103 + } 104 + 105 + @external 106 + namespace app.bsky.embed.recordWithMedia { 107 + model Main { } 108 + } 109 + 110 + @external 111 + namespace com.atproto.label.defs { 112 + model SelfLabels { } 113 + } 114 + 115 + @external 116 + namespace com.atproto.repo.strongRef { 117 + model Main { } 118 + }
+5 -5
packages/emitter/test/integration/atproto/input/app/bsky/feed/postgate.tsp
··· 2 2 3 3 namespace app.bsky.feed.postgate { 4 4 @rec("tid") 5 - @doc("Record defining interaction rules for a post. The record key (rkey) of the postgate record must match the record key of the post, and that record must be in the same repository.") 5 + /** Record defining interaction rules for a post. The record key (rkey) of the postgate record must match the record key of the post, and that record must be in the same repository. */ 6 6 model Main { 7 - @doc("Reference (AT-URI) to the post record.") 7 + /** Reference (AT-URI) to the post record. */ 8 8 @required 9 9 post: atUri; 10 10 11 11 @required createdAt: datetime; 12 12 13 - @doc("List of AT-URIs embedding this post that the author has detached from.") 13 + /** List of AT-URIs embedding this post that the author has detached from. */ 14 14 @maxItems(50) 15 15 detachedEmbeddingUris?: atUri[]; 16 16 17 - @doc("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.") 17 + /** 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. */ 18 18 @maxItems(5) 19 19 embeddingRules?: (DisableRule | unknown)[]; 20 20 } 21 21 22 - @doc("Disables embedding of this post.") 22 + /** Disables embedding of this post. */ 23 23 model DisableRule {} 24 24 }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/feed/repost.tsp
··· 2 2 3 3 namespace app.bsky.feed.repost { 4 4 @rec("tid") 5 - @doc("Record representing a 'repost' of an existing Bluesky post.") 5 + /** Record representing a 'repost' of an existing Bluesky post. */ 6 6 model Main { 7 7 @required subject: com.atproto.repo.strongRef.Main; 8 8 @required createdAt: datetime; 9 9 via?: com.atproto.repo.strongRef.Main; 10 10 } 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace com.atproto.repo.strongRef { 17 + model Main { } 18 + }
+21 -14
packages/emitter/test/integration/atproto/input/app/bsky/feed/searchPosts.tsp
··· 7 7 namespace app.bsky.feed.searchPosts { 8 8 model BadQueryString {} 9 9 10 - @doc("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.") 10 + /** 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. */ 11 11 @query 12 12 @errors(BadQueryString) 13 13 op main( 14 - @doc("Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended.") 15 - q: string, 14 + /** Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. */ 15 + @required q: string, 16 16 17 - @doc("Specifies the ranking order of results.") 17 + /** Specifies the ranking order of results. */ 18 18 sort?: "top" | "latest" | string = "latest", 19 19 20 - @doc("Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD).") 20 + /** Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD). */ 21 21 since?: string, 22 22 23 - @doc("Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD).") 23 + /** Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD). */ 24 24 until?: string, 25 25 26 - @doc("Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions.") 26 + /** Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions. */ 27 27 mentions?: atIdentifier, 28 28 29 - @doc("Filter to posts by the given account. Handles are resolved to DID before query-time.") 29 + /** Filter to posts by the given account. Handles are resolved to DID before query-time. */ 30 30 author?: atIdentifier, 31 31 32 - @doc("Filter to posts in the given language. Expected to be based on post language field, though server may override language detection.") 32 + /** Filter to posts in the given language. Expected to be based on post language field, though server may override language detection. */ 33 33 lang?: language, 34 34 35 - @doc("Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization.") 35 + /** Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization. */ 36 36 domain?: string, 37 37 38 - @doc("Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching.") 38 + /** Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching. */ 39 39 url?: uri, 40 40 41 - @doc("Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching.") 41 + /** Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching. */ 42 42 tag?: TagString[], 43 43 44 44 @minValue(1) 45 45 @maxValue(100) 46 46 limit?: int32 = 25, 47 47 48 - @doc("Optional pagination mechanism; may not necessarily allow scrolling through entire result set.") 48 + /** Optional pagination mechanism; may not necessarily allow scrolling through entire result set. */ 49 49 cursor?: string 50 50 ): { 51 51 cursor?: string; 52 52 53 - @doc("Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits.") 53 + /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ 54 54 hitsTotal?: int32; 55 55 56 56 @required posts: app.bsky.feed.defs.PostView[]; 57 57 }; 58 58 } 59 + 60 + // --- Externals --- 61 + 62 + @external 63 + namespace app.bsky.feed.defs { 64 + model PostView { } 65 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/feed/sendInteractions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.feed.sendInteractions { 4 - @doc("Send information about interactions with feed items back to the feed generator that served them.") 4 + /** Send information about interactions with feed items back to the feed generator that served them. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required interactions: app.bsky.feed.defs.Interaction[]; 8 8 }): {}; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace app.bsky.feed.defs { 15 + model Interaction { } 16 + }
+8 -8
packages/emitter/test/integration/atproto/input/app/bsky/feed/threadgate.tsp
··· 2 2 3 3 namespace app.bsky.feed.threadgate { 4 4 @rec("tid") 5 - @doc("Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository.") 5 + /** Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository. */ 6 6 model Main { 7 - @doc("Reference (AT-URI) to the post record.") 7 + /** Reference (AT-URI) to the post record. */ 8 8 @required 9 9 post: atUri; 10 10 11 - @doc("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.") 11 + /** 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. */ 12 12 @maxItems(5) 13 13 allow?: (MentionRule | FollowerRule | FollowingRule | ListRule | unknown)[]; 14 14 15 15 @required createdAt: datetime; 16 16 17 - @doc("List of hidden reply URIs.") 17 + /** List of hidden reply URIs. */ 18 18 @maxItems(50) 19 19 hiddenReplies?: atUri[]; 20 20 } 21 21 22 - @doc("Allow replies from actors mentioned in your post.") 22 + /** Allow replies from actors mentioned in your post. */ 23 23 model MentionRule {} 24 24 25 - @doc("Allow replies from actors who follow you.") 25 + /** Allow replies from actors who follow you. */ 26 26 model FollowerRule {} 27 27 28 - @doc("Allow replies from actors you follow.") 28 + /** Allow replies from actors you follow. */ 29 29 model FollowingRule {} 30 30 31 - @doc("Allow replies from actors on a list.") 31 + /** Allow replies from actors on a list. */ 32 32 model ListRule { 33 33 @required list: atUri; 34 34 }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/block.tsp
··· 2 2 3 3 namespace app.bsky.graph.block { 4 4 @rec("tid") 5 - @doc("Record declaring a 'block' relationship against another account. NOTE: blocks are public in Bluesky; see blog posts for details.") 5 + /** Record declaring a 'block' relationship against another account. NOTE: blocks are public in Bluesky; see blog posts for details. */ 6 6 model Main { 7 - @doc("DID of the account to be blocked.") 7 + /** DID of the account to be blocked. */ 8 8 @required 9 9 subject: did; 10 10
+33 -11
packages/emitter/test/integration/atproto/input/app/bsky/graph/defs.tsp
··· 97 97 98 98 union ListPurpose { 99 99 string, 100 - 101 - Modlist: "app.bsky.graph.defs#modlist", 102 - Curatelist: "app.bsky.graph.defs#curatelist", 103 - Referencelist: "app.bsky.graph.defs#referencelist", 100 + Modlist, 101 + Curatelist, 102 + Referencelist, 104 103 } 105 104 106 - @doc("A list of actors to apply an aggregate moderation action (mute/block) on.") 105 + /** A list of actors to apply an aggregate moderation action (mute/block) on. */ 107 106 @token 108 107 model Modlist {} 109 108 110 - @doc("A list of actors used for curation purposes such as list feeds or interaction gating.") 109 + /** A list of actors used for curation purposes such as list feeds or interaction gating. */ 111 110 @token 112 111 model Curatelist {} 113 112 114 - @doc("A list of actors used for only for reference purposes such as within a starter pack.") 113 + /** A list of actors used for only for reference purposes such as within a starter pack. */ 115 114 @token 116 115 model Referencelist {} 117 116 ··· 120 119 blocked?: atUri; 121 120 } 122 121 123 - @doc("indicates that a handle or DID could not be resolved") 122 + /** indicates that a handle or DID could not be resolved */ 124 123 model NotFoundActor { 125 124 @required actor: atIdentifier; 126 125 ··· 129 128 notFound: boolean = true; 130 129 } 131 130 132 - @doc("lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object)") 131 + /** lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object) */ 133 132 model Relationship { 134 133 @required did: did; 135 134 136 - @doc("if the actor follows this DID, this is the AT-URI of the follow record") 135 + /** if the actor follows this DID, this is the AT-URI of the follow record */ 137 136 following?: atUri; 138 137 139 - @doc("if the actor is followed by this DID, contains the AT-URI of the follow record") 138 + /** if the actor is followed by this DID, contains the AT-URI of the follow record */ 140 139 followedBy?: atUri; 141 140 } 142 141 } 142 + 143 + // --- Externals --- 144 + 145 + @external 146 + namespace com.atproto.label.defs { 147 + model Label { } 148 + } 149 + 150 + @external 151 + namespace app.bsky.actor.defs { 152 + model ProfileView { } 153 + model ProfileViewBasic { } 154 + } 155 + 156 + @external 157 + namespace app.bsky.richtext.facet { 158 + model Main { } 159 + } 160 + 161 + @external 162 + namespace app.bsky.feed.defs { 163 + model GeneratorView { } 164 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/follow.tsp
··· 2 2 3 3 namespace app.bsky.graph.follow { 4 4 @rec("tid") 5 - @doc("Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView.") 5 + /** Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView. */ 6 6 model Main { 7 7 @required subject: did; 8 8 @required createdAt: datetime;
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getActorStarterPacks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getActorStarterPacks { 4 - @doc("Get a list of starter packs created by the actor.") 4 + /** Get a list of starter packs created by the actor. */ 5 5 @query 6 6 op main( 7 7 @required actor: atIdentifier, ··· 16 16 @required starterPacks: app.bsky.graph.defs.StarterPackViewBasic[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace app.bsky.graph.defs { 24 + model StarterPackViewBasic { } 25 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getBlocks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getBlocks { 4 - @doc("Enumerates which accounts the requesting account is currently blocking. Requires auth.") 4 + /** Enumerates which accounts the requesting account is currently blocking. Requires auth. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required blocks: app.bsky.actor.defs.ProfileView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.actor.defs { 22 + model ProfileView { } 23 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollowers.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getFollowers { 4 - @doc("Enumerates accounts which follow a specified account (actor).") 4 + /** Enumerates accounts which follow a specified account (actor). */ 5 5 @query 6 6 op main( 7 - actor: atIdentifier, 7 + @required actor: atIdentifier, 8 8 9 9 @minValue(1) 10 10 @maxValue(100) ··· 17 17 @required followers: app.bsky.actor.defs.ProfileView[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace app.bsky.actor.defs { 25 + model ProfileView { } 26 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollows.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getFollows { 4 - @doc("Enumerates accounts which a specified account (actor) follows.") 4 + /** Enumerates accounts which a specified account (actor) follows. */ 5 5 @query 6 6 op main( 7 - actor: atIdentifier, 7 + @required actor: atIdentifier, 8 8 9 9 @minValue(1) 10 10 @maxValue(100) ··· 17 17 @required follows: app.bsky.actor.defs.ProfileView[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace app.bsky.actor.defs { 25 + model ProfileView { } 26 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/getKnownFollowers.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getKnownFollowers { 4 - @doc("Enumerates accounts which follow a specified account (actor) and are followed by the viewer.") 4 + /** Enumerates accounts which follow a specified account (actor) and are followed by the viewer. */ 5 5 @query 6 6 op main( 7 - actor: atIdentifier, 7 + @required actor: atIdentifier, 8 8 9 9 @minValue(1) 10 10 @maxValue(100) ··· 17 17 @required followers: app.bsky.actor.defs.ProfileView[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace app.bsky.actor.defs { 25 + model ProfileView { } 26 + }
+11 -3
packages/emitter/test/integration/atproto/input/app/bsky/graph/getList.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getList { 4 - @doc("Gets a 'view' (with additional context) of a specified list.") 4 + /** Gets a 'view' (with additional context) of a specified list. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) of the list record to hydrate.") 8 - list: atUri, 7 + /** Reference (AT-URI) of the list record to hydrate. */ 8 + @required list: atUri, 9 9 10 10 @minValue(1) 11 11 @maxValue(100) ··· 18 18 @required items: app.bsky.graph.defs.ListItemView[]; 19 19 }; 20 20 } 21 + 22 + // --- Externals --- 23 + 24 + @external 25 + namespace app.bsky.graph.defs { 26 + model ListView { } 27 + model ListItemView { } 28 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListBlocks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getListBlocks { 4 - @doc("Get mod lists that the requesting account (actor) is blocking. Requires auth.") 4 + /** Get mod lists that the requesting account (actor) is blocking. Requires auth. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required lists: app.bsky.graph.defs.ListView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.graph.defs { 22 + model ListView { } 23 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListMutes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getListMutes { 4 - @doc("Enumerates mod lists that the requesting account (actor) currently has muted. Requires auth.") 4 + /** Enumerates mod lists that the requesting account (actor) currently has muted. Requires auth. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required lists: app.bsky.graph.defs.ListView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.graph.defs { 22 + model ListView { } 23 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/graph/getLists.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getLists { 4 - @doc("Enumerates the lists created by a specified account (actor).") 4 + /** Enumerates the lists created by a specified account (actor). */ 5 5 @query 6 6 op main( 7 - @doc("The account (actor) to enumerate lists from.") 8 - actor: atIdentifier, 7 + /** The account (actor) to enumerate lists from. */ 8 + @required actor: atIdentifier, 9 9 10 10 @minValue(1) 11 11 @maxValue(100) ··· 13 13 14 14 cursor?: string, 15 15 16 - @doc("Optional filter by list purpose. If not specified, all supported types are returned.") 16 + /** Optional filter by list purpose. If not specified, all supported types are returned. */ 17 17 purposes?: ("modlist" | "curatelist" | string)[] 18 18 ): { 19 19 cursor?: string; 20 20 @required lists: app.bsky.graph.defs.ListView[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.graph.defs { 28 + model ListView { } 29 + }
+12 -4
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListsWithMembership.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getListsWithMembership { 4 - @doc("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.") 4 + /** 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. */ 5 5 @query 6 6 op main( 7 - @doc("The account (actor) to check for membership.") 7 + /** The account (actor) to check for membership. */ 8 8 @required 9 9 actor: atIdentifier, 10 10 ··· 14 14 15 15 cursor?: string, 16 16 17 - @doc("Optional filter by list purpose. If not specified, all supported types are returned.") 17 + /** Optional filter by list purpose. If not specified, all supported types are returned. */ 18 18 purposes?: ("modlist" | "curatelist" | string)[] 19 19 ): { 20 20 cursor?: string; 21 21 @required listsWithMembership: ListWithMembership[]; 22 22 }; 23 23 24 - @doc("A list and an optional list item indicating membership of a target user to that list.") 24 + /** A list and an optional list item indicating membership of a target user to that list. */ 25 25 model ListWithMembership { 26 26 @required list: app.bsky.graph.defs.ListView; 27 27 listItem?: app.bsky.graph.defs.ListItemView; 28 28 } 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace app.bsky.graph.defs { 35 + model ListView { } 36 + model ListItemView { } 37 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getMutes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getMutes { 4 - @doc("Enumerates accounts that the requesting account (actor) currently has muted. Requires auth.") 4 + /** Enumerates accounts that the requesting account (actor) currently has muted. Requires auth. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required mutes: app.bsky.actor.defs.ProfileView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.actor.defs { 22 + model ProfileView { } 23 + }
+12 -4
packages/emitter/test/integration/atproto/input/app/bsky/graph/getRelationships.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getRelationships { 4 - @doc("Enumerates public relationships between one account, and a list of other accounts. Does not require auth.") 4 + /** Enumerates public relationships between one account, and a list of other accounts. Does not require auth. */ 5 5 @query 6 6 @errors(ActorNotFound) 7 7 op main( 8 - @doc("Primary account requesting relationships for.") 8 + /** Primary account requesting relationships for. */ 9 9 @required 10 10 actor: atIdentifier, 11 11 12 - @doc("List of 'other' accounts to be related back to the primary.") 12 + /** List of 'other' accounts to be related back to the primary. */ 13 13 @maxItems(30) 14 14 others?: atIdentifier[] 15 15 ): { ··· 23 23 )[]; 24 24 }; 25 25 26 - @doc("the primary actor at-identifier could not be resolved") 26 + /** the primary actor at-identifier could not be resolved */ 27 27 model ActorNotFound {} 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.graph.defs { 34 + model Relationship { } 35 + model NotFoundActor { } 36 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPack.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getStarterPack { 4 - @doc("Gets a view of a starter pack.") 4 + /** Gets a view of a starter pack. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) of the starter pack record.") 7 + /** Reference (AT-URI) of the starter pack record. */ 8 8 @required 9 9 starterPack: atUri 10 10 ): { 11 11 @required starterPack: app.bsky.graph.defs.StarterPackView; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.graph.defs { 19 + model StarterPackView { } 20 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getStarterPacks { 4 - @doc("Get views for a list of starter packs.") 4 + /** Get views for a list of starter packs. */ 5 5 @query 6 6 op main( 7 7 @maxItems(25) ··· 11 11 @required starterPacks: app.bsky.graph.defs.StarterPackViewBasic[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.graph.defs { 19 + model StarterPackViewBasic { } 20 + }
+11 -3
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacksWithMembership.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getStarterPacksWithMembership { 4 - @doc("Enumerates the starter packs created by the session user, and includes membership information about `actor` in those starter packs. Requires auth.") 4 + /** Enumerates the starter packs created by the session user, and includes membership information about `actor` in those starter packs. Requires auth. */ 5 5 @query 6 6 op main( 7 - @doc("The account (actor) to check for membership.") 7 + /** The account (actor) to check for membership. */ 8 8 @required 9 9 actor: atIdentifier, 10 10 ··· 18 18 @required starterPacksWithMembership: StarterPackWithMembership[]; 19 19 }; 20 20 21 - @doc("A starter pack and an optional list item indicating membership of a target user to that starter pack.") 21 + /** A starter pack and an optional list item indicating membership of a target user to that starter pack. */ 22 22 model StarterPackWithMembership { 23 23 @required starterPack: app.bsky.graph.defs.StarterPackView; 24 24 listItem?: app.bsky.graph.defs.ListItemView; 25 25 } 26 26 } 27 + 28 + // --- Externals --- 29 + 30 + @external 31 + namespace app.bsky.graph.defs { 32 + model StarterPackView { } 33 + model ListItemView { } 34 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/graph/getSuggestedFollowsByActor.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.getSuggestedFollowsByActor { 4 - @doc("Enumerates follows similar to a given account (actor). Expected use is to recommend additional accounts immediately after following one account.") 4 + /** Enumerates follows similar to a given account (actor). Expected use is to recommend additional accounts immediately after following one account. */ 5 5 @query 6 6 op main( 7 - actor: atIdentifier 7 + @required actor: atIdentifier 8 8 ): { 9 9 @required 10 10 suggestions: app.bsky.actor.defs.ProfileView[]; 11 11 12 - @doc("If true, response has fallen-back to generic results, and is not scoped using relativeToDid") 12 + /** If true, response has fallen-back to generic results, and is not scoped using relativeToDid */ 13 13 isFallback?: boolean = false; 14 14 15 - @doc("Snowflake for this recommendation, use when submitting recommendation events.") 15 + /** Snowflake for this recommendation, use when submitting recommendation events. */ 16 16 recId?: int32; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace app.bsky.actor.defs { 24 + model ProfileView { } 25 + }
+20 -3
packages/emitter/test/integration/atproto/input/app/bsky/graph/list.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.list { 4 - @doc("Record representing a list of accounts (actors). Scope includes both moderation-oriented lists and curration-oriented lists.") 4 + /** Record representing a list of accounts (actors). Scope includes both moderation-oriented lists and curration-oriented lists. */ 5 5 @rec("tid") 6 6 model Main { 7 - @doc("Display name for list; can not be empty.") 7 + /** Display name for list; can not be empty. */ 8 8 @minLength(1) 9 9 @maxLength(64) 10 10 @required 11 11 name: string; 12 12 13 - @doc("Defines the purpose of the list (aka, moderation-oriented or curration-oriented)") 13 + /** Defines the purpose of the list (aka, moderation-oriented or curration-oriented) */ 14 14 @required 15 15 purpose: app.bsky.graph.defs.ListPurpose; 16 16 ··· 27 27 @required createdAt: datetime; 28 28 } 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace app.bsky.graph.defs { 35 + model ListPurpose { } 36 + } 37 + 38 + @external 39 + namespace app.bsky.richtext.facet { 40 + model Main { } 41 + } 42 + 43 + @external 44 + namespace com.atproto.label.defs { 45 + model SelfLabels { } 46 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/listblock.tsp
··· 2 2 3 3 namespace app.bsky.graph.listblock { 4 4 @rec("tid") 5 - @doc("Record representing a block relationship against an entire an entire list of accounts (actors).") 5 + /** Record representing a block relationship against an entire an entire list of accounts (actors). */ 6 6 model Main { 7 - @doc("Reference (AT-URI) to the mod list record.") 7 + /** Reference (AT-URI) to the mod list record. */ 8 8 @required 9 9 subject: atUri; 10 10
+3 -3
packages/emitter/test/integration/atproto/input/app/bsky/graph/listitem.tsp
··· 2 2 3 3 namespace app.bsky.graph.listitem { 4 4 @rec("tid") 5 - @doc("Record representing an account's inclusion on a specific list. The AppView will ignore duplicate listitem records.") 5 + /** Record representing an account's inclusion on a specific list. The AppView will ignore duplicate listitem records. */ 6 6 model Main { 7 - @doc("The account which is included on the list.") 7 + /** The account which is included on the list. */ 8 8 @required 9 9 subject: did; 10 10 11 - @doc("Reference (AT-URI) to the list record (app.bsky.graph.list).") 11 + /** Reference (AT-URI) to the list record (app.bsky.graph.list). */ 12 12 @required 13 13 list: atUri; 14 14
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/muteActor.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.muteActor { 4 - @doc("Creates a mute relationship for the specified account. Mutes are private in Bluesky. Requires auth.") 4 + /** Creates a mute relationship for the specified account. Mutes are private in Bluesky. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required actor: atIdentifier;
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/muteActorList.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.muteActorList { 4 - @doc("Creates a mute relationship for the specified list of accounts. Mutes are private in Bluesky. Requires auth.") 4 + /** Creates a mute relationship for the specified list of accounts. Mutes are private in Bluesky. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required list: atUri;
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/muteThread.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.muteThread { 4 - @doc("Mutes a thread preventing notifications from the thread and any of its children. Mutes are private in Bluesky. Requires auth.") 4 + /** Mutes a thread preventing notifications from the thread and any of its children. Mutes are private in Bluesky. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required root: atUri;
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/graph/searchStarterPacks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.searchStarterPacks { 4 - @doc("Find starter packs matching search criteria. Does not require auth.") 4 + /** Find starter packs matching search criteria. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("Search query string. Syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended.") 7 + /** Search query string. Syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. */ 8 8 @required 9 9 q: string, 10 10 ··· 18 18 @required starterPacks: app.bsky.graph.defs.StarterPackViewBasic[]; 19 19 }; 20 20 } 21 + 22 + // --- Externals --- 23 + 24 + @external 25 + namespace app.bsky.graph.defs { 26 + model StarterPackViewBasic { } 27 + }
+10 -3
packages/emitter/test/integration/atproto/input/app/bsky/graph/starterpack.tsp
··· 2 2 3 3 namespace app.bsky.graph.starterpack { 4 4 @rec("tid") 5 - @doc("Record defining a starter pack of actors and feeds for new users.") 5 + /** Record defining a starter pack of actors and feeds for new users. */ 6 6 model Main { 7 - @doc("Display name for starter pack; can not be empty.") 7 + /** Display name for starter pack; can not be empty. */ 8 8 @minLength(1) 9 9 @maxLength(500) 10 10 @maxGraphemes(50) ··· 17 17 18 18 descriptionFacets?: app.bsky.richtext.facet.Main[]; 19 19 20 - @doc("Reference (AT-URI) to the list record.") 20 + /** Reference (AT-URI) to the list record. */ 21 21 @required 22 22 list: atUri; 23 23 ··· 33 33 uri: atUri; 34 34 } 35 35 } 36 + 37 + // --- Externals --- 38 + 39 + @external 40 + namespace app.bsky.richtext.facet { 41 + model Main { } 42 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/unmuteActor.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.unmuteActor { 4 - @doc("Unmutes the specified account. Requires auth.") 4 + /** Unmutes the specified account. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required actor: atIdentifier;
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/unmuteActorList.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.unmuteActorList { 4 - @doc("Unmutes the specified list of accounts. Requires auth.") 4 + /** Unmutes the specified list of accounts. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required list: atUri;
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/graph/unmuteThread.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.graph.unmuteThread { 4 - @doc("Unmutes the specified thread. Requires auth.") 4 + /** Unmutes the specified thread. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required root: atUri;
+5 -5
packages/emitter/test/integration/atproto/input/app/bsky/graph/verification.tsp
··· 2 2 3 3 namespace app.bsky.graph.verification { 4 4 @rec("tid") 5 - @doc("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.") 5 + /** 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. */ 6 6 model Main { 7 - @doc("DID of the subject the verification applies to.") 7 + /** DID of the subject the verification applies to. */ 8 8 @required 9 9 subject: did; 10 10 11 - @doc("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.") 11 + /** 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. */ 12 12 @required 13 13 handle: handle; 14 14 15 - @doc("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.") 15 + /** 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. */ 16 16 @required 17 17 displayName: string; 18 18 19 - @doc("Date of when the verification was created.") 19 + /** Date of when the verification was created. */ 20 20 @required 21 21 createdAt: datetime; 22 22 }
+25 -5
packages/emitter/test/integration/atproto/input/app/bsky/labeler/defs.tsp
··· 31 31 32 32 labels?: com.atproto.label.defs.Label[]; 33 33 34 - @doc("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.") 34 + /** 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. */ 35 35 reasonTypes?: com.atproto.moderation.defs.ReasonType[]; 36 36 37 - @doc("The set of subject types (account, record, etc) this service accepts reports on.") 37 + /** The set of subject types (account, record, etc) this service accepts reports on. */ 38 38 subjectTypes?: com.atproto.moderation.defs.SubjectType[]; 39 39 40 - @doc("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.") 40 + /** 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. */ 41 41 subjectCollections?: nsid[]; 42 42 } 43 43 ··· 46 46 } 47 47 48 48 model LabelerPolicies { 49 - @doc("The label values which this labeler publishes. May include global or custom labels.") 49 + /** The label values which this labeler publishes. May include global or custom labels. */ 50 50 @required 51 51 labelValues: com.atproto.label.defs.LabelValue[]; 52 52 53 - @doc("Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler.") 53 + /** Label values created by this labeler and scoped exclusively to it. Labels defined here will override global label definitions for this labeler. */ 54 54 labelValueDefinitions?: com.atproto.label.defs.LabelValueDefinition[]; 55 55 } 56 56 } 57 + 58 + // --- Externals --- 59 + 60 + @external 61 + namespace app.bsky.actor.defs { 62 + model ProfileView { } 63 + } 64 + 65 + @external 66 + namespace com.atproto.label.defs { 67 + model Label { } 68 + model LabelValue { } 69 + model LabelValueDefinition { } 70 + } 71 + 72 + @external 73 + namespace com.atproto.moderation.defs { 74 + model ReasonType { } 75 + model SubjectType { } 76 + }
+9 -1
packages/emitter/test/integration/atproto/input/app/bsky/labeler/getServices.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.labeler.getServices { 4 - @doc("Get information about a list of labeler services.") 4 + /** Get information about a list of labeler services. */ 5 5 @query 6 6 op main( 7 7 @required dids: did[], ··· 15 15 )[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace app.bsky.labeler.defs { 23 + model LabelerView { } 24 + model LabelerViewDetailed { } 25 + }
+22 -4
packages/emitter/test/integration/atproto/input/app/bsky/labeler/service.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.labeler.service { 4 - @doc("A declaration of the existence of labeler service.") 4 + /** A declaration of the existence of labeler service. */ 5 5 @rec("literal:self") 6 6 model Main { 7 7 @required policies: app.bsky.labeler.defs.LabelerPolicies; ··· 10 10 11 11 @required createdAt: datetime; 12 12 13 - @doc("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.") 13 + /** 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. */ 14 14 reasonTypes?: com.atproto.moderation.defs.ReasonType[]; 15 15 16 - @doc("The set of subject types (account, record, etc) this service accepts reports on.") 16 + /** The set of subject types (account, record, etc) this service accepts reports on. */ 17 17 subjectTypes?: com.atproto.moderation.defs.SubjectType[]; 18 18 19 - @doc("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.") 19 + /** 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. */ 20 20 subjectCollections?: nsid[]; 21 21 } 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.labeler.defs { 28 + model LabelerPolicies { } 29 + } 30 + 31 + @external 32 + namespace com.atproto.label.defs { 33 + model SelfLabels { } 34 + } 35 + 36 + @external 37 + namespace com.atproto.moderation.defs { 38 + model ReasonType { } 39 + model SubjectType { } 40 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/notification/declaration.tsp
··· 2 2 3 3 namespace app.bsky.notification.declaration { 4 4 @rec("literal:self") 5 - @doc("A declaration of the user's choices related to notifications that can be produced by them.") 5 + /** A declaration of the user's choices related to notifications that can be produced by them. */ 6 6 model Main { 7 - @doc("A declaration of the user's preference for allowing activity subscriptions from other users. Absence of a record implies 'followers'.") 7 + /** A declaration of the user's preference for allowing activity subscriptions from other users. Absence of a record implies 'followers'. */ 8 8 @required 9 9 allowSubscriptions: "followers" | "mutuals" | "none" | string; 10 10 }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/defs.tsp
··· 40 40 @required reply: boolean; 41 41 } 42 42 43 - @doc("Object used to store activity subscription data in stash.") 43 + /** Object used to store activity subscription data in stash. */ 44 44 model SubjectActivitySubscription { 45 45 @required subject: did; 46 46 @required activitySubscription: ActivitySubscription;
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/getPreferences.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.getPreferences { 4 - @doc("Get notification-related preferences for an account. Requires auth.") 4 + /** Get notification-related preferences for an account. Requires auth. */ 5 5 @query 6 6 op main(): { 7 7 @required preferences: app.bsky.notification.defs.Preferences; 8 8 }; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace app.bsky.notification.defs { 15 + model Preferences { } 16 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/getUnreadCount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.getUnreadCount { 4 - @doc("Count the number of unread notifications for the requesting account. Requires auth.") 4 + /** Count the number of unread notifications for the requesting account. Requires auth. */ 5 5 @query 6 6 op main( 7 7 priority?: boolean,
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/listActivitySubscriptions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.listActivitySubscriptions { 4 - @doc("Enumerate all accounts to which the requesting account is subscribed to receive notifications for. Requires auth.") 4 + /** Enumerate all accounts to which the requesting account is subscribed to receive notifications for. Requires auth. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 14 14 @required subscriptions: app.bsky.actor.defs.ProfileView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.actor.defs { 22 + model ProfileView { } 23 + }
+16 -4
packages/emitter/test/integration/atproto/input/app/bsky/notification/listNotifications.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A reason that matches the reason property of #notification.") 3 + /** A reason that matches the reason property of #notification. */ 4 4 scalar ReasonString extends string; 5 5 6 6 namespace app.bsky.notification.listNotifications { 7 - @doc("Enumerate notifications for the requesting account. Requires auth.") 7 + /** Enumerate notifications for the requesting account. Requires auth. */ 8 8 @query 9 9 op main( 10 - @doc("Notification reasons to include in response.") 10 + /** Notification reasons to include in response. */ 11 11 reasons?: ReasonString[], 12 12 13 13 @minValue(1) ··· 29 29 @required cid: cid; 30 30 @required author: app.bsky.actor.defs.ProfileView; 31 31 32 - @doc("The reason why this notification was delivered - e.g. your post was liked, or you received a new follower.") 32 + /** The reason why this notification was delivered - e.g. your post was liked, or you received a new follower. */ 33 33 @required 34 34 reason: "like" | "repost" | "follow" | "mention" | "reply" | "quote" | "starterpack-joined" | "verified" | "unverified" | "like-via-repost" | "repost-via-repost" | "subscribed-post" | string; 35 35 ··· 40 40 labels?: com.atproto.label.defs.Label[]; 41 41 }; 42 42 } 43 + 44 + // --- Externals --- 45 + 46 + @external 47 + namespace app.bsky.actor.defs { 48 + model ProfileView { } 49 + } 50 + 51 + @external 52 + namespace com.atproto.label.defs { 53 + model Label { } 54 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/putActivitySubscription.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.putActivitySubscription { 4 - @doc("Puts an activity subscription entry. The key should be omitted for creation and provided for updates. Requires auth.") 4 + /** Puts an activity subscription entry. The key should be omitted for creation and provided for updates. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required subject: did; ··· 13 13 activitySubscription?: app.bsky.notification.defs.ActivitySubscription; 14 14 }; 15 15 } 16 + 17 + // --- Externals --- 18 + 19 + @external 20 + namespace app.bsky.notification.defs { 21 + model ActivitySubscription { } 22 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/putPreferences.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.putPreferences { 4 - @doc("Set notification-related preferences for an account. Requires auth.") 4 + /** Set notification-related preferences for an account. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required priority: boolean;
+11 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/putPreferencesV2.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.putPreferencesV2 { 4 - @doc("Set notification-related preferences for an account. Requires auth.") 4 + /** Set notification-related preferences for an account. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 chat?: app.bsky.notification.defs.ChatPreference; ··· 21 21 @required preferences: app.bsky.notification.defs.Preferences; 22 22 }; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace app.bsky.notification.defs { 29 + model ChatPreference { } 30 + model FilterablePreference { } 31 + model Preference { } 32 + model Preferences { } 33 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/notification/registerPush.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.registerPush { 4 - @doc("Register to receive push notifications, via a specified service, for the requesting account. Requires auth.") 4 + /** Register to receive push notifications, via a specified service, for the requesting account. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required serviceDid: did; ··· 9 9 @required platform: "ios" | "android" | "web" | string; 10 10 @required appId: string; 11 11 12 - @doc("Set to true when the actor is age restricted") 12 + /** Set to true when the actor is age restricted */ 13 13 ageRestricted?: boolean; 14 14 }): void; 15 15 }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/unregisterPush.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.unregisterPush { 4 - @doc("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.") 4 + /** 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. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required serviceDid: did;
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/notification/updateSeen.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.notification.updateSeen { 4 - @doc("Notify server that the requesting account has seen notifications. Requires auth.") 4 + /** Notify server that the requesting account has seen notifications. Requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required seenAt: datetime;
+5 -5
packages/emitter/test/integration/atproto/input/app/bsky/richtext/facet.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.richtext.facet { 4 - @doc("Annotation of a sub-string within rich text.") 4 + /** Annotation of a sub-string within rich text. */ 5 5 model Main { 6 6 @required index: ByteSlice; 7 7 8 8 @required features: (Mention | Link | Tag | unknown)[]; 9 9 } 10 10 11 - @doc("Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.") 11 + /** Facet feature for mention of another account. The text is usually a handle, including a `@` prefix, but the facet reference is a DID. */ 12 12 model Mention { 13 13 @required did: did; 14 14 } 15 15 16 - @doc("Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.") 16 + /** Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL. */ 17 17 model Link { 18 18 @required uri: uri; 19 19 } 20 20 21 - @doc("Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags').") 21 + /** Facet feature for a hashtag. The text usually includes a `#` prefix, but the facet reference should not (except in the case of 'double hash tags'). */ 22 22 model Tag { 23 23 @maxLength(640) 24 24 @maxGraphemes(64) ··· 26 26 tag: string; 27 27 } 28 28 29 - @doc("Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.") 29 + /** Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets. */ 30 30 model ByteSlice { 31 31 @minValue(0) 32 32 @required
+30 -17
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/defs.tsp
··· 45 45 model ThreadItemPost { 46 46 @required post: app.bsky.feed.defs.PostView; 47 47 48 - @doc("This post has more parents that were not present in the response. This is just a boolean, without the number of parents.") 48 + /** This post has more parents that were not present in the response. This is just a boolean, without the number of parents. */ 49 49 @required 50 50 moreParents: boolean; 51 51 52 - @doc("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.") 52 + /** 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. */ 53 53 @required 54 54 moreReplies: integer; 55 55 56 - @doc("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.") 56 + /** 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. */ 57 57 @required 58 58 opThread: boolean; 59 59 60 - @doc("The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread.") 60 + /** The threadgate created by the author indicates this post as a reply to be hidden for everyone consuming the thread. */ 61 61 @required 62 62 hiddenByThreadgate: boolean; 63 63 64 - @doc("This is by an account muted by the viewer requesting it.") 64 + /** This is by an account muted by the viewer requesting it. */ 65 65 @required 66 66 mutedByViewer: boolean; 67 67 } ··· 74 74 @required author: app.bsky.feed.defs.BlockedAuthor; 75 75 } 76 76 77 - @doc("The computed state of the age assurance process, returned to the user in question on certain authenticated requests.") 77 + /** The computed state of the age assurance process, returned to the user in question on certain authenticated requests. */ 78 78 model AgeAssuranceState { 79 - @doc("The timestamp when this state was last updated.") 79 + /** The timestamp when this state was last updated. */ 80 80 lastInitiatedAt?: datetime; 81 81 82 - @doc("The status of the age assurance process.") 82 + /** The status of the age assurance process. */ 83 83 @required 84 84 status: "unknown" | "pending" | "assured" | "blocked" | string; 85 85 } 86 86 87 - @doc("Object used to store age assurance data in stash.") 87 + /** Object used to store age assurance data in stash. */ 88 88 model AgeAssuranceEvent { 89 - @doc("The date and time of this write operation.") 89 + /** The date and time of this write operation. */ 90 90 @required 91 91 createdAt: datetime; 92 92 93 - @doc("The status of the age assurance process.") 93 + /** The status of the age assurance process. */ 94 94 @required 95 95 status: "unknown" | "pending" | "assured" | string; 96 96 97 - @doc("The unique identifier for this instance of the age assurance flow, in UUID format.") 97 + /** The unique identifier for this instance of the age assurance flow, in UUID format. */ 98 98 @required 99 99 attemptId: string; 100 100 101 - @doc("The email used for AA.") 101 + /** The email used for AA. */ 102 102 email?: string; 103 103 104 - @doc("The IP address used when initiating the AA flow.") 104 + /** The IP address used when initiating the AA flow. */ 105 105 initIp?: string; 106 106 107 - @doc("The user agent used when initiating the AA flow.") 107 + /** The user agent used when initiating the AA flow. */ 108 108 initUa?: string; 109 109 110 - @doc("The IP address used when completing the AA flow.") 110 + /** The IP address used when completing the AA flow. */ 111 111 completeIp?: string; 112 112 113 - @doc("The user agent used when completing the AA flow.") 113 + /** The user agent used when completing the AA flow. */ 114 114 completeUa?: string; 115 115 } 116 116 } 117 + 118 + // --- Externals --- 119 + 120 + @external 121 + namespace app.bsky.actor.defs { 122 + model ProfileViewBasic { } 123 + } 124 + 125 + @external 126 + namespace app.bsky.feed.defs { 127 + model PostView { } 128 + model BlockedAuthor { } 129 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getAgeAssuranceState.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getAgeAssuranceState { 4 - @doc("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.") 4 + /** 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. */ 5 5 @query 6 6 op main(): app.bsky.unspecced.defs.AgeAssuranceState; 7 7 } 8 + 9 + // --- Externals --- 10 + 11 + @external 12 + namespace app.bsky.unspecced.defs { 13 + model AgeAssuranceState { } 14 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getConfig.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getConfig { 4 - @doc("Get miscellaneous runtime configuration.") 4 + /** Get miscellaneous runtime configuration. */ 5 5 @query 6 6 op main(): { 7 7 checkEmailConfirmed?: boolean;
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getOnboardingSuggestedStarterPacks { 4 - @doc("Get a list of suggested starterpacks for onboarding") 4 + /** Get a list of suggested starterpacks for onboarding */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 11 11 @required starterPacks: app.bsky.graph.defs.StarterPackView[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.graph.defs { 19 + model StarterPackView { } 20 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getOnboardingSuggestedStarterPacksSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton { 4 - @doc("Get a skeleton of suggested starterpacks for onboarding. Intended to be called and hydrated by app.bsky.unspecced.getOnboardingSuggestedStarterPacks") 4 + /** Get a skeleton of suggested starterpacks for onboarding. Intended to be called and hydrated by app.bsky.unspecced.getOnboardingSuggestedStarterPacks */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 8 8 viewer?: did, 9 9 10 10 @minValue(1)
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPopularFeedGenerators.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getPopularFeedGenerators { 4 - @doc("An unspecced view of globally popular feed generators.") 4 + /** An unspecced view of globally popular feed generators. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 15 15 @required feeds: app.bsky.feed.defs.GeneratorView[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace app.bsky.feed.defs { 23 + model GeneratorView { } 24 + }
+12 -5
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadOtherV2.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getPostThreadOtherV2 { 4 - @doc("(NOTE: this endpoint is under development and WILL change without notice. Don't use it until it is moved out of `unspecced` or your application WILL break) Get additional posts under a thread e.g. replies hidden by threadgate. Based on an anchor post at any depth of the tree, returns top-level replies below that anchor. It does not include ancestors nor the anchor itself. This should be called after exhausting `app.bsky.unspecced.getPostThreadV2`. Does not require auth, but additional metadata and filtering will be applied for authed requests.") 4 + /** (NOTE: this endpoint is under development and WILL change without notice. Don't use it until it is moved out of `unspecced` or your application WILL break) Get additional posts under a thread e.g. replies hidden by threadgate. Based on an anchor post at any depth of the tree, returns top-level replies below that anchor. It does not include ancestors nor the anchor itself. This should be called after exhausting `app.bsky.unspecced.getPostThreadV2`. Does not require auth, but additional metadata and filtering will be applied for authed requests. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) to post record. This is the anchor post.") 7 + /** Reference (AT-URI) to post record. This is the anchor post. */ 8 8 @required 9 9 anchor: atUri, 10 10 11 - @doc("Whether to prioritize posts from followed users. It only has effect when the user is authenticated.") 11 + /** Whether to prioritize posts from followed users. It only has effect when the user is authenticated. */ 12 12 prioritizeFollowedUsers?: boolean = false 13 13 ): { 14 - @doc("A flat list of other thread items. The depth of each item is indicated by the depth property inside the item.") 14 + /** A flat list of other thread items. The depth of each item is indicated by the depth property inside the item. */ 15 15 @required 16 16 thread: ThreadItem[]; 17 17 }; ··· 19 19 model ThreadItem { 20 20 @required uri: atUri; 21 21 22 - @doc("The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths.") 22 + /** The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths. */ 23 23 @required 24 24 depth: integer; 25 25 26 26 @required value: (app.bsky.unspecced.defs.ThreadItemPost | unknown); 27 27 } 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.unspecced.defs { 34 + model ThreadItemPost { } 35 + }
+25 -10
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadV2.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getPostThreadV2 { 4 - @doc("(NOTE: this endpoint is under development and WILL change without notice. Don't use it until it is moved out of `unspecced` or your application WILL break) Get posts in a thread. It is based in an anchor post at any depth of the tree, and returns posts above it (recursively resolving the parent, without further branching to their replies) and below it (recursive replies, with branching to their replies). Does not require auth, but additional metadata and filtering will be applied for authed requests.") 4 + /** (NOTE: this endpoint is under development and WILL change without notice. Don't use it until it is moved out of `unspecced` or your application WILL break) Get posts in a thread. It is based in an anchor post at any depth of the tree, and returns posts above it (recursively resolving the parent, without further branching to their replies) and below it (recursive replies, with branching to their replies). Does not require auth, but additional metadata and filtering will be applied for authed requests. */ 5 5 @query 6 6 op main( 7 - @doc("Reference (AT-URI) to post record. This is the anchor post, and the thread will be built around it. It can be any post in the tree, not necessarily a root post.") 7 + /** Reference (AT-URI) to post record. This is the anchor post, and the thread will be built around it. It can be any post in the tree, not necessarily a root post. */ 8 8 @required 9 9 anchor: atUri, 10 10 11 - @doc("Whether to include parents above the anchor.") 11 + /** Whether to include parents above the anchor. */ 12 12 above?: boolean = true, 13 13 14 - @doc("How many levels of replies to include below the anchor.") 14 + /** How many levels of replies to include below the anchor. */ 15 15 @minValue(0) 16 16 @maxValue(20) 17 17 below?: int32 = 6, 18 18 19 - @doc("Maximum of replies to include at each level of the thread, except for the direct replies to the anchor, which are (NOTE: currently, during unspecced phase) all returned (NOTE: later they might be paginated).") 19 + /** Maximum of replies to include at each level of the thread, except for the direct replies to the anchor, which are (NOTE: currently, during unspecced phase) all returned (NOTE: later they might be paginated). */ 20 20 @minValue(0) 21 21 @maxValue(100) 22 22 branchingFactor?: int32 = 10, 23 23 24 - @doc("Whether to prioritize posts from followed users. It only has effect when the user is authenticated.") 24 + /** Whether to prioritize posts from followed users. It only has effect when the user is authenticated. */ 25 25 prioritizeFollowedUsers?: boolean = false, 26 26 27 - @doc("Sorting for the thread replies.") 27 + /** Sorting for the thread replies. */ 28 28 sort?: "newest" | "oldest" | "top" | string = "oldest" 29 29 ): { 30 - @doc("A flat list of thread items. The depth of each item is indicated by the depth property inside the item.") 30 + /** A flat list of thread items. The depth of each item is indicated by the depth property inside the item. */ 31 31 @required 32 32 thread: ThreadItem[]; 33 33 34 34 threadgate?: app.bsky.feed.defs.ThreadgateView; 35 35 36 - @doc("Whether this thread has additional replies. If true, a call can be made to the `getPostThreadOtherV2` endpoint to retrieve them.") 36 + /** Whether this thread has additional replies. If true, a call can be made to the `getPostThreadOtherV2` endpoint to retrieve them. */ 37 37 @required 38 38 hasOtherReplies: boolean; 39 39 }; ··· 41 41 model ThreadItem { 42 42 @required uri: atUri; 43 43 44 - @doc("The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths.") 44 + /** The nesting level of this item in the thread. Depth 0 means the anchor item. Items above have negative depths, items below have positive depths. */ 45 45 @required 46 46 depth: integer; 47 47 ··· 55 55 ); 56 56 } 57 57 } 58 + 59 + // --- Externals --- 60 + 61 + @external 62 + namespace app.bsky.feed.defs { 63 + model ThreadgateView { } 64 + } 65 + 66 + @external 67 + namespace app.bsky.unspecced.defs { 68 + model ThreadItemPost { } 69 + model ThreadItemNoUnauthenticated { } 70 + model ThreadItemNotFound { } 71 + model ThreadItemBlocked { } 72 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedFeeds.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedFeeds { 4 - @doc("Get a list of suggested feeds") 4 + /** Get a list of suggested feeds */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 11 11 @required feeds: app.bsky.feed.defs.GeneratorView[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.feed.defs { 19 + model GeneratorView { } 20 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedFeedsSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedFeedsSkeleton { 4 - @doc("Get a skeleton of suggested feeds. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedFeeds") 4 + /** Get a skeleton of suggested feeds. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedFeeds */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 8 8 viewer?: did, 9 9 10 10 @minValue(1)
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedStarterPacks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedStarterPacks { 4 - @doc("Get a list of suggested starterpacks") 4 + /** Get a list of suggested starterpacks */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 11 11 @required starterPacks: app.bsky.graph.defs.StarterPackView[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.graph.defs { 19 + model StarterPackView { } 20 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedStarterPacksSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedStarterPacksSkeleton { 4 - @doc("Get a skeleton of suggested starterpacks. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedStarterpacks") 4 + /** Get a skeleton of suggested starterpacks. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedStarterpacks */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 8 8 viewer?: did, 9 9 10 10 @minValue(1)
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedUsers.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedUsers { 4 - @doc("Get a list of suggested users") 4 + /** Get a list of suggested users */ 5 5 @query 6 6 op main( 7 - @doc("Category of users to get suggestions for.") 7 + /** Category of users to get suggestions for. */ 8 8 category?: string, 9 9 10 10 @minValue(1) ··· 14 14 @required actors: app.bsky.actor.defs.ProfileView[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.actor.defs { 22 + model ProfileView { } 23 + }
+3 -3
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedUsersSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestedUsersSkeleton { 4 - @doc("Get a skeleton of suggested users. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedUsers") 4 + /** Get a skeleton of suggested users. Intended to be called and hydrated by app.bsky.unspecced.getSuggestedUsers */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 8 8 viewer?: did, 9 9 10 - @doc("Category of users to get suggestions for.") 10 + /** Category of users to get suggestions for. */ 11 11 category?: string, 12 12 13 13 @minValue(1)
+12 -5
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestionsSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getSuggestionsSkeleton { 4 - @doc("Get a skeleton of suggested actors. Intended to be called and then hydrated through app.bsky.actor.getSuggestions") 4 + /** Get a skeleton of suggested actors. Intended to be called and then hydrated through app.bsky.actor.getSuggestions */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking.") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. */ 8 8 viewer?: did, 9 9 10 10 @minValue(1) ··· 13 13 14 14 cursor?: string, 15 15 16 - @doc("DID of the account to get suggestions relative to. If not provided, suggestions will be based on the viewer.") 16 + /** DID of the account to get suggestions relative to. If not provided, suggestions will be based on the viewer. */ 17 17 relativeToDid?: did 18 18 ): { 19 19 cursor?: string; 20 20 @required actors: app.bsky.unspecced.defs.SkeletonSearchActor[]; 21 21 22 - @doc("DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer.") 22 + /** DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer. */ 23 23 relativeToDid?: did; 24 24 25 - @doc("Snowflake for this recommendation, use when submitting recommendation events.") 25 + /** Snowflake for this recommendation, use when submitting recommendation events. */ 26 26 recId?: integer; 27 27 }; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace app.bsky.unspecced.defs { 34 + model SkeletonSearchActor { } 35 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTaggedSuggestions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getTaggedSuggestions { 4 - @doc("Get a list of suggestions (feeds and users) tagged with categories") 4 + /** Get a list of suggestions (feeds and users) tagged with categories */ 5 5 @query 6 6 op main(): { 7 7 @required suggestions: Suggestion[];
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendingTopics.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getTrendingTopics { 4 - @doc("Get a list of trending topics") 4 + /** Get a list of trending topics */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking.") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. */ 8 8 viewer?: did, 9 9 10 10 @minValue(1) ··· 15 15 @required suggested: app.bsky.unspecced.defs.TrendingTopic[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace app.bsky.unspecced.defs { 23 + model TrendingTopic { } 24 + }
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrends.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getTrends { 4 - @doc("Get the current trends on the network") 4 + /** Get the current trends on the network */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 11 11 @required trends: app.bsky.unspecced.defs.TrendView[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace app.bsky.unspecced.defs { 19 + model TrendView { } 20 + }
+9 -2
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendsSkeleton.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.unspecced.getTrendsSkeleton { 4 - @doc("Get the skeleton of trends on the network. Intended to be called and then hydrated through app.bsky.unspecced.getTrends") 4 + /** Get the skeleton of trends on the network. Intended to be called and then hydrated through app.bsky.unspecced.getTrends */ 5 5 @query 6 6 op main( 7 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 7 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 8 8 viewer?: did, 9 9 10 10 @minValue(1) ··· 14 14 @required trends: app.bsky.unspecced.defs.SkeletonTrend[]; 15 15 }; 16 16 } 17 + 18 + // --- Externals --- 19 + 20 + @external 21 + namespace app.bsky.unspecced.defs { 22 + model SkeletonTrend { } 23 + }
+11 -4
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/initAgeAssurance.tsp
··· 5 5 model DidTooLong {} 6 6 model InvalidInitiation {} 7 7 8 - @doc("Initiate age assurance for an account. This is a one-time action that will start the process of verifying the user's age.") 8 + /** Initiate age assurance for an account. This is a one-time action that will start the process of verifying the user's age. */ 9 9 @procedure 10 10 @errors(InvalidEmail, DidTooLong, InvalidInitiation) 11 11 op main(input: { 12 - @doc("The user's email address to receive assurance instructions.") 12 + /** The user's email address to receive assurance instructions. */ 13 13 @required 14 14 email: string; 15 15 16 - @doc("The user's preferred language for communication during the assurance process.") 16 + /** The user's preferred language for communication during the assurance process. */ 17 17 @required 18 18 language: string; 19 19 20 - @doc("An ISO 3166-1 alpha-2 code of the user's location.") 20 + /** An ISO 3166-1 alpha-2 code of the user's location. */ 21 21 @required 22 22 countryCode: string; 23 23 }): app.bsky.unspecced.defs.AgeAssuranceState; 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace app.bsky.unspecced.defs { 30 + model AgeAssuranceState { } 31 + }
+14 -7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchActorsSkeleton.tsp
··· 3 3 namespace app.bsky.unspecced.searchActorsSkeleton { 4 4 model BadQueryString {} 5 5 6 - @doc("Backend Actors (profile) search, returns only skeleton.") 6 + /** Backend Actors (profile) search, returns only skeleton. */ 7 7 @query 8 8 @errors(BadQueryString) 9 9 op main( 10 - @doc("Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. For typeahead search, only simple term match is supported, not full syntax.") 11 - q: string, 10 + /** Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. For typeahead search, only simple term match is supported, not full syntax. */ 11 + @required q: string, 12 12 13 - @doc("DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking.") 13 + /** DID of the account making the request (not included for public/unauthenticated queries). Used to boost followed accounts in ranking. */ 14 14 viewer?: did, 15 15 16 - @doc("If true, acts as fast/simple 'typeahead' query.") 16 + /** If true, acts as fast/simple 'typeahead' query. */ 17 17 typeahead?: boolean, 18 18 19 19 @minValue(1) 20 20 @maxValue(100) 21 21 limit?: integer = 25, 22 22 23 - @doc("Optional pagination mechanism; may not necessarily allow scrolling through entire result set.") 23 + /** Optional pagination mechanism; may not necessarily allow scrolling through entire result set. */ 24 24 cursor?: string 25 25 ): { 26 26 cursor?: string; 27 27 28 - @doc("Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits.") 28 + /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ 29 29 hitsTotal?: integer; 30 30 31 31 @required actors: app.bsky.unspecced.defs.SkeletonSearchActor[]; 32 32 }; 33 33 } 34 + 35 + // --- Externals --- 36 + 37 + @external 38 + namespace app.bsky.unspecced.defs { 39 + model SkeletonSearchActor { } 40 + }
+22 -15
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchPostsSkeleton.tsp
··· 7 7 namespace app.bsky.unspecced.searchPostsSkeleton { 8 8 model BadQueryString {} 9 9 10 - @doc("Backend Posts search, returns only skeleton") 10 + /** Backend Posts search, returns only skeleton */ 11 11 @query 12 12 @errors(BadQueryString) 13 13 op main( 14 - @doc("Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended.") 15 - q: string, 14 + /** Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. */ 15 + @required q: string, 16 16 17 - @doc("Specifies the ranking order of results.") 17 + /** Specifies the ranking order of results. */ 18 18 sort?: "top" | "latest" | string = "latest", 19 19 20 - @doc("Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD).") 20 + /** Filter results for posts after the indicated datetime (inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYYY-MM-DD). */ 21 21 since?: string, 22 22 23 - @doc("Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD).") 23 + /** Filter results for posts before the indicated datetime (not inclusive). Expected to use 'sortAt' timestamp, which may not match 'createdAt'. Can be a datetime, or just an ISO date (YYY-MM-DD). */ 24 24 until?: string, 25 25 26 - @doc("Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions.") 26 + /** Filter to posts which mention the given account. Handles are resolved to DID before query-time. Only matches rich-text facet mentions. */ 27 27 mentions?: atIdentifier, 28 28 29 - @doc("Filter to posts by the given account. Handles are resolved to DID before query-time.") 29 + /** Filter to posts by the given account. Handles are resolved to DID before query-time. */ 30 30 author?: atIdentifier, 31 31 32 - @doc("Filter to posts in the given language. Expected to be based on post language field, though server may override language detection.") 32 + /** Filter to posts in the given language. Expected to be based on post language field, though server may override language detection. */ 33 33 lang?: language, 34 34 35 - @doc("Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization.") 35 + /** Filter to posts with URLs (facet links or embeds) linking to the given domain (hostname). Server may apply hostname normalization. */ 36 36 domain?: string, 37 37 38 - @doc("Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching.") 38 + /** Filter to posts with links (facet links or embeds) pointing to this URL. Server may apply URL normalization or fuzzy matching. */ 39 39 url?: uri, 40 40 41 - @doc("Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching.") 41 + /** Filter to posts with the given tag (hashtag), based on rich-text facet or tag field. Do not include the hash (#) prefix. Multiple tags can be specified, with 'AND' matching. */ 42 42 tag?: SkeletonSearchTagString[], 43 43 44 - @doc("DID of the account making the request (not included for public/unauthenticated queries). Used for 'from:me' queries.") 44 + /** DID of the account making the request (not included for public/unauthenticated queries). Used for 'from:me' queries. */ 45 45 viewer?: did, 46 46 47 47 @minValue(1) 48 48 @maxValue(100) 49 49 limit?: integer = 25, 50 50 51 - @doc("Optional pagination mechanism; may not necessarily allow scrolling through entire result set.") 51 + /** Optional pagination mechanism; may not necessarily allow scrolling through entire result set. */ 52 52 cursor?: string 53 53 ): { 54 54 cursor?: string; 55 55 56 - @doc("Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits.") 56 + /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ 57 57 hitsTotal?: integer; 58 58 59 59 @required posts: app.bsky.unspecced.defs.SkeletonSearchPost[]; 60 60 }; 61 61 } 62 + 63 + // --- Externals --- 64 + 65 + @external 66 + namespace app.bsky.unspecced.defs { 67 + model SkeletonSearchPost { } 68 + }
+13 -6
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchStarterPacksSkeleton.tsp
··· 3 3 namespace app.bsky.unspecced.searchStarterPacksSkeleton { 4 4 model BadQueryString {} 5 5 6 - @doc("Backend Starter Pack search, returns only skeleton.") 6 + /** Backend Starter Pack search, returns only skeleton. */ 7 7 @query 8 8 @errors(BadQueryString) 9 9 op main( 10 - @doc("Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended.") 11 - q: string, 10 + /** Search query string; syntax, phrase, boolean, and faceting is unspecified, but Lucene query syntax is recommended. */ 11 + @required q: string, 12 12 13 - @doc("DID of the account making the request (not included for public/unauthenticated queries).") 13 + /** DID of the account making the request (not included for public/unauthenticated queries). */ 14 14 viewer?: did, 15 15 16 16 @minValue(1) 17 17 @maxValue(100) 18 18 limit?: integer = 25, 19 19 20 - @doc("Optional pagination mechanism; may not necessarily allow scrolling through entire result set.") 20 + /** Optional pagination mechanism; may not necessarily allow scrolling through entire result set. */ 21 21 cursor?: string 22 22 ): { 23 23 cursor?: string; 24 24 25 - @doc("Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits.") 25 + /** Count of search hits. Optional, may be rounded/truncated, and may not be possible to paginate through all hits. */ 26 26 hitsTotal?: integer; 27 27 28 28 @required starterPacks: app.bsky.unspecced.defs.SkeletonSearchStarterPack[]; 29 29 }; 30 30 } 31 + 32 + // --- Externals --- 33 + 34 + @external 35 + namespace app.bsky.unspecced.defs { 36 + model SkeletonSearchStarterPack { } 37 + }
+2 -2
packages/emitter/test/integration/atproto/input/app/bsky/video/defs.tsp
··· 5 5 @required jobId: string; 6 6 @required did: did; 7 7 8 - @doc("The state of the video processing job. All values not listed as a known value indicate that the job is in process.") 8 + /** The state of the video processing job. All values not listed as a known value indicate that the job is in process. */ 9 9 @required 10 10 state: "JOB_STATE_COMPLETED" | "JOB_STATE_FAILED" | string; 11 11 12 - @doc("Progress within the current processing state.") 12 + /** Progress within the current processing state. */ 13 13 @minValue(0) 14 14 @maxValue(100) 15 15 progress?: integer;
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/video/getJobStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.video.getJobStatus { 4 - @doc("Get status details for a video processing job.") 4 + /** Get status details for a video processing job. */ 5 5 @query 6 6 op main( 7 7 @required jobId: string ··· 9 9 @required jobStatus: app.bsky.video.defs.JobStatus; 10 10 }; 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace app.bsky.video.defs { 17 + model JobStatus { } 18 + }
+1 -1
packages/emitter/test/integration/atproto/input/app/bsky/video/getUploadLimits.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.video.getUploadLimits { 4 - @doc("Get video upload limits for the authenticated user.") 4 + /** Get video upload limits for the authenticated user. */ 5 5 @query 6 6 op main(): { 7 7 @required canUpload: boolean;
+8 -1
packages/emitter/test/integration/atproto/input/app/bsky/video/uploadVideo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace app.bsky.video.uploadVideo { 4 - @doc("Upload a video to be processed then stored on the PDS.") 4 + /** Upload a video to be processed then stored on the PDS. */ 5 5 @procedure 6 6 op main( 7 7 @encoding("video/mp4") ··· 10 10 @required jobStatus: app.bsky.video.defs.JobStatus; 11 11 }; 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace app.bsky.video.defs { 18 + model JobStatus { } 19 + }
+1 -1
packages/emitter/test/integration/atproto/input/chat/bsky/actor/declaration.tsp
··· 2 2 3 3 namespace chat.bsky.actor.declaration { 4 4 @rec("literal:self") 5 - @doc("A declaration of a Bluesky chat account.") 5 + /** A declaration of a Bluesky chat account. */ 6 6 model Main { 7 7 @required allowIncoming: "all" | "none" | "following" | string; 8 8 }
+15 -1
packages/emitter/test/integration/atproto/input/chat/bsky/actor/defs.tsp
··· 14 14 viewer?: app.bsky.actor.defs.ViewerState; 15 15 labels?: com.atproto.label.defs.Label[]; 16 16 17 - @doc("Set to true when the actor cannot actively participate in conversations") 17 + /** Set to true when the actor cannot actively participate in conversations */ 18 18 chatDisabled?: boolean; 19 19 20 20 verification?: app.bsky.actor.defs.VerificationState; 21 21 } 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace app.bsky.actor.defs { 28 + model ProfileAssociated { } 29 + model ViewerState { } 30 + model VerificationState { } 31 + } 32 + 33 + @external 34 + namespace com.atproto.label.defs { 35 + model Label { } 36 + }
+1 -1
packages/emitter/test/integration/atproto/input/chat/bsky/convo/acceptConvo.tsp
··· 5 5 op main(input: { 6 6 @required convoId: string; 7 7 }): { 8 - @doc("Rev when the convo was accepted. If not present, the convo was already accepted.") 8 + /** Rev when the convo was accepted. If not present, the convo was already accepted. */ 9 9 rev?: string; 10 10 }; 11 11 }
+11 -4
packages/emitter/test/integration/atproto/input/chat/bsky/convo/addReaction.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace chat.bsky.convo.addReaction { 4 - @doc("Indicates that the message has been deleted and reactions can no longer be added/removed.") 4 + /** Indicates that the message has been deleted and reactions can no longer be added/removed. */ 5 5 model ReactionMessageDeleted {} 6 6 7 - @doc("Indicates that the message has the maximum number of reactions allowed for a single user, and the requested reaction wasn't yet present. If it was already present, the request will not fail since it is idempotent.") 7 + /** Indicates that the message has the maximum number of reactions allowed for a single user, and the requested reaction wasn't yet present. If it was already present, the request will not fail since it is idempotent. */ 8 8 model ReactionLimitReached {} 9 9 10 - @doc("Indicates the value for the reaction is not acceptable. In general, this means it is not an emoji.") 10 + /** Indicates the value for the reaction is not acceptable. In general, this means it is not an emoji. */ 11 11 model ReactionInvalidValue {} 12 12 13 - @doc("Adds an emoji reaction to a message. Requires authentication. It is idempotent, so multiple calls from the same user with the same emoji result in a single reaction.") 13 + /** Adds an emoji reaction to a message. Requires authentication. It is idempotent, so multiple calls from the same user with the same emoji result in a single reaction. */ 14 14 @procedure 15 15 @errors(ReactionMessageDeleted, ReactionLimitReached, ReactionInvalidValue) 16 16 op main(input: { ··· 27 27 @required message: chat.bsky.convo.defs.MessageView; 28 28 }; 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace chat.bsky.convo.defs { 35 + model MessageView { } 36 + }
+21 -3
packages/emitter/test/integration/atproto/input/chat/bsky/convo/defs.tsp
··· 13 13 @required 14 14 text: string; 15 15 16 - @doc("Annotations of text (mentions, URLs, hashtags, etc)") 16 + /** Annotations of text (mentions, URLs, hashtags, etc) */ 17 17 facets?: app.bsky.richtext.facet.Main[]; 18 18 19 19 embed?: (app.bsky.embed.record.Main | unknown); ··· 28 28 @required 29 29 text: string; 30 30 31 - @doc("Annotations of text (mentions, URLs, hashtags, etc)") 31 + /** Annotations of text (mentions, URLs, hashtags, etc) */ 32 32 facets?: app.bsky.richtext.facet.Main[]; 33 33 34 34 embed?: (app.bsky.embed.record.View | unknown); 35 35 36 - @doc("Reactions to this message, in ascending order of creation time.") 36 + /** Reactions to this message, in ascending order of creation time. */ 37 37 reactions?: ReactionView[]; 38 38 39 39 @required sender: MessageViewSender; ··· 139 139 @required reaction: ReactionView; 140 140 } 141 141 } 142 + 143 + // --- Externals --- 144 + 145 + @external 146 + namespace app.bsky.richtext.facet { 147 + model Main { } 148 + } 149 + 150 + @external 151 + namespace app.bsky.embed.`record` { 152 + model Main { } 153 + model View { } 154 + } 155 + 156 + @external 157 + namespace chat.bsky.actor.defs { 158 + model ProfileViewBasic { } 159 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/deleteMessageForSelf.tsp
··· 7 7 @required messageId: string; 8 8 }): chat.bsky.convo.defs.DeletedMessageView; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace chat.bsky.convo.defs { 15 + model DeletedMessageView { } 16 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvo.tsp
··· 6 6 @required convo: chat.bsky.convo.defs.ConvoView; 7 7 }; 8 8 } 9 + 10 + // --- Externals --- 11 + 12 + @external 13 + namespace chat.bsky.convo.defs { 14 + model ConvoView { } 15 + }
+8 -1
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoAvailability.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace chat.bsky.convo.getConvoAvailability { 4 - @doc("Get whether the requester and the other members can chat. If an existing convo is found for these members, it is returned.") 4 + /** Get whether the requester and the other members can chat. If an existing convo is found for these members, it is returned. */ 5 5 @query 6 6 op main( 7 7 @minItems(1) ··· 13 13 convo?: chat.bsky.convo.defs.ConvoView; 14 14 }; 15 15 } 16 + 17 + // --- Externals --- 18 + 19 + @external 20 + namespace chat.bsky.convo.defs { 21 + model ConvoView { } 22 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoForMembers.tsp
··· 11 11 @required convo: chat.bsky.convo.defs.ConvoView; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace chat.bsky.convo.defs { 19 + model ConvoView { } 20 + }
+16
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getLog.tsp
··· 21 21 )[]; 22 22 }; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace chat.bsky.convo.defs { 29 + model LogBeginConvo { } 30 + model LogAcceptConvo { } 31 + model LogLeaveConvo { } 32 + model LogMuteConvo { } 33 + model LogUnmuteConvo { } 34 + model LogCreateMessage { } 35 + model LogDeleteMessage { } 36 + model LogReadMessage { } 37 + model LogAddReaction { } 38 + model LogRemoveReaction { } 39 + }
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getMessages.tsp
··· 21 21 )[]; 22 22 }; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace chat.bsky.convo.defs { 29 + model MessageView { } 30 + model DeletedMessageView { } 31 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/listConvos.tsp
··· 17 17 @required convos: chat.bsky.convo.defs.ConvoView[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace chat.bsky.convo.defs { 25 + model ConvoView { } 26 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/muteConvo.tsp
··· 8 8 @required convo: chat.bsky.convo.defs.ConvoView; 9 9 }; 10 10 } 11 + 12 + // --- Externals --- 13 + 14 + @external 15 + namespace chat.bsky.convo.defs { 16 + model ConvoView { } 17 + }
+10 -3
packages/emitter/test/integration/atproto/input/chat/bsky/convo/removeReaction.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace chat.bsky.convo.removeReaction { 4 - @doc("Indicates that the message has been deleted and reactions can no longer be added/removed.") 4 + /** Indicates that the message has been deleted and reactions can no longer be added/removed. */ 5 5 model ReactionMessageDeleted {} 6 6 7 - @doc("Indicates the value for the reaction is not acceptable. In general, this means it is not an emoji.") 7 + /** Indicates the value for the reaction is not acceptable. In general, this means it is not an emoji. */ 8 8 model ReactionInvalidValue {} 9 9 10 - @doc("Removes an emoji reaction from a message. Requires authentication. It is idempotent, so multiple calls from the same user with the same emoji result in that reaction not being present, even if it already wasn't.") 10 + /** Removes an emoji reaction from a message. Requires authentication. It is idempotent, so multiple calls from the same user with the same emoji result in that reaction not being present, even if it already wasn't. */ 11 11 @procedure 12 12 @errors(ReactionMessageDeleted, ReactionInvalidValue) 13 13 op main(input: { ··· 24 24 @required message: chat.bsky.convo.defs.MessageView; 25 25 }; 26 26 } 27 + 28 + // --- Externals --- 29 + 30 + @external 31 + namespace chat.bsky.convo.defs { 32 + model MessageView { } 33 + }
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessage.tsp
··· 7 7 @required message: chat.bsky.convo.defs.MessageInput; 8 8 }): chat.bsky.convo.defs.MessageView; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace chat.bsky.convo.defs { 15 + model MessageInput { } 16 + model MessageView { } 17 + }
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessageBatch.tsp
··· 15 15 @required message: chat.bsky.convo.defs.MessageInput; 16 16 } 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace chat.bsky.convo.defs { 23 + model MessageView { } 24 + model MessageInput { } 25 + }
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/unmuteConvo.tsp
··· 8 8 @required convo: chat.bsky.convo.defs.ConvoView; 9 9 }; 10 10 } 11 + 12 + // --- Externals --- 13 + 14 + @external 15 + namespace chat.bsky.convo.defs { 16 + model ConvoView { } 17 + }
+1 -1
packages/emitter/test/integration/atproto/input/chat/bsky/convo/updateAllRead.tsp
··· 5 5 op main(input: { 6 6 status?: "request" | "accepted" | string; 7 7 }): { 8 - @doc("The count of updated convos.") 8 + /** The count of updated convos. */ 9 9 @required 10 10 updatedCount: integer; 11 11 };
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/updateRead.tsp
··· 9 9 @required convo: chat.bsky.convo.defs.ConvoView; 10 10 }; 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace chat.bsky.convo.defs { 17 + model ConvoView { } 18 + }
+9 -1
packages/emitter/test/integration/atproto/input/chat/bsky/moderation/getMessageContext.tsp
··· 3 3 namespace chat.bsky.moderation.getMessageContext { 4 4 @query 5 5 op main( 6 - @doc("Conversation that the message is from. NOTE: this field will eventually be required.") 6 + /** Conversation that the message is from. NOTE: this field will eventually be required. */ 7 7 convoId?: string, 8 8 9 9 @required messageId: string, ··· 20 20 )[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace chat.bsky.convo.defs { 28 + model MessageView { } 29 + model DeletedMessageView { } 30 + }
+8
packages/emitter/test/integration/atproto/input/com/atproto/admin/defs.tsp
··· 38 38 @required value: string; 39 39 } 40 40 } 41 + 42 + // --- Externals --- 43 + 44 + @external 45 + namespace com.atproto.server.defs { 46 + model InviteCode { } 47 + model InviteCodeUse { } 48 + }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/deleteAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.deleteAccount { 4 - @doc("Delete a user account as an administrator.") 4 + /** Delete a user account as an administrator. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/admin/disableAccountInvites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.disableAccountInvites { 4 - @doc("Disable an account from receiving new invite codes, but does not invalidate existing codes.") 4 + /** Disable an account from receiving new invite codes, but does not invalidate existing codes. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required account: did; 8 8 9 - @doc("Optional reason for disabled invites.") 9 + /** Optional reason for disabled invites. */ 10 10 note?: string; 11 11 }): void; 12 12 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/disableInviteCodes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.disableInviteCodes { 4 - @doc("Disable some set of codes and/or all codes associated with a set of users.") 4 + /** Disable some set of codes and/or all codes associated with a set of users. */ 5 5 @procedure 6 6 op main(input: { 7 7 codes?: string[];
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/admin/enableAccountInvites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.enableAccountInvites { 4 - @doc("Re-enable an account's ability to receive invite codes.") 4 + /** Re-enable an account's ability to receive invite codes. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required account: did; 8 8 9 - @doc("Optional reason for enabled invites.") 9 + /** Optional reason for enabled invites. */ 10 10 note?: string; 11 11 }): void; 12 12 }
+8 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getAccountInfo { 4 - @doc("Get details about an account.") 4 + /** Get details about an account. */ 5 5 @query 6 6 op main( 7 7 @required did: did 8 8 ): com.atproto.admin.defs.AccountView; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace com.atproto.admin.defs { 15 + model AccountView { } 16 + }
+8 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getAccountInfos { 4 - @doc("Get details about some accounts.") 4 + /** Get details about some accounts. */ 5 5 @query 6 6 op main( 7 7 @required dids: did[] ··· 9 9 @required infos: com.atproto.admin.defs.AccountView[]; 10 10 }; 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace com.atproto.admin.defs { 17 + model AccountView { } 18 + }
+8 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/getInviteCodes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getInviteCodes { 4 - @doc("Get an admin view of invite codes.") 4 + /** Get an admin view of invite codes. */ 5 5 @query 6 6 op main( 7 7 sort?: "recent" | "usage" | string = "recent", ··· 16 16 @required codes: com.atproto.server.defs.InviteCode[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace com.atproto.server.defs { 24 + model InviteCode { } 25 + }
+15 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/getSubjectStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getSubjectStatus { 4 - @doc("Get the service-specific admin status of a subject (account, record, or blob).") 4 + /** Get the service-specific admin status of a subject (account, record, or blob). */ 5 5 @query 6 6 op main( 7 7 did?: did, ··· 20 20 deactivated?: com.atproto.admin.defs.StatusAttr; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace com.atproto.admin.defs { 28 + model RepoRef { } 29 + model RepoBlobRef { } 30 + model StatusAttr { } 31 + } 32 + 33 + @external 34 + namespace com.atproto.repo.strongRef { 35 + model Main { } 36 + }
+8 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/searchAccounts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.searchAccounts { 4 - @doc("Get list of accounts that matches your search query.") 4 + /** Get list of accounts that matches your search query. */ 5 5 @query 6 6 op main( 7 7 email?: string, ··· 15 15 @required accounts: com.atproto.admin.defs.AccountView[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace com.atproto.admin.defs { 23 + model AccountView { } 24 + }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/admin/sendEmail.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.sendEmail { 4 - @doc("Send email to a user's account email address.") 4 + /** Send email to a user's account email address. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required recipientDid: did; ··· 9 9 subject?: string; 10 10 @required senderDid: did; 11 11 12 - @doc("Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers") 12 + /** Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers */ 13 13 comment?: string; 14 14 }): { 15 15 @required sent: boolean;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateAccountEmail.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountEmail { 4 - @doc("Administrative action to update an account's email.") 4 + /** Administrative action to update an account's email. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 account: atIdentifier; 10 10
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateAccountHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountHandle { 4 - @doc("Administrative action to update an account's handle.") 4 + /** Administrative action to update an account's handle. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateAccountPassword.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountPassword { 4 - @doc("Update the password for a user account as an administrator.") 4 + /** Update the password for a user account as an administrator. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateAccountSigningKey.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountSigningKey { 4 - @doc("Administrative action to update an account's signing key in their Did document.") 4 + /** Administrative action to update an account's signing key in their Did document. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did; 8 8 9 - @doc("Did-key formatted public key") 9 + /** Did-key formatted public key */ 10 10 @required 11 11 signingKey: did; 12 12 }): void;
+15 -1
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateSubjectStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateSubjectStatus { 4 - @doc("Update the service-specific admin status of a subject (account, record, or blob).") 4 + /** Update the service-specific admin status of a subject (account, record, or blob). */ 5 5 @procedure 6 6 op main(input: { 7 7 @required ··· 26 26 takedown?: com.atproto.admin.defs.StatusAttr; 27 27 }; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace com.atproto.admin.defs { 34 + model RepoRef { } 35 + model RepoBlobRef { } 36 + model StatusAttr { } 37 + } 38 + 39 + @external 40 + namespace com.atproto.repo.strongRef { 41 + model Main { } 42 + }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/identity/defs.tsp
··· 4 4 model IdentityInfo { 5 5 @required did: did; 6 6 7 - @doc("The validated handle of the account; or 'handle.invalid' if the handle did not bi-directionally match the DID document.") 7 + /** The validated handle of the account; or 'handle.invalid' if the handle did not bi-directionally match the DID document. */ 8 8 @required 9 9 handle: handle; 10 10 11 - @doc("The complete DID document for the identity.") 11 + /** The complete DID document for the identity. */ 12 12 @required 13 13 didDoc: unknown; 14 14 }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/identity/getRecommendedDidCredentials.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.getRecommendedDidCredentials { 4 - @doc("Describe the credentials that should be included in the DID doc of an account that is migrating to this service.") 4 + /** Describe the credentials that should be included in the DID doc of an account that is migrating to this service. */ 5 5 @query 6 6 op main(): { 7 - @doc("Recommended rotation keys for PLC dids. Should be undefined (or ignored) for did:webs.") 7 + /** Recommended rotation keys for PLC dids. Should be undefined (or ignored) for did:webs. */ 8 8 rotationKeys?: string[]; 9 9 10 10 alsoKnownAs?: string[];
+11 -4
packages/emitter/test/integration/atproto/input/com/atproto/identity/refreshIdentity.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.refreshIdentity { 4 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 4 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 5 5 model HandleNotFound {} 6 6 7 - @doc("The DID resolution process confirmed that there is no current DID.") 7 + /** The DID resolution process confirmed that there is no current DID. */ 8 8 model DidNotFound {} 9 9 10 - @doc("The DID previously existed, but has been deactivated.") 10 + /** The DID previously existed, but has been deactivated. */ 11 11 model DidDeactivated {} 12 12 13 - @doc("Request that the server re-resolve an identity (DID and handle). The server may ignore this request, or require authentication, depending on the role, implementation, and policy of the server.") 13 + /** Request that the server re-resolve an identity (DID and handle). The server may ignore this request, or require authentication, depending on the role, implementation, and policy of the server. */ 14 14 @procedure 15 15 @errors(HandleNotFound, DidNotFound, DidDeactivated) 16 16 op main(input: { 17 17 @required identifier: atIdentifier; 18 18 }): com.atproto.identity.defs.IdentityInfo; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace com.atproto.identity.defs { 25 + model IdentityInfo { } 26 + }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/identity/requestPlcOperationSignature.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.requestPlcOperationSignature { 4 - @doc("Request an email with a code to in order to request a signed PLC operation. Requires Auth.") 4 + /** Request an email with a code to in order to request a signed PLC operation. Requires Auth. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/identity/resolveDid.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveDid { 4 - @doc("Resolves DID to DID document. Does not bi-directionally verify handle.") 4 + /** Resolves DID to DID document. Does not bi-directionally verify handle. */ 5 5 @query 6 6 @errors(DidNotFound, DidDeactivated) 7 7 op main( 8 - @doc("DID to resolve.") 8 + /** DID to resolve. */ 9 9 @required 10 10 did: did 11 11 ): { 12 - @doc("The complete DID document for the identity.") 12 + /** The complete DID document for the identity. */ 13 13 @required 14 14 didDoc: unknown; 15 15 }; 16 16 17 - @doc("The DID resolution process confirmed that there is no current DID.") 17 + /** The DID resolution process confirmed that there is no current DID. */ 18 18 model DidNotFound {} 19 19 20 - @doc("The DID previously existed, but has been deactivated.") 20 + /** The DID previously existed, but has been deactivated. */ 21 21 model DidDeactivated {} 22 22 }
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/identity/resolveHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveHandle { 4 - @doc("Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document.") 4 + /** Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document. */ 5 5 @query 6 6 @errors(HandleNotFound) 7 7 op main( 8 - @doc("The handle to resolve.") 8 + /** The handle to resolve. */ 9 9 @required 10 10 handle: handle 11 11 ): { 12 12 @required did: did; 13 13 }; 14 14 15 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 15 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 16 16 model HandleNotFound {} 17 17 }
+12 -5
packages/emitter/test/integration/atproto/input/com/atproto/identity/resolveIdentity.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveIdentity { 4 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 4 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 5 5 model HandleNotFound {} 6 6 7 - @doc("The DID resolution process confirmed that there is no current DID.") 7 + /** The DID resolution process confirmed that there is no current DID. */ 8 8 model DidNotFound {} 9 9 10 - @doc("The DID previously existed, but has been deactivated.") 10 + /** The DID previously existed, but has been deactivated. */ 11 11 model DidDeactivated {} 12 12 13 - @doc("Resolves an identity (DID or Handle) to a full identity (DID document and verified handle).") 13 + /** Resolves an identity (DID or Handle) to a full identity (DID document and verified handle). */ 14 14 @query 15 15 @errors(HandleNotFound, DidNotFound, DidDeactivated) 16 16 op main( 17 - @doc("Handle or DID to resolve.") 17 + /** Handle or DID to resolve. */ 18 18 @required 19 19 identifier: atIdentifier 20 20 ): com.atproto.identity.defs.IdentityInfo; 21 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace com.atproto.identity.defs { 27 + model IdentityInfo { } 28 + }
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/identity/signPlcOperation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.signPlcOperation { 4 - @doc("Signs a PLC operation to update some value(s) in the requesting DID's document.") 4 + /** Signs a PLC operation to update some value(s) in the requesting DID's document. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("A token received through com.atproto.identity.requestPlcOperationSignature") 7 + /** A token received through com.atproto.identity.requestPlcOperationSignature */ 8 8 token?: string; 9 9 10 10 rotationKeys?: string[]; ··· 15 15 16 16 services?: unknown; 17 17 }): { 18 - @doc("A signed DID PLC operation.") 18 + /** A signed DID PLC operation. */ 19 19 @required 20 20 operation: unknown; 21 21 };
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/identity/submitPlcOperation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.submitPlcOperation { 4 - @doc("Validates a PLC operation to ensure that it doesn't violate a service's constraints or get the identity into a bad state, then submits it to the PLC registry") 4 + /** Validates a PLC operation to ensure that it doesn't violate a service's constraints or get the identity into a bad state, then submits it to the PLC registry */ 5 5 @procedure 6 6 op main(input: { 7 7 @required operation: unknown;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/identity/updateHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.updateHandle { 4 - @doc("Updates the current account's handle. Verifies handle validity, and updates did:plc document if necessary. Implemented by PDS, and requires auth.") 4 + /** Updates the current account's handle. Verifies handle validity, and updates did:plc document if necessary. Implemented by PDS, and requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The new handle.") 7 + /** The new handle. */ 8 8 @required 9 9 handle: handle; 10 10 }): void;
+23 -23
packages/emitter/test/integration/atproto/input/com/atproto/label/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.label.defs { 4 - @doc("Metadata tag on an atproto resource (eg, repo or record).") 4 + /** Metadata tag on an atproto resource (eg, repo or record). */ 5 5 model Label { 6 - @doc("The AT Protocol version of the label object.") 6 + /** The AT Protocol version of the label object. */ 7 7 ver?: integer; 8 8 9 - @doc("DID of the actor who created this label.") 9 + /** DID of the actor who created this label. */ 10 10 @required 11 11 src: did; 12 12 13 - @doc("AT URI of the record, repository (account), or other resource that this label applies to.") 13 + /** AT URI of the record, repository (account), or other resource that this label applies to. */ 14 14 @required 15 15 uri: uri; 16 16 17 - @doc("Optionally, CID specifying the specific version of 'uri' resource this label applies to.") 17 + /** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */ 18 18 cid?: cid; 19 19 20 - @doc("The short string name of the value or type of this label.") 20 + /** The short string name of the value or type of this label. */ 21 21 @maxLength(128) 22 22 @required 23 23 val: string; 24 24 25 - @doc("If true, this is a negation label, overwriting a previous label.") 25 + /** If true, this is a negation label, overwriting a previous label. */ 26 26 neg?: boolean; 27 27 28 - @doc("Timestamp when this label was created.") 28 + /** Timestamp when this label was created. */ 29 29 @required 30 30 cts: datetime; 31 31 32 - @doc("Timestamp at which this label expires (no longer applies).") 32 + /** Timestamp at which this label expires (no longer applies). */ 33 33 exp?: datetime; 34 34 35 - @doc("Signature of dag-cbor encoded label.") 35 + /** Signature of dag-cbor encoded label. */ 36 36 sig?: bytes; 37 37 } 38 38 39 - @doc("Metadata tags on an atproto record, published by the author within the record.") 39 + /** Metadata tags on an atproto record, published by the author within the record. */ 40 40 model SelfLabels { 41 41 @maxItems(10) 42 42 @required 43 43 values: SelfLabel[]; 44 44 } 45 45 46 - @doc("Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.") 46 + /** Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel. */ 47 47 model SelfLabel { 48 - @doc("The short string name of the value or type of this label.") 48 + /** The short string name of the value or type of this label. */ 49 49 @maxLength(128) 50 50 @required 51 51 val: string; 52 52 } 53 53 54 - @doc("Declares a label value and its expected interpretations and behaviors.") 54 + /** Declares a label value and its expected interpretations and behaviors. */ 55 55 model LabelValueDefinition { 56 - @doc("The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).") 56 + /** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */ 57 57 @maxLength(100) 58 58 @maxGraphemes(100) 59 59 @required 60 60 identifier: string; 61 61 62 - @doc("How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.") 62 + /** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */ 63 63 @required 64 64 severity: "inform" | "alert" | "none" | string; 65 65 66 - @doc("What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.") 66 + /** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */ 67 67 @required 68 68 blurs: "content" | "media" | "none" | string; 69 69 70 - @doc("The default setting for this label.") 70 + /** The default setting for this label. */ 71 71 defaultSetting?: "ignore" | "warn" | "hide" | string = "warn"; 72 72 73 - @doc("Does the user need to have adult content enabled in order to configure this label?") 73 + /** Does the user need to have adult content enabled in order to configure this label? */ 74 74 adultOnly?: boolean; 75 75 76 76 @required 77 77 locales: LabelValueDefinitionStrings[]; 78 78 } 79 79 80 - @doc("Strings which describe the label in the UI, localized into a specific language.") 80 + /** Strings which describe the label in the UI, localized into a specific language. */ 81 81 model LabelValueDefinitionStrings { 82 - @doc("The code of the language these strings are written in.") 82 + /** The code of the language these strings are written in. */ 83 83 @required 84 84 lang: language; 85 85 86 - @doc("A short human-readable name for the label.") 86 + /** A short human-readable name for the label. */ 87 87 @maxGraphemes(64) 88 88 @maxLength(640) 89 89 @required 90 90 name: string; 91 91 92 - @doc("A longer description of what the label means and why it might be applied.") 92 + /** A longer description of what the label means and why it might be applied. */ 93 93 @maxGraphemes(10000) 94 94 @maxLength(100000) 95 95 @required
+10 -3
packages/emitter/test/integration/atproto/input/com/atproto/label/queryLabels.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.label.queryLabels { 4 - @doc("Find labels relevant to the provided AT-URI patterns. Public endpoint for moderation services, though may return different or additional results with auth.") 4 + /** Find labels relevant to the provided AT-URI patterns. Public endpoint for moderation services, though may return different or additional results with auth. */ 5 5 @query 6 6 op main( 7 - @doc("List of AT URI patterns to match (boolean 'OR'). Each may be a prefix (ending with '*'; will match inclusive of the string leading to '*'), or a full URI.") 7 + /** List of AT URI patterns to match (boolean 'OR'). Each may be a prefix (ending with '*'; will match inclusive of the string leading to '*'), or a full URI. */ 8 8 @required 9 9 uriPatterns: string[], 10 10 11 - @doc("Optional list of label sources (DIDs) to filter on.") 11 + /** Optional list of label sources (DIDs) to filter on. */ 12 12 sources?: did[], 13 13 14 14 @minValue(1) ··· 21 21 @required labels: com.atproto.label.defs.Label[]; 22 22 }; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace com.atproto.label.defs { 29 + model Label { } 30 + }
+10 -2
packages/emitter/test/integration/atproto/input/com/atproto/label/subscribeLabels.tsp
··· 13 13 message?: string; 14 14 } 15 15 16 - @doc("Subscribe to stream of labels (and negations). Public endpoint implemented by mod services. Uses same sequencing scheme as repo event stream.") 16 + /** Subscribe to stream of labels (and negations). Public endpoint implemented by mod services. Uses same sequencing scheme as repo event stream. */ 17 17 @subscription 18 18 @errors(FutureCursor) 19 19 op main( 20 - @doc("The last known event seq number to backfill from.") 20 + /** The last known event seq number to backfill from. */ 21 21 cursor?: integer 22 22 ): (Labels | Info); 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace com.atproto.label.defs { 29 + model Label { } 30 + model Info { } 31 + }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/lexicon/schema.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.lexicon.schema { 4 - @doc("Representation of Lexicon schemas themselves, when published as atproto records. Note that the schema language is not defined in Lexicon; this meta schema currently only includes a single version field ('lexicon'). See the atproto specifications for description of the other expected top-level fields ('id', 'defs', etc).") 4 + /** Representation of Lexicon schemas themselves, when published as atproto records. Note that the schema language is not defined in Lexicon; this meta schema currently only includes a single version field ('lexicon'). See the atproto specifications for description of the other expected top-level fields ('id', 'defs', etc). */ 5 5 @rec("nsid") 6 6 model Main { 7 - @doc("Indicates the 'version' of the Lexicon language. Must be '1' for the current atproto/Lexicon schema system.") 7 + /** Indicates the 'version' of the Lexicon language. Must be '1' for the current atproto/Lexicon schema system. */ 8 8 @required 9 9 lexicon: integer; 10 10 }
+23 -6
packages/emitter/test/integration/atproto/input/com/atproto/moderation/createReport.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.moderation.createReport { 4 - @doc("Submit a moderation report regarding an atproto account or record. Implemented by moderation services (with PDS proxying), and requires auth.") 4 + /** Submit a moderation report regarding an atproto account or record. Implemented by moderation services (with PDS proxying), and requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Indicates the broad category of violation the report is for.") 7 + /** Indicates the broad category of violation the report is for. */ 8 8 @required 9 9 reasonType: com.atproto.moderation.defs.ReasonType; 10 10 11 11 @maxGraphemes(2000) 12 12 @maxLength(20000) 13 - @doc("Additional context about the content and violation.") 13 + /** Additional context about the content and violation. */ 14 14 reason?: string; 15 15 16 16 @required ··· 40 40 @required createdAt: datetime; 41 41 }; 42 42 43 - @doc("Moderation tool information for tracing the source of the action") 43 + /** Moderation tool information for tracing the source of the action */ 44 44 model ModTool { 45 - @doc("Name/identifier of the source (e.g., 'bsky-app/android', 'bsky-web/chrome')") 45 + /** Name/identifier of the source (e.g., 'bsky-app/android', 'bsky-web/chrome') */ 46 46 @required 47 47 name: string; 48 48 49 - @doc("Additional arbitrary metadata about the source") 49 + /** Additional arbitrary metadata about the source */ 50 50 meta?: unknown; 51 51 } 52 52 } 53 + 54 + // --- Externals --- 55 + 56 + @external 57 + namespace com.atproto.moderation.defs { 58 + model ReasonType { } 59 + } 60 + 61 + @external 62 + namespace com.atproto.admin.defs { 63 + model RepoRef { } 64 + } 65 + 66 + @external 67 + namespace com.atproto.repo.strongRef { 68 + model Main { } 69 + }
+111 -69
packages/emitter/test/integration/atproto/input/com/atproto/moderation/defs.tsp
··· 3 3 namespace com.atproto.moderation.defs { 4 4 union ReasonType { 5 5 string, 6 - 7 - ReasonSpam: "com.atproto.moderation.defs#reasonSpam", 8 - ReasonViolation: "com.atproto.moderation.defs#reasonViolation", 9 - ReasonMisleading: "com.atproto.moderation.defs#reasonMisleading", 10 - ReasonSexual: "com.atproto.moderation.defs#reasonSexual", 11 - ReasonRude: "com.atproto.moderation.defs#reasonRude", 12 - ReasonOther: "com.atproto.moderation.defs#reasonOther", 13 - ReasonAppeal: "com.atproto.moderation.defs#reasonAppeal", 14 - 15 - ToolsOzoneReportReasonAppeal: "tools.ozone.report.defs#reasonAppeal", 16 - 17 - ToolsOzoneReportReasonViolenceAnimalWelfare: "tools.ozone.report.defs#reasonViolenceAnimalWelfare", 18 - ToolsOzoneReportReasonViolenceThreats: "tools.ozone.report.defs#reasonViolenceThreats", 19 - ToolsOzoneReportReasonViolenceGraphicContent: "tools.ozone.report.defs#reasonViolenceGraphicContent", 20 - ToolsOzoneReportReasonViolenceSelfHarm: "tools.ozone.report.defs#reasonViolenceSelfHarm", 21 - ToolsOzoneReportReasonViolenceGlorification: "tools.ozone.report.defs#reasonViolenceGlorification", 22 - ToolsOzoneReportReasonViolenceExtremistContent: "tools.ozone.report.defs#reasonViolenceExtremistContent", 23 - ToolsOzoneReportReasonViolenceTrafficking: "tools.ozone.report.defs#reasonViolenceTrafficking", 24 - ToolsOzoneReportReasonViolenceOther: "tools.ozone.report.defs#reasonViolenceOther", 25 - 26 - ToolsOzoneReportReasonSexualAbuseContent: "tools.ozone.report.defs#reasonSexualAbuseContent", 27 - ToolsOzoneReportReasonSexualNCII: "tools.ozone.report.defs#reasonSexualNCII", 28 - ToolsOzoneReportReasonSexualSextortion: "tools.ozone.report.defs#reasonSexualSextortion", 29 - ToolsOzoneReportReasonSexualDeepfake: "tools.ozone.report.defs#reasonSexualDeepfake", 30 - ToolsOzoneReportReasonSexualAnimal: "tools.ozone.report.defs#reasonSexualAnimal", 31 - ToolsOzoneReportReasonSexualUnlabeled: "tools.ozone.report.defs#reasonSexualUnlabeled", 32 - ToolsOzoneReportReasonSexualOther: "tools.ozone.report.defs#reasonSexualOther", 33 - 34 - ToolsOzoneReportReasonChildSafetyCSAM: "tools.ozone.report.defs#reasonChildSafetyCSAM", 35 - ToolsOzoneReportReasonChildSafetyGroom: "tools.ozone.report.defs#reasonChildSafetyGroom", 36 - ToolsOzoneReportReasonChildSafetyMinorPrivacy: "tools.ozone.report.defs#reasonChildSafetyMinorPrivacy", 37 - ToolsOzoneReportReasonChildSafetyEndangerment: "tools.ozone.report.defs#reasonChildSafetyEndangerment", 38 - ToolsOzoneReportReasonChildSafetyHarassment: "tools.ozone.report.defs#reasonChildSafetyHarassment", 39 - ToolsOzoneReportReasonChildSafetyPromotion: "tools.ozone.report.defs#reasonChildSafetyPromotion", 40 - ToolsOzoneReportReasonChildSafetyOther: "tools.ozone.report.defs#reasonChildSafetyOther", 41 - 42 - ToolsOzoneReportReasonHarassmentTroll: "tools.ozone.report.defs#reasonHarassmentTroll", 43 - ToolsOzoneReportReasonHarassmentTargeted: "tools.ozone.report.defs#reasonHarassmentTargeted", 44 - ToolsOzoneReportReasonHarassmentHateSpeech: "tools.ozone.report.defs#reasonHarassmentHateSpeech", 45 - ToolsOzoneReportReasonHarassmentDoxxing: "tools.ozone.report.defs#reasonHarassmentDoxxing", 46 - ToolsOzoneReportReasonHarassmentOther: "tools.ozone.report.defs#reasonHarassmentOther", 47 - 48 - ToolsOzoneReportReasonMisleadingBot: "tools.ozone.report.defs#reasonMisleadingBot", 49 - ToolsOzoneReportReasonMisleadingImpersonation: "tools.ozone.report.defs#reasonMisleadingImpersonation", 50 - ToolsOzoneReportReasonMisleadingSpam: "tools.ozone.report.defs#reasonMisleadingSpam", 51 - ToolsOzoneReportReasonMisleadingScam: "tools.ozone.report.defs#reasonMisleadingScam", 52 - ToolsOzoneReportReasonMisleadingSyntheticContent: "tools.ozone.report.defs#reasonMisleadingSyntheticContent", 53 - ToolsOzoneReportReasonMisleadingMisinformation: "tools.ozone.report.defs#reasonMisleadingMisinformation", 54 - ToolsOzoneReportReasonMisleadingOther: "tools.ozone.report.defs#reasonMisleadingOther", 55 - 56 - ToolsOzoneReportReasonRuleSiteSecurity: "tools.ozone.report.defs#reasonRuleSiteSecurity", 57 - ToolsOzoneReportReasonRuleStolenContent: "tools.ozone.report.defs#reasonRuleStolenContent", 58 - ToolsOzoneReportReasonRuleProhibitedSales: "tools.ozone.report.defs#reasonRuleProhibitedSales", 59 - ToolsOzoneReportReasonRuleBanEvasion: "tools.ozone.report.defs#reasonRuleBanEvasion", 60 - ToolsOzoneReportReasonRuleOther: "tools.ozone.report.defs#reasonRuleOther", 61 - 62 - ToolsOzoneReportReasonCivicElectoralProcess: "tools.ozone.report.defs#reasonCivicElectoralProcess", 63 - ToolsOzoneReportReasonCivicDisclosure: "tools.ozone.report.defs#reasonCivicDisclosure", 64 - ToolsOzoneReportReasonCivicInterference: "tools.ozone.report.defs#reasonCivicInterference", 65 - ToolsOzoneReportReasonCivicMisinformation: "tools.ozone.report.defs#reasonCivicMisinformation", 66 - ToolsOzoneReportReasonCivicImpersonation: "tools.ozone.report.defs#reasonCivicImpersonation", 6 + ReasonSpam, 7 + ReasonViolation, 8 + ReasonMisleading, 9 + ReasonSexual, 10 + ReasonRude, 11 + ReasonOther, 12 + ReasonAppeal, 13 + tools.ozone.report.defs.ReasonAppeal, 14 + tools.ozone.report.defs.ReasonViolenceAnimalWelfare, 15 + tools.ozone.report.defs.ReasonViolenceThreats, 16 + tools.ozone.report.defs.ReasonViolenceGraphicContent, 17 + tools.ozone.report.defs.ReasonViolenceSelfHarm, 18 + tools.ozone.report.defs.ReasonViolenceGlorification, 19 + tools.ozone.report.defs.ReasonViolenceExtremistContent, 20 + tools.ozone.report.defs.ReasonViolenceTrafficking, 21 + tools.ozone.report.defs.ReasonViolenceOther, 22 + tools.ozone.report.defs.ReasonSexualAbuseContent, 23 + tools.ozone.report.defs.ReasonSexualNCII, 24 + tools.ozone.report.defs.ReasonSexualSextortion, 25 + tools.ozone.report.defs.ReasonSexualDeepfake, 26 + tools.ozone.report.defs.ReasonSexualAnimal, 27 + tools.ozone.report.defs.ReasonSexualUnlabeled, 28 + tools.ozone.report.defs.ReasonSexualOther, 29 + tools.ozone.report.defs.ReasonChildSafetyCSAM, 30 + tools.ozone.report.defs.ReasonChildSafetyGroom, 31 + tools.ozone.report.defs.ReasonChildSafetyMinorPrivacy, 32 + tools.ozone.report.defs.ReasonChildSafetyEndangerment, 33 + tools.ozone.report.defs.ReasonChildSafetyHarassment, 34 + tools.ozone.report.defs.ReasonChildSafetyPromotion, 35 + tools.ozone.report.defs.ReasonChildSafetyOther, 36 + tools.ozone.report.defs.ReasonHarassmentTroll, 37 + tools.ozone.report.defs.ReasonHarassmentTargeted, 38 + tools.ozone.report.defs.ReasonHarassmentHateSpeech, 39 + tools.ozone.report.defs.ReasonHarassmentDoxxing, 40 + tools.ozone.report.defs.ReasonHarassmentOther, 41 + tools.ozone.report.defs.ReasonMisleadingBot, 42 + tools.ozone.report.defs.ReasonMisleadingImpersonation, 43 + tools.ozone.report.defs.ReasonMisleadingSpam, 44 + tools.ozone.report.defs.ReasonMisleadingScam, 45 + tools.ozone.report.defs.ReasonMisleadingSyntheticContent, 46 + tools.ozone.report.defs.ReasonMisleadingMisinformation, 47 + tools.ozone.report.defs.ReasonMisleadingOther, 48 + tools.ozone.report.defs.ReasonRuleSiteSecurity, 49 + tools.ozone.report.defs.ReasonRuleStolenContent, 50 + tools.ozone.report.defs.ReasonRuleProhibitedSales, 51 + tools.ozone.report.defs.ReasonRuleBanEvasion, 52 + tools.ozone.report.defs.ReasonRuleOther, 53 + tools.ozone.report.defs.ReasonCivicElectoralProcess, 54 + tools.ozone.report.defs.ReasonCivicDisclosure, 55 + tools.ozone.report.defs.ReasonCivicInterference, 56 + tools.ozone.report.defs.ReasonCivicMisinformation, 57 + tools.ozone.report.defs.ReasonCivicImpersonation, 67 58 } 68 59 69 - @doc("Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`.") 60 + /** Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`. */ 70 61 @token 71 62 model ReasonSpam {} 72 63 73 - @doc("Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`.") 64 + /** Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`. */ 74 65 @token 75 66 model ReasonViolation {} 76 67 77 - @doc("Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`.") 68 + /** Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`. */ 78 69 @token 79 70 model ReasonMisleading {} 80 71 81 - @doc("Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`.") 72 + /** Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`. */ 82 73 @token 83 74 model ReasonSexual {} 84 75 85 - @doc("Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`.") 76 + /** Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`. */ 86 77 @token 87 78 model ReasonRude {} 88 79 89 - @doc("Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`.") 80 + /** Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`. */ 90 81 @token 91 82 model ReasonOther {} 92 83 93 - @doc("Appeal a previously taken moderation action") 84 + /** Appeal a previously taken moderation action */ 94 85 @token 95 86 model ReasonAppeal {} 96 87 97 - @doc("Tag describing a type of subject that might be reported.") 88 + /** Tag describing a type of subject that might be reported. */ 98 89 union SubjectType { 99 90 "account", 100 91 "record", ··· 102 93 string, 103 94 } 104 95 } 96 + 97 + // --- Externals --- 98 + 99 + @external 100 + namespace tools.ozone.report.defs { 101 + @token model ReasonAppeal { } 102 + @token model ReasonChildSafetyCSAM { } 103 + @token model ReasonChildSafetyEndangerment { } 104 + @token model ReasonChildSafetyGroom { } 105 + @token model ReasonChildSafetyHarassment { } 106 + @token model ReasonChildSafetyMinorPrivacy { } 107 + @token model ReasonChildSafetyOther { } 108 + @token model ReasonChildSafetyPromotion { } 109 + @token model ReasonCivicDisclosure { } 110 + @token model ReasonCivicElectoralProcess { } 111 + @token model ReasonCivicImpersonation { } 112 + @token model ReasonCivicInterference { } 113 + @token model ReasonCivicMisinformation { } 114 + @token model ReasonHarassmentDoxxing { } 115 + @token model ReasonHarassmentHateSpeech { } 116 + @token model ReasonHarassmentOther { } 117 + @token model ReasonHarassmentTargeted { } 118 + @token model ReasonHarassmentTroll { } 119 + @token model ReasonMisleadingBot { } 120 + @token model ReasonMisleadingImpersonation { } 121 + @token model ReasonMisleadingMisinformation { } 122 + @token model ReasonMisleadingOther { } 123 + @token model ReasonMisleadingScam { } 124 + @token model ReasonMisleadingSpam { } 125 + @token model ReasonMisleadingSyntheticContent { } 126 + @token model ReasonRuleBanEvasion { } 127 + @token model ReasonRuleOther { } 128 + @token model ReasonRuleProhibitedSales { } 129 + @token model ReasonRuleSiteSecurity { } 130 + @token model ReasonRuleStolenContent { } 131 + @token model ReasonSexualAbuseContent { } 132 + @token model ReasonSexualAnimal { } 133 + @token model ReasonSexualDeepfake { } 134 + @token model ReasonSexualNCII { } 135 + @token model ReasonSexualOther { } 136 + @token model ReasonSexualSextortion { } 137 + @token model ReasonSexualUnlabeled { } 138 + @token model ReasonViolenceAnimalWelfare { } 139 + @token model ReasonViolenceExtremistContent { } 140 + @token model ReasonViolenceGlorification { } 141 + @token model ReasonViolenceGraphicContent { } 142 + @token model ReasonViolenceOther { } 143 + @token model ReasonViolenceSelfHarm { } 144 + @token model ReasonViolenceThreats { } 145 + @token model ReasonViolenceTrafficking { } 146 + }
+16 -9
packages/emitter/test/integration/atproto/input/com/atproto/repo/applyWrites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.applyWrites { 4 - @doc("Indicates that the 'swapCommit' parameter did not match current commit.") 4 + /** Indicates that the 'swapCommit' parameter did not match current commit. */ 5 5 model InvalidSwap {} 6 6 7 7 @closed ··· 20 20 DeleteResult, 21 21 } 22 22 23 - @doc("Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.") 23 + /** Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS. */ 24 24 @procedure 25 25 @errors(InvalidSwap) 26 26 op main(input: { 27 - @doc("The handle or DID of the repo (aka, current account).") 27 + /** The handle or DID of the repo (aka, current account). */ 28 28 @required 29 29 repo: atIdentifier; 30 30 31 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.") 31 + /** Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons. */ 32 32 validate?: boolean; 33 33 34 34 @required 35 35 writes: WriteAction[]; 36 36 37 - @doc("If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.") 37 + /** If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations. */ 38 38 swapCommit?: cid; 39 39 }): { 40 40 commit?: com.atproto.repo.defs.CommitMeta; 41 41 results?: WriteResult[]; 42 42 }; 43 43 44 - @doc("Operation which creates a new record.") 44 + /** Operation which creates a new record. */ 45 45 model Create { 46 46 @required collection: nsid; 47 47 48 - @doc("NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.") 48 + /** NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility. */ 49 49 @maxLength(512) 50 50 rkey?: recordKey; 51 51 52 52 @required value: unknown; 53 53 } 54 54 55 - @doc("Operation which updates an existing record.") 55 + /** Operation which updates an existing record. */ 56 56 model Update { 57 57 @required collection: nsid; 58 58 @required rkey: recordKey; 59 59 @required value: unknown; 60 60 } 61 61 62 - @doc("Operation which deletes an existing record.") 62 + /** Operation which deletes an existing record. */ 63 63 model Delete { 64 64 @required collection: nsid; 65 65 @required rkey: recordKey; ··· 79 79 80 80 model DeleteResult {} 81 81 } 82 + 83 + // --- Externals --- 84 + 85 + @external 86 + namespace com.atproto.repo.defs { 87 + model CommitMeta { } 88 + }
+15 -8
packages/emitter/test/integration/atproto/input/com/atproto/repo/createRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.createRecord { 4 - @doc("Create a single new repository record. Requires auth, implemented by PDS.") 4 + /** Create a single new repository record. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @maxLength(512) 18 18 rkey?: recordKey; 19 19 20 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.") 20 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 21 21 validate?: boolean; 22 22 23 - @doc("The record itself. Must contain a $type field.") 23 + /** The record itself. Must contain a $type field. */ 24 24 @required 25 25 record: unknown; 26 26 27 - @doc("Compare and swap with the previous commit by CID.") 27 + /** Compare and swap with the previous commit by CID. */ 28 28 swapCommit?: cid; 29 29 }): { 30 30 @required uri: atUri; ··· 33 33 validationStatus?: "valid" | "unknown" | string; 34 34 }; 35 35 36 - @doc("Indicates that 'swapCommit' didn't match current repo commit.") 36 + /** Indicates that 'swapCommit' didn't match current repo commit. */ 37 37 model InvalidSwap {} 38 38 } 39 + 40 + // --- Externals --- 41 + 42 + @external 43 + namespace com.atproto.repo.defs { 44 + model CommitMeta { } 45 + }
+13 -6
packages/emitter/test/integration/atproto/input/com/atproto/repo/deleteRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.deleteRecord { 4 - @doc("Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.") 4 + /** Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @required 18 18 rkey: recordKey; 19 19 20 - @doc("Compare and swap with the previous record by CID.") 20 + /** Compare and swap with the previous record by CID. */ 21 21 swapRecord?: cid; 22 22 23 - @doc("Compare and swap with the previous commit by CID.") 23 + /** Compare and swap with the previous commit by CID. */ 24 24 swapCommit?: cid; 25 25 }): { 26 26 commit?: com.atproto.repo.defs.CommitMeta; ··· 28 28 29 29 model InvalidSwap {} 30 30 } 31 + 32 + // --- Externals --- 33 + 34 + @external 35 + namespace com.atproto.repo.defs { 36 + model CommitMeta { } 37 + }
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/repo/describeRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.describeRepo { 4 - @doc("Get information about an account and repository, including the list of collections. Does not require auth.") 4 + /** Get information about an account and repository, including the list of collections. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 repo: atIdentifier 10 10 ): { 11 11 @required handle: handle; 12 12 @required did: did; 13 13 14 - @doc("The complete DID document for this account.") 14 + /** The complete DID document for this account. */ 15 15 @required 16 16 didDoc: unknown; 17 17 18 - @doc("List of all the collections (NSIDs) for which this repo contains at least one record.") 18 + /** List of all the collections (NSIDs) for which this repo contains at least one record. */ 19 19 @required 20 20 collections: nsid[]; 21 21 22 - @doc("Indicates if handle is currently valid (resolves bi-directionally)") 22 + /** Indicates if handle is currently valid (resolves bi-directionally) */ 23 23 @required 24 24 handleIsCorrect: boolean; 25 25 };
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/repo/getRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.getRecord { 4 - @doc("Get a single record from a repository. Does not require auth.") 4 + /** Get a single record from a repository. Does not require auth. */ 5 5 @query 6 6 @errors(RecordNotFound) 7 7 op main( 8 - @doc("The handle or DID of the repo.") 8 + /** The handle or DID of the repo. */ 9 9 @required 10 10 repo: atIdentifier, 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid, 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @required 18 18 rkey: recordKey, 19 19 20 - @doc("The CID of the version of the record. If not specified, then return the most recent version.") 20 + /** The CID of the version of the record. If not specified, then return the most recent version. */ 21 21 cid?: cid 22 22 ): { 23 23 @required uri: atUri;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/repo/importRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.importRepo { 4 - @doc("Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.") 4 + /** Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set. */ 5 5 @procedure 6 6 op main( 7 7 @encoding("application/vnd.ipld.car")
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/repo/listMissingBlobs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.listMissingBlobs { 4 - @doc("Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.") 4 + /** Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow. */ 5 5 @query 6 6 op main( 7 7 @minValue(1)
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/repo/listRecords.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.listRecords { 4 - @doc("List a range of records in a repository, matching a specific collection. Does not require auth.") 4 + /** List a range of records in a repository, matching a specific collection. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 repo: atIdentifier, 10 10 11 - @doc("The NSID of the record type.") 11 + /** The NSID of the record type. */ 12 12 @required 13 13 collection: nsid, 14 14 15 - @doc("The number of records to return.") 15 + /** The number of records to return. */ 16 16 @minValue(1) 17 17 @maxValue(100) 18 18 limit?: int32 = 50, 19 19 20 20 cursor?: string, 21 21 22 - @doc("Flag to reverse the order of the returned records.") 22 + /** Flag to reverse the order of the returned records. */ 23 23 reverse?: boolean 24 24 ): { 25 25 cursor?: string;
+15 -8
packages/emitter/test/integration/atproto/input/com/atproto/repo/putRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.putRecord { 4 - @doc("Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.") 4 + /** Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @maxLength(512) 18 18 @required 19 19 rkey: recordKey; 20 20 21 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.") 21 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 22 22 validate?: boolean; 23 23 24 - @doc("The record to write.") 24 + /** The record to write. */ 25 25 @required 26 26 record: unknown; 27 27 28 - @doc("Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation") 28 + /** Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation */ 29 29 swapRecord?: cid | null; 30 30 31 - @doc("Compare and swap with the previous commit by CID.") 31 + /** Compare and swap with the previous commit by CID. */ 32 32 swapCommit?: cid; 33 33 }): { 34 34 @required uri: atUri; ··· 39 39 40 40 model InvalidSwap {} 41 41 } 42 + 43 + // --- Externals --- 44 + 45 + @external 46 + namespace com.atproto.repo.defs { 47 + model CommitMeta { } 48 + }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/repo/strongRef.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A URI with a content-hash fingerprint.") 3 + /** A URI with a content-hash fingerprint. */ 4 4 namespace com.atproto.repo.strongRef { 5 5 model Main { 6 6 @required uri: atUri;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/repo/uploadBlob.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.uploadBlob { 4 - @doc("Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.") 4 + /** Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS. */ 5 5 @procedure 6 6 op main( 7 7 @encoding("*/*")
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/activateAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.activateAccount { 4 - @doc("Activates a currently deactivated account. Used to finalize account migration after the account's repo is imported and identity is setup.") 4 + /** Activates a currently deactivated account. Used to finalize account migration after the account's repo is imported and identity is setup. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/checkAccountStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.checkAccountStatus { 4 - @doc("Returns the status of an account, especially as pertaining to import or recovery. Can be called many times over the course of an account migration. Requires auth and can only be called pertaining to oneself.") 4 + /** Returns the status of an account, especially as pertaining to import or recovery. Can be called many times over the course of an account migration. Requires auth and can only be called pertaining to oneself. */ 5 5 @query 6 6 op main(): { 7 7 @required activated: boolean;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/confirmEmail.tsp
··· 6 6 model InvalidToken {} 7 7 model InvalidEmail {} 8 8 9 - @doc("Confirm an email using a token from com.atproto.server.requestEmailConfirmation.") 9 + /** Confirm an email using a token from com.atproto.server.requestEmailConfirmation. */ 10 10 @procedure 11 11 @errors(AccountNotFound, ExpiredToken, InvalidToken, InvalidEmail) 12 12 op main(input: {
+9 -9
packages/emitter/test/integration/atproto/input/com/atproto/server/createAccount.tsp
··· 9 9 model UnresolvableDid {} 10 10 model IncompatibleDidDoc {} 11 11 12 - @doc("Account login session returned on successful account creation.") 12 + /** Account login session returned on successful account creation. */ 13 13 @inline 14 14 model Output { 15 15 @required accessJwt: string; 16 16 @required refreshJwt: string; 17 17 @required handle: handle; 18 18 19 - @doc("The DID of the new account.") 19 + /** The DID of the new account. */ 20 20 @required 21 21 did: did; 22 22 23 - @doc("Complete DID document.") 23 + /** Complete DID document. */ 24 24 didDoc?: unknown; 25 25 } 26 26 27 - @doc("Create an account. Implemented by PDS.") 27 + /** Create an account. Implemented by PDS. */ 28 28 @procedure 29 29 @errors(InvalidHandle, InvalidPassword, InvalidInviteCode, HandleNotAvailable, UnsupportedDomain, UnresolvableDid, IncompatibleDidDoc) 30 30 op main(input: { 31 31 email?: string; 32 32 33 - @doc("Requested handle for the account.") 33 + /** Requested handle for the account. */ 34 34 @required 35 35 handle: handle; 36 36 37 - @doc("Pre-existing atproto DID, being imported to a new account.") 37 + /** Pre-existing atproto DID, being imported to a new account. */ 38 38 did?: did; 39 39 40 40 inviteCode?: string; 41 41 verificationCode?: string; 42 42 verificationPhone?: string; 43 43 44 - @doc("Initial account password. May need to meet instance-specific password strength requirements.") 44 + /** Initial account password. May need to meet instance-specific password strength requirements. */ 45 45 password?: string; 46 46 47 - @doc("DID PLC rotation key (aka, recovery key) to be included in PLC creation operation.") 47 + /** DID PLC rotation key (aka, recovery key) to be included in PLC creation operation. */ 48 48 recoveryKey?: string; 49 49 50 - @doc("A signed DID PLC operation to be submitted as part of importing an existing account to this instance. NOTE: this optional field may be updated when full account migration is implemented.") 50 + /** A signed DID PLC operation to be submitted as part of importing an existing account to this instance. NOTE: this optional field may be updated when full account migration is implemented. */ 51 51 plcOp?: unknown; 52 52 }): Output; 53 53 }
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/server/createAppPassword.tsp
··· 10 10 privileged?: boolean; 11 11 } 12 12 13 - @doc("Create an App Password.") 13 + /** Create an App Password. */ 14 14 @procedure 15 15 @errors(AccountTakedown) 16 16 op main(input: { 17 - @doc("A short name for the App Password, to help distinguish them.") 17 + /** A short name for the App Password, to help distinguish them. */ 18 18 @required 19 19 name: string; 20 20 21 - @doc("If an app password has 'privileged' access to possibly sensitive account state. Meant for use with trusted clients.") 21 + /** If an app password has 'privileged' access to possibly sensitive account state. Meant for use with trusted clients. */ 22 22 privileged?: boolean; 23 23 }): AppPassword; 24 24 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/createInviteCode.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.createInviteCode { 4 - @doc("Create an invite code.") 4 + /** Create an invite code. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required useCount: integer;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/createInviteCodes.tsp
··· 6 6 @required codes: string[]; 7 7 } 8 8 9 - @doc("Create invite codes.") 9 + /** Create invite codes. */ 10 10 @procedure 11 11 op main(input: { 12 12 @required codeCount: integer = 1;
+4 -4
packages/emitter/test/integration/atproto/input/com/atproto/server/createSession.tsp
··· 5 5 6 6 model AuthFactorTokenRequired {} 7 7 8 - @doc("Create an authentication session.") 8 + /** Create an authentication session. */ 9 9 @procedure 10 10 @errors(AccountTakedown, AuthFactorTokenRequired) 11 11 op main(input: { 12 - @doc("Handle or other identifier supported by the server for the authenticating user.") 12 + /** Handle or other identifier supported by the server for the authenticating user. */ 13 13 @required 14 14 identifier: string; 15 15 ··· 17 17 18 18 authFactorToken?: string; 19 19 20 - @doc("When true, instead of throwing error for takendown accounts, a valid response with a narrow scoped token will be returned") 20 + /** When true, instead of throwing error for takendown accounts, a valid response with a narrow scoped token will be returned */ 21 21 allowTakendown?: boolean; 22 22 }): { 23 23 @required accessJwt: string; ··· 30 30 emailAuthFactor?: boolean; 31 31 active?: boolean; 32 32 33 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 33 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 34 34 status?: "takendown" | "suspended" | "deactivated" | string; 35 35 }; 36 36 }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/server/deactivateAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.deactivateAccount { 4 - @doc("Deactivates a currently active account. Stops serving of repo, and future writes to repo until reactivated. Used to finalize account migration with the old host after the account has been activated on the new host.") 4 + /** Deactivates a currently active account. Stops serving of repo, and future writes to repo until reactivated. Used to finalize account migration with the old host after the account has been activated on the new host. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("A recommendation to server as to how long they should hold onto the deactivated account before deleting.") 7 + /** A recommendation to server as to how long they should hold onto the deactivated account before deleting. */ 8 8 deleteAfter?: datetime; 9 9 }): void; 10 10 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/deleteAccount.tsp
··· 4 4 model ExpiredToken {} 5 5 model InvalidToken {} 6 6 7 - @doc("Delete an actor's account with a token and password. Can only be called after requesting a deletion token. Requires auth.") 7 + /** Delete an actor's account with a token and password. Can only be called after requesting a deletion token. Requires auth. */ 8 8 @procedure 9 9 @errors(ExpiredToken, InvalidToken) 10 10 op main(input: {
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/deleteSession.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.deleteSession { 4 - @doc("Delete the current session. Requires auth.") 4 + /** Delete the current session. Requires auth. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+6 -6
packages/emitter/test/integration/atproto/input/com/atproto/server/describeServer.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.describeServer { 4 - @doc("Describes the server's account creation requirements and capabilities. Implemented by PDS.") 4 + /** Describes the server's account creation requirements and capabilities. Implemented by PDS. */ 5 5 @query 6 6 op main(): { 7 7 @required did: did; 8 8 9 - @doc("If true, an invite code must be supplied to create an account on this instance.") 9 + /** If true, an invite code must be supplied to create an account on this instance. */ 10 10 inviteCodeRequired?: boolean; 11 11 12 - @doc("If true, a phone verification token must be supplied to create an account on this instance.") 12 + /** If true, a phone verification token must be supplied to create an account on this instance. */ 13 13 phoneVerificationRequired?: boolean; 14 14 15 - @doc("List of domain suffixes that can be used in account handles.") 15 + /** List of domain suffixes that can be used in account handles. */ 16 16 @required 17 17 availableUserDomains: string[]; 18 18 19 - @doc("URLs of service policy documents.") 19 + /** URLs of service policy documents. */ 20 20 links?: Links; 21 21 22 - @doc("Contact information") 22 + /** Contact information */ 23 23 contact?: Contact; 24 24 }; 25 25
+9 -2
packages/emitter/test/integration/atproto/input/com/atproto/server/getAccountInviteCodes.tsp
··· 3 3 namespace com.atproto.server.getAccountInviteCodes { 4 4 model DuplicateCreate {} 5 5 6 - @doc("Get all invite codes for the current account. Requires auth.") 6 + /** Get all invite codes for the current account. Requires auth. */ 7 7 @query 8 8 @errors(DuplicateCreate) 9 9 op main( 10 10 includeUsed?: boolean = true, 11 11 12 - @doc("Controls whether any new 'earned' but not 'created' invites should be created.") 12 + /** Controls whether any new 'earned' but not 'created' invites should be created. */ 13 13 createAvailable?: boolean = true 14 14 ): { 15 15 @required codes: com.atproto.server.defs.InviteCode[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace com.atproto.server.defs { 23 + model InviteCode { } 24 + }
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/server/getServiceAuth.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.getServiceAuth { 4 - @doc("Indicates that the requested expiration date is not a valid. May be in the past or may be reliant on the requested scopes.") 4 + /** Indicates that the requested expiration date is not a valid. May be in the past or may be reliant on the requested scopes. */ 5 5 model BadExpiration {} 6 6 7 - @doc("Get a signed token on behalf of the requesting DID for the requested service.") 7 + /** Get a signed token on behalf of the requesting DID for the requested service. */ 8 8 @query 9 9 @errors(BadExpiration) 10 10 op main( 11 - @doc("The DID of the service that the token will be used to authenticate with") 11 + /** The DID of the service that the token will be used to authenticate with */ 12 12 @required 13 13 aud: did, 14 14 15 - @doc("The time in Unix Epoch seconds that the JWT expires. Defaults to 60 seconds in the future. The service may enforce certain time bounds on tokens depending on the requested scope.") 15 + /** The time in Unix Epoch seconds that the JWT expires. Defaults to 60 seconds in the future. The service may enforce certain time bounds on tokens depending on the requested scope. */ 16 16 exp?: integer, 17 17 18 - @doc("Lexicon (XRPC) method to bind the requested token to") 18 + /** Lexicon (XRPC) method to bind the requested token to */ 19 19 lxm?: nsid 20 20 ): { 21 21 @required token: string;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/server/getSession.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.getSession { 4 - @doc("Get information about the current auth session. Requires auth.") 4 + /** Get information about the current auth session. Requires auth. */ 5 5 @query 6 6 op main(): { 7 7 @required handle: handle; ··· 12 12 didDoc?: unknown; 13 13 active?: boolean; 14 14 15 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 15 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 16 16 status?: "takendown" | "suspended" | "deactivated" | string; 17 17 }; 18 18 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/listAppPasswords.tsp
··· 9 9 privileged?: boolean; 10 10 } 11 11 12 - @doc("List all App Passwords.") 12 + /** List all App Passwords. */ 13 13 @query 14 14 @errors(AccountTakedown) 15 15 op main(): {
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/server/refreshSession.tsp
··· 3 3 namespace com.atproto.server.refreshSession { 4 4 model AccountTakedown {} 5 5 6 - @doc("Refresh an authentication session. Requires auth using the 'refreshJwt' (not the 'accessJwt').") 6 + /** Refresh an authentication session. Requires auth using the 'refreshJwt' (not the 'accessJwt'). */ 7 7 @procedure 8 8 @errors(AccountTakedown) 9 9 op main(): { ··· 14 14 didDoc?: unknown; 15 15 active?: boolean; 16 16 17 - @doc("Hosting status of the account. If not specified, then assume 'active'.") 17 + /** Hosting status of the account. If not specified, then assume 'active'. */ 18 18 status?: "takendown" | "suspended" | "deactivated" | string; 19 19 }; 20 20 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/requestAccountDelete.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestAccountDelete { 4 - @doc("Initiate a user account deletion via email.") 4 + /** Initiate a user account deletion via email. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/requestEmailConfirmation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestEmailConfirmation { 4 - @doc("Request an email with a code to confirm ownership of email.") 4 + /** Request an email with a code to confirm ownership of email. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/requestEmailUpdate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestEmailUpdate { 4 - @doc("Request a token in order to update email.") 4 + /** Request a token in order to update email. */ 5 5 @procedure 6 6 op main(): { 7 7 @required tokenRequired: boolean;
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/requestPasswordReset.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestPasswordReset { 4 - @doc("Initiate a user account password reset via email.") 4 + /** Initiate a user account password reset via email. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required email: string;
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/server/reserveSigningKey.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.reserveSigningKey { 4 - @doc("Reserve a repo signing key, for use with account creation. Necessary so that a DID PLC update operation can be constructed during an account migraiton. Public and does not require auth; implemented by PDS. NOTE: this endpoint may change when full account migration is implemented.") 4 + /** Reserve a repo signing key, for use with account creation. Necessary so that a DID PLC update operation can be constructed during an account migraiton. Public and does not require auth; implemented by PDS. NOTE: this endpoint may change when full account migration is implemented. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The DID to reserve a key for.") 7 + /** The DID to reserve a key for. */ 8 8 did?: did; 9 9 }): { 10 - @doc("The public key for the reserved signing key, in did:key serialization.") 10 + /** The public key for the reserved signing key, in did:key serialization. */ 11 11 @required 12 12 signingKey: string; 13 13 };
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/resetPassword.tsp
··· 4 4 model ExpiredToken {} 5 5 model InvalidToken {} 6 6 7 - @doc("Reset a user account password using a token.") 7 + /** Reset a user account password using a token. */ 8 8 @procedure 9 9 @errors(ExpiredToken, InvalidToken) 10 10 op main(input: {
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/server/revokeAppPassword.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.revokeAppPassword { 4 - @doc("Revoke an App Password by name.") 4 + /** Revoke an App Password by name. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required name: string;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/server/updateEmail.tsp
··· 5 5 model InvalidToken {} 6 6 model TokenRequired {} 7 7 8 - @doc("Update an account's email.") 8 + /** Update an account's email. */ 9 9 @procedure 10 10 @errors(ExpiredToken, InvalidToken, TokenRequired) 11 11 op main(input: { 12 12 @required email: string; 13 13 emailAuthFactor?: boolean; 14 14 15 - @doc("Requires a token from com.atproto.sever.requestEmailUpdate if the account's email has been confirmed.") 15 + /** Requires a token from com.atproto.sever.requestEmailUpdate if the account's email has been confirmed. */ 16 16 token?: string; 17 17 }): void; 18 18 }
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/sync/getBlob.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getBlob { 4 - @doc("Get a blob associated with a given account. Returns the full blob as originally uploaded. Does not require auth; implemented by PDS.") 4 + /** Get a blob associated with a given account. Returns the full blob as originally uploaded. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("*/*") 7 7 @errors(BlobNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the account.") 9 + /** The DID of the account. */ 10 10 @required 11 11 did: did, 12 12 13 - @doc("The CID of the blob to fetch") 13 + /** The CID of the blob to fetch */ 14 14 @required 15 15 cid: cid 16 16 ): void;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/getBlocks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getBlocks { 4 - @doc("Get data blocks from a given repo, by CID. For example, intermediate MST nodes, or records. Does not require auth; implemented by PDS.") 4 + /** Get data blocks from a given repo, by CID. For example, intermediate MST nodes, or records. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(BlockNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/getCheckout.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getCheckout { 4 - @doc("DEPRECATED - please use com.atproto.sync.getRepo instead") 4 + /** DEPRECATED - please use com.atproto.sync.getRepo instead */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): void;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/getHead.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getHead { 4 - @doc("DEPRECATED - please use com.atproto.sync.getLatestCommit instead") 4 + /** DEPRECATED - please use com.atproto.sync.getLatestCommit instead */ 5 5 @query 6 6 @errors(HeadNotFound) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): {
+11 -4
packages/emitter/test/integration/atproto/input/com/atproto/sync/getHostStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getHostStatus { 4 - @doc("Returns information about a specified upstream host, as consumed by the server. Implemented by relays.") 4 + /** Returns information about a specified upstream host, as consumed by the server. Implemented by relays. */ 5 5 @query 6 6 @errors(HostNotFound) 7 7 op main( 8 - @doc("Hostname of the host (eg, PDS or relay) being queried.") 8 + /** Hostname of the host (eg, PDS or relay) being queried. */ 9 9 @required 10 10 hostname: string 11 11 ): { 12 12 @required hostname: string; 13 13 14 - @doc("Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor).") 14 + /** Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor). */ 15 15 seq?: integer; 16 16 17 - @doc("Number of accounts on the server which are associated with the upstream host. Note that the upstream may actually have more accounts.") 17 + /** Number of accounts on the server which are associated with the upstream host. Note that the upstream may actually have more accounts. */ 18 18 accountCount?: integer; 19 19 20 20 status?: com.atproto.sync.defs.HostStatus; ··· 22 22 23 23 model HostNotFound {} 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace com.atproto.sync.defs { 30 + model HostStatus { } 31 + }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/getLatestCommit.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getLatestCommit { 4 - @doc("Get the current commit CID & revision of the specified repo. Does not require auth.") 4 + /** Get the current commit CID & revision of the specified repo. Does not require auth. */ 5 5 @query 6 6 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): {
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/sync/getRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRecord { 4 - @doc("Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.") 4 + /** Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(RecordNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12 13 13 @required collection: nsid, 14 14 15 - @doc("Record Key") 15 + /** Record Key */ 16 16 @required 17 17 rkey: recordKey 18 18 ): void;
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/sync/getRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRepo { 4 - @doc("Download a repository export as CAR file. Optionally only a 'diff' since a previous revision. Does not require auth; implemented by PDS.") 4 + /** Download a repository export as CAR file. Optionally only a 'diff' since a previous revision. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12 13 - @doc("The revision ('rev') of the repo to create a diff from.") 13 + /** The revision ('rev') of the repo to create a diff from. */ 14 14 since?: tid 15 15 ): void; 16 16
+4 -4
packages/emitter/test/integration/atproto/input/com/atproto/sync/getRepoStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRepoStatus { 4 - @doc("Get the hosting status for a repository, on this server. Expected to be implemented by PDS and Relay.") 4 + /** Get the hosting status for a repository, on this server. Expected to be implemented by PDS and Relay. */ 5 5 @query 6 6 @errors(RepoNotFound) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): { 12 12 @required did: did; 13 13 @required active: boolean; 14 14 15 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 15 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 16 16 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 17 17 18 - @doc("Optional field, the current rev of the repo, if active=true") 18 + /** Optional field, the current rev of the repo, if active=true */ 19 19 rev?: tid; 20 20 }; 21 21
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/sync/listBlobs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listBlobs { 4 - @doc("List blob CIDs for an account, since some repo revision. Does not require auth; implemented by PDS.") 4 + /** List blob CIDs for an account, since some repo revision. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did, 11 11 12 - @doc("Optional revision of the repo to list blobs since.") 12 + /** Optional revision of the repo to list blobs since. */ 13 13 since?: tid, 14 14 15 15 @minValue(1)
+11 -4
packages/emitter/test/integration/atproto/input/com/atproto/sync/listHosts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listHosts { 4 - @doc("Enumerates upstream hosts (eg, PDS or relay instances) that this service consumes from. Implemented by relays.") 4 + /** Enumerates upstream hosts (eg, PDS or relay instances) that this service consumes from. Implemented by relays. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 12 12 ): { 13 13 cursor?: string; 14 14 15 - @doc("Sort order is not formally specified. Recommended order is by time host was first seen by the server, with oldest first.") 15 + /** Sort order is not formally specified. Recommended order is by time host was first seen by the server, with oldest first. */ 16 16 @required 17 17 hosts: Host[]; 18 18 }; 19 19 20 20 model Host { 21 - @doc("hostname of server; not a URL (no scheme)") 21 + /** hostname of server; not a URL (no scheme) */ 22 22 @required 23 23 hostname: string; 24 24 25 - @doc("Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor).") 25 + /** Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor). */ 26 26 seq?: integer; 27 27 28 28 accountCount?: integer; 29 29 status?: com.atproto.sync.defs.HostStatus; 30 30 } 31 31 } 32 + 33 + // --- Externals --- 34 + 35 + @external 36 + namespace com.atproto.sync.defs { 37 + model HostStatus { } 38 + }
+3 -3
packages/emitter/test/integration/atproto/input/com/atproto/sync/listRepos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listRepos { 4 - @doc("Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay.") 4 + /** Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 17 17 model Repo { 18 18 @required did: did; 19 19 20 - @doc("Current repo commit CID") 20 + /** Current repo commit CID */ 21 21 @required 22 22 head: cid; 23 23 24 24 @required rev: tid; 25 25 active?: boolean; 26 26 27 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 27 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 28 28 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 29 29 } 30 30 }
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/listReposByCollection.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listReposByCollection { 4 - @doc("Enumerates all the DIDs which have records with the given collection NSID.") 4 + /** Enumerates all the DIDs which have records with the given collection NSID. */ 5 5 @query 6 6 op main( 7 7 @required collection: nsid, 8 8 9 - @doc("Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists.") 9 + /** Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists. */ 10 10 @minValue(1) 11 11 @maxValue(2000) 12 12 limit?: int32 = 500,
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/notifyOfUpdate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.notifyOfUpdate { 4 - @doc("Notify a crawling service of a recent update, and that crawling should resume. Intended use is after a gap between repo stream events caused the crawling service to disconnect. Does not require auth; implemented by Relay. DEPRECATED: just use com.atproto.sync.requestCrawl") 4 + /** Notify a crawling service of a recent update, and that crawling should resume. Intended use is after a gap between repo stream events caused the crawling service to disconnect. Does not require auth; implemented by Relay. DEPRECATED: just use com.atproto.sync.requestCrawl */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Hostname of the current service (usually a PDS) that is notifying of update.") 7 + /** Hostname of the current service (usually a PDS) that is notifying of update. */ 8 8 @required 9 9 hostname: string; 10 10 }): void;
+2 -2
packages/emitter/test/integration/atproto/input/com/atproto/sync/requestCrawl.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.requestCrawl { 4 - @doc("Request a service to persistently crawl hosted repos. Expected use is new PDS instances declaring their existence to Relays. Does not require auth.") 4 + /** Request a service to persistently crawl hosted repos. Expected use is new PDS instances declaring their existence to Relays. Does not require auth. */ 5 5 @procedure 6 6 @errors(HostBanned) 7 7 op main(input: { 8 - @doc("Hostname of the current service (eg, PDS) that is requesting to be crawled.") 8 + /** Hostname of the current service (eg, PDS) that is requesting to be crawled. */ 9 9 @required 10 10 hostname: string; 11 11 }): void;
+30 -30
packages/emitter/test/integration/atproto/input/com/atproto/sync/subscribeRepos.tsp
··· 3 3 namespace com.atproto.sync.subscribeRepos { 4 4 @subscription 5 5 @errors(FutureCursor, ConsumerTooSlow) 6 - @doc("Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. See the atproto specifications for details around stream sequencing, repo versioning, CAR diff format, and more. Public and does not require auth; implemented by PDS and Relay.") 6 + /** Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. See the atproto specifications for details around stream sequencing, repo versioning, CAR diff format, and more. Public and does not require auth; implemented by PDS and Relay. */ 7 7 op main( 8 - @doc("The last known event seq number to backfill from.") 8 + /** The last known event seq number to backfill from. */ 9 9 cursor?: integer 10 10 ): (Commit | Sync | Identity | Account | Info | unknown); 11 11 12 12 model FutureCursor {} 13 13 14 - @doc("If the consumer of the stream can not keep up with events, and a backlog gets too large, the server will drop the connection.") 14 + /** If the consumer of the stream can not keep up with events, and a backlog gets too large, the server will drop the connection. */ 15 15 model ConsumerTooSlow {} 16 16 17 - @doc("Represents an update of repository state. Note that empty commits are allowed, which include no repo data changes, but an update to rev and signature.") 17 + /** Represents an update of repository state. Note that empty commits are allowed, which include no repo data changes, but an update to rev and signature. */ 18 18 model Commit { 19 - @doc("The stream sequence number of this message.") 19 + /** The stream sequence number of this message. */ 20 20 @required 21 21 seq: integer; 22 22 23 - @doc("DEPRECATED -- unused") 23 + /** DEPRECATED -- unused */ 24 24 @required 25 25 rebase: boolean; 26 26 27 - @doc("DEPRECATED -- replaced by #sync event and data limits. Indicates that this commit contained too many ops, or data size was too large. Consumers will need to make a separate request to get missing data.") 27 + /** DEPRECATED -- replaced by #sync event and data limits. Indicates that this commit contained too many ops, or data size was too large. Consumers will need to make a separate request to get missing data. */ 28 28 @required 29 29 tooBig: boolean; 30 30 31 - @doc("The repo this event comes from. Note that all other message types name this field 'did'.") 31 + /** The repo this event comes from. Note that all other message types name this field 'did'. */ 32 32 @required 33 33 repo: did; 34 34 35 - @doc("Repo commit object CID.") 35 + /** Repo commit object CID. */ 36 36 @required 37 37 commit: cidLink; 38 38 39 - @doc("The rev of the emitted commit. Note that this information is also in the commit object included in blocks, unless this is a tooBig event.") 39 + /** The rev of the emitted commit. Note that this information is also in the commit object included in blocks, unless this is a tooBig event. */ 40 40 @required 41 41 rev: tid; 42 42 43 - @doc("The rev of the last emitted commit from this repo (if any).") 43 + /** The rev of the last emitted commit from this repo (if any). */ 44 44 @required 45 45 since: tid | null; 46 46 47 - @doc("CAR file containing relevant blocks, as a diff since the previous repo state. The commit must be included as a block, and the commit block CID must be the first entry in the CAR header 'roots' list.") 47 + /** CAR file containing relevant blocks, as a diff since the previous repo state. The commit must be included as a block, and the commit block CID must be the first entry in the CAR header 'roots' list. */ 48 48 @maxBytes(2000000) 49 49 @required 50 50 blocks: bytes; 51 51 52 52 @maxItems(200) 53 53 @required 54 - @doc("List of repo mutation operations in this commit (eg, records created, updated, or deleted).") 54 + /** List of repo mutation operations in this commit (eg, records created, updated, or deleted). */ 55 55 ops: RepoOp[]; 56 56 57 - @doc("DEPRECATED -- will soon always be empty. List of new blobs (by CID) referenced by records in this commit.") 57 + /** DEPRECATED -- will soon always be empty. List of new blobs (by CID) referenced by records in this commit. */ 58 58 @required blobs: cidLink[]; 59 59 60 - @doc("The root CID of the MST tree for the previous commit from this repo (indicated by the 'since' revision field in this message). Corresponds to the 'data' field in the repo commit object. NOTE: this field is effectively required for the 'inductive' version of firehose.") 60 + /** The root CID of the MST tree for the previous commit from this repo (indicated by the 'since' revision field in this message). Corresponds to the 'data' field in the repo commit object. NOTE: this field is effectively required for the 'inductive' version of firehose. */ 61 61 prevData?: cidLink; 62 62 63 - @doc("Timestamp of when this message was originally broadcast.") 63 + /** Timestamp of when this message was originally broadcast. */ 64 64 @required 65 65 time: datetime; 66 66 } 67 67 68 - @doc("Updates the repo to a new state, without necessarily including that state on the firehose. Used to recover from broken commit streams, data loss incidents, or in situations where upstream host does not know recent state of the repository.") 68 + /** Updates the repo to a new state, without necessarily including that state on the firehose. Used to recover from broken commit streams, data loss incidents, or in situations where upstream host does not know recent state of the repository. */ 69 69 model Sync { 70 - @doc("The stream sequence number of this message.") 70 + /** The stream sequence number of this message. */ 71 71 @required 72 72 seq: integer; 73 73 74 - @doc("The account this repo event corresponds to. Must match that in the commit object.") 74 + /** The account this repo event corresponds to. Must match that in the commit object. */ 75 75 @required 76 76 did: did; 77 77 78 - @doc("CAR file containing the commit, as a block. The CAR header must include the commit block CID as the first 'root'.") 78 + /** CAR file containing the commit, as a block. The CAR header must include the commit block CID as the first 'root'. */ 79 79 @maxBytes(10000) 80 80 @required 81 81 blocks: bytes; 82 82 83 - @doc("The rev of the commit. This value must match that in the commit object.") 83 + /** The rev of the commit. This value must match that in the commit object. */ 84 84 @required 85 85 rev: string; 86 86 87 - @doc("Timestamp of when this message was originally broadcast.") 87 + /** Timestamp of when this message was originally broadcast. */ 88 88 @required 89 89 time: datetime; 90 90 } 91 91 92 - @doc("Represents a change to an account's identity. Could be an updated handle, signing key, or pds hosting endpoint. Serves as a prod to all downstream services to refresh their identity cache.") 92 + /** Represents a change to an account's identity. Could be an updated handle, signing key, or pds hosting endpoint. Serves as a prod to all downstream services to refresh their identity cache. */ 93 93 model Identity { 94 94 @required seq: integer; 95 95 @required did: did; 96 96 @required time: datetime; 97 97 98 - @doc("The current handle for the account, or 'handle.invalid' if validation fails. This field is optional, might have been validated or passed-through from an upstream source. Semantics and behaviors for PDS vs Relay may evolve in the future; see atproto specs for more details.") 98 + /** The current handle for the account, or 'handle.invalid' if validation fails. This field is optional, might have been validated or passed-through from an upstream source. Semantics and behaviors for PDS vs Relay may evolve in the future; see atproto specs for more details. */ 99 99 handle?: handle; 100 100 } 101 101 102 - @doc("Represents a change to an account's status on a host (eg, PDS or Relay). The semantics of this event are that the status is at the host which emitted the event, not necessarily that at the currently active PDS. Eg, a Relay takedown would emit a takedown with active=false, even if the PDS is still active.") 102 + /** Represents a change to an account's status on a host (eg, PDS or Relay). The semantics of this event are that the status is at the host which emitted the event, not necessarily that at the currently active PDS. Eg, a Relay takedown would emit a takedown with active=false, even if the PDS is still active. */ 103 103 model Account { 104 104 @required seq: integer; 105 105 @required did: did; 106 106 @required time: datetime; 107 107 108 - @doc("Indicates that the account has a repository which can be fetched from the host that emitted this event.") 108 + /** Indicates that the account has a repository which can be fetched from the host that emitted this event. */ 109 109 @required 110 110 active: boolean; 111 111 112 - @doc("If active=false, this optional field indicates a reason for why the account is not active.") 112 + /** If active=false, this optional field indicates a reason for why the account is not active. */ 113 113 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 114 114 } 115 115 ··· 118 118 message?: string; 119 119 } 120 120 121 - @doc("A repo operation, ie a mutation of a single record.") 121 + /** A repo operation, ie a mutation of a single record. */ 122 122 model RepoOp { 123 123 @required action: "create" | "update" | "delete" | string; 124 124 @required path: string; 125 125 126 - @doc("For creates and updates, the new record CID. For deletions, null.") 126 + /** For creates and updates, the new record CID. For deletions, null. */ 127 127 @required 128 128 cid: cidLink | null; 129 129 130 - @doc("For updates and deletes, the previous record CID (required for inductive firehose). For creations, field should not be defined.") 130 + /** For updates and deletes, the previous record CID (required for inductive firehose). For creations, field should not be defined. */ 131 131 prev?: cidLink; 132 132 } 133 133 }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/temp/addReservedHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.addReservedHandle { 4 - @doc("Add a handle to the set of reserved handles.") 4 + /** Add a handle to the set of reserved handles. */ 5 5 @procedure 6 6 op main( 7 7 input: {
+11 -11
packages/emitter/test/integration/atproto/input/com/atproto/temp/checkHandleAvailability.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.checkHandleAvailability { 4 - @doc("An invalid email was provided.") 4 + /** An invalid email was provided. */ 5 5 model InvalidEmail {} 6 6 7 - @doc("Indicates the provided handle is available.") 7 + /** Indicates the provided handle is available. */ 8 8 model ResultAvailable {} 9 9 10 - @doc("Indicates the provided handle is unavailable and gives suggestions of available handles.") 10 + /** Indicates the provided handle is unavailable and gives suggestions of available handles. */ 11 11 model ResultUnavailable { 12 - @doc("List of suggested handles based on the provided inputs.") 12 + /** List of suggested handles based on the provided inputs. */ 13 13 @required 14 14 suggestions: Suggestion[]; 15 15 } ··· 17 17 model Suggestion { 18 18 @required handle: handle; 19 19 20 - @doc("Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics.") 20 + /** Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics. */ 21 21 @required 22 22 method: string; 23 23 } 24 24 25 - @doc("Checks whether the provided handle is available. If the handle is not available, available suggestions will be returned. Optional inputs will be used to generate suggestions.") 25 + /** Checks whether the provided handle is available. If the handle is not available, available suggestions will be returned. Optional inputs will be used to generate suggestions. */ 26 26 @query 27 27 @errors(InvalidEmail) 28 28 op main( 29 - @doc("Tentative handle. Will be checked for availability or used to build handle suggestions.") 30 - handle: handle, 29 + /** Tentative handle. Will be checked for availability or used to build handle suggestions. */ 30 + @required handle: handle, 31 31 32 - @doc("User-provided email. Might be used to build handle suggestions.") 32 + /** User-provided email. Might be used to build handle suggestions. */ 33 33 email?: string, 34 34 35 - @doc("User-provided birth date. Might be used to build handle suggestions.") 35 + /** User-provided birth date. Might be used to build handle suggestions. */ 36 36 birthDate?: datetime 37 37 ): { 38 - @doc("Echo of the input handle.") 38 + /** Echo of the input handle. */ 39 39 @required 40 40 handle: handle; 41 41
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/temp/checkSignupQueue.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.checkSignupQueue { 4 - @doc("Check accounts location in signup queue.") 4 + /** Check accounts location in signup queue. */ 5 5 @query 6 6 op main(): { 7 7 @required activated: boolean;
+5 -5
packages/emitter/test/integration/atproto/input/com/atproto/temp/dereferenceScope.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.dereferenceScope { 4 - @doc("An invalid scope reference was provided.") 4 + /** An invalid scope reference was provided. */ 5 5 model InvalidScopeReference {} 6 6 7 - @doc("Allows finding the oauth permission scope from a reference") 7 + /** Allows finding the oauth permission scope from a reference */ 8 8 @query 9 9 @errors(InvalidScopeReference) 10 10 op main( 11 - @doc("The scope reference (starts with 'ref:')") 12 - scope: string 11 + /** The scope reference (starts with 'ref:') */ 12 + @required scope: string 13 13 ): { 14 - @doc("The full oauth permission scope") 14 + /** The full oauth permission scope */ 15 15 @required 16 16 scope: string; 17 17 };
+8 -1
packages/emitter/test/integration/atproto/input/com/atproto/temp/fetchLabels.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.fetchLabels { 4 - @doc("DEPRECATED: use queryLabels or subscribeLabels instead -- Fetch all labels from a labeler created after a certain date.") 4 + /** DEPRECATED: use queryLabels or subscribeLabels instead -- Fetch all labels from a labeler created after a certain date. */ 5 5 @query 6 6 op main( 7 7 since?: integer, ··· 13 13 @required labels: com.atproto.label.defs.Label[]; 14 14 }; 15 15 } 16 + 17 + // --- Externals --- 18 + 19 + @external 20 + namespace com.atproto.label.defs { 21 + model Label { } 22 + }
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/temp/requestPhoneVerification.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.requestPhoneVerification { 4 - @doc("Request a verification code to be sent to the supplied phone number") 4 + /** Request a verification code to be sent to the supplied phone number */ 5 5 @procedure 6 6 op main( 7 7 input: {
+1 -1
packages/emitter/test/integration/atproto/input/com/atproto/temp/revokeAccountCredentials.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.revokeAccountCredentials { 4 - @doc("Revoke sessions, password, and app passwords associated with account. May be resolved by a password reset.") 4 + /** Revoke sessions, password, and app passwords associated with account. May be resolved by a password reset. */ 5 5 @procedure 6 6 op main( 7 7 input: {
+13 -6
packages/emitter/test/integration/atproto/input/tools/ozone/communication/createTemplate.tsp
··· 3 3 namespace tools.ozone.communication.createTemplate { 4 4 model DuplicateTemplateName {} 5 5 6 - @doc("Administrative action to create a new, re-usable communication (email for now) template.") 6 + /** Administrative action to create a new, re-usable communication (email for now) template. */ 7 7 @procedure 8 8 @errors(DuplicateTemplateName) 9 9 op main(input: { 10 - @doc("Subject of the message, used in emails.") 10 + /** Subject of the message, used in emails. */ 11 11 @required 12 12 subject: string; 13 13 14 - @doc("Content of the template, markdown supported, can contain variable placeholders.") 14 + /** Content of the template, markdown supported, can contain variable placeholders. */ 15 15 @required 16 16 contentMarkdown: string; 17 17 18 - @doc("Name of the template.") 18 + /** Name of the template. */ 19 19 @required 20 20 name: string; 21 21 22 - @doc("Message language.") 22 + /** Message language. */ 23 23 lang?: language; 24 24 25 - @doc("DID of the user who is creating the template.") 25 + /** DID of the user who is creating the template. */ 26 26 createdBy?: did; 27 27 }): tools.ozone.communication.defs.TemplateView; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace tools.ozone.communication.defs { 34 + model TemplateView { } 35 + }
+5 -5
packages/emitter/test/integration/atproto/input/tools/ozone/communication/defs.tsp
··· 4 4 model TemplateView { 5 5 @required id: string; 6 6 7 - @doc("Name of the template.") 7 + /** Name of the template. */ 8 8 @required 9 9 name: string; 10 10 11 - @doc("Content of the template, can contain markdown and variable placeholders.") 11 + /** Content of the template, can contain markdown and variable placeholders. */ 12 12 subject?: string; 13 13 14 - @doc("Subject of the message, used in emails.") 14 + /** Subject of the message, used in emails. */ 15 15 @required 16 16 contentMarkdown: string; 17 17 18 18 @required disabled: boolean; 19 19 20 - @doc("Message language.") 20 + /** Message language. */ 21 21 lang?: language; 22 22 23 - @doc("DID of the user who last updated the template.") 23 + /** DID of the user who last updated the template. */ 24 24 @required 25 25 lastUpdatedBy: did; 26 26
+1 -1
packages/emitter/test/integration/atproto/input/tools/ozone/communication/deleteTemplate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.communication.deleteTemplate { 4 - @doc("Delete a communication template.") 4 + /** Delete a communication template. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required id: string;
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/communication/listTemplates.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.communication.listTemplates { 4 - @doc("Get list of all communication templates.") 4 + /** Get list of all communication templates. */ 5 5 @query 6 6 op main(): { 7 7 @required communicationTemplates: tools.ozone.communication.defs.TemplateView[]; 8 8 }; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace tools.ozone.communication.defs { 15 + model TemplateView { } 16 + }
+14 -7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/updateTemplate.tsp
··· 3 3 namespace tools.ozone.communication.updateTemplate { 4 4 model DuplicateTemplateName {} 5 5 6 - @doc("Administrative action to update an existing communication template. Allows passing partial fields to patch specific fields only.") 6 + /** Administrative action to update an existing communication template. Allows passing partial fields to patch specific fields only. */ 7 7 @procedure 8 8 @errors(DuplicateTemplateName) 9 9 op main(input: { 10 - @doc("ID of the template to be updated.") 10 + /** ID of the template to be updated. */ 11 11 @required 12 12 id: string; 13 13 14 - @doc("Name of the template.") 14 + /** Name of the template. */ 15 15 name?: string; 16 16 17 - @doc("Message language.") 17 + /** Message language. */ 18 18 lang?: language; 19 19 20 - @doc("Content of the template, markdown supported, can contain variable placeholders.") 20 + /** Content of the template, markdown supported, can contain variable placeholders. */ 21 21 contentMarkdown?: string; 22 22 23 - @doc("Subject of the message, used in emails.") 23 + /** Subject of the message, used in emails. */ 24 24 subject?: string; 25 25 26 - @doc("DID of the user who is updating the template.") 26 + /** DID of the user who is updating the template. */ 27 27 updatedBy?: did; 28 28 29 29 disabled?: boolean; 30 30 }): tools.ozone.communication.defs.TemplateView; 31 31 } 32 + 33 + // --- Externals --- 34 + 35 + @external 36 + namespace tools.ozone.communication.defs { 37 + model TemplateView { } 38 + }
+1 -1
packages/emitter/test/integration/atproto/input/tools/ozone/hosting/getAccountHistory.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.hosting.getAccountHistory { 4 - @doc("Get account history, e.g. log of updated email addresses or other identity information.") 4 + /** Get account history, e.g. log of updated email addresses or other identity information. */ 5 5 @query 6 6 op main( 7 7 @required did: did,
+130 -96
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/defs.tsp
··· 94 94 subjectBlobCids?: cid[]; 95 95 subjectRepoHandle?: string; 96 96 97 - @doc("Timestamp referencing the first moderation status impacting event was emitted on the subject") 97 + /** Timestamp referencing the first moderation status impacting event was emitted on the subject */ 98 98 @required 99 99 createdAt: datetime; 100 100 101 - @doc("Timestamp referencing when the last update was made to the moderation status of the subject") 101 + /** Timestamp referencing when the last update was made to the moderation status of the subject */ 102 102 @required 103 103 updatedAt: datetime; 104 104 105 105 @required reviewState: SubjectReviewState; 106 106 107 - @doc("Sticky comment on the subject.") 107 + /** Sticky comment on the subject. */ 108 108 comment?: string; 109 109 110 - @doc("Numeric value representing the level of priority. Higher score means higher priority.") 110 + /** Numeric value representing the level of priority. Higher score means higher priority. */ 111 111 @minValue(0) 112 112 @maxValue(100) 113 113 priorityScore?: int32; ··· 118 118 lastReviewedAt?: datetime; 119 119 lastReportedAt?: datetime; 120 120 121 - @doc("Timestamp referencing when the author of the subject appealed a moderation action") 121 + /** Timestamp referencing when the author of the subject appealed a moderation action */ 122 122 lastAppealedAt?: datetime; 123 123 124 124 takendown?: boolean; 125 125 126 - @doc("True indicates that the a previously taken moderator action was appealed against, by the author of the content. False indicates last appeal was resolved by moderators.") 126 + /** True indicates that the a previously taken moderator action was appealed against, by the author of the content. False indicates last appeal was resolved by moderators. */ 127 127 appealed?: boolean; 128 128 129 129 suspendUntil?: datetime; 130 130 tags?: string[]; 131 131 132 - @doc("Statistics related to the account subject") 132 + /** Statistics related to the account subject */ 133 133 accountStats?: AccountStats; 134 134 135 - @doc("Statistics related to the record subjects authored by the subject's account") 135 + /** Statistics related to the record subjects authored by the subject's account */ 136 136 recordsStats?: RecordsStats; 137 137 138 - @doc("Current age assurance state of the subject.") 138 + /** Current age assurance state of the subject. */ 139 139 ageAssuranceState?: "pending" | "assured" | "unknown" | "reset" | "blocked" | string; 140 140 141 - @doc("Whether or not the last successful update to age assurance was made by the user or admin.") 141 + /** Whether or not the last successful update to age assurance was made by the user or admin. */ 142 142 ageAssuranceUpdatedBy?: "admin" | "user" | string; 143 143 } 144 144 145 - @doc("Detailed view of a subject. For record subjects, the author's repo and profile will be returned.") 145 + /** Detailed view of a subject. For record subjects, the author's repo and profile will be returned. */ 146 146 model SubjectView { 147 147 @required type: com.atproto.moderation.defs.SubjectType; 148 148 @required subject: string; ··· 152 152 record?: RecordViewDetail; 153 153 } 154 154 155 - @doc("Statistics about a particular account subject") 155 + /** Statistics about a particular account subject */ 156 156 model AccountStats { 157 - @doc("Total number of reports on the account") 157 + /** Total number of reports on the account */ 158 158 reportCount?: int32; 159 159 160 - @doc("Total number of appeals against a moderation action on the account") 160 + /** Total number of appeals against a moderation action on the account */ 161 161 appealCount?: int32; 162 162 163 - @doc("Number of times the account was suspended") 163 + /** Number of times the account was suspended */ 164 164 suspendCount?: int32; 165 165 166 - @doc("Number of times the account was escalated") 166 + /** Number of times the account was escalated */ 167 167 escalateCount?: int32; 168 168 169 - @doc("Number of times the account was taken down") 169 + /** Number of times the account was taken down */ 170 170 takedownCount?: int32; 171 171 } 172 172 173 - @doc("Statistics about a set of record subject items") 173 + /** Statistics about a set of record subject items */ 174 174 model RecordsStats { 175 - @doc("Cumulative sum of the number of reports on the items in the set") 175 + /** Cumulative sum of the number of reports on the items in the set */ 176 176 totalReports?: int32; 177 177 178 - @doc("Number of items that were reported at least once") 178 + /** Number of items that were reported at least once */ 179 179 reportedCount?: int32; 180 180 181 - @doc("Number of items that were escalated at least once") 181 + /** Number of items that were escalated at least once */ 182 182 escalatedCount?: int32; 183 183 184 - @doc("Number of items that were appealed at least once") 184 + /** Number of items that were appealed at least once */ 185 185 appealedCount?: int32; 186 186 187 - @doc("Total number of item in the set") 187 + /** Total number of item in the set */ 188 188 subjectCount?: int32; 189 189 190 190 @doc("Number of item currently in \"reviewOpen\" or \"reviewEscalated\" state") ··· 193 193 @doc("Number of item currently in \"reviewNone\" or \"reviewClosed\" state") 194 194 processedCount?: int32; 195 195 196 - @doc("Number of item currently taken down") 196 + /** Number of item currently taken down */ 197 197 takendownCount?: int32; 198 198 } 199 199 200 200 union SubjectReviewState { 201 - "#reviewOpen", 202 - "#reviewEscalated", 203 - "#reviewClosed", 204 - "#reviewNone", 201 + ReviewOpen, 202 + ReviewEscalated, 203 + ReviewClosed, 204 + ReviewNone, 205 205 string, 206 206 } 207 207 208 - @doc("Moderator review status of a subject: Open. Indicates that the subject needs to be reviewed by a moderator") 208 + /** Moderator review status of a subject: Open. Indicates that the subject needs to be reviewed by a moderator */ 209 209 @token 210 210 model ReviewOpen {} 211 211 212 - @doc("Moderator review status of a subject: Escalated. Indicates that the subject was escalated for review by a moderator") 212 + /** Moderator review status of a subject: Escalated. Indicates that the subject was escalated for review by a moderator */ 213 213 @token 214 214 model ReviewEscalated {} 215 215 216 - @doc("Moderator review status of a subject: Closed. Indicates that the subject was already reviewed and resolved by a moderator") 216 + /** Moderator review status of a subject: Closed. Indicates that the subject was already reviewed and resolved by a moderator */ 217 217 @token 218 218 model ReviewClosed {} 219 219 220 - @doc("Moderator review status of a subject: Unnecessary. Indicates that the subject does not need a review at the moment but there is probably some moderation related metadata available for it") 220 + /** Moderator review status of a subject: Unnecessary. Indicates that the subject does not need a review at the moment but there is probably some moderation related metadata available for it */ 221 221 @token 222 222 model ReviewNone {} 223 223 224 - @doc("Take down a subject permanently or temporarily") 224 + /** Take down a subject permanently or temporarily */ 225 225 model ModEventTakedown { 226 226 comment?: string; 227 227 228 - @doc("Indicates how long the takedown should be in effect before automatically expiring.") 228 + /** Indicates how long the takedown should be in effect before automatically expiring. */ 229 229 durationInHours?: int32; 230 230 231 - @doc("If true, all other reports on content authored by this account will be resolved (acknowledged).") 231 + /** If true, all other reports on content authored by this account will be resolved (acknowledged). */ 232 232 acknowledgeAccountSubjects?: boolean; 233 233 234 - @doc("Names/Keywords of the policies that drove the decision.") 234 + /** Names/Keywords of the policies that drove the decision. */ 235 235 @maxItems(5) 236 236 policies?: string[]; 237 237 } 238 238 239 - @doc("Revert take down action on a subject") 239 + /** Revert take down action on a subject */ 240 240 model ModEventReverseTakedown { 241 - @doc("Describe reasoning behind the reversal.") 241 + /** Describe reasoning behind the reversal. */ 242 242 comment?: string; 243 243 } 244 244 245 - @doc("Resolve appeal on a subject") 245 + /** Resolve appeal on a subject */ 246 246 model ModEventResolveAppeal { 247 - @doc("Describe resolution.") 247 + /** Describe resolution. */ 248 248 comment?: string; 249 249 } 250 250 251 - @doc("Add a comment to a subject. An empty comment will clear any previously set sticky comment.") 251 + /** Add a comment to a subject. An empty comment will clear any previously set sticky comment. */ 252 252 model ModEventComment { 253 253 comment?: string; 254 254 255 - @doc("Make the comment persistent on the subject") 255 + /** Make the comment persistent on the subject */ 256 256 sticky?: boolean; 257 257 } 258 258 259 - @doc("Report a subject") 259 + /** Report a subject */ 260 260 model ModEventReport { 261 261 comment?: string; 262 262 263 - @doc("Set to true if the reporter was muted from reporting at the time of the event. These reports won't impact the reviewState of the subject.") 263 + /** Set to true if the reporter was muted from reporting at the time of the event. These reports won't impact the reviewState of the subject. */ 264 264 isReporterMuted?: boolean; 265 265 266 266 @required reportType: com.atproto.moderation.defs.ReasonType; 267 267 } 268 268 269 - @doc("Apply/Negate labels on a subject") 269 + /** Apply/Negate labels on a subject */ 270 270 model ModEventLabel { 271 271 comment?: string; 272 272 @required createLabelVals: string[]; 273 273 @required negateLabelVals: string[]; 274 274 275 - @doc("Indicates how long the label will remain on the subject. Only applies on labels that are being added.") 275 + /** Indicates how long the label will remain on the subject. Only applies on labels that are being added. */ 276 276 durationInHours?: int32; 277 277 } 278 278 279 - @doc("Set priority score of the subject. Higher score means higher priority.") 279 + /** Set priority score of the subject. Higher score means higher priority. */ 280 280 model ModEventPriorityScore { 281 281 comment?: string; 282 282 ··· 286 286 score: int32; 287 287 } 288 288 289 - @doc("Age assurance info coming directly from users. Only works on DID subjects.") 289 + /** Age assurance info coming directly from users. Only works on DID subjects. */ 290 290 model AgeAssuranceEvent { 291 - @doc("The date and time of this write operation.") 291 + /** The date and time of this write operation. */ 292 292 @required 293 293 createdAt: datetime; 294 294 295 - @doc("The status of the age assurance process.") 295 + /** The status of the age assurance process. */ 296 296 @required 297 297 status: "unknown" | "pending" | "assured" | string; 298 298 299 - @doc("The unique identifier for this instance of the age assurance flow, in UUID format.") 299 + /** The unique identifier for this instance of the age assurance flow, in UUID format. */ 300 300 @required 301 301 attemptId: string; 302 302 303 - @doc("The IP address used when initiating the AA flow.") 303 + /** The IP address used when initiating the AA flow. */ 304 304 initIp?: string; 305 305 306 - @doc("The user agent used when initiating the AA flow.") 306 + /** The user agent used when initiating the AA flow. */ 307 307 initUa?: string; 308 308 309 - @doc("The IP address used when completing the AA flow.") 309 + /** The IP address used when completing the AA flow. */ 310 310 completeIp?: string; 311 311 312 - @doc("The user agent used when completing the AA flow.") 312 + /** The user agent used when completing the AA flow. */ 313 313 completeUa?: string; 314 314 } 315 315 316 - @doc("Age assurance status override by moderators. Only works on DID subjects.") 316 + /** Age assurance status override by moderators. Only works on DID subjects. */ 317 317 model AgeAssuranceOverrideEvent { 318 - @doc("Comment describing the reason for the override.") 318 + /** Comment describing the reason for the override. */ 319 319 @required 320 320 comment: string; 321 321 322 - @doc("The status to be set for the user decided by a moderator, overriding whatever value the user had previously. Use reset to default to original state.") 322 + /** The status to be set for the user decided by a moderator, overriding whatever value the user had previously. Use reset to default to original state. */ 323 323 @required 324 324 status: "assured" | "reset" | "blocked" | string; 325 325 } 326 326 327 - @doc("Account credentials revocation by moderators. Only works on DID subjects.") 327 + /** Account credentials revocation by moderators. Only works on DID subjects. */ 328 328 model RevokeAccountCredentialsEvent { 329 - @doc("Comment describing the reason for the revocation.") 329 + /** Comment describing the reason for the revocation. */ 330 330 @required 331 331 comment: string; 332 332 } ··· 334 334 model ModEventAcknowledge { 335 335 comment?: string; 336 336 337 - @doc("If true, all other reports on content authored by this account will be resolved (acknowledged).") 337 + /** If true, all other reports on content authored by this account will be resolved (acknowledged). */ 338 338 acknowledgeAccountSubjects?: boolean; 339 339 } 340 340 ··· 342 342 comment?: string; 343 343 } 344 344 345 - @doc("Mute incoming reports on a subject") 345 + /** Mute incoming reports on a subject */ 346 346 model ModEventMute { 347 347 comment?: string; 348 348 349 - @doc("Indicates how long the subject should remain muted.") 349 + /** Indicates how long the subject should remain muted. */ 350 350 @required 351 351 durationInHours: int32; 352 352 } 353 353 354 - @doc("Unmute action on a subject") 354 + /** Unmute action on a subject */ 355 355 model ModEventUnmute { 356 - @doc("Describe reasoning behind the reversal.") 356 + /** Describe reasoning behind the reversal. */ 357 357 comment?: string; 358 358 } 359 359 360 - @doc("Mute incoming reports from an account") 360 + /** Mute incoming reports from an account */ 361 361 model ModEventMuteReporter { 362 362 comment?: string; 363 363 364 - @doc("Indicates how long the account should remain muted. Falsy value here means a permanent mute.") 364 + /** Indicates how long the account should remain muted. Falsy value here means a permanent mute. */ 365 365 durationInHours?: int32; 366 366 } 367 367 368 - @doc("Unmute incoming reports from an account") 368 + /** Unmute incoming reports from an account */ 369 369 model ModEventUnmuteReporter { 370 - @doc("Describe reasoning behind the reversal.") 370 + /** Describe reasoning behind the reversal. */ 371 371 comment?: string; 372 372 } 373 373 374 - @doc("Keep a log of outgoing email to a user") 374 + /** Keep a log of outgoing email to a user */ 375 375 model ModEventEmail { 376 - @doc("The subject line of the email sent to the user.") 376 + /** The subject line of the email sent to the user. */ 377 377 @required 378 378 subjectLine: string; 379 379 380 - @doc("The content of the email sent to the user.") 380 + /** The content of the email sent to the user. */ 381 381 content?: string; 382 382 383 - @doc("Additional comment about the outgoing comm.") 383 + /** Additional comment about the outgoing comm. */ 384 384 comment?: string; 385 385 } 386 386 387 - @doc("Divert a record's blobs to a 3rd party service for further scanning/tagging") 387 + /** Divert a record's blobs to a 3rd party service for further scanning/tagging */ 388 388 model ModEventDivert { 389 389 comment?: string; 390 390 } 391 391 392 - @doc("Add/Remove a tag on a subject") 392 + /** Add/Remove a tag on a subject */ 393 393 model ModEventTag { 394 - @doc("Tags to be added to the subject. If already exists, won't be duplicated.") 394 + /** Tags to be added to the subject. If already exists, won't be duplicated. */ 395 395 @required 396 396 add: string[]; 397 397 398 - @doc("Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated.") 398 + /** Tags to be removed to the subject. Ignores a tag If it doesn't exist, won't be duplicated. */ 399 399 @required 400 400 remove: string[]; 401 401 402 - @doc("Additional comment about added/removed tags.") 402 + /** Additional comment about added/removed tags. */ 403 403 comment?: string; 404 404 } 405 405 406 - @doc("Logs account status related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking.") 406 + /** Logs account status related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */ 407 407 model AccountEvent { 408 408 comment?: string; 409 409 @required timestamp: datetime; 410 410 411 - @doc("Indicates that the account has a repository which can be fetched from the host that emitted this event.") 411 + /** Indicates that the account has a repository which can be fetched from the host that emitted this event. */ 412 412 @required 413 413 active: boolean; 414 414 415 415 status?: "unknown" | "deactivated" | "deleted" | "takendown" | "suspended" | "tombstoned" | string; 416 416 } 417 417 418 - @doc("Logs identity related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking.") 418 + /** Logs identity related events on a repo subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */ 419 419 model IdentityEvent { 420 420 comment?: string; 421 421 handle?: handle; ··· 424 424 @required timestamp: datetime; 425 425 } 426 426 427 - @doc("Logs lifecycle event on a record subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking.") 427 + /** Logs lifecycle event on a record subject. Normally captured by automod from the firehose and emitted to ozone for historical tracking. */ 428 428 model RecordEvent { 429 429 comment?: string; 430 430 @required timestamp: datetime; ··· 546 546 model ReporterStats { 547 547 @required did: did; 548 548 549 - @doc("The total number of reports made by the user on accounts.") 549 + /** The total number of reports made by the user on accounts. */ 550 550 @required 551 551 accountReportCount: int32; 552 552 553 - @doc("The total number of reports made by the user on records.") 553 + /** The total number of reports made by the user on records. */ 554 554 @required 555 555 recordReportCount: int32; 556 556 557 - @doc("The total number of accounts reported by the user.") 557 + /** The total number of accounts reported by the user. */ 558 558 @required 559 559 reportedAccountCount: int32; 560 560 561 - @doc("The total number of records reported by the user.") 561 + /** The total number of records reported by the user. */ 562 562 @required 563 563 reportedRecordCount: int32; 564 564 565 - @doc("The total number of accounts taken down as a result of the user's reports.") 565 + /** The total number of accounts taken down as a result of the user's reports. */ 566 566 @required 567 567 takendownAccountCount: int32; 568 568 569 - @doc("The total number of records taken down as a result of the user's reports.") 569 + /** The total number of records taken down as a result of the user's reports. */ 570 570 @required 571 571 takendownRecordCount: int32; 572 572 573 - @doc("The total number of accounts labeled as a result of the user's reports.") 573 + /** The total number of accounts labeled as a result of the user's reports. */ 574 574 @required 575 575 labeledAccountCount: int32; 576 576 577 - @doc("The total number of records labeled as a result of the user's reports.") 577 + /** The total number of records labeled as a result of the user's reports. */ 578 578 @required 579 579 labeledRecordCount: int32; 580 580 } 581 581 582 - @doc("Moderation tool information for tracing the source of the action") 582 + /** Moderation tool information for tracing the source of the action */ 583 583 model ModTool { 584 - @doc("Name/identifier of the source (e.g., 'automod', 'ozone/workspace')") 584 + /** Name/identifier of the source (e.g., 'automod', 'ozone/workspace') */ 585 585 @required 586 586 name: string; 587 587 588 - @doc("Additional arbitrary metadata about the source") 588 + /** Additional arbitrary metadata about the source */ 589 589 meta?: unknown; 590 590 } 591 591 592 - @doc("Moderation event timeline event for a PLC create operation") 592 + /** Moderation event timeline event for a PLC create operation */ 593 593 @token 594 594 model TimelineEventPlcCreate {} 595 595 596 - @doc("Moderation event timeline event for generic PLC operation") 596 + /** Moderation event timeline event for generic PLC operation */ 597 597 @token 598 598 model TimelineEventPlcOperation {} 599 599 600 - @doc("Moderation event timeline event for a PLC tombstone operation") 600 + /** Moderation event timeline event for a PLC tombstone operation */ 601 601 @token 602 602 model TimelineEventPlcTombstone {} 603 603 } 604 + 605 + // --- Externals --- 606 + 607 + @external 608 + namespace com.atproto.admin.defs { 609 + model RepoRef { } 610 + model ThreatSignature { } 611 + } 612 + 613 + @external 614 + namespace com.atproto.repo.strongRef { 615 + model Main { } 616 + } 617 + 618 + @external 619 + namespace chat.bsky.convo.defs { 620 + model MessageRef { } 621 + } 622 + 623 + @external 624 + namespace com.atproto.moderation.defs { 625 + model SubjectType { } 626 + model ReasonType { } 627 + } 628 + 629 + @external 630 + namespace com.atproto.server.defs { 631 + model InviteCode { } 632 + } 633 + 634 + @external 635 + namespace com.atproto.label.defs { 636 + model Label { } 637 + }
+43 -3
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/emitEvent.tsp
··· 3 3 namespace tools.ozone.moderation.emitEvent { 4 4 model SubjectHasAction {} 5 5 6 - @doc("An event with the same external ID already exists for the subject.") 6 + /** An event with the same external ID already exists for the subject. */ 7 7 model DuplicateExternalId {} 8 8 9 - @doc("Take a moderation action on an actor.") 9 + /** Take a moderation action on an actor. */ 10 10 @procedure 11 11 @errors(SubjectHasAction, DuplicateExternalId) 12 12 op main(input: { ··· 50 50 51 51 modTool?: tools.ozone.moderation.defs.ModTool; 52 52 53 - @doc("An optional external ID for the event, used to deduplicate events from external systems. Fails when an event of same type with the same external ID exists for the same subject.") 53 + /** An optional external ID for the event, used to deduplicate events from external systems. Fails when an event of same type with the same external ID exists for the same subject. */ 54 54 externalId?: string; 55 55 }): tools.ozone.moderation.defs.ModEventView; 56 56 } 57 + 58 + // --- Externals --- 59 + 60 + @external 61 + namespace tools.ozone.moderation.defs { 62 + model ModEventTakedown { } 63 + model ModEventAcknowledge { } 64 + model ModEventEscalate { } 65 + model ModEventComment { } 66 + model ModEventLabel { } 67 + model ModEventReport { } 68 + model ModEventMute { } 69 + model ModEventUnmute { } 70 + model ModEventMuteReporter { } 71 + model ModEventUnmuteReporter { } 72 + model ModEventReverseTakedown { } 73 + model ModEventResolveAppeal { } 74 + model ModEventEmail { } 75 + model ModEventDivert { } 76 + model ModEventTag { } 77 + model AccountEvent { } 78 + model IdentityEvent { } 79 + model RecordEvent { } 80 + model ModEventPriorityScore { } 81 + model AgeAssuranceEvent { } 82 + model AgeAssuranceOverrideEvent { } 83 + model RevokeAccountCredentialsEvent { } 84 + model ModTool { } 85 + model ModEventView { } 86 + } 87 + 88 + @external 89 + namespace com.atproto.admin.defs { 90 + model RepoRef { } 91 + } 92 + 93 + @external 94 + namespace com.atproto.repo.strongRef { 95 + model Main { } 96 + }
+40 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getAccountTimeline.tsp
··· 3 3 namespace tools.ozone.moderation.getAccountTimeline { 4 4 model RepoNotFound {} 5 5 6 - @doc("Get timeline of all available events of an account. This includes moderation events, account history and did history.") 6 + /** Get timeline of all available events of an account. This includes moderation events, account history and did history. */ 7 7 @query 8 8 @errors(RepoNotFound) 9 9 op main(@required did: did): { ··· 55 55 @required count: integer; 56 56 } 57 57 } 58 + 59 + // --- Externals --- 60 + 61 + @external 62 + namespace tools.ozone.moderation.defs { 63 + model ModEventTakedown { } 64 + model ModEventReverseTakedown { } 65 + model ModEventComment { } 66 + model ModEventReport { } 67 + model ModEventLabel { } 68 + model ModEventAcknowledge { } 69 + model ModEventEscalate { } 70 + model ModEventMute { } 71 + model ModEventUnmute { } 72 + model ModEventMuteReporter { } 73 + model ModEventUnmuteReporter { } 74 + model ModEventEmail { } 75 + model ModEventResolveAppeal { } 76 + model ModEventDivert { } 77 + model ModEventTag { } 78 + model AccountEvent { } 79 + model IdentityEvent { } 80 + model RecordEvent { } 81 + model ModEventPriorityScore { } 82 + model RevokeAccountCredentialsEvent { } 83 + model AgeAssuranceEvent { } 84 + model AgeAssuranceOverrideEvent { } 85 + @token model TimelineEventPlcCreate { } 86 + @token model TimelineEventPlcOperation { } 87 + @token model TimelineEventPlcTombstone { } 88 + } 89 + 90 + @external 91 + namespace tools.ozone.hosting.getAccountHistory { 92 + model AccountCreated { } 93 + model EmailConfirmed { } 94 + model PasswordUpdated { } 95 + model HandleUpdated { } 96 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getEvent.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.getEvent { 4 - @doc("Get details about a moderation event.") 4 + /** Get details about a moderation event. */ 5 5 @query 6 6 op main(@required id: integer): tools.ozone.moderation.defs.ModEventViewDetail; 7 7 } 8 + 9 + // --- Externals --- 10 + 11 + @external 12 + namespace tools.ozone.moderation.defs { 13 + model ModEventViewDetail { } 14 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecord.tsp
··· 3 3 namespace tools.ozone.moderation.getRecord { 4 4 model RecordNotFound {} 5 5 6 - @doc("Get details about a record.") 6 + /** Get details about a record. */ 7 7 @query 8 8 @errors(RecordNotFound) 9 9 op main(@required uri: atUri, cid?: cid): tools.ozone.moderation.defs.RecordViewDetail; 10 10 } 11 + 12 + // --- Externals --- 13 + 14 + @external 15 + namespace tools.ozone.moderation.defs { 16 + model RecordViewDetail { } 17 + }
+9 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecords.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.getRecords { 4 - @doc("Get details about some records.") 4 + /** Get details about some records. */ 5 5 @query 6 6 op main( 7 7 @maxItems(100) ··· 16 16 )[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace tools.ozone.moderation.defs { 24 + model RecordViewDetail { } 25 + model RecordViewNotFound { } 26 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepo.tsp
··· 3 3 namespace tools.ozone.moderation.getRepo { 4 4 model RepoNotFound {} 5 5 6 - @doc("Get details about a repository.") 6 + /** Get details about a repository. */ 7 7 @query 8 8 @errors(RepoNotFound) 9 9 op main(@required did: did): tools.ozone.moderation.defs.RepoViewDetail; 10 10 } 11 + 12 + // --- Externals --- 13 + 14 + @external 15 + namespace tools.ozone.moderation.defs { 16 + model RepoViewDetail { } 17 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getReporterStats.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.getReporterStats { 4 - @doc("Get reporter stats for a list of users.") 4 + /** Get reporter stats for a list of users. */ 5 5 @query 6 6 op main( 7 7 @maxItems(100) ··· 11 11 @required stats: tools.ozone.moderation.defs.ReporterStats[]; 12 12 }; 13 13 } 14 + 15 + // --- Externals --- 16 + 17 + @external 18 + namespace tools.ozone.moderation.defs { 19 + model ReporterStats { } 20 + }
+9 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.getRepos { 4 - @doc("Get details about some repositories.") 4 + /** Get details about some repositories. */ 5 5 @query 6 6 op main( 7 7 @maxItems(100) ··· 16 16 )[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace tools.ozone.moderation.defs { 24 + model RepoViewDetail { } 25 + model RepoViewNotFound { } 26 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getSubjects.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.getSubjects { 4 - @doc("Get details about subjects.") 4 + /** Get details about subjects. */ 5 5 @query 6 6 op main( 7 7 @minItems(1) ··· 12 12 @required subjects: tools.ozone.moderation.defs.SubjectView[]; 13 13 }; 14 14 } 15 + 16 + // --- Externals --- 17 + 18 + @external 19 + namespace tools.ozone.moderation.defs { 20 + model SubjectView { } 21 + }
+25 -18
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryEvents.tsp
··· 8 8 "desc", 9 9 } 10 10 11 - @doc("List moderation events related to a subject.") 11 + /** List moderation events related to a subject. */ 12 12 @query 13 13 op main( 14 - @doc("The types of events (fully qualified string in the format of tools.ozone.moderation.defs#modEvent<name>) to filter by. If not specified, all events are returned.") 14 + /** The types of events (fully qualified string in the format of tools.ozone.moderation.defs#modEvent<name>) to filter by. If not specified, all events are returned. */ 15 15 types?: string[], 16 16 17 17 createdBy?: did, 18 18 19 - @doc("Sort direction for the events. Defaults to descending order of created at timestamp.") 19 + /** Sort direction for the events. Defaults to descending order of created at timestamp. */ 20 20 sortDirection?: SortDirection = "desc", 21 21 22 - @doc("Retrieve events created after a given timestamp") 22 + /** Retrieve events created after a given timestamp */ 23 23 createdAfter?: datetime, 24 24 25 - @doc("Retrieve events created before a given timestamp") 25 + /** Retrieve events created before a given timestamp */ 26 26 createdBefore?: datetime, 27 27 28 28 subject?: uri, 29 29 30 30 @maxItems(20) 31 - @doc("If specified, only events where the subject belongs to the given collections will be returned. When subjectType is set to 'account', this will be ignored.") 31 + /** If specified, only events where the subject belongs to the given collections will be returned. When subjectType is set to 'account', this will be ignored. */ 32 32 collections?: nsid[], 33 33 34 - @doc("If specified, only events where the subject is of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored.") 34 + /** If specified, only events where the subject is of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored. */ 35 35 subjectType?: "account" | "record" | string, 36 36 37 - @doc("If true, events on all record types (posts, lists, profile etc.) or records from given 'collections' param, owned by the did are returned.") 37 + /** If true, events on all record types (posts, lists, profile etc.) or records from given 'collections' param, owned by the did are returned. */ 38 38 includeAllUserRecords?: boolean = false, 39 39 40 40 @minValue(1) 41 41 @maxValue(100) 42 42 limit?: int32 = 50, 43 43 44 - @doc("If true, only events with comments are returned") 44 + /** If true, only events with comments are returned */ 45 45 hasComment?: boolean, 46 46 47 - @doc("If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition.") 47 + /** If specified, only events with comments containing the keyword are returned. Apply || separator to use multiple keywords and match using OR condition. */ 48 48 comment?: string, 49 49 50 - @doc("If specified, only events where all of these labels were added are returned") 50 + /** If specified, only events where all of these labels were added are returned */ 51 51 addedLabels?: string[], 52 52 53 - @doc("If specified, only events where all of these labels were removed are returned") 53 + /** If specified, only events where all of these labels were removed are returned */ 54 54 removedLabels?: string[], 55 55 56 - @doc("If specified, only events where all of these tags were added are returned") 56 + /** If specified, only events where all of these tags were added are returned */ 57 57 addedTags?: string[], 58 58 59 - @doc("If specified, only events where all of these tags were removed are returned") 59 + /** If specified, only events where all of these tags were removed are returned */ 60 60 removedTags?: string[], 61 61 62 62 reportTypes?: string[], 63 63 64 - @doc("If specified, only events where the action policies match any of the given policies are returned") 64 + /** If specified, only events where the action policies match any of the given policies are returned */ 65 65 policies?: string[], 66 66 67 - @doc("If specified, only events where the modTool name matches any of the given values are returned") 67 + /** If specified, only events where the modTool name matches any of the given values are returned */ 68 68 modTool?: string[], 69 69 70 - @doc("If specified, only events where the batchId matches the given value are returned") 70 + /** If specified, only events where the batchId matches the given value are returned */ 71 71 batchId?: string, 72 72 73 - @doc("If specified, only events where the age assurance state matches the given value are returned") 73 + /** If specified, only events where the age assurance state matches the given value are returned */ 74 74 ageAssuranceState?: "pending" | "assured" | "unknown" | "reset" | "blocked" | string, 75 75 76 76 cursor?: string ··· 80 80 @required events: tools.ozone.moderation.defs.ModEventView[]; 81 81 }; 82 82 } 83 + 84 + // --- Externals --- 85 + 86 + @external 87 + namespace tools.ozone.moderation.defs { 88 + model ModEventView { } 89 + }
+37 -30
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryStatuses.tsp
··· 18 18 "desc", 19 19 } 20 20 21 - @doc("View moderation statuses of subjects (record or repo).") 21 + /** View moderation statuses of subjects (record or repo). */ 22 22 @query 23 23 op main( 24 - @doc("Number of queues being used by moderators. Subjects will be split among all queues.") 24 + /** Number of queues being used by moderators. Subjects will be split among all queues. */ 25 25 queueCount?: integer, 26 26 27 - @doc("Index of the queue to fetch subjects from. Works only when queueCount value is specified.") 27 + /** Index of the queue to fetch subjects from. Works only when queueCount value is specified. */ 28 28 queueIndex?: integer, 29 29 30 - @doc("A seeder to shuffle/balance the queue items.") 30 + /** A seeder to shuffle/balance the queue items. */ 31 31 queueSeed?: string, 32 32 33 - @doc("All subjects, or subjects from given 'collections' param, belonging to the account specified in the 'subject' param will be returned.") 33 + /** All subjects, or subjects from given 'collections' param, belonging to the account specified in the 'subject' param will be returned. */ 34 34 includeAllUserRecords?: boolean, 35 35 36 - @doc("The subject to get the status for.") 36 + /** The subject to get the status for. */ 37 37 subject?: uri, 38 38 39 - @doc("Search subjects by keyword from comments") 39 + /** Search subjects by keyword from comments */ 40 40 comment?: string, 41 41 42 - @doc("Search subjects reported after a given timestamp") 42 + /** Search subjects reported after a given timestamp */ 43 43 reportedAfter?: datetime, 44 44 45 - @doc("Search subjects reported before a given timestamp") 45 + /** Search subjects reported before a given timestamp */ 46 46 reportedBefore?: datetime, 47 47 48 - @doc("Search subjects reviewed after a given timestamp") 48 + /** Search subjects reviewed after a given timestamp */ 49 49 reviewedAfter?: datetime, 50 50 51 - @doc("Search subjects where the associated record/account was deleted after a given timestamp") 51 + /** Search subjects where the associated record/account was deleted after a given timestamp */ 52 52 hostingDeletedAfter?: datetime, 53 53 54 - @doc("Search subjects where the associated record/account was deleted before a given timestamp") 54 + /** Search subjects where the associated record/account was deleted before a given timestamp */ 55 55 hostingDeletedBefore?: datetime, 56 56 57 - @doc("Search subjects where the associated record/account was updated after a given timestamp") 57 + /** Search subjects where the associated record/account was updated after a given timestamp */ 58 58 hostingUpdatedAfter?: datetime, 59 59 60 - @doc("Search subjects where the associated record/account was updated before a given timestamp") 60 + /** Search subjects where the associated record/account was updated before a given timestamp */ 61 61 hostingUpdatedBefore?: datetime, 62 62 63 - @doc("Search subjects by the status of the associated record/account") 63 + /** Search subjects by the status of the associated record/account */ 64 64 hostingStatuses?: string[], 65 65 66 - @doc("Search subjects reviewed before a given timestamp") 66 + /** Search subjects reviewed before a given timestamp */ 67 67 reviewedBefore?: datetime, 68 68 69 - @doc("By default, we don't include muted subjects in the results. Set this to true to include them.") 69 + /** By default, we don't include muted subjects in the results. Set this to true to include them. */ 70 70 includeMuted?: boolean, 71 71 72 - @doc("When set to true, only muted subjects and reporters will be returned.") 72 + /** When set to true, only muted subjects and reporters will be returned. */ 73 73 onlyMuted?: boolean, 74 74 75 - @doc("Specify when fetching subjects in a certain state") 75 + /** Specify when fetching subjects in a certain state */ 76 76 reviewState?: string, 77 77 78 78 ignoreSubjects?: uri[], 79 79 80 - @doc("Get all subject statuses that were reviewed by a specific moderator") 80 + /** Get all subject statuses that were reviewed by a specific moderator */ 81 81 lastReviewedBy?: did, 82 82 83 83 sortField?: SortField = "lastReportedAt", 84 84 85 85 sortDirection?: SortDirection = "desc", 86 86 87 - @doc("Get subjects that were taken down") 87 + /** Get subjects that were taken down */ 88 88 takendown?: boolean, 89 89 90 - @doc("Get subjects in unresolved appealed status") 90 + /** Get subjects in unresolved appealed status */ 91 91 appealed?: boolean, 92 92 93 93 @minValue(1) 94 94 @maxValue(100) 95 95 limit?: int32 = 50, 96 96 97 - @doc("Items in this array are applied with OR filters. To apply AND filter, put all tags in the same string and separate using && characters") 97 + /** Items in this array are applied with OR filters. To apply AND filter, put all tags in the same string and separate using && characters */ 98 98 @maxItems(25) 99 99 tags?: string[], 100 100 ··· 103 103 cursor?: string, 104 104 105 105 @maxItems(20) 106 - @doc("If specified, subjects belonging to the given collections will be returned. When subjectType is set to 'account', this will be ignored.") 106 + /** If specified, subjects belonging to the given collections will be returned. When subjectType is set to 'account', this will be ignored. */ 107 107 collections?: nsid[], 108 108 109 - @doc("If specified, subjects of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored.") 109 + /** If specified, subjects of the given type (account or record) will be returned. When this is set to 'account' the 'collections' parameter will be ignored. When includeAllUserRecords or subject is set, this will be ignored. */ 110 110 subjectType?: "account" | "record" | string, 111 111 112 - @doc("If specified, only subjects that belong to an account that has at least this many suspensions will be returned.") 112 + /** If specified, only subjects that belong to an account that has at least this many suspensions will be returned. */ 113 113 minAccountSuspendCount?: integer, 114 114 115 - @doc("If specified, only subjects that belong to an account that has at least this many reported records will be returned.") 115 + /** If specified, only subjects that belong to an account that has at least this many reported records will be returned. */ 116 116 minReportedRecordsCount?: integer, 117 117 118 - @doc("If specified, only subjects that belong to an account that has at least this many taken down records will be returned.") 118 + /** If specified, only subjects that belong to an account that has at least this many taken down records will be returned. */ 119 119 minTakendownRecordsCount?: integer, 120 120 121 121 @minValue(0) 122 122 @maxValue(100) 123 - @doc("If specified, only subjects that have priority score value above the given value will be returned.") 123 + /** If specified, only subjects that have priority score value above the given value will be returned. */ 124 124 minPriorityScore?: integer, 125 125 126 - @doc("If specified, only subjects with the given age assurance state will be returned.") 126 + /** If specified, only subjects with the given age assurance state will be returned. */ 127 127 ageAssuranceState?: "pending" | "assured" | "unknown" | "reset" | "blocked" | string 128 128 ): { 129 129 cursor?: string; ··· 131 131 @required subjectStatuses: tools.ozone.moderation.defs.SubjectStatusView[]; 132 132 }; 133 133 } 134 + 135 + // --- Externals --- 136 + 137 + @external 138 + namespace tools.ozone.moderation.defs { 139 + model SubjectStatusView { } 140 + }
+9 -2
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/searchRepos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.moderation.searchRepos { 4 - @doc("Find repositories based on a search term.") 4 + /** Find repositories based on a search term. */ 5 5 @query 6 6 op main( 7 - @doc("DEPRECATED: use 'q' instead") 7 + /** DEPRECATED: use 'q' instead */ 8 8 term?: string, 9 9 10 10 q?: string, ··· 20 20 @required repos: tools.ozone.moderation.defs.RepoView[]; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace tools.ozone.moderation.defs { 28 + model RepoView { } 29 + }
+90 -98
packages/emitter/test/integration/atproto/input/tools/ozone/report/defs.tsp
··· 3 3 namespace tools.ozone.report.defs { 4 4 union ReasonType { 5 5 string, 6 - 7 - ReasonAppeal: "tools.ozone.report.defs#reasonAppeal", 8 - 9 - ReasonViolenceAnimalWelfare: "tools.ozone.report.defs#reasonViolenceAnimalWelfare", 10 - ReasonViolenceThreats: "tools.ozone.report.defs#reasonViolenceThreats", 11 - ReasonViolenceGraphicContent: "tools.ozone.report.defs#reasonViolenceGraphicContent", 12 - ReasonViolenceSelfHarm: "tools.ozone.report.defs#reasonViolenceSelfHarm", 13 - ReasonViolenceGlorification: "tools.ozone.report.defs#reasonViolenceGlorification", 14 - ReasonViolenceExtremistContent: "tools.ozone.report.defs#reasonViolenceExtremistContent", 15 - ReasonViolenceTrafficking: "tools.ozone.report.defs#reasonViolenceTrafficking", 16 - ReasonViolenceOther: "tools.ozone.report.defs#reasonViolenceOther", 17 - 18 - ReasonSexualAbuseContent: "tools.ozone.report.defs#reasonSexualAbuseContent", 19 - ReasonSexualNCII: "tools.ozone.report.defs#reasonSexualNCII", 20 - ReasonSexualSextortion: "tools.ozone.report.defs#reasonSexualSextortion", 21 - ReasonSexualDeepfake: "tools.ozone.report.defs#reasonSexualDeepfake", 22 - ReasonSexualAnimal: "tools.ozone.report.defs#reasonSexualAnimal", 23 - ReasonSexualUnlabeled: "tools.ozone.report.defs#reasonSexualUnlabeled", 24 - ReasonSexualOther: "tools.ozone.report.defs#reasonSexualOther", 25 - 26 - ReasonChildSafetyCSAM: "tools.ozone.report.defs#reasonChildSafetyCSAM", 27 - ReasonChildSafetyGroom: "tools.ozone.report.defs#reasonChildSafetyGroom", 28 - ReasonChildSafetyMinorPrivacy: "tools.ozone.report.defs#reasonChildSafetyMinorPrivacy", 29 - ReasonChildSafetyEndangerment: "tools.ozone.report.defs#reasonChildSafetyEndangerment", 30 - ReasonChildSafetyHarassment: "tools.ozone.report.defs#reasonChildSafetyHarassment", 31 - ReasonChildSafetyPromotion: "tools.ozone.report.defs#reasonChildSafetyPromotion", 32 - ReasonChildSafetyOther: "tools.ozone.report.defs#reasonChildSafetyOther", 33 - 34 - ReasonHarassmentTroll: "tools.ozone.report.defs#reasonHarassmentTroll", 35 - ReasonHarassmentTargeted: "tools.ozone.report.defs#reasonHarassmentTargeted", 36 - ReasonHarassmentHateSpeech: "tools.ozone.report.defs#reasonHarassmentHateSpeech", 37 - ReasonHarassmentDoxxing: "tools.ozone.report.defs#reasonHarassmentDoxxing", 38 - ReasonHarassmentOther: "tools.ozone.report.defs#reasonHarassmentOther", 39 - 40 - ReasonMisleadingBot: "tools.ozone.report.defs#reasonMisleadingBot", 41 - ReasonMisleadingImpersonation: "tools.ozone.report.defs#reasonMisleadingImpersonation", 42 - ReasonMisleadingSpam: "tools.ozone.report.defs#reasonMisleadingSpam", 43 - ReasonMisleadingScam: "tools.ozone.report.defs#reasonMisleadingScam", 44 - ReasonMisleadingSyntheticContent: "tools.ozone.report.defs#reasonMisleadingSyntheticContent", 45 - ReasonMisleadingMisinformation: "tools.ozone.report.defs#reasonMisleadingMisinformation", 46 - ReasonMisleadingOther: "tools.ozone.report.defs#reasonMisleadingOther", 47 - 48 - ReasonRuleSiteSecurity: "tools.ozone.report.defs#reasonRuleSiteSecurity", 49 - ReasonRuleStolenContent: "tools.ozone.report.defs#reasonRuleStolenContent", 50 - ReasonRuleProhibitedSales: "tools.ozone.report.defs#reasonRuleProhibitedSales", 51 - ReasonRuleBanEvasion: "tools.ozone.report.defs#reasonRuleBanEvasion", 52 - ReasonRuleOther: "tools.ozone.report.defs#reasonRuleOther", 53 - 54 - ReasonCivicElectoralProcess: "tools.ozone.report.defs#reasonCivicElectoralProcess", 55 - ReasonCivicDisclosure: "tools.ozone.report.defs#reasonCivicDisclosure", 56 - ReasonCivicInterference: "tools.ozone.report.defs#reasonCivicInterference", 57 - ReasonCivicMisinformation: "tools.ozone.report.defs#reasonCivicMisinformation", 58 - ReasonCivicImpersonation: "tools.ozone.report.defs#reasonCivicImpersonation", 6 + ReasonAppeal, 7 + ReasonViolenceAnimalWelfare, 8 + ReasonViolenceThreats, 9 + ReasonViolenceGraphicContent, 10 + ReasonViolenceSelfHarm, 11 + ReasonViolenceGlorification, 12 + ReasonViolenceExtremistContent, 13 + ReasonViolenceTrafficking, 14 + ReasonViolenceOther, 15 + ReasonSexualAbuseContent, 16 + ReasonSexualNCII, 17 + ReasonSexualSextortion, 18 + ReasonSexualDeepfake, 19 + ReasonSexualAnimal, 20 + ReasonSexualUnlabeled, 21 + ReasonSexualOther, 22 + ReasonChildSafetyCSAM, 23 + ReasonChildSafetyGroom, 24 + ReasonChildSafetyMinorPrivacy, 25 + ReasonChildSafetyEndangerment, 26 + ReasonChildSafetyHarassment, 27 + ReasonChildSafetyPromotion, 28 + ReasonChildSafetyOther, 29 + ReasonHarassmentTroll, 30 + ReasonHarassmentTargeted, 31 + ReasonHarassmentHateSpeech, 32 + ReasonHarassmentDoxxing, 33 + ReasonHarassmentOther, 34 + ReasonMisleadingBot, 35 + ReasonMisleadingImpersonation, 36 + ReasonMisleadingSpam, 37 + ReasonMisleadingScam, 38 + ReasonMisleadingSyntheticContent, 39 + ReasonMisleadingMisinformation, 40 + ReasonMisleadingOther, 41 + ReasonRuleSiteSecurity, 42 + ReasonRuleStolenContent, 43 + ReasonRuleProhibitedSales, 44 + ReasonRuleBanEvasion, 45 + ReasonRuleOther, 46 + ReasonCivicElectoralProcess, 47 + ReasonCivicDisclosure, 48 + ReasonCivicInterference, 49 + ReasonCivicMisinformation, 50 + ReasonCivicImpersonation, 59 51 } 60 52 61 - @doc("Appeal a previously taken moderation action") 53 + /** Appeal a previously taken moderation action */ 62 54 @token 63 55 model ReasonAppeal {} 64 56 65 - @doc("Animal welfare violations") 57 + /** Animal welfare violations */ 66 58 @token 67 59 model ReasonViolenceAnimalWelfare {} 68 60 69 - @doc("Threats or incitement") 61 + /** Threats or incitement */ 70 62 @token 71 63 model ReasonViolenceThreats {} 72 64 73 - @doc("Graphic violent content") 65 + /** Graphic violent content */ 74 66 @token 75 67 model ReasonViolenceGraphicContent {} 76 68 77 - @doc("Self harm") 69 + /** Self harm */ 78 70 @token 79 71 model ReasonViolenceSelfHarm {} 80 72 81 - @doc("Glorification of violence") 73 + /** Glorification of violence */ 82 74 @token 83 75 model ReasonViolenceGlorification {} 84 76 85 - @doc("Extremist content. These reports will be sent only be sent to the application's Moderation Authority.") 77 + /** Extremist content. These reports will be sent only be sent to the application's Moderation Authority. */ 86 78 @token 87 79 model ReasonViolenceExtremistContent {} 88 80 89 - @doc("Human trafficking") 81 + /** Human trafficking */ 90 82 @token 91 83 model ReasonViolenceTrafficking {} 92 84 93 - @doc("Other violent content") 85 + /** Other violent content */ 94 86 @token 95 87 model ReasonViolenceOther {} 96 88 97 - @doc("Adult sexual abuse content") 89 + /** Adult sexual abuse content */ 98 90 @token 99 91 model ReasonSexualAbuseContent {} 100 92 101 - @doc("Non-consensual intimate imagery") 93 + /** Non-consensual intimate imagery */ 102 94 @token 103 95 model ReasonSexualNCII {} 104 96 105 - @doc("Sextortion") 97 + /** Sextortion */ 106 98 @token 107 99 model ReasonSexualSextortion {} 108 100 109 - @doc("Deepfake adult content") 101 + /** Deepfake adult content */ 110 102 @token 111 103 model ReasonSexualDeepfake {} 112 104 113 - @doc("Animal sexual abuse") 105 + /** Animal sexual abuse */ 114 106 @token 115 107 model ReasonSexualAnimal {} 116 108 117 - @doc("Unlabelled adult content") 109 + /** Unlabelled adult content */ 118 110 @token 119 111 model ReasonSexualUnlabeled {} 120 112 121 - @doc("Other sexual violence content") 113 + /** Other sexual violence content */ 122 114 @token 123 115 model ReasonSexualOther {} 124 116 125 - @doc("Child sexual abuse material (CSAM). These reports will be sent only be sent to the application's Moderation Authority.") 117 + /** Child sexual abuse material (CSAM). These reports will be sent only be sent to the application's Moderation Authority. */ 126 118 @token 127 119 model ReasonChildSafetyCSAM {} 128 120 129 - @doc("Grooming or predatory behavior. These reports will be sent only be sent to the application's Moderation Authority.") 121 + /** Grooming or predatory behavior. These reports will be sent only be sent to the application's Moderation Authority. */ 130 122 @token 131 123 model ReasonChildSafetyGroom {} 132 124 133 - @doc("Privacy violation involving a minor") 125 + /** Privacy violation involving a minor */ 134 126 @token 135 127 model ReasonChildSafetyMinorPrivacy {} 136 128 137 - @doc("Child endangerment. These reports will be sent only be sent to the application's Moderation Authority.") 129 + /** Child endangerment. These reports will be sent only be sent to the application's Moderation Authority. */ 138 130 @token 139 131 model ReasonChildSafetyEndangerment {} 140 132 141 - @doc("Harassment or bullying of minors") 133 + /** Harassment or bullying of minors */ 142 134 @token 143 135 model ReasonChildSafetyHarassment {} 144 136 145 - @doc("Promotion of child exploitation. These reports will be sent only be sent to the application's Moderation Authority.") 137 + /** Promotion of child exploitation. These reports will be sent only be sent to the application's Moderation Authority. */ 146 138 @token 147 139 model ReasonChildSafetyPromotion {} 148 140 149 - @doc("Other child safety. These reports will be sent only be sent to the application's Moderation Authority.") 141 + /** Other child safety. These reports will be sent only be sent to the application's Moderation Authority. */ 150 142 @token 151 143 model ReasonChildSafetyOther {} 152 144 153 - @doc("Trolling") 145 + /** Trolling */ 154 146 @token 155 147 model ReasonHarassmentTroll {} 156 148 157 - @doc("Targeted harassment") 149 + /** Targeted harassment */ 158 150 @token 159 151 model ReasonHarassmentTargeted {} 160 152 161 - @doc("Hate speech") 153 + /** Hate speech */ 162 154 @token 163 155 model ReasonHarassmentHateSpeech {} 164 156 165 - @doc("Doxxing") 157 + /** Doxxing */ 166 158 @token 167 159 model ReasonHarassmentDoxxing {} 168 160 169 - @doc("Other harassing or hateful content") 161 + /** Other harassing or hateful content */ 170 162 @token 171 163 model ReasonHarassmentOther {} 172 164 173 - @doc("Fake account or bot") 165 + /** Fake account or bot */ 174 166 @token 175 167 model ReasonMisleadingBot {} 176 168 177 - @doc("Impersonation") 169 + /** Impersonation */ 178 170 @token 179 171 model ReasonMisleadingImpersonation {} 180 172 181 - @doc("Spam") 173 + /** Spam */ 182 174 @token 183 175 model ReasonMisleadingSpam {} 184 176 185 - @doc("Scam") 177 + /** Scam */ 186 178 @token 187 179 model ReasonMisleadingScam {} 188 180 189 - @doc("Unlabelled gen-AI or synthetic content") 181 + /** Unlabelled gen-AI or synthetic content */ 190 182 @token 191 183 model ReasonMisleadingSyntheticContent {} 192 184 193 - @doc("Harmful false claims") 185 + /** Harmful false claims */ 194 186 @token 195 187 model ReasonMisleadingMisinformation {} 196 188 197 - @doc("Other misleading content") 189 + /** Other misleading content */ 198 190 @token 199 191 model ReasonMisleadingOther {} 200 192 201 - @doc("Hacking or system attacks") 193 + /** Hacking or system attacks */ 202 194 @token 203 195 model ReasonRuleSiteSecurity {} 204 196 205 - @doc("Stolen content") 197 + /** Stolen content */ 206 198 @token 207 199 model ReasonRuleStolenContent {} 208 200 209 - @doc("Promoting or selling prohibited items or services") 201 + /** Promoting or selling prohibited items or services */ 210 202 @token 211 203 model ReasonRuleProhibitedSales {} 212 204 213 - @doc("Banned user returning") 205 + /** Banned user returning */ 214 206 @token 215 207 model ReasonRuleBanEvasion {} 216 208 217 - @doc("Other") 209 + /** Other */ 218 210 @token 219 211 model ReasonRuleOther {} 220 212 221 - @doc("Electoral process violations") 213 + /** Electoral process violations */ 222 214 @token 223 215 model ReasonCivicElectoralProcess {} 224 216 225 - @doc("Disclosure & transparency violations") 217 + /** Disclosure & transparency violations */ 226 218 @token 227 219 model ReasonCivicDisclosure {} 228 220 229 - @doc("Voter intimidation or interference") 221 + /** Voter intimidation or interference */ 230 222 @token 231 223 model ReasonCivicInterference {} 232 224 233 - @doc("Election misinformation") 225 + /** Election misinformation */ 234 226 @token 235 227 model ReasonCivicMisinformation {} 236 228 237 - @doc("Impersonation of electoral officials/entities") 229 + /** Impersonation of electoral officials/entities */ 238 230 @token 239 231 model ReasonCivicImpersonation {} 240 232 }
+16 -6
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/addRule.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.addRule { 4 - @doc("The provided URL is invalid") 4 + /** The provided URL is invalid */ 5 5 model InvalidUrl {} 6 6 7 - @doc("A rule for this URL/domain already exists") 7 + /** A rule for this URL/domain already exists */ 8 8 model RuleAlreadyExists {} 9 9 10 - @doc("Add a new URL safety rule") 10 + /** Add a new URL safety rule */ 11 11 @procedure 12 12 @errors(InvalidUrl, RuleAlreadyExists) 13 13 op main(input: { 14 - @doc("The URL or domain to apply the rule to") 14 + /** The URL or domain to apply the rule to */ 15 15 @required 16 16 url: string; 17 17 ··· 21 21 22 22 @required reason: tools.ozone.safelink.defs.ReasonType; 23 23 24 - @doc("Optional comment about the decision") 24 + /** Optional comment about the decision */ 25 25 comment?: string; 26 26 27 - @doc("Author DID. Only respected when using admin auth") 27 + /** Author DID. Only respected when using admin auth */ 28 28 createdBy?: did; 29 29 }): tools.ozone.safelink.defs.Event; 30 30 } 31 + 32 + // --- Externals --- 33 + 34 + @external 35 + namespace tools.ozone.safelink.defs { 36 + model PatternType { } 37 + model ActionType { } 38 + model ReasonType { } 39 + model Event { } 40 + }
+11 -11
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.defs { 4 - @doc("An event for URL safety decisions") 4 + /** An event for URL safety decisions */ 5 5 model Event { 6 - @doc("Auto-incrementing row ID") 6 + /** Auto-incrementing row ID */ 7 7 @required 8 8 id: int32; 9 9 10 10 @required eventType: EventType; 11 11 12 - @doc("The URL that this rule applies to") 12 + /** The URL that this rule applies to */ 13 13 @required 14 14 url: string; 15 15 ··· 17 17 @required action: ActionType; 18 18 @required reason: ReasonType; 19 19 20 - @doc("DID of the user who created this rule") 20 + /** DID of the user who created this rule */ 21 21 @required 22 22 createdBy: did; 23 23 24 24 @required createdAt: datetime; 25 25 26 - @doc("Optional comment about the decision") 26 + /** Optional comment about the decision */ 27 27 comment?: string; 28 28 } 29 29 ··· 55 55 string, 56 56 } 57 57 58 - @doc("Input for creating a URL safety rule") 58 + /** Input for creating a URL safety rule */ 59 59 model UrlRule { 60 - @doc("The URL or domain to apply the rule to") 60 + /** The URL or domain to apply the rule to */ 61 61 @required 62 62 url: string; 63 63 ··· 65 65 @required action: ActionType; 66 66 @required reason: ReasonType; 67 67 68 - @doc("Optional comment about the decision") 68 + /** Optional comment about the decision */ 69 69 comment?: string; 70 70 71 - @doc("DID of the user added the rule.") 71 + /** DID of the user added the rule. */ 72 72 @required 73 73 createdBy: did; 74 74 75 - @doc("Timestamp when the rule was created") 75 + /** Timestamp when the rule was created */ 76 76 @required 77 77 createdAt: datetime; 78 78 79 - @doc("Timestamp when the rule was last updated") 79 + /** Timestamp when the rule was last updated */ 80 80 @required 81 81 updatedAt: datetime; 82 82 }
+14 -7
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryEvents.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.queryEvents { 4 - @doc("Query URL safety audit events") 4 + /** Query URL safety audit events */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Cursor for pagination") 7 + /** Cursor for pagination */ 8 8 cursor?: string; 9 9 10 10 @minValue(1) 11 11 @maxValue(100) 12 - @doc("Maximum number of results to return") 12 + /** Maximum number of results to return */ 13 13 limit?: int32 = 50; 14 14 15 - @doc("Filter by specific URLs or domains") 15 + /** Filter by specific URLs or domains */ 16 16 urls?: string[]; 17 17 18 - @doc("Filter by pattern type") 18 + /** Filter by pattern type */ 19 19 patternType?: string; 20 20 21 - @doc("Sort direction") 21 + /** Sort direction */ 22 22 sortDirection?: "asc" | "desc" | string = "desc"; 23 23 }): { 24 - @doc("Next cursor for pagination. Only present if there are more results.") 24 + /** Next cursor for pagination. Only present if there are more results. */ 25 25 cursor?: string; 26 26 27 27 @required events: tools.ozone.safelink.defs.Event[]; 28 28 }; 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace tools.ozone.safelink.defs { 35 + model Event { } 36 + }
+17 -10
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryRules.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.queryRules { 4 - @doc("Query URL safety rules") 4 + /** Query URL safety rules */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Cursor for pagination") 7 + /** Cursor for pagination */ 8 8 cursor?: string; 9 9 10 10 @minValue(1) 11 11 @maxValue(100) 12 - @doc("Maximum number of results to return") 12 + /** Maximum number of results to return */ 13 13 limit?: int32 = 50; 14 14 15 - @doc("Filter by specific URLs or domains") 15 + /** Filter by specific URLs or domains */ 16 16 urls?: string[]; 17 17 18 - @doc("Filter by pattern type") 18 + /** Filter by pattern type */ 19 19 patternType?: string; 20 20 21 - @doc("Filter by action types") 21 + /** Filter by action types */ 22 22 actions?: string[]; 23 23 24 - @doc("Filter by reason type") 24 + /** Filter by reason type */ 25 25 reason?: string; 26 26 27 - @doc("Filter by rule creator") 27 + /** Filter by rule creator */ 28 28 createdBy?: did; 29 29 30 - @doc("Sort direction") 30 + /** Sort direction */ 31 31 sortDirection?: "asc" | "desc" | string = "desc"; 32 32 }): { 33 - @doc("Next cursor for pagination. Only present if there are more results.") 33 + /** Next cursor for pagination. Only present if there are more results. */ 34 34 cursor?: string; 35 35 36 36 @required rules: tools.ozone.safelink.defs.UrlRule[]; 37 37 }; 38 38 } 39 + 40 + // --- Externals --- 41 + 42 + @external 43 + namespace tools.ozone.safelink.defs { 44 + model UrlRule { } 45 + }
+13 -5
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/removeRule.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.removeRule { 4 - @doc("No active rule found for this URL/domain") 4 + /** No active rule found for this URL/domain */ 5 5 model RuleNotFound {} 6 6 7 - @doc("Remove an existing URL safety rule") 7 + /** Remove an existing URL safety rule */ 8 8 @procedure 9 9 @errors(RuleNotFound) 10 10 op main(input: { 11 - @doc("The URL or domain to remove the rule for") 11 + /** The URL or domain to remove the rule for */ 12 12 @required 13 13 url: string; 14 14 15 15 @required pattern: tools.ozone.safelink.defs.PatternType; 16 16 17 - @doc("Optional comment about why the rule is being removed") 17 + /** Optional comment about why the rule is being removed */ 18 18 comment?: string; 19 19 20 - @doc("Optional DID of the user. Only respected when using admin auth.") 20 + /** Optional DID of the user. Only respected when using admin auth. */ 21 21 createdBy?: did; 22 22 }): tools.ozone.safelink.defs.Event; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace tools.ozone.safelink.defs { 29 + model PatternType { } 30 + model Event { } 31 + }
+15 -5
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/updateRule.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.safelink.updateRule { 4 - @doc("No active rule found for this URL/domain") 4 + /** No active rule found for this URL/domain */ 5 5 model RuleNotFound {} 6 6 7 - @doc("Update an existing URL safety rule") 7 + /** Update an existing URL safety rule */ 8 8 @procedure 9 9 @errors(RuleNotFound) 10 10 op main(input: { 11 - @doc("The URL or domain to update the rule for") 11 + /** The URL or domain to update the rule for */ 12 12 @required 13 13 url: string; 14 14 ··· 18 18 19 19 @required reason: tools.ozone.safelink.defs.ReasonType; 20 20 21 - @doc("Optional comment about the update") 21 + /** Optional comment about the update */ 22 22 comment?: string; 23 23 24 - @doc("Optional DID to credit as the creator. Only respected for admin_token authentication.") 24 + /** Optional DID to credit as the creator. Only respected for admin_token authentication. */ 25 25 createdBy?: did; 26 26 }): tools.ozone.safelink.defs.Event; 27 27 } 28 + 29 + // --- Externals --- 30 + 31 + @external 32 + namespace tools.ozone.safelink.defs { 33 + model PatternType { } 34 + model ActionType { } 35 + model ReasonType { } 36 + model Event { } 37 + }
+18 -9
packages/emitter/test/integration/atproto/input/tools/ozone/server/getConfig.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.server.getConfig { 4 - @doc("Get details about ozone's server configuration.") 4 + /** Get details about ozone's server configuration. */ 5 5 @query 6 6 op main(): { 7 7 appview?: ServiceConfig; ··· 10 10 chat?: ServiceConfig; 11 11 viewer?: ViewerConfig; 12 12 13 - @doc("The did of the verifier used for verification.") 13 + /** The did of the verifier used for verification. */ 14 14 verifierDid?: did; 15 15 }; 16 16 ··· 19 19 } 20 20 21 21 model ViewerConfig { 22 - role?: ( 23 - | "tools.ozone.team.defs#roleAdmin" 24 - | "tools.ozone.team.defs#roleModerator" 25 - | "tools.ozone.team.defs#roleTriage" 26 - | "tools.ozone.team.defs#roleVerifier" 27 - | string 28 - ); 22 + role?: 23 + | tools.ozone.team.defs.RoleAdmin 24 + | tools.ozone.team.defs.RoleModerator 25 + | tools.ozone.team.defs.RoleTriage 26 + | tools.ozone.team.defs.RoleVerifier 27 + | string; 29 28 } 30 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace tools.ozone.team.defs { 35 + @token model RoleAdmin { } 36 + @token model RoleModerator { } 37 + @token model RoleTriage { } 38 + @token model RoleVerifier { } 39 + }
+3 -3
packages/emitter/test/integration/atproto/input/tools/ozone/set/addValues.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.set.addValues { 4 - @doc("Add values to a specific set. Attempting to add values to a set that does not exist will result in an error.") 4 + /** Add values to a specific set. Attempting to add values to a set that does not exist will result in an error. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Name of the set to add values to") 7 + /** Name of the set to add values to */ 8 8 @required 9 9 name: string; 10 10 11 11 @minItems(1) 12 12 @maxItems(1000) 13 - @doc("Array of string values to add to the set") 13 + /** Array of string values to add to the set */ 14 14 @required 15 15 values: string[]; 16 16 }): void;
+3 -3
packages/emitter/test/integration/atproto/input/tools/ozone/set/deleteSet.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.set.deleteSet { 4 - @doc("set with the given name does not exist") 4 + /** set with the given name does not exist */ 5 5 model SetNotFound {} 6 6 7 - @doc("Delete an entire set. Attempting to delete a set that does not exist will result in an error.") 7 + /** Delete an entire set. Attempting to delete a set that does not exist will result in an error. */ 8 8 @procedure 9 9 @errors(SetNotFound) 10 10 op main(input: { 11 - @doc("Name of the set to delete") 11 + /** Name of the set to delete */ 12 12 @required 13 13 name: string; 14 14 }): {};
+4 -4
packages/emitter/test/integration/atproto/input/tools/ozone/set/deleteValues.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.set.deleteValues { 4 - @doc("set with the given name does not exist") 4 + /** set with the given name does not exist */ 5 5 model SetNotFound {} 6 6 7 - @doc("Delete values from a specific set. Attempting to delete values that are not in the set will not result in an error") 7 + /** Delete values from a specific set. Attempting to delete values that are not in the set will not result in an error */ 8 8 @procedure 9 9 @errors(SetNotFound) 10 10 op main(input: { 11 - @doc("Name of the set to delete values from") 11 + /** Name of the set to delete values from */ 12 12 @required 13 13 name: string; 14 14 15 15 @minItems(1) 16 - @doc("Array of string values to delete from the set") 16 + /** Array of string values to delete from the set */ 17 17 @required 18 18 values: string[]; 19 19 }): void;
+9 -2
packages/emitter/test/integration/atproto/input/tools/ozone/set/getValues.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.set.getValues { 4 - @doc("set with the given name does not exist") 4 + /** set with the given name does not exist */ 5 5 model SetNotFound {} 6 6 7 - @doc("Get a specific set and its values") 7 + /** Get a specific set and its values */ 8 8 @query 9 9 @errors(SetNotFound) 10 10 op main( ··· 23 23 cursor?: string; 24 24 }; 25 25 } 26 + 27 + // --- Externals --- 28 + 29 + @external 30 + namespace tools.ozone.set.defs { 31 + model SetView { } 32 + }
+9 -2
packages/emitter/test/integration/atproto/input/tools/ozone/set/querySets.tsp
··· 16 16 "desc", 17 17 } 18 18 19 - @doc("Query available sets") 19 + /** Query available sets */ 20 20 @query 21 21 op main( 22 22 @minValue(1) ··· 29 29 30 30 sortBy?: SortBy = "name", 31 31 32 - @doc("Defaults to ascending order of name field.") 32 + /** Defaults to ascending order of name field. */ 33 33 sortDirection?: SortDirection = "asc" 34 34 ): { 35 35 @required sets: tools.ozone.set.defs.SetView[]; ··· 37 37 cursor?: string; 38 38 }; 39 39 } 40 + 41 + // --- Externals --- 42 + 43 + @external 44 + namespace tools.ozone.set.defs { 45 + model SetView { } 46 + }
+9 -1
packages/emitter/test/integration/atproto/input/tools/ozone/set/upsertSet.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.set.upsertSet { 4 - @doc("Create or update set metadata") 4 + /** Create or update set metadata */ 5 5 @procedure 6 6 op main(input: tools.ozone.set.defs.Set): tools.ozone.set.defs.SetView; 7 7 } 8 + 9 + // --- Externals --- 10 + 11 + @external 12 + namespace tools.ozone.set.defs { 13 + model Set { } 14 + model SetView { } 15 + }
+14 -4
packages/emitter/test/integration/atproto/input/tools/ozone/setting/defs.tsp
··· 16 16 updatedAt?: datetime; 17 17 18 18 managerRole?: 19 - | "tools.ozone.team.defs#roleModerator" 20 - | "tools.ozone.team.defs#roleTriage" 21 - | "tools.ozone.team.defs#roleAdmin" 22 - | "tools.ozone.team.defs#roleVerifier" 19 + | tools.ozone.team.defs.RoleModerator 20 + | tools.ozone.team.defs.RoleTriage 21 + | tools.ozone.team.defs.RoleAdmin 22 + | tools.ozone.team.defs.RoleVerifier 23 23 | string; 24 24 25 25 @required scope: "instance" | "personal" | string; ··· 27 27 @required lastUpdatedBy: did; 28 28 } 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace tools.ozone.team.defs { 35 + @token model RoleModerator { } 36 + @token model RoleTriage { } 37 + @token model RoleAdmin { } 38 + @token model RoleVerifier { } 39 + }
+10 -3
packages/emitter/test/integration/atproto/input/tools/ozone/setting/listOptions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.setting.listOptions { 4 - @doc("List settings with optional filtering") 4 + /** List settings with optional filtering */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 12 12 13 13 scope?: "instance" | "personal" | string = "instance", 14 14 15 - @doc("Filter keys by prefix") 15 + /** Filter keys by prefix */ 16 16 prefix?: string, 17 17 18 18 @maxItems(100) 19 - @doc("Filter for only the specified keys. Ignored if prefix is provided") 19 + /** Filter for only the specified keys. Ignored if prefix is provided */ 20 20 keys?: nsid[] 21 21 ): { 22 22 cursor?: string; ··· 24 24 @required options: tools.ozone.setting.defs.Option[]; 25 25 }; 26 26 } 27 + 28 + // --- Externals --- 29 + 30 + @external 31 + namespace tools.ozone.setting.defs { 32 + model Option { } 33 + }
+1 -1
packages/emitter/test/integration/atproto/input/tools/ozone/setting/removeOptions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.setting.removeOptions { 4 - @doc("Delete settings by key") 4 + /** Delete settings by key */ 5 5 @procedure 6 6 op main(input: { 7 7 @minItems(1)
+20 -5
packages/emitter/test/integration/atproto/input/tools/ozone/setting/upsertOption.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.setting.upsertOption { 4 - @doc("Create or update setting option") 4 + /** Create or update setting option */ 5 5 @procedure 6 6 op main(input: { 7 7 @required key: nsid; ··· 14 14 description?: string; 15 15 16 16 managerRole?: 17 - | "tools.ozone.team.defs#roleModerator" 18 - | "tools.ozone.team.defs#roleTriage" 19 - | "tools.ozone.team.defs#roleVerifier" 20 - | "tools.ozone.team.defs#roleAdmin" 17 + | tools.ozone.team.defs.RoleModerator 18 + | tools.ozone.team.defs.RoleTriage 19 + | tools.ozone.team.defs.RoleVerifier 20 + | tools.ozone.team.defs.RoleAdmin 21 21 | string; 22 22 }): { 23 23 @required option: tools.ozone.setting.defs.Option; 24 24 }; 25 25 } 26 + 27 + // --- Externals --- 28 + 29 + @external 30 + namespace tools.ozone.team.defs { 31 + @token model RoleModerator { } 32 + @token model RoleTriage { } 33 + @token model RoleVerifier { } 34 + @token model RoleAdmin { } 35 + } 36 + 37 + @external 38 + namespace tools.ozone.setting.defs { 39 + model Option { } 40 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findCorrelation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.signature.findCorrelation { 4 - @doc("Find all correlated threat signatures between 2 or more accounts.") 4 + /** Find all correlated threat signatures between 2 or more accounts. */ 5 5 @query 6 6 op main(@required dids: did[]): { 7 7 @required details: tools.ozone.signature.defs.SigDetail[]; 8 8 }; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace tools.ozone.signature.defs { 15 + model SigDetail { } 16 + }
+13 -1
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findRelatedAccounts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.signature.findRelatedAccounts { 4 - @doc("Get accounts that share some matching threat signatures with the root account.") 4 + /** Get accounts that share some matching threat signatures with the root account. */ 5 5 @query 6 6 op main( 7 7 @required did: did, ··· 23 23 similarities?: tools.ozone.signature.defs.SigDetail[]; 24 24 } 25 25 } 26 + 27 + // --- Externals --- 28 + 29 + @external 30 + namespace com.atproto.admin.defs { 31 + model AccountView { } 32 + } 33 + 34 + @external 35 + namespace tools.ozone.signature.defs { 36 + model SigDetail { } 37 + }
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/signature/searchAccounts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.signature.searchAccounts { 4 - @doc("Search for accounts that match one or more threat signature values.") 4 + /** Search for accounts that match one or more threat signature values. */ 5 5 @query 6 6 op main( 7 7 @required values: string[], ··· 17 17 @required accounts: com.atproto.admin.defs.AccountView[]; 18 18 }; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace com.atproto.admin.defs { 25 + model AccountView { } 26 + }
+19 -9
packages/emitter/test/integration/atproto/input/tools/ozone/team/addMember.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.team.addMember { 4 - @doc("Member already exists in the team.") 4 + /** Member already exists in the team. */ 5 5 model MemberAlreadyExists {} 6 6 7 - @doc("Add a member to the ozone team. Requires admin role.") 7 + /** Add a member to the ozone team. Requires admin role. */ 8 8 @procedure 9 9 @errors(MemberAlreadyExists) 10 10 op main(input: { 11 11 @required did: did; 12 12 13 13 @required 14 - role: ( 15 - | "tools.ozone.team.defs#roleAdmin" 16 - | "tools.ozone.team.defs#roleModerator" 17 - | "tools.ozone.team.defs#roleVerifier" 18 - | "tools.ozone.team.defs#roleTriage" 19 - | string 20 - ); 14 + role: 15 + | tools.ozone.team.defs.RoleAdmin 16 + | tools.ozone.team.defs.RoleModerator 17 + | tools.ozone.team.defs.RoleVerifier 18 + | tools.ozone.team.defs.RoleTriage 19 + | string; 21 20 }): tools.ozone.team.defs.Member; 22 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace tools.ozone.team.defs { 27 + @token model RoleAdmin { } 28 + @token model RoleModerator { } 29 + @token model RoleVerifier { } 30 + @token model RoleTriage { } 31 + model Member { } 32 + }
+12 -5
packages/emitter/test/integration/atproto/input/tools/ozone/team/defs.tsp
··· 8 8 createdAt?: datetime; 9 9 updatedAt?: datetime; 10 10 lastUpdatedBy?: string; 11 - @required role: "#roleAdmin" | "#roleModerator" | "#roleTriage" | "#roleVerifier" | string; 11 + @required role: RoleAdmin | RoleModerator | RoleTriage | RoleVerifier | string; 12 12 } 13 13 14 - @doc("Admin role. Highest level of access, can perform all actions.") 14 + /** Admin role. Highest level of access, can perform all actions. */ 15 15 @token 16 16 model RoleAdmin {} 17 17 18 - @doc("Moderator role. Can perform most actions.") 18 + /** Moderator role. Can perform most actions. */ 19 19 @token 20 20 model RoleModerator {} 21 21 22 - @doc("Triage role. Mostly intended for monitoring and escalating issues.") 22 + /** Triage role. Mostly intended for monitoring and escalating issues. */ 23 23 @token 24 24 model RoleTriage {} 25 25 26 - @doc("Verifier role. Only allowed to issue verifications.") 26 + /** Verifier role. Only allowed to issue verifications. */ 27 27 @token 28 28 model RoleVerifier {} 29 29 } 30 + 31 + // --- Externals --- 32 + 33 + @external 34 + namespace app.bsky.actor.defs { 35 + model ProfileViewDetailed { } 36 + }
+3 -3
packages/emitter/test/integration/atproto/input/tools/ozone/team/deleteMember.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.team.deleteMember { 4 - @doc("The member being deleted does not exist") 4 + /** The member being deleted does not exist */ 5 5 model MemberNotFound {} 6 6 7 - @doc("You can not delete yourself from the team") 7 + /** You can not delete yourself from the team */ 8 8 model CannotDeleteSelf {} 9 9 10 - @doc("Delete a member from ozone team. Requires admin role.") 10 + /** Delete a member from ozone team. Requires admin role. */ 11 11 @procedure 12 12 @errors(MemberNotFound, CannotDeleteSelf) 13 13 op main(input: {
+8 -1
packages/emitter/test/integration/atproto/input/tools/ozone/team/listMembers.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.team.listMembers { 4 - @doc("List all members with access to the ozone service.") 4 + /** List all members with access to the ozone service. */ 5 5 @query 6 6 op main( 7 7 q?: string, ··· 18 18 @required members: tools.ozone.team.defs.Member[]; 19 19 }; 20 20 } 21 + 22 + // --- Externals --- 23 + 24 + @external 25 + namespace tools.ozone.team.defs { 26 + model Member { } 27 + }
+19 -9
packages/emitter/test/integration/atproto/input/tools/ozone/team/updateMember.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.team.updateMember { 4 - @doc("The member being updated does not exist in the team") 4 + /** The member being updated does not exist in the team */ 5 5 model MemberNotFound {} 6 6 7 - @doc("Update a member in the ozone service. Requires admin role.") 7 + /** Update a member in the ozone service. Requires admin role. */ 8 8 @procedure 9 9 @errors(MemberNotFound) 10 10 op main(input: { 11 11 @required did: did; 12 12 disabled?: boolean; 13 13 14 - role?: ( 15 - | "tools.ozone.team.defs#roleAdmin" 16 - | "tools.ozone.team.defs#roleModerator" 17 - | "tools.ozone.team.defs#roleVerifier" 18 - | "tools.ozone.team.defs#roleTriage" 19 - | string 20 - ); 14 + role?: 15 + | tools.ozone.team.defs.RoleAdmin 16 + | tools.ozone.team.defs.RoleModerator 17 + | tools.ozone.team.defs.RoleVerifier 18 + | tools.ozone.team.defs.RoleTriage 19 + | string; 21 20 }): tools.ozone.team.defs.Member; 22 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace tools.ozone.team.defs { 27 + @token model RoleAdmin { } 28 + @token model RoleModerator { } 29 + @token model RoleVerifier { } 30 + @token model RoleTriage { } 31 + model Member { } 32 + }
+18 -10
packages/emitter/test/integration/atproto/input/tools/ozone/verification/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.verification.defs { 4 - @doc("Verification data for the associated subject.") 4 + /** Verification data for the associated subject. */ 5 5 model VerificationView { 6 - @doc("The user who issued this verification.") 6 + /** The user who issued this verification. */ 7 7 @required 8 8 issuer: did; 9 9 10 - @doc("The AT-URI of the verification record.") 10 + /** The AT-URI of the verification record. */ 11 11 @required 12 12 uri: atUri; 13 13 14 - @doc("The subject of the verification.") 14 + /** The subject of the verification. */ 15 15 @required 16 16 subject: did; 17 17 18 - @doc("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.") 18 + /** 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. */ 19 19 @required 20 20 handle: handle; 21 21 22 - @doc("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.") 22 + /** 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. */ 23 23 @required 24 24 displayName: string; 25 25 26 - @doc("Timestamp when the verification was created.") 26 + /** Timestamp when the verification was created. */ 27 27 @required 28 28 createdAt: datetime; 29 29 30 - @doc("Describes the reason for revocation, also indicating that the verification is no longer valid.") 30 + /** Describes the reason for revocation, also indicating that the verification is no longer valid. */ 31 31 revokeReason?: string; 32 32 33 - @doc("Timestamp when the verification was revoked.") 33 + /** Timestamp when the verification was revoked. */ 34 34 revokedAt?: datetime; 35 35 36 - @doc("The user who revoked this verification.") 36 + /** The user who revoked this verification. */ 37 37 revokedBy?: did; 38 38 39 39 subjectProfile?: (never | unknown); ··· 52 52 ); 53 53 } 54 54 } 55 + 56 + // --- Externals --- 57 + 58 + @external 59 + namespace tools.ozone.moderation.defs { 60 + model RepoViewDetail { } 61 + model RepoViewNotFound { } 62 + }
+16 -9
packages/emitter/test/integration/atproto/input/tools/ozone/verification/grantVerifications.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.verification.grantVerifications { 4 - @doc("Grant verifications to multiple subjects. Allows batch processing of up to 100 verifications at once.") 4 + /** Grant verifications to multiple subjects. Allows batch processing of up to 100 verifications at once. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Array of verification requests to process") 7 + /** Array of verification requests to process */ 8 8 @maxItems(100) 9 9 @required 10 10 verifications: VerificationInput[]; ··· 14 14 }; 15 15 16 16 model VerificationInput { 17 - @doc("The did of the subject being verified") 17 + /** The did of the subject being verified */ 18 18 @required 19 19 subject: did; 20 20 21 - @doc("Handle of the subject the verification applies to at the moment of verifying.") 21 + /** Handle of the subject the verification applies to at the moment of verifying. */ 22 22 @required 23 23 handle: handle; 24 24 25 - @doc("Display name of the subject the verification applies to at the moment of verifying.") 25 + /** Display name of the subject the verification applies to at the moment of verifying. */ 26 26 @required 27 27 displayName: string; 28 28 29 - @doc("Timestamp for verification record. Defaults to current time when not specified.") 29 + /** Timestamp for verification record. Defaults to current time when not specified. */ 30 30 createdAt?: datetime; 31 31 } 32 32 33 - @doc("Error object for failed verifications.") 33 + /** Error object for failed verifications. */ 34 34 model GrantError { 35 - @doc("Error message describing the reason for failure.") 35 + /** Error message describing the reason for failure. */ 36 36 @required 37 37 error: string; 38 38 39 - @doc("The did of the subject being verified") 39 + /** The did of the subject being verified */ 40 40 @required 41 41 subject: did; 42 42 } 43 43 } 44 + 45 + // --- Externals --- 46 + 47 + @external 48 + namespace tools.ozone.verification.defs { 49 + model VerificationView { } 50 + }
+16 -9
packages/emitter/test/integration/atproto/input/tools/ozone/verification/listVerifications.tsp
··· 8 8 "desc", 9 9 } 10 10 11 - @doc("List verifications") 11 + /** List verifications */ 12 12 @query 13 13 op main( 14 - @doc("Pagination cursor") 14 + /** Pagination cursor */ 15 15 cursor?: string, 16 16 17 - @doc("Maximum number of results to return") 17 + /** Maximum number of results to return */ 18 18 @minValue(1) 19 19 @maxValue(100) 20 20 limit?: int32 = 50, 21 21 22 - @doc("Filter to verifications created after this timestamp") 22 + /** Filter to verifications created after this timestamp */ 23 23 createdAfter?: datetime, 24 24 25 - @doc("Filter to verifications created before this timestamp") 25 + /** Filter to verifications created before this timestamp */ 26 26 createdBefore?: datetime, 27 27 28 - @doc("Filter to verifications from specific issuers") 28 + /** Filter to verifications from specific issuers */ 29 29 @maxItems(100) 30 30 issuers?: did[], 31 31 32 - @doc("Filter to specific verified DIDs") 32 + /** Filter to specific verified DIDs */ 33 33 @maxItems(100) 34 34 subjects?: did[], 35 35 36 - @doc("Sort direction for creation date") 36 + /** Sort direction for creation date */ 37 37 sortDirection?: SortDirection = "desc", 38 38 39 - @doc("Filter to verifications that are revoked or not. By default, includes both.") 39 + /** Filter to verifications that are revoked or not. By default, includes both. */ 40 40 isRevoked?: boolean 41 41 ): { 42 42 cursor?: string; 43 43 @required verifications: tools.ozone.verification.defs.VerificationView[]; 44 44 }; 45 45 } 46 + 47 + // --- Externals --- 48 + 49 + @external 50 + namespace tools.ozone.verification.defs { 51 + model VerificationView { } 52 + }
+8 -8
packages/emitter/test/integration/atproto/input/tools/ozone/verification/revokeVerifications.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace tools.ozone.verification.revokeVerifications { 4 - @doc("Revoke previously granted verifications in batches of up to 100.") 4 + /** Revoke previously granted verifications in batches of up to 100. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Array of verification record uris to revoke") 7 + /** Array of verification record uris to revoke */ 8 8 @maxItems(100) 9 9 @required 10 10 uris: atUri[]; 11 11 12 - @doc("Reason for revoking the verification. This is optional and can be omitted if not needed.") 12 + /** Reason for revoking the verification. This is optional and can be omitted if not needed. */ 13 13 @maxLength(1000) 14 14 revokeReason?: string; 15 15 }): { 16 - @doc("List of verification uris successfully revoked") 16 + /** List of verification uris successfully revoked */ 17 17 @required 18 18 revokedVerifications: atUri[]; 19 19 20 - @doc("List of verification uris that couldn't be revoked, including failure reasons") 20 + /** List of verification uris that couldn't be revoked, including failure reasons */ 21 21 @required 22 22 failedRevocations: RevokeError[]; 23 23 }; 24 24 25 - @doc("Error object for failed revocations") 25 + /** Error object for failed revocations */ 26 26 model RevokeError { 27 - @doc("The AT-URI of the verification record that failed to revoke.") 27 + /** The AT-URI of the verification record that failed to revoke. */ 28 28 @required 29 29 uri: atUri; 30 30 31 - @doc("Description of the error that occurred during revocation.") 31 + /** Description of the error that occurred during revocation. */ 32 32 @required 33 33 error: string; 34 34 }
+2 -2
packages/emitter/test/integration/atproto/output/app/bsky/richtext/facet.json
··· 16 16 }, 17 17 "mention": { 18 18 "type": "object", 19 - "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", 19 + "description": "Facet feature for mention of another account. The text is usually a handle, including a `@` prefix, but the facet reference is a DID.", 20 20 "required": ["did"], 21 21 "properties": { 22 22 "did": { "type": "string", "format": "did" } ··· 32 32 }, 33 33 "tag": { 34 34 "type": "object", 35 - "description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags').", 35 + "description": "Facet feature for a hashtag. The text usually includes a `#` prefix, but the facet reference should not (except in the case of 'double hash tags').", 36 36 "required": ["tag"], 37 37 "properties": { 38 38 "tag": { "type": "string", "maxLength": 640, "maxGraphemes": 64 }
+4 -4
packages/emitter/test/integration/atproto/output/tools/ozone/moderation/defs.json
··· 319 319 "subjectReviewState": { 320 320 "type": "string", 321 321 "knownValues": [ 322 - "#reviewOpen", 323 - "#reviewEscalated", 324 - "#reviewClosed", 325 - "#reviewNone" 322 + "tools.ozone.moderation.defs#reviewOpen", 323 + "tools.ozone.moderation.defs#reviewEscalated", 324 + "tools.ozone.moderation.defs#reviewClosed", 325 + "tools.ozone.moderation.defs#reviewNone" 326 326 ] 327 327 }, 328 328 "reviewOpen": {
+4 -4
packages/emitter/test/integration/atproto/output/tools/ozone/team/defs.json
··· 18 18 "role": { 19 19 "type": "string", 20 20 "knownValues": [ 21 - "#roleAdmin", 22 - "#roleModerator", 23 - "#roleTriage", 24 - "#roleVerifier" 21 + "tools.ozone.team.defs#roleAdmin", 22 + "tools.ozone.team.defs#roleModerator", 23 + "tools.ozone.team.defs#roleTriage", 24 + "tools.ozone.team.defs#roleVerifier" 25 25 ] 26 26 } 27 27 }
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/defs.tsp
··· 38 38 @required value: string; 39 39 } 40 40 } 41 + 42 + // --- Externals --- 43 + 44 + @external 45 + namespace com.atproto.server.defs { 46 + model InviteCode { } 47 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/deleteAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.deleteAccount { 4 - @doc("Delete a user account as an administrator.") 4 + /** Delete a user account as an administrator. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/disableAccountInvites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.disableAccountInvites { 4 - @doc("Disable an account from receiving new invite codes, but does not invalidate existing codes.") 4 + /** Disable an account from receiving new invite codes, but does not invalidate existing codes. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required account: did; 8 8 9 - @doc("Optional reason for disabled invites.") 9 + /** Optional reason for disabled invites. */ 10 10 note?: string; 11 11 }): void; 12 12 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/disableInviteCodes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.disableInviteCodes { 4 - @doc("Disable some set of codes and/or all codes associated with a set of users.") 4 + /** Disable some set of codes and/or all codes associated with a set of users. */ 5 5 @procedure 6 6 op main(input: { 7 7 codes?: string[];
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/enableAccountInvites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.enableAccountInvites { 4 - @doc("Re-enable an account's ability to receive invite codes.") 4 + /** Re-enable an account's ability to receive invite codes. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required account: did; 8 8 9 - @doc("Optional reason for enabled invites.") 9 + /** Optional reason for enabled invites. */ 10 10 note?: string; 11 11 }): void; 12 12 }
+8 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getAccountInfo { 4 - @doc("Get details about an account.") 4 + /** Get details about an account. */ 5 5 @query 6 6 op main( 7 7 @required did: did 8 8 ): com.atproto.admin.defs.AccountView; 9 9 } 10 + 11 + // --- Externals --- 12 + 13 + @external 14 + namespace com.atproto.admin.defs { 15 + model AccountView { } 16 + }
+8 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getAccountInfos { 4 - @doc("Get details about some accounts.") 4 + /** Get details about some accounts. */ 5 5 @query 6 6 op main( 7 7 @required dids: did[] ··· 9 9 @required infos: com.atproto.admin.defs.AccountView[]; 10 10 }; 11 11 } 12 + 13 + // --- Externals --- 14 + 15 + @external 16 + namespace com.atproto.admin.defs { 17 + model AccountView { } 18 + }
+8 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getInviteCodes.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getInviteCodes { 4 - @doc("Get an admin view of invite codes.") 4 + /** Get an admin view of invite codes. */ 5 5 @query 6 6 op main( 7 7 sort?: "recent" | "usage" | string = "recent", ··· 16 16 @required codes: com.atproto.server.defs.InviteCode[]; 17 17 }; 18 18 } 19 + 20 + // --- Externals --- 21 + 22 + @external 23 + namespace com.atproto.server.defs { 24 + model InviteCode { } 25 + }
+15 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getSubjectStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.getSubjectStatus { 4 - @doc("Get the service-specific admin status of a subject (account, record, or blob).") 4 + /** Get the service-specific admin status of a subject (account, record, or blob). */ 5 5 @query 6 6 op main( 7 7 did?: did, ··· 20 20 deactivated?: com.atproto.admin.defs.StatusAttr; 21 21 }; 22 22 } 23 + 24 + // --- Externals --- 25 + 26 + @external 27 + namespace com.atproto.admin.defs { 28 + model RepoRef { } 29 + model RepoBlobRef { } 30 + model StatusAttr { } 31 + } 32 + 33 + @external 34 + namespace com.atproto.repo.strongRef { 35 + model Main { } 36 + }
+8 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/searchAccounts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.searchAccounts { 4 - @doc("Get list of accounts that matches your search query.") 4 + /** Get list of accounts that matches your search query. */ 5 5 @query 6 6 op main( 7 7 email?: string, ··· 15 15 @required accounts: com.atproto.admin.defs.AccountView[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace com.atproto.admin.defs { 23 + model AccountView { } 24 + }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/sendEmail.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.sendEmail { 4 - @doc("Send email to a user's account email address.") 4 + /** Send email to a user's account email address. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required recipientDid: did; ··· 9 9 subject?: string; 10 10 @required senderDid: did; 11 11 12 - @doc("Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers") 12 + /** Additional comment by the sender that won't be used in the email itself but helpful to provide more context for moderators/reviewers */ 13 13 comment?: string; 14 14 }): { 15 15 @required sent: boolean;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateAccountEmail.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountEmail { 4 - @doc("Administrative action to update an account's email.") 4 + /** Administrative action to update an account's email. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 account: atIdentifier; 10 10
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateAccountHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountHandle { 4 - @doc("Administrative action to update an account's handle.") 4 + /** Administrative action to update an account's handle. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateAccountPassword.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountPassword { 4 - @doc("Update the password for a user account as an administrator.") 4 + /** Update the password for a user account as an administrator. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateAccountSigningKey.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateAccountSigningKey { 4 - @doc("Administrative action to update an account's signing key in their Did document.") 4 + /** Administrative action to update an account's signing key in their Did document. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required did: did; 8 8 9 - @doc("Did-key formatted public key") 9 + /** Did-key formatted public key */ 10 10 @required 11 11 signingKey: did; 12 12 }): void;
+15 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateSubjectStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.admin.updateSubjectStatus { 4 - @doc("Update the service-specific admin status of a subject (account, record, or blob).") 4 + /** Update the service-specific admin status of a subject (account, record, or blob). */ 5 5 @procedure 6 6 op main(input: { 7 7 @required ··· 26 26 takedown?: com.atproto.admin.defs.StatusAttr; 27 27 }; 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace com.atproto.admin.defs { 34 + model RepoRef { } 35 + model RepoBlobRef { } 36 + model StatusAttr { } 37 + } 38 + 39 + @external 40 + namespace com.atproto.repo.strongRef { 41 + model Main { } 42 + }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/defs.tsp
··· 4 4 model IdentityInfo { 5 5 @required did: did; 6 6 7 - @doc("The validated handle of the account; or 'handle.invalid' if the handle did not bi-directionally match the DID document.") 7 + /** The validated handle of the account; or 'handle.invalid' if the handle did not bi-directionally match the DID document. */ 8 8 @required 9 9 handle: handle; 10 10 11 - @doc("The complete DID document for the identity.") 11 + /** The complete DID document for the identity. */ 12 12 @required 13 13 didDoc: unknown; 14 14 }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/getRecommendedDidCredentials.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.getRecommendedDidCredentials { 4 - @doc("Describe the credentials that should be included in the DID doc of an account that is migrating to this service.") 4 + /** Describe the credentials that should be included in the DID doc of an account that is migrating to this service. */ 5 5 @query 6 6 op main(): { 7 - @doc("Recommended rotation keys for PLC dids. Should be undefined (or ignored) for did:webs.") 7 + /** Recommended rotation keys for PLC dids. Should be undefined (or ignored) for did:webs. */ 8 8 rotationKeys?: string[]; 9 9 10 10 alsoKnownAs?: string[];
+11 -4
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/refreshIdentity.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.refreshIdentity { 4 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 4 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 5 5 model HandleNotFound {} 6 6 7 - @doc("The DID resolution process confirmed that there is no current DID.") 7 + /** The DID resolution process confirmed that there is no current DID. */ 8 8 model DidNotFound {} 9 9 10 - @doc("The DID previously existed, but has been deactivated.") 10 + /** The DID previously existed, but has been deactivated. */ 11 11 model DidDeactivated {} 12 12 13 - @doc("Request that the server re-resolve an identity (DID and handle). The server may ignore this request, or require authentication, depending on the role, implementation, and policy of the server.") 13 + /** Request that the server re-resolve an identity (DID and handle). The server may ignore this request, or require authentication, depending on the role, implementation, and policy of the server. */ 14 14 @procedure 15 15 @errors(HandleNotFound, DidNotFound, DidDeactivated) 16 16 op main(input: { 17 17 @required identifier: atIdentifier; 18 18 }): com.atproto.identity.defs.IdentityInfo; 19 19 } 20 + 21 + // --- Externals --- 22 + 23 + @external 24 + namespace com.atproto.identity.defs { 25 + model IdentityInfo { } 26 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/requestPlcOperationSignature.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.requestPlcOperationSignature { 4 - @doc("Request an email with a code to in order to request a signed PLC operation. Requires Auth.") 4 + /** Request an email with a code to in order to request a signed PLC operation. Requires Auth. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/resolveDid.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveDid { 4 - @doc("Resolves DID to DID document. Does not bi-directionally verify handle.") 4 + /** Resolves DID to DID document. Does not bi-directionally verify handle. */ 5 5 @query 6 6 @errors(DidNotFound, DidDeactivated) 7 7 op main( 8 - @doc("DID to resolve.") 8 + /** DID to resolve. */ 9 9 @required 10 10 did: did 11 11 ): { 12 - @doc("The complete DID document for the identity.") 12 + /** The complete DID document for the identity. */ 13 13 @required 14 14 didDoc: unknown; 15 15 }; 16 16 17 - @doc("The DID resolution process confirmed that there is no current DID.") 17 + /** The DID resolution process confirmed that there is no current DID. */ 18 18 model DidNotFound {} 19 19 20 - @doc("The DID previously existed, but has been deactivated.") 20 + /** The DID previously existed, but has been deactivated. */ 21 21 model DidDeactivated {} 22 22 }
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/resolveHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveHandle { 4 - @doc("Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document.") 4 + /** Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document. */ 5 5 @query 6 6 @errors(HandleNotFound) 7 7 op main( 8 - @doc("The handle to resolve.") 8 + /** The handle to resolve. */ 9 9 @required 10 10 handle: handle 11 11 ): { 12 12 @required did: did; 13 13 }; 14 14 15 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 15 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 16 16 model HandleNotFound {} 17 17 }
+12 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/resolveIdentity.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.resolveIdentity { 4 - @doc("The resolution process confirmed that the handle does not resolve to any DID.") 4 + /** The resolution process confirmed that the handle does not resolve to any DID. */ 5 5 model HandleNotFound {} 6 6 7 - @doc("The DID resolution process confirmed that there is no current DID.") 7 + /** The DID resolution process confirmed that there is no current DID. */ 8 8 model DidNotFound {} 9 9 10 - @doc("The DID previously existed, but has been deactivated.") 10 + /** The DID previously existed, but has been deactivated. */ 11 11 model DidDeactivated {} 12 12 13 - @doc("Resolves an identity (DID or Handle) to a full identity (DID document and verified handle).") 13 + /** Resolves an identity (DID or Handle) to a full identity (DID document and verified handle). */ 14 14 @query 15 15 @errors(HandleNotFound, DidNotFound, DidDeactivated) 16 16 op main( 17 - @doc("Handle or DID to resolve.") 17 + /** Handle or DID to resolve. */ 18 18 @required 19 19 identifier: atIdentifier 20 20 ): com.atproto.identity.defs.IdentityInfo; 21 21 } 22 + 23 + // --- Externals --- 24 + 25 + @external 26 + namespace com.atproto.identity.defs { 27 + model IdentityInfo { } 28 + }
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/signPlcOperation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.signPlcOperation { 4 - @doc("Signs a PLC operation to update some value(s) in the requesting DID's document.") 4 + /** Signs a PLC operation to update some value(s) in the requesting DID's document. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("A token received through com.atproto.identity.requestPlcOperationSignature") 7 + /** A token received through com.atproto.identity.requestPlcOperationSignature */ 8 8 token?: string; 9 9 10 10 rotationKeys?: string[]; ··· 15 15 16 16 services?: unknown; 17 17 }): { 18 - @doc("A signed DID PLC operation.") 18 + /** A signed DID PLC operation. */ 19 19 @required 20 20 operation: unknown; 21 21 };
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/submitPlcOperation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.submitPlcOperation { 4 - @doc("Validates a PLC operation to ensure that it doesn't violate a service's constraints or get the identity into a bad state, then submits it to the PLC registry") 4 + /** Validates a PLC operation to ensure that it doesn't violate a service's constraints or get the identity into a bad state, then submits it to the PLC registry */ 5 5 @procedure 6 6 op main(input: { 7 7 @required operation: unknown;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/updateHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.identity.updateHandle { 4 - @doc("Updates the current account's handle. Verifies handle validity, and updates did:plc document if necessary. Implemented by PDS, and requires auth.") 4 + /** Updates the current account's handle. Verifies handle validity, and updates did:plc document if necessary. Implemented by PDS, and requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The new handle.") 7 + /** The new handle. */ 8 8 @required 9 9 handle: handle; 10 10 }): void;
+23 -23
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.label.defs { 4 - @doc("Metadata tag on an atproto resource (eg, repo or record).") 4 + /** Metadata tag on an atproto resource (eg, repo or record). */ 5 5 model Label { 6 - @doc("The AT Protocol version of the label object.") 6 + /** The AT Protocol version of the label object. */ 7 7 ver?: integer; 8 8 9 - @doc("DID of the actor who created this label.") 9 + /** DID of the actor who created this label. */ 10 10 @required 11 11 src: did; 12 12 13 - @doc("AT URI of the record, repository (account), or other resource that this label applies to.") 13 + /** AT URI of the record, repository (account), or other resource that this label applies to. */ 14 14 @required 15 15 uri: uri; 16 16 17 - @doc("Optionally, CID specifying the specific version of 'uri' resource this label applies to.") 17 + /** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */ 18 18 cid?: cid; 19 19 20 - @doc("The short string name of the value or type of this label.") 20 + /** The short string name of the value or type of this label. */ 21 21 @maxLength(128) 22 22 @required 23 23 val: string; 24 24 25 - @doc("If true, this is a negation label, overwriting a previous label.") 25 + /** If true, this is a negation label, overwriting a previous label. */ 26 26 neg?: boolean; 27 27 28 - @doc("Timestamp when this label was created.") 28 + /** Timestamp when this label was created. */ 29 29 @required 30 30 cts: datetime; 31 31 32 - @doc("Timestamp at which this label expires (no longer applies).") 32 + /** Timestamp at which this label expires (no longer applies). */ 33 33 exp?: datetime; 34 34 35 - @doc("Signature of dag-cbor encoded label.") 35 + /** Signature of dag-cbor encoded label. */ 36 36 sig?: bytes; 37 37 } 38 38 39 - @doc("Metadata tags on an atproto record, published by the author within the record.") 39 + /** Metadata tags on an atproto record, published by the author within the record. */ 40 40 model SelfLabels { 41 41 @maxItems(10) 42 42 @required 43 43 values: SelfLabel[]; 44 44 } 45 45 46 - @doc("Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.") 46 + /** Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel. */ 47 47 model SelfLabel { 48 - @doc("The short string name of the value or type of this label.") 48 + /** The short string name of the value or type of this label. */ 49 49 @maxLength(128) 50 50 @required 51 51 val: string; 52 52 } 53 53 54 - @doc("Declares a label value and its expected interpretations and behaviors.") 54 + /** Declares a label value and its expected interpretations and behaviors. */ 55 55 model LabelValueDefinition { 56 - @doc("The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).") 56 + /** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */ 57 57 @maxLength(100) 58 58 @maxGraphemes(100) 59 59 @required 60 60 identifier: string; 61 61 62 - @doc("How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.") 62 + /** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */ 63 63 @required 64 64 severity: "inform" | "alert" | "none" | string; 65 65 66 - @doc("What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.") 66 + /** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */ 67 67 @required 68 68 blurs: "content" | "media" | "none" | string; 69 69 70 - @doc("The default setting for this label.") 70 + /** The default setting for this label. */ 71 71 defaultSetting?: "ignore" | "warn" | "hide" | string = "warn"; 72 72 73 - @doc("Does the user need to have adult content enabled in order to configure this label?") 73 + /** Does the user need to have adult content enabled in order to configure this label? */ 74 74 adultOnly?: boolean; 75 75 76 76 @required 77 77 locales: LabelValueDefinitionStrings[]; 78 78 } 79 79 80 - @doc("Strings which describe the label in the UI, localized into a specific language.") 80 + /** Strings which describe the label in the UI, localized into a specific language. */ 81 81 model LabelValueDefinitionStrings { 82 - @doc("The code of the language these strings are written in.") 82 + /** The code of the language these strings are written in. */ 83 83 @required 84 84 lang: language; 85 85 86 - @doc("A short human-readable name for the label.") 86 + /** A short human-readable name for the label. */ 87 87 @maxGraphemes(64) 88 88 @maxLength(640) 89 89 @required 90 90 name: string; 91 91 92 - @doc("A longer description of what the label means and why it might be applied.") 92 + /** A longer description of what the label means and why it might be applied. */ 93 93 @maxGraphemes(10000) 94 94 @maxLength(100000) 95 95 @required
+10 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/queryLabels.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.label.queryLabels { 4 - @doc("Find labels relevant to the provided AT-URI patterns. Public endpoint for moderation services, though may return different or additional results with auth.") 4 + /** Find labels relevant to the provided AT-URI patterns. Public endpoint for moderation services, though may return different or additional results with auth. */ 5 5 @query 6 6 op main( 7 - @doc("List of AT URI patterns to match (boolean 'OR'). Each may be a prefix (ending with '*'; will match inclusive of the string leading to '*'), or a full URI.") 7 + /** List of AT URI patterns to match (boolean 'OR'). Each may be a prefix (ending with '*'; will match inclusive of the string leading to '*'), or a full URI. */ 8 8 @required 9 9 uriPatterns: string[], 10 10 11 - @doc("Optional list of label sources (DIDs) to filter on.") 11 + /** Optional list of label sources (DIDs) to filter on. */ 12 12 sources?: did[], 13 13 14 14 @minValue(1) ··· 21 21 @required labels: com.atproto.label.defs.Label[]; 22 22 }; 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace com.atproto.label.defs { 29 + model Label { } 30 + }
+9 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/subscribeLabels.tsp
··· 13 13 message?: string; 14 14 } 15 15 16 - @doc("Subscribe to stream of labels (and negations). Public endpoint implemented by mod services. Uses same sequencing scheme as repo event stream.") 16 + /** Subscribe to stream of labels (and negations). Public endpoint implemented by mod services. Uses same sequencing scheme as repo event stream. */ 17 17 @subscription 18 18 @errors(FutureCursor) 19 19 op main( 20 - @doc("The last known event seq number to backfill from.") 20 + /** The last known event seq number to backfill from. */ 21 21 cursor?: integer 22 22 ): (Labels | Info); 23 23 } 24 + 25 + // --- Externals --- 26 + 27 + @external 28 + namespace com.atproto.label.defs { 29 + model Label { } 30 + }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/lexicon/schema.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.lexicon.schema { 4 - @doc("Representation of Lexicon schemas themselves, when published as atproto records. Note that the schema language is not defined in Lexicon; this meta schema currently only includes a single version field ('lexicon'). See the atproto specifications for description of the other expected top-level fields ('id', 'defs', etc).") 4 + /** Representation of Lexicon schemas themselves, when published as atproto records. Note that the schema language is not defined in Lexicon; this meta schema currently only includes a single version field ('lexicon'). See the atproto specifications for description of the other expected top-level fields ('id', 'defs', etc). */ 5 5 @rec("nsid") 6 6 model Main { 7 - @doc("Indicates the 'version' of the Lexicon language. Must be '1' for the current atproto/Lexicon schema system.") 7 + /** Indicates the 'version' of the Lexicon language. Must be '1' for the current atproto/Lexicon schema system. */ 8 8 @required 9 9 lexicon: integer; 10 10 }
+23 -6
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/createReport.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.moderation.createReport { 4 - @doc("Submit a moderation report regarding an atproto account or record. Implemented by moderation services (with PDS proxying), and requires auth.") 4 + /** Submit a moderation report regarding an atproto account or record. Implemented by moderation services (with PDS proxying), and requires auth. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Indicates the broad category of violation the report is for.") 7 + /** Indicates the broad category of violation the report is for. */ 8 8 @required 9 9 reasonType: com.atproto.moderation.defs.ReasonType; 10 10 11 11 @maxGraphemes(2000) 12 12 @maxLength(20000) 13 - @doc("Additional context about the content and violation.") 13 + /** Additional context about the content and violation. */ 14 14 reason?: string; 15 15 16 16 @required ··· 40 40 @required createdAt: datetime; 41 41 }; 42 42 43 - @doc("Moderation tool information for tracing the source of the action") 43 + /** Moderation tool information for tracing the source of the action */ 44 44 model ModTool { 45 - @doc("Name/identifier of the source (e.g., 'bsky-app/android', 'bsky-web/chrome')") 45 + /** Name/identifier of the source (e.g., 'bsky-app/android', 'bsky-web/chrome') */ 46 46 @required 47 47 name: string; 48 48 49 - @doc("Additional arbitrary metadata about the source") 49 + /** Additional arbitrary metadata about the source */ 50 50 meta?: unknown; 51 51 } 52 52 } 53 + 54 + // --- Externals --- 55 + 56 + @external 57 + namespace com.atproto.moderation.defs { 58 + model ReasonType { } 59 + } 60 + 61 + @external 62 + namespace com.atproto.admin.defs { 63 + model RepoRef { } 64 + } 65 + 66 + @external 67 + namespace com.atproto.repo.strongRef { 68 + model Main { } 69 + }
+59 -8
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/defs.tsp
··· 66 66 ToolsOzoneReportReasonCivicImpersonation: "tools.ozone.report.defs#reasonCivicImpersonation", 67 67 } 68 68 69 - @doc("Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`.") 69 + /** Spam: frequent unwanted promotion, replies, mentions. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingSpam`. */ 70 70 @token 71 71 model ReasonSpam {} 72 72 73 - @doc("Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`.") 73 + /** Direct violation of server rules, laws, terms of service. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`. */ 74 74 @token 75 75 model ReasonViolation {} 76 76 77 - @doc("Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`.") 77 + /** Misleading identity, affiliation, or content. Prefer new lexicon definition `tools.ozone.report.defs#reasonMisleadingOther`. */ 78 78 @token 79 79 model ReasonMisleading {} 80 80 81 - @doc("Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`.") 81 + /** Unwanted or mislabeled sexual content. Prefer new lexicon definition `tools.ozone.report.defs#reasonSexualUnlabeled`. */ 82 82 @token 83 83 model ReasonSexual {} 84 84 85 - @doc("Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`.") 85 + /** Rude, harassing, explicit, or otherwise unwelcoming behavior. Prefer new lexicon definition `tools.ozone.report.defs#reasonHarassmentOther`. */ 86 86 @token 87 87 model ReasonRude {} 88 88 89 - @doc("Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`.") 89 + /** Reports not falling under another report category. Prefer new lexicon definition `tools.ozone.report.defs#reasonRuleOther`. */ 90 90 @token 91 91 model ReasonOther {} 92 92 93 - @doc("Appeal a previously taken moderation action") 93 + /** Appeal a previously taken moderation action */ 94 94 @token 95 95 model ReasonAppeal {} 96 96 97 - @doc("Tag describing a type of subject that might be reported.") 97 + /** Tag describing a type of subject that might be reported. */ 98 98 union SubjectType { 99 99 "account", 100 100 "record", ··· 102 102 string, 103 103 } 104 104 } 105 + 106 + // --- Externals --- 107 + 108 + @external 109 + namespace tools.ozone.report.defs { 110 + @token model ReasonAppeal { } 111 + @token model ReasonViolenceAnimalWelfare { } 112 + @token model ReasonViolenceThreats { } 113 + @token model ReasonViolenceGraphicContent { } 114 + @token model ReasonViolenceSelfHarm { } 115 + @token model ReasonViolenceGlorification { } 116 + @token model ReasonViolenceExtremistContent { } 117 + @token model ReasonViolenceTrafficking { } 118 + @token model ReasonViolenceOther { } 119 + @token model ReasonSexualAbuseContent { } 120 + @token model ReasonSexualNCII { } 121 + @token model ReasonSexualSextortion { } 122 + @token model ReasonSexualDeepfake { } 123 + @token model ReasonSexualAnimal { } 124 + @token model ReasonSexualUnlabeled { } 125 + @token model ReasonSexualOther { } 126 + @token model ReasonChildSafetyCSAM { } 127 + @token model ReasonChildSafetyGroom { } 128 + @token model ReasonChildSafetyMinorPrivacy { } 129 + @token model ReasonChildSafetyEndangerment { } 130 + @token model ReasonChildSafetyHarassment { } 131 + @token model ReasonChildSafetyPromotion { } 132 + @token model ReasonChildSafetyOther { } 133 + @token model ReasonHarassmentTroll { } 134 + @token model ReasonHarassmentTargeted { } 135 + @token model ReasonHarassmentHateSpeech { } 136 + @token model ReasonHarassmentDoxxing { } 137 + @token model ReasonHarassmentOther { } 138 + @token model ReasonMisleadingBot { } 139 + @token model ReasonMisleadingImpersonation { } 140 + @token model ReasonMisleadingSpam { } 141 + @token model ReasonMisleadingScam { } 142 + @token model ReasonMisleadingSyntheticContent { } 143 + @token model ReasonMisleadingMisinformation { } 144 + @token model ReasonMisleadingOther { } 145 + @token model ReasonRuleSiteSecurity { } 146 + @token model ReasonRuleStolenContent { } 147 + @token model ReasonRuleProhibitedSales { } 148 + @token model ReasonRuleBanEvasion { } 149 + @token model ReasonRuleOther { } 150 + @token model ReasonCivicElectoralProcess { } 151 + @token model ReasonCivicDisclosure { } 152 + @token model ReasonCivicInterference { } 153 + @token model ReasonCivicMisinformation { } 154 + @token model ReasonCivicImpersonation { } 155 + }
+16 -9
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/applyWrites.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.applyWrites { 4 - @doc("Indicates that the 'swapCommit' parameter did not match current commit.") 4 + /** Indicates that the 'swapCommit' parameter did not match current commit. */ 5 5 model InvalidSwap {} 6 6 7 7 @closed ··· 20 20 DeleteResult, 21 21 } 22 22 23 - @doc("Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.") 23 + /** Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS. */ 24 24 @procedure 25 25 @errors(InvalidSwap) 26 26 op main(input: { 27 - @doc("The handle or DID of the repo (aka, current account).") 27 + /** The handle or DID of the repo (aka, current account). */ 28 28 @required 29 29 repo: atIdentifier; 30 30 31 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.") 31 + /** Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons. */ 32 32 validate?: boolean; 33 33 34 34 @required 35 35 writes: WriteAction[]; 36 36 37 - @doc("If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.") 37 + /** If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations. */ 38 38 swapCommit?: cid; 39 39 }): { 40 40 commit?: com.atproto.repo.defs.CommitMeta; 41 41 results?: WriteResult[]; 42 42 }; 43 43 44 - @doc("Operation which creates a new record.") 44 + /** Operation which creates a new record. */ 45 45 model Create { 46 46 @required collection: nsid; 47 47 48 - @doc("NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.") 48 + /** NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility. */ 49 49 @maxLength(512) 50 50 rkey?: recordKey; 51 51 52 52 @required value: unknown; 53 53 } 54 54 55 - @doc("Operation which updates an existing record.") 55 + /** Operation which updates an existing record. */ 56 56 model Update { 57 57 @required collection: nsid; 58 58 @required rkey: recordKey; 59 59 @required value: unknown; 60 60 } 61 61 62 - @doc("Operation which deletes an existing record.") 62 + /** Operation which deletes an existing record. */ 63 63 model Delete { 64 64 @required collection: nsid; 65 65 @required rkey: recordKey; ··· 79 79 80 80 model DeleteResult {} 81 81 } 82 + 83 + // --- Externals --- 84 + 85 + @external 86 + namespace com.atproto.repo.defs { 87 + model CommitMeta { } 88 + }
+15 -8
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/createRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.createRecord { 4 - @doc("Create a single new repository record. Requires auth, implemented by PDS.") 4 + /** Create a single new repository record. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @maxLength(512) 18 18 rkey?: recordKey; 19 19 20 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.") 20 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 21 21 validate?: boolean; 22 22 23 - @doc("The record itself. Must contain a $type field.") 23 + /** The record itself. Must contain a $type field. */ 24 24 @required 25 25 record: unknown; 26 26 27 - @doc("Compare and swap with the previous commit by CID.") 27 + /** Compare and swap with the previous commit by CID. */ 28 28 swapCommit?: cid; 29 29 }): { 30 30 @required uri: atUri; ··· 33 33 validationStatus?: "valid" | "unknown" | string; 34 34 }; 35 35 36 - @doc("Indicates that 'swapCommit' didn't match current repo commit.") 36 + /** Indicates that 'swapCommit' didn't match current repo commit. */ 37 37 model InvalidSwap {} 38 38 } 39 + 40 + // --- Externals --- 41 + 42 + @external 43 + namespace com.atproto.repo.defs { 44 + model CommitMeta { } 45 + }
+13 -6
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/deleteRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.deleteRecord { 4 - @doc("Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.") 4 + /** Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @required 18 18 rkey: recordKey; 19 19 20 - @doc("Compare and swap with the previous record by CID.") 20 + /** Compare and swap with the previous record by CID. */ 21 21 swapRecord?: cid; 22 22 23 - @doc("Compare and swap with the previous commit by CID.") 23 + /** Compare and swap with the previous commit by CID. */ 24 24 swapCommit?: cid; 25 25 }): { 26 26 commit?: com.atproto.repo.defs.CommitMeta; ··· 28 28 29 29 model InvalidSwap {} 30 30 } 31 + 32 + // --- Externals --- 33 + 34 + @external 35 + namespace com.atproto.repo.defs { 36 + model CommitMeta { } 37 + }
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/describeRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.describeRepo { 4 - @doc("Get information about an account and repository, including the list of collections. Does not require auth.") 4 + /** Get information about an account and repository, including the list of collections. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 repo: atIdentifier 10 10 ): { 11 11 @required handle: handle; 12 12 @required did: did; 13 13 14 - @doc("The complete DID document for this account.") 14 + /** The complete DID document for this account. */ 15 15 @required 16 16 didDoc: unknown; 17 17 18 - @doc("List of all the collections (NSIDs) for which this repo contains at least one record.") 18 + /** List of all the collections (NSIDs) for which this repo contains at least one record. */ 19 19 @required 20 20 collections: nsid[]; 21 21 22 - @doc("Indicates if handle is currently valid (resolves bi-directionally)") 22 + /** Indicates if handle is currently valid (resolves bi-directionally) */ 23 23 @required 24 24 handleIsCorrect: boolean; 25 25 };
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/getRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.getRecord { 4 - @doc("Get a single record from a repository. Does not require auth.") 4 + /** Get a single record from a repository. Does not require auth. */ 5 5 @query 6 6 @errors(RecordNotFound) 7 7 op main( 8 - @doc("The handle or DID of the repo.") 8 + /** The handle or DID of the repo. */ 9 9 @required 10 10 repo: atIdentifier, 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid, 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @required 18 18 rkey: recordKey, 19 19 20 - @doc("The CID of the version of the record. If not specified, then return the most recent version.") 20 + /** The CID of the version of the record. If not specified, then return the most recent version. */ 21 21 cid?: cid 22 22 ): { 23 23 @required uri: atUri;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/importRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.importRepo { 4 - @doc("Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.") 4 + /** Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set. */ 5 5 @procedure 6 6 op main( 7 7 @encoding("application/vnd.ipld.car")
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/listMissingBlobs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.listMissingBlobs { 4 - @doc("Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.") 4 + /** Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow. */ 5 5 @query 6 6 op main( 7 7 @minValue(1)
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/listRecords.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.listRecords { 4 - @doc("List a range of records in a repository, matching a specific collection. Does not require auth.") 4 + /** List a range of records in a repository, matching a specific collection. Does not require auth. */ 5 5 @query 6 6 op main( 7 - @doc("The handle or DID of the repo.") 7 + /** The handle or DID of the repo. */ 8 8 @required 9 9 repo: atIdentifier, 10 10 11 - @doc("The NSID of the record type.") 11 + /** The NSID of the record type. */ 12 12 @required 13 13 collection: nsid, 14 14 15 - @doc("The number of records to return.") 15 + /** The number of records to return. */ 16 16 @minValue(1) 17 17 @maxValue(100) 18 18 limit?: int32 = 50, 19 19 20 20 cursor?: string, 21 21 22 - @doc("Flag to reverse the order of the returned records.") 22 + /** Flag to reverse the order of the returned records. */ 23 23 reverse?: boolean 24 24 ): { 25 25 cursor?: string;
+15 -8
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/putRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.putRecord { 4 - @doc("Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.") 4 + /** Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS. */ 5 5 @errors(InvalidSwap) 6 6 @procedure 7 7 op main(input: { 8 - @doc("The handle or DID of the repo (aka, current account).") 8 + /** The handle or DID of the repo (aka, current account). */ 9 9 @required 10 10 repo: atIdentifier; 11 11 12 - @doc("The NSID of the record collection.") 12 + /** The NSID of the record collection. */ 13 13 @required 14 14 collection: nsid; 15 15 16 - @doc("The Record Key.") 16 + /** The Record Key. */ 17 17 @maxLength(512) 18 18 @required 19 19 rkey: recordKey; 20 20 21 - @doc("Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.") 21 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 22 22 validate?: boolean; 23 23 24 - @doc("The record to write.") 24 + /** The record to write. */ 25 25 @required 26 26 record: unknown; 27 27 28 - @doc("Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation") 28 + /** Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation */ 29 29 swapRecord?: cid | null; 30 30 31 - @doc("Compare and swap with the previous commit by CID.") 31 + /** Compare and swap with the previous commit by CID. */ 32 32 swapCommit?: cid; 33 33 }): { 34 34 @required uri: atUri; ··· 39 39 40 40 model InvalidSwap {} 41 41 } 42 + 43 + // --- Externals --- 44 + 45 + @external 46 + namespace com.atproto.repo.defs { 47 + model CommitMeta { } 48 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/strongRef.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A URI with a content-hash fingerprint.") 3 + /** A URI with a content-hash fingerprint. */ 4 4 namespace com.atproto.repo.strongRef { 5 5 model Main { 6 6 @required uri: atUri;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/uploadBlob.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.repo.uploadBlob { 4 - @doc("Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.") 4 + /** Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS. */ 5 5 @procedure 6 6 op main( 7 7 @encoding("*/*")
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/activateAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.activateAccount { 4 - @doc("Activates a currently deactivated account. Used to finalize account migration after the account's repo is imported and identity is setup.") 4 + /** Activates a currently deactivated account. Used to finalize account migration after the account's repo is imported and identity is setup. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/checkAccountStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.checkAccountStatus { 4 - @doc("Returns the status of an account, especially as pertaining to import or recovery. Can be called many times over the course of an account migration. Requires auth and can only be called pertaining to oneself.") 4 + /** Returns the status of an account, especially as pertaining to import or recovery. Can be called many times over the course of an account migration. Requires auth and can only be called pertaining to oneself. */ 5 5 @query 6 6 op main(): { 7 7 @required activated: boolean;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/confirmEmail.tsp
··· 6 6 model InvalidToken {} 7 7 model InvalidEmail {} 8 8 9 - @doc("Confirm an email using a token from com.atproto.server.requestEmailConfirmation.") 9 + /** Confirm an email using a token from com.atproto.server.requestEmailConfirmation. */ 10 10 @procedure 11 11 @errors(AccountNotFound, ExpiredToken, InvalidToken, InvalidEmail) 12 12 op main(input: {
+9 -9
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/createAccount.tsp
··· 9 9 model UnresolvableDid {} 10 10 model IncompatibleDidDoc {} 11 11 12 - @doc("Account login session returned on successful account creation.") 12 + /** Account login session returned on successful account creation. */ 13 13 @inline 14 14 model Output { 15 15 @required accessJwt: string; 16 16 @required refreshJwt: string; 17 17 @required handle: handle; 18 18 19 - @doc("The DID of the new account.") 19 + /** The DID of the new account. */ 20 20 @required 21 21 did: did; 22 22 23 - @doc("Complete DID document.") 23 + /** Complete DID document. */ 24 24 didDoc?: unknown; 25 25 } 26 26 27 - @doc("Create an account. Implemented by PDS.") 27 + /** Create an account. Implemented by PDS. */ 28 28 @procedure 29 29 @errors(InvalidHandle, InvalidPassword, InvalidInviteCode, HandleNotAvailable, UnsupportedDomain, UnresolvableDid, IncompatibleDidDoc) 30 30 op main(input: { 31 31 email?: string; 32 32 33 - @doc("Requested handle for the account.") 33 + /** Requested handle for the account. */ 34 34 @required 35 35 handle: handle; 36 36 37 - @doc("Pre-existing atproto DID, being imported to a new account.") 37 + /** Pre-existing atproto DID, being imported to a new account. */ 38 38 did?: did; 39 39 40 40 inviteCode?: string; 41 41 verificationCode?: string; 42 42 verificationPhone?: string; 43 43 44 - @doc("Initial account password. May need to meet instance-specific password strength requirements.") 44 + /** Initial account password. May need to meet instance-specific password strength requirements. */ 45 45 password?: string; 46 46 47 - @doc("DID PLC rotation key (aka, recovery key) to be included in PLC creation operation.") 47 + /** DID PLC rotation key (aka, recovery key) to be included in PLC creation operation. */ 48 48 recoveryKey?: string; 49 49 50 - @doc("A signed DID PLC operation to be submitted as part of importing an existing account to this instance. NOTE: this optional field may be updated when full account migration is implemented.") 50 + /** A signed DID PLC operation to be submitted as part of importing an existing account to this instance. NOTE: this optional field may be updated when full account migration is implemented. */ 51 51 plcOp?: unknown; 52 52 }): Output; 53 53 }
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/createAppPassword.tsp
··· 10 10 privileged?: boolean; 11 11 } 12 12 13 - @doc("Create an App Password.") 13 + /** Create an App Password. */ 14 14 @procedure 15 15 @errors(AccountTakedown) 16 16 op main(input: { 17 - @doc("A short name for the App Password, to help distinguish them.") 17 + /** A short name for the App Password, to help distinguish them. */ 18 18 @required 19 19 name: string; 20 20 21 - @doc("If an app password has 'privileged' access to possibly sensitive account state. Meant for use with trusted clients.") 21 + /** If an app password has 'privileged' access to possibly sensitive account state. Meant for use with trusted clients. */ 22 22 privileged?: boolean; 23 23 }): AppPassword; 24 24 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/createInviteCode.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.createInviteCode { 4 - @doc("Create an invite code.") 4 + /** Create an invite code. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required useCount: integer;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/createInviteCodes.tsp
··· 6 6 @required codes: string[]; 7 7 } 8 8 9 - @doc("Create invite codes.") 9 + /** Create invite codes. */ 10 10 @procedure 11 11 op main(input: { 12 12 @required codeCount: integer = 1;
+4 -4
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/createSession.tsp
··· 5 5 6 6 model AuthFactorTokenRequired {} 7 7 8 - @doc("Create an authentication session.") 8 + /** Create an authentication session. */ 9 9 @procedure 10 10 @errors(AccountTakedown, AuthFactorTokenRequired) 11 11 op main(input: { 12 - @doc("Handle or other identifier supported by the server for the authenticating user.") 12 + /** Handle or other identifier supported by the server for the authenticating user. */ 13 13 @required 14 14 identifier: string; 15 15 ··· 17 17 18 18 authFactorToken?: string; 19 19 20 - @doc("When true, instead of throwing error for takendown accounts, a valid response with a narrow scoped token will be returned") 20 + /** When true, instead of throwing error for takendown accounts, a valid response with a narrow scoped token will be returned */ 21 21 allowTakendown?: boolean; 22 22 }): { 23 23 @required accessJwt: string; ··· 30 30 emailAuthFactor?: boolean; 31 31 active?: boolean; 32 32 33 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 33 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 34 34 status?: "takendown" | "suspended" | "deactivated" | string; 35 35 }; 36 36 }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/deactivateAccount.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.deactivateAccount { 4 - @doc("Deactivates a currently active account. Stops serving of repo, and future writes to repo until reactivated. Used to finalize account migration with the old host after the account has been activated on the new host.") 4 + /** Deactivates a currently active account. Stops serving of repo, and future writes to repo until reactivated. Used to finalize account migration with the old host after the account has been activated on the new host. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("A recommendation to server as to how long they should hold onto the deactivated account before deleting.") 7 + /** A recommendation to server as to how long they should hold onto the deactivated account before deleting. */ 8 8 deleteAfter?: datetime; 9 9 }): void; 10 10 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/deleteAccount.tsp
··· 4 4 model ExpiredToken {} 5 5 model InvalidToken {} 6 6 7 - @doc("Delete an actor's account with a token and password. Can only be called after requesting a deletion token. Requires auth.") 7 + /** Delete an actor's account with a token and password. Can only be called after requesting a deletion token. Requires auth. */ 8 8 @procedure 9 9 @errors(ExpiredToken, InvalidToken) 10 10 op main(input: {
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/deleteSession.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.deleteSession { 4 - @doc("Delete the current session. Requires auth.") 4 + /** Delete the current session. Requires auth. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+6 -6
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/describeServer.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.describeServer { 4 - @doc("Describes the server's account creation requirements and capabilities. Implemented by PDS.") 4 + /** Describes the server's account creation requirements and capabilities. Implemented by PDS. */ 5 5 @query 6 6 op main(): { 7 7 @required did: did; 8 8 9 - @doc("If true, an invite code must be supplied to create an account on this instance.") 9 + /** If true, an invite code must be supplied to create an account on this instance. */ 10 10 inviteCodeRequired?: boolean; 11 11 12 - @doc("If true, a phone verification token must be supplied to create an account on this instance.") 12 + /** If true, a phone verification token must be supplied to create an account on this instance. */ 13 13 phoneVerificationRequired?: boolean; 14 14 15 - @doc("List of domain suffixes that can be used in account handles.") 15 + /** List of domain suffixes that can be used in account handles. */ 16 16 @required 17 17 availableUserDomains: string[]; 18 18 19 - @doc("URLs of service policy documents.") 19 + /** URLs of service policy documents. */ 20 20 links?: Links; 21 21 22 - @doc("Contact information") 22 + /** Contact information */ 23 23 contact?: Contact; 24 24 }; 25 25
+9 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/getAccountInviteCodes.tsp
··· 3 3 namespace com.atproto.server.getAccountInviteCodes { 4 4 model DuplicateCreate {} 5 5 6 - @doc("Get all invite codes for the current account. Requires auth.") 6 + /** Get all invite codes for the current account. Requires auth. */ 7 7 @query 8 8 @errors(DuplicateCreate) 9 9 op main( 10 10 includeUsed?: boolean = true, 11 11 12 - @doc("Controls whether any new 'earned' but not 'created' invites should be created.") 12 + /** Controls whether any new 'earned' but not 'created' invites should be created. */ 13 13 createAvailable?: boolean = true 14 14 ): { 15 15 @required codes: com.atproto.server.defs.InviteCode[]; 16 16 }; 17 17 } 18 + 19 + // --- Externals --- 20 + 21 + @external 22 + namespace com.atproto.server.defs { 23 + model InviteCode { } 24 + }
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/getServiceAuth.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.getServiceAuth { 4 - @doc("Indicates that the requested expiration date is not a valid. May be in the past or may be reliant on the requested scopes.") 4 + /** Indicates that the requested expiration date is not a valid. May be in the past or may be reliant on the requested scopes. */ 5 5 model BadExpiration {} 6 6 7 - @doc("Get a signed token on behalf of the requesting DID for the requested service.") 7 + /** Get a signed token on behalf of the requesting DID for the requested service. */ 8 8 @query 9 9 @errors(BadExpiration) 10 10 op main( 11 - @doc("The DID of the service that the token will be used to authenticate with") 11 + /** The DID of the service that the token will be used to authenticate with */ 12 12 @required 13 13 aud: did, 14 14 15 - @doc("The time in Unix Epoch seconds that the JWT expires. Defaults to 60 seconds in the future. The service may enforce certain time bounds on tokens depending on the requested scope.") 15 + /** The time in Unix Epoch seconds that the JWT expires. Defaults to 60 seconds in the future. The service may enforce certain time bounds on tokens depending on the requested scope. */ 16 16 exp?: integer, 17 17 18 - @doc("Lexicon (XRPC) method to bind the requested token to") 18 + /** Lexicon (XRPC) method to bind the requested token to */ 19 19 lxm?: nsid 20 20 ): { 21 21 @required token: string;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/getSession.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.getSession { 4 - @doc("Get information about the current auth session. Requires auth.") 4 + /** Get information about the current auth session. Requires auth. */ 5 5 @query 6 6 op main(): { 7 7 @required handle: handle; ··· 12 12 didDoc?: unknown; 13 13 active?: boolean; 14 14 15 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 15 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 16 16 status?: "takendown" | "suspended" | "deactivated" | string; 17 17 }; 18 18 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/listAppPasswords.tsp
··· 9 9 privileged?: boolean; 10 10 } 11 11 12 - @doc("List all App Passwords.") 12 + /** List all App Passwords. */ 13 13 @query 14 14 @errors(AccountTakedown) 15 15 op main(): {
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/refreshSession.tsp
··· 3 3 namespace com.atproto.server.refreshSession { 4 4 model AccountTakedown {} 5 5 6 - @doc("Refresh an authentication session. Requires auth using the 'refreshJwt' (not the 'accessJwt').") 6 + /** Refresh an authentication session. Requires auth using the 'refreshJwt' (not the 'accessJwt'). */ 7 7 @procedure 8 8 @errors(AccountTakedown) 9 9 op main(): { ··· 14 14 didDoc?: unknown; 15 15 active?: boolean; 16 16 17 - @doc("Hosting status of the account. If not specified, then assume 'active'.") 17 + /** Hosting status of the account. If not specified, then assume 'active'. */ 18 18 status?: "takendown" | "suspended" | "deactivated" | string; 19 19 }; 20 20 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/requestAccountDelete.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestAccountDelete { 4 - @doc("Initiate a user account deletion via email.") 4 + /** Initiate a user account deletion via email. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/requestEmailConfirmation.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestEmailConfirmation { 4 - @doc("Request an email with a code to confirm ownership of email.") 4 + /** Request an email with a code to confirm ownership of email. */ 5 5 @procedure 6 6 op main(): void; 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/requestEmailUpdate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestEmailUpdate { 4 - @doc("Request a token in order to update email.") 4 + /** Request a token in order to update email. */ 5 5 @procedure 6 6 op main(): { 7 7 @required tokenRequired: boolean;
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/requestPasswordReset.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.requestPasswordReset { 4 - @doc("Initiate a user account password reset via email.") 4 + /** Initiate a user account password reset via email. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required email: string;
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/reserveSigningKey.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.reserveSigningKey { 4 - @doc("Reserve a repo signing key, for use with account creation. Necessary so that a DID PLC update operation can be constructed during an account migraiton. Public and does not require auth; implemented by PDS. NOTE: this endpoint may change when full account migration is implemented.") 4 + /** Reserve a repo signing key, for use with account creation. Necessary so that a DID PLC update operation can be constructed during an account migraiton. Public and does not require auth; implemented by PDS. NOTE: this endpoint may change when full account migration is implemented. */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("The DID to reserve a key for.") 7 + /** The DID to reserve a key for. */ 8 8 did?: did; 9 9 }): { 10 - @doc("The public key for the reserved signing key, in did:key serialization.") 10 + /** The public key for the reserved signing key, in did:key serialization. */ 11 11 @required 12 12 signingKey: string; 13 13 };
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/resetPassword.tsp
··· 4 4 model ExpiredToken {} 5 5 model InvalidToken {} 6 6 7 - @doc("Reset a user account password using a token.") 7 + /** Reset a user account password using a token. */ 8 8 @procedure 9 9 @errors(ExpiredToken, InvalidToken) 10 10 op main(input: {
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/revokeAppPassword.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.server.revokeAppPassword { 4 - @doc("Revoke an App Password by name.") 4 + /** Revoke an App Password by name. */ 5 5 @procedure 6 6 op main(input: { 7 7 @required name: string;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/updateEmail.tsp
··· 5 5 model InvalidToken {} 6 6 model TokenRequired {} 7 7 8 - @doc("Update an account's email.") 8 + /** Update an account's email. */ 9 9 @procedure 10 10 @errors(ExpiredToken, InvalidToken, TokenRequired) 11 11 op main(input: { 12 12 @required email: string; 13 13 emailAuthFactor?: boolean; 14 14 15 - @doc("Requires a token from com.atproto.sever.requestEmailUpdate if the account's email has been confirmed.") 15 + /** Requires a token from com.atproto.sever.requestEmailUpdate if the account's email has been confirmed. */ 16 16 token?: string; 17 17 }): void; 18 18 }
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getBlob.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getBlob { 4 - @doc("Get a blob associated with a given account. Returns the full blob as originally uploaded. Does not require auth; implemented by PDS.") 4 + /** Get a blob associated with a given account. Returns the full blob as originally uploaded. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("*/*") 7 7 @errors(BlobNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the account.") 9 + /** The DID of the account. */ 10 10 @required 11 11 did: did, 12 12 13 - @doc("The CID of the blob to fetch") 13 + /** The CID of the blob to fetch */ 14 14 @required 15 15 cid: cid 16 16 ): void;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getBlocks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getBlocks { 4 - @doc("Get data blocks from a given repo, by CID. For example, intermediate MST nodes, or records. Does not require auth; implemented by PDS.") 4 + /** Get data blocks from a given repo, by CID. For example, intermediate MST nodes, or records. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(BlockNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getCheckout.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getCheckout { 4 - @doc("DEPRECATED - please use com.atproto.sync.getRepo instead") 4 + /** DEPRECATED - please use com.atproto.sync.getRepo instead */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): void;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getHead.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getHead { 4 - @doc("DEPRECATED - please use com.atproto.sync.getLatestCommit instead") 4 + /** DEPRECATED - please use com.atproto.sync.getLatestCommit instead */ 5 5 @query 6 6 @errors(HeadNotFound) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): {
+11 -4
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getHostStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getHostStatus { 4 - @doc("Returns information about a specified upstream host, as consumed by the server. Implemented by relays.") 4 + /** Returns information about a specified upstream host, as consumed by the server. Implemented by relays. */ 5 5 @query 6 6 @errors(HostNotFound) 7 7 op main( 8 - @doc("Hostname of the host (eg, PDS or relay) being queried.") 8 + /** Hostname of the host (eg, PDS or relay) being queried. */ 9 9 @required 10 10 hostname: string 11 11 ): { 12 12 @required hostname: string; 13 13 14 - @doc("Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor).") 14 + /** Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor). */ 15 15 seq?: integer; 16 16 17 - @doc("Number of accounts on the server which are associated with the upstream host. Note that the upstream may actually have more accounts.") 17 + /** Number of accounts on the server which are associated with the upstream host. Note that the upstream may actually have more accounts. */ 18 18 accountCount?: integer; 19 19 20 20 status?: com.atproto.sync.defs.HostStatus; ··· 22 22 23 23 model HostNotFound {} 24 24 } 25 + 26 + // --- Externals --- 27 + 28 + @external 29 + namespace com.atproto.sync.defs { 30 + model HostStatus { } 31 + }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getLatestCommit.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getLatestCommit { 4 - @doc("Get the current commit CID & revision of the specified repo. Does not require auth.") 4 + /** Get the current commit CID & revision of the specified repo. Does not require auth. */ 5 5 @query 6 6 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): {
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRecord { 4 - @doc("Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.") 4 + /** Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(RecordNotFound, RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12 13 13 @required collection: nsid, 14 14 15 - @doc("Record Key") 15 + /** Record Key */ 16 16 @required 17 17 rkey: recordKey 18 18 ): void;
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getRepo.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRepo { 4 - @doc("Download a repository export as CAR file. Optionally only a 'diff' since a previous revision. Does not require auth; implemented by PDS.") 4 + /** Download a repository export as CAR file. Optionally only a 'diff' since a previous revision. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @encoding("application/vnd.ipld.car") 7 7 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 8 8 op main( 9 - @doc("The DID of the repo.") 9 + /** The DID of the repo. */ 10 10 @required 11 11 did: did, 12 12 13 - @doc("The revision ('rev') of the repo to create a diff from.") 13 + /** The revision ('rev') of the repo to create a diff from. */ 14 14 since?: tid 15 15 ): void; 16 16
+4 -4
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getRepoStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.getRepoStatus { 4 - @doc("Get the hosting status for a repository, on this server. Expected to be implemented by PDS and Relay.") 4 + /** Get the hosting status for a repository, on this server. Expected to be implemented by PDS and Relay. */ 5 5 @query 6 6 @errors(RepoNotFound) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did 11 11 ): { 12 12 @required did: did; 13 13 @required active: boolean; 14 14 15 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 15 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 16 16 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 17 17 18 - @doc("Optional field, the current rev of the repo, if active=true") 18 + /** Optional field, the current rev of the repo, if active=true */ 19 19 rev?: tid; 20 20 }; 21 21
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listBlobs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listBlobs { 4 - @doc("List blob CIDs for an account, since some repo revision. Does not require auth; implemented by PDS.") 4 + /** List blob CIDs for an account, since some repo revision. Does not require auth; implemented by PDS. */ 5 5 @query 6 6 @errors(RepoNotFound, RepoTakendown, RepoSuspended, RepoDeactivated) 7 7 op main( 8 - @doc("The DID of the repo.") 8 + /** The DID of the repo. */ 9 9 @required 10 10 did: did, 11 11 12 - @doc("Optional revision of the repo to list blobs since.") 12 + /** Optional revision of the repo to list blobs since. */ 13 13 since?: tid, 14 14 15 15 @minValue(1)
+11 -4
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listHosts.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listHosts { 4 - @doc("Enumerates upstream hosts (eg, PDS or relay instances) that this service consumes from. Implemented by relays.") 4 + /** Enumerates upstream hosts (eg, PDS or relay instances) that this service consumes from. Implemented by relays. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 12 12 ): { 13 13 cursor?: string; 14 14 15 - @doc("Sort order is not formally specified. Recommended order is by time host was first seen by the server, with oldest first.") 15 + /** Sort order is not formally specified. Recommended order is by time host was first seen by the server, with oldest first. */ 16 16 @required 17 17 hosts: Host[]; 18 18 }; 19 19 20 20 model Host { 21 - @doc("hostname of server; not a URL (no scheme)") 21 + /** hostname of server; not a URL (no scheme) */ 22 22 @required 23 23 hostname: string; 24 24 25 - @doc("Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor).") 25 + /** Recent repo stream event sequence number. May be delayed from actual stream processing (eg, persisted cursor not in-memory cursor). */ 26 26 seq?: integer; 27 27 28 28 accountCount?: integer; 29 29 status?: com.atproto.sync.defs.HostStatus; 30 30 } 31 31 } 32 + 33 + // --- Externals --- 34 + 35 + @external 36 + namespace com.atproto.sync.defs { 37 + model HostStatus { } 38 + }
+3 -3
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listRepos.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listRepos { 4 - @doc("Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay.") 4 + /** Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay. */ 5 5 @query 6 6 op main( 7 7 @minValue(1) ··· 17 17 model Repo { 18 18 @required did: did; 19 19 20 - @doc("Current repo commit CID") 20 + /** Current repo commit CID */ 21 21 @required 22 22 head: cid; 23 23 24 24 @required rev: tid; 25 25 active?: boolean; 26 26 27 - @doc("If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted.") 27 + /** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */ 28 28 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 29 29 } 30 30 }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listReposByCollection.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.listReposByCollection { 4 - @doc("Enumerates all the DIDs which have records with the given collection NSID.") 4 + /** Enumerates all the DIDs which have records with the given collection NSID. */ 5 5 @query 6 6 op main( 7 7 @required collection: nsid, 8 8 9 - @doc("Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists.") 9 + /** Maximum size of response set. Recommend setting a large maximum (1000+) when enumerating large DID lists. */ 10 10 @minValue(1) 11 11 @maxValue(2000) 12 12 limit?: int32 = 500,
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/notifyOfUpdate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.notifyOfUpdate { 4 - @doc("Notify a crawling service of a recent update, and that crawling should resume. Intended use is after a gap between repo stream events caused the crawling service to disconnect. Does not require auth; implemented by Relay. DEPRECATED: just use com.atproto.sync.requestCrawl") 4 + /** Notify a crawling service of a recent update, and that crawling should resume. Intended use is after a gap between repo stream events caused the crawling service to disconnect. Does not require auth; implemented by Relay. DEPRECATED: just use com.atproto.sync.requestCrawl */ 5 5 @procedure 6 6 op main(input: { 7 - @doc("Hostname of the current service (usually a PDS) that is notifying of update.") 7 + /** Hostname of the current service (usually a PDS) that is notifying of update. */ 8 8 @required 9 9 hostname: string; 10 10 }): void;
+2 -2
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/requestCrawl.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.sync.requestCrawl { 4 - @doc("Request a service to persistently crawl hosted repos. Expected use is new PDS instances declaring their existence to Relays. Does not require auth.") 4 + /** Request a service to persistently crawl hosted repos. Expected use is new PDS instances declaring their existence to Relays. Does not require auth. */ 5 5 @procedure 6 6 @errors(HostBanned) 7 7 op main(input: { 8 - @doc("Hostname of the current service (eg, PDS) that is requesting to be crawled.") 8 + /** Hostname of the current service (eg, PDS) that is requesting to be crawled. */ 9 9 @required 10 10 hostname: string; 11 11 }): void;
+30 -30
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/subscribeRepos.tsp
··· 3 3 namespace com.atproto.sync.subscribeRepos { 4 4 @subscription 5 5 @errors(FutureCursor, ConsumerTooSlow) 6 - @doc("Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. See the atproto specifications for details around stream sequencing, repo versioning, CAR diff format, and more. Public and does not require auth; implemented by PDS and Relay.") 6 + /** Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. See the atproto specifications for details around stream sequencing, repo versioning, CAR diff format, and more. Public and does not require auth; implemented by PDS and Relay. */ 7 7 op main( 8 - @doc("The last known event seq number to backfill from.") 8 + /** The last known event seq number to backfill from. */ 9 9 cursor?: integer 10 10 ): (Commit | Sync | Identity | Account | Info | unknown); 11 11 12 12 model FutureCursor {} 13 13 14 - @doc("If the consumer of the stream can not keep up with events, and a backlog gets too large, the server will drop the connection.") 14 + /** If the consumer of the stream can not keep up with events, and a backlog gets too large, the server will drop the connection. */ 15 15 model ConsumerTooSlow {} 16 16 17 - @doc("Represents an update of repository state. Note that empty commits are allowed, which include no repo data changes, but an update to rev and signature.") 17 + /** Represents an update of repository state. Note that empty commits are allowed, which include no repo data changes, but an update to rev and signature. */ 18 18 model Commit { 19 - @doc("The stream sequence number of this message.") 19 + /** The stream sequence number of this message. */ 20 20 @required 21 21 seq: integer; 22 22 23 - @doc("DEPRECATED -- unused") 23 + /** DEPRECATED -- unused */ 24 24 @required 25 25 rebase: boolean; 26 26 27 - @doc("DEPRECATED -- replaced by #sync event and data limits. Indicates that this commit contained too many ops, or data size was too large. Consumers will need to make a separate request to get missing data.") 27 + /** DEPRECATED -- replaced by #sync event and data limits. Indicates that this commit contained too many ops, or data size was too large. Consumers will need to make a separate request to get missing data. */ 28 28 @required 29 29 tooBig: boolean; 30 30 31 - @doc("The repo this event comes from. Note that all other message types name this field 'did'.") 31 + /** The repo this event comes from. Note that all other message types name this field 'did'. */ 32 32 @required 33 33 repo: did; 34 34 35 - @doc("Repo commit object CID.") 35 + /** Repo commit object CID. */ 36 36 @required 37 37 commit: cidLink; 38 38 39 - @doc("The rev of the emitted commit. Note that this information is also in the commit object included in blocks, unless this is a tooBig event.") 39 + /** The rev of the emitted commit. Note that this information is also in the commit object included in blocks, unless this is a tooBig event. */ 40 40 @required 41 41 rev: tid; 42 42 43 - @doc("The rev of the last emitted commit from this repo (if any).") 43 + /** The rev of the last emitted commit from this repo (if any). */ 44 44 @required 45 45 since: tid | null; 46 46 47 - @doc("CAR file containing relevant blocks, as a diff since the previous repo state. The commit must be included as a block, and the commit block CID must be the first entry in the CAR header 'roots' list.") 47 + /** CAR file containing relevant blocks, as a diff since the previous repo state. The commit must be included as a block, and the commit block CID must be the first entry in the CAR header 'roots' list. */ 48 48 @maxBytes(2000000) 49 49 @required 50 50 blocks: bytes; 51 51 52 52 @maxItems(200) 53 53 @required 54 - @doc("List of repo mutation operations in this commit (eg, records created, updated, or deleted).") 54 + /** List of repo mutation operations in this commit (eg, records created, updated, or deleted). */ 55 55 ops: RepoOp[]; 56 56 57 - @doc("DEPRECATED -- will soon always be empty. List of new blobs (by CID) referenced by records in this commit.") 57 + /** DEPRECATED -- will soon always be empty. List of new blobs (by CID) referenced by records in this commit. */ 58 58 @required blobs: cidLink[]; 59 59 60 - @doc("The root CID of the MST tree for the previous commit from this repo (indicated by the 'since' revision field in this message). Corresponds to the 'data' field in the repo commit object. NOTE: this field is effectively required for the 'inductive' version of firehose.") 60 + /** The root CID of the MST tree for the previous commit from this repo (indicated by the 'since' revision field in this message). Corresponds to the 'data' field in the repo commit object. NOTE: this field is effectively required for the 'inductive' version of firehose. */ 61 61 prevData?: cidLink; 62 62 63 - @doc("Timestamp of when this message was originally broadcast.") 63 + /** Timestamp of when this message was originally broadcast. */ 64 64 @required 65 65 time: datetime; 66 66 } 67 67 68 - @doc("Updates the repo to a new state, without necessarily including that state on the firehose. Used to recover from broken commit streams, data loss incidents, or in situations where upstream host does not know recent state of the repository.") 68 + /** Updates the repo to a new state, without necessarily including that state on the firehose. Used to recover from broken commit streams, data loss incidents, or in situations where upstream host does not know recent state of the repository. */ 69 69 model Sync { 70 - @doc("The stream sequence number of this message.") 70 + /** The stream sequence number of this message. */ 71 71 @required 72 72 seq: integer; 73 73 74 - @doc("The account this repo event corresponds to. Must match that in the commit object.") 74 + /** The account this repo event corresponds to. Must match that in the commit object. */ 75 75 @required 76 76 did: did; 77 77 78 - @doc("CAR file containing the commit, as a block. The CAR header must include the commit block CID as the first 'root'.") 78 + /** CAR file containing the commit, as a block. The CAR header must include the commit block CID as the first 'root'. */ 79 79 @maxBytes(10000) 80 80 @required 81 81 blocks: bytes; 82 82 83 - @doc("The rev of the commit. This value must match that in the commit object.") 83 + /** The rev of the commit. This value must match that in the commit object. */ 84 84 @required 85 85 rev: string; 86 86 87 - @doc("Timestamp of when this message was originally broadcast.") 87 + /** Timestamp of when this message was originally broadcast. */ 88 88 @required 89 89 time: datetime; 90 90 } 91 91 92 - @doc("Represents a change to an account's identity. Could be an updated handle, signing key, or pds hosting endpoint. Serves as a prod to all downstream services to refresh their identity cache.") 92 + /** Represents a change to an account's identity. Could be an updated handle, signing key, or pds hosting endpoint. Serves as a prod to all downstream services to refresh their identity cache. */ 93 93 model Identity { 94 94 @required seq: integer; 95 95 @required did: did; 96 96 @required time: datetime; 97 97 98 - @doc("The current handle for the account, or 'handle.invalid' if validation fails. This field is optional, might have been validated or passed-through from an upstream source. Semantics and behaviors for PDS vs Relay may evolve in the future; see atproto specs for more details.") 98 + /** The current handle for the account, or 'handle.invalid' if validation fails. This field is optional, might have been validated or passed-through from an upstream source. Semantics and behaviors for PDS vs Relay may evolve in the future; see atproto specs for more details. */ 99 99 handle?: handle; 100 100 } 101 101 102 - @doc("Represents a change to an account's status on a host (eg, PDS or Relay). The semantics of this event are that the status is at the host which emitted the event, not necessarily that at the currently active PDS. Eg, a Relay takedown would emit a takedown with active=false, even if the PDS is still active.") 102 + /** Represents a change to an account's status on a host (eg, PDS or Relay). The semantics of this event are that the status is at the host which emitted the event, not necessarily that at the currently active PDS. Eg, a Relay takedown would emit a takedown with active=false, even if the PDS is still active. */ 103 103 model Account { 104 104 @required seq: integer; 105 105 @required did: did; 106 106 @required time: datetime; 107 107 108 - @doc("Indicates that the account has a repository which can be fetched from the host that emitted this event.") 108 + /** Indicates that the account has a repository which can be fetched from the host that emitted this event. */ 109 109 @required 110 110 active: boolean; 111 111 112 - @doc("If active=false, this optional field indicates a reason for why the account is not active.") 112 + /** If active=false, this optional field indicates a reason for why the account is not active. */ 113 113 status?: "takendown" | "suspended" | "deleted" | "deactivated" | "desynchronized" | "throttled" | string; 114 114 } 115 115 ··· 118 118 message?: string; 119 119 } 120 120 121 - @doc("A repo operation, ie a mutation of a single record.") 121 + /** A repo operation, ie a mutation of a single record. */ 122 122 model RepoOp { 123 123 @required action: "create" | "update" | "delete" | string; 124 124 @required path: string; 125 125 126 - @doc("For creates and updates, the new record CID. For deletions, null.") 126 + /** For creates and updates, the new record CID. For deletions, null. */ 127 127 @required 128 128 cid: cidLink | null; 129 129 130 - @doc("For updates and deletes, the previous record CID (required for inductive firehose). For creations, field should not be defined.") 130 + /** For updates and deletes, the previous record CID (required for inductive firehose). For creations, field should not be defined. */ 131 131 prev?: cidLink; 132 132 } 133 133 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/addReservedHandle.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.addReservedHandle { 4 - @doc("Add a handle to the set of reserved handles.") 4 + /** Add a handle to the set of reserved handles. */ 5 5 @procedure 6 6 op main( 7 7 input: {
+11 -11
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/checkHandleAvailability.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.checkHandleAvailability { 4 - @doc("An invalid email was provided.") 4 + /** An invalid email was provided. */ 5 5 model InvalidEmail {} 6 6 7 - @doc("Indicates the provided handle is available.") 7 + /** Indicates the provided handle is available. */ 8 8 model ResultAvailable {} 9 9 10 - @doc("Indicates the provided handle is unavailable and gives suggestions of available handles.") 10 + /** Indicates the provided handle is unavailable and gives suggestions of available handles. */ 11 11 model ResultUnavailable { 12 - @doc("List of suggested handles based on the provided inputs.") 12 + /** List of suggested handles based on the provided inputs. */ 13 13 @required 14 14 suggestions: Suggestion[]; 15 15 } ··· 17 17 model Suggestion { 18 18 @required handle: handle; 19 19 20 - @doc("Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics.") 20 + /** Method used to build this suggestion. Should be considered opaque to clients. Can be used for metrics. */ 21 21 @required 22 22 method: string; 23 23 } 24 24 25 - @doc("Checks whether the provided handle is available. If the handle is not available, available suggestions will be returned. Optional inputs will be used to generate suggestions.") 25 + /** Checks whether the provided handle is available. If the handle is not available, available suggestions will be returned. Optional inputs will be used to generate suggestions. */ 26 26 @query 27 27 @errors(InvalidEmail) 28 28 op main( 29 - @doc("Tentative handle. Will be checked for availability or used to build handle suggestions.") 30 - handle: handle, 29 + /** Tentative handle. Will be checked for availability or used to build handle suggestions. */ 30 + @required handle: handle, 31 31 32 - @doc("User-provided email. Might be used to build handle suggestions.") 32 + /** User-provided email. Might be used to build handle suggestions. */ 33 33 email?: string, 34 34 35 - @doc("User-provided birth date. Might be used to build handle suggestions.") 35 + /** User-provided birth date. Might be used to build handle suggestions. */ 36 36 birthDate?: datetime 37 37 ): { 38 - @doc("Echo of the input handle.") 38 + /** Echo of the input handle. */ 39 39 @required 40 40 handle: handle; 41 41
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/checkSignupQueue.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.checkSignupQueue { 4 - @doc("Check accounts location in signup queue.") 4 + /** Check accounts location in signup queue. */ 5 5 @query 6 6 op main(): { 7 7 @required activated: boolean;
+5 -5
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/dereferenceScope.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.dereferenceScope { 4 - @doc("An invalid scope reference was provided.") 4 + /** An invalid scope reference was provided. */ 5 5 model InvalidScopeReference {} 6 6 7 - @doc("Allows finding the oauth permission scope from a reference") 7 + /** Allows finding the oauth permission scope from a reference */ 8 8 @query 9 9 @errors(InvalidScopeReference) 10 10 op main( 11 - @doc("The scope reference (starts with 'ref:')") 12 - scope: string 11 + /** The scope reference (starts with 'ref:') */ 12 + @required scope: string 13 13 ): { 14 - @doc("The full oauth permission scope") 14 + /** The full oauth permission scope */ 15 15 @required 16 16 scope: string; 17 17 };
+8 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/fetchLabels.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.fetchLabels { 4 - @doc("DEPRECATED: use queryLabels or subscribeLabels instead -- Fetch all labels from a labeler created after a certain date.") 4 + /** DEPRECATED: use queryLabels or subscribeLabels instead -- Fetch all labels from a labeler created after a certain date. */ 5 5 @query 6 6 op main( 7 7 since?: integer, ··· 13 13 @required labels: com.atproto.label.defs.Label[]; 14 14 }; 15 15 } 16 + 17 + // --- Externals --- 18 + 19 + @external 20 + namespace com.atproto.label.defs { 21 + model Label { } 22 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/requestPhoneVerification.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.requestPhoneVerification { 4 - @doc("Request a verification code to be sent to the supplied phone number") 4 + /** Request a verification code to be sent to the supplied phone number */ 5 5 @procedure 6 6 op main( 7 7 input: {
+1 -1
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/revokeAccountCredentials.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.atproto.temp.revokeAccountCredentials { 4 - @doc("Revoke sessions, password, and app passwords associated with account. May be resolved by a password reset.") 4 + /** Revoke sessions, password, and app passwords associated with account. May be resolved by a password reset. */ 5 5 @procedure 6 6 op main( 7 7 input: {
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/blockquote.tsp
··· 6 6 facets?: `pub`.leaflet.richtext.facet.Main[]; 7 7 } 8 8 } 9 + 10 + // --- Externals --- 11 + 12 + @external 13 + namespace `pub`.leaflet.richtext.facet { 14 + model Main { } 15 + }
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/bskyPost.tsp
··· 5 5 @required postRef: com.atproto.repo.strongRef.Main; 6 6 } 7 7 } 8 + 9 + // --- Externals --- 10 + 11 + @external 12 + namespace com.atproto.repo.strongRef { 13 + model Main { } 14 + }
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/header.tsp
··· 10 10 facets?: `pub`.leaflet.richtext.facet.Main[]; 11 11 } 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace `pub`.leaflet.richtext.facet { 18 + model Main { } 19 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/image.tsp
··· 4 4 model Main { 5 5 @required image: Blob<#["image/*"], 1000000>; 6 6 7 - @doc("Alt text description of the image, for accessibility.") 7 + /** Alt text description of the image, for accessibility. */ 8 8 alt?: string; 9 9 10 10 @required aspectRatio: AspectRatio;
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/text.tsp
··· 6 6 facets?: `pub`.leaflet.richtext.facet.Main[]; 7 7 } 8 8 } 9 + 10 + // --- Externals --- 11 + 12 + @external 13 + namespace `pub`.leaflet.richtext.facet { 14 + model Main { } 15 + }
+17
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/unorderedList.tsp
··· 10 10 children?: ListItem[]; 11 11 } 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace `pub`.leaflet.blocks.text { 18 + model Main { } 19 + } 20 + 21 + @external 22 + namespace `pub`.leaflet.blocks.header { 23 + model Main { } 24 + } 25 + 26 + @external 27 + namespace `pub`.leaflet.blocks.image { 28 + model Main { } 29 + }
+14 -2
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/comment.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A lexicon for comments on documents") 3 + /** A lexicon for comments on documents */ 4 4 namespace `pub`.leaflet.comment { 5 - @doc("Record containing a comment") 5 + /** Record containing a comment */ 6 6 @rec("tid") 7 7 model Main { 8 8 @required subject: atUri; ··· 26 26 @required parent: atUri; 27 27 } 28 28 } 29 + 30 + // --- Externals --- 31 + 32 + @external 33 + namespace `pub`.leaflet.richtext.facet { 34 + model Main { } 35 + } 36 + 37 + @external 38 + namespace `pub`.leaflet.pages.linearDocument { 39 + model Quote { } 40 + }
+14 -2
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/document.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("A lexicon for long form rich media documents") 3 + /** A lexicon for long form rich media documents */ 4 4 namespace `pub`.leaflet.document { 5 - @doc("Record containing a document") 5 + /** Record containing a document */ 6 6 @rec("tid") 7 7 model Main { 8 8 @maxGraphemes(128) ··· 23 23 @required pages: (`pub`.leaflet.pages.linearDocument.Main | unknown)[]; 24 24 } 25 25 } 26 + 27 + // --- Externals --- 28 + 29 + @external 30 + namespace com.atproto.repo.strongRef { 31 + model Main { } 32 + } 33 + 34 + @external 35 + namespace `pub`.leaflet.pages.linearDocument { 36 + model Main { } 37 + }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/graph/subscription.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace `pub`.leaflet.graph.subscription { 4 - @doc("Record declaring a subscription to a publication") 4 + /** Record declaring a subscription to a publication */ 5 5 @rec("tid") 6 6 model Main { 7 7 @required publication: atUri;
+57
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/pages/linearDocument.tsp
··· 43 43 @required offset: integer; 44 44 } 45 45 } 46 + 47 + // --- Externals --- 48 + 49 + @external 50 + namespace `pub`.leaflet.blocks.iframe { 51 + model Main { } 52 + } 53 + 54 + @external 55 + namespace `pub`.leaflet.blocks.text { 56 + model Main { } 57 + } 58 + 59 + @external 60 + namespace `pub`.leaflet.blocks.blockquote { 61 + model Main { } 62 + } 63 + 64 + @external 65 + namespace `pub`.leaflet.blocks.header { 66 + model Main { } 67 + } 68 + 69 + @external 70 + namespace `pub`.leaflet.blocks.image { 71 + model Main { } 72 + } 73 + 74 + @external 75 + namespace `pub`.leaflet.blocks.unorderedList { 76 + model Main { } 77 + } 78 + 79 + @external 80 + namespace `pub`.leaflet.blocks.website { 81 + model Main { } 82 + } 83 + 84 + @external 85 + namespace `pub`.leaflet.blocks.math { 86 + model Main { } 87 + } 88 + 89 + @external 90 + namespace `pub`.leaflet.blocks.code { 91 + model Main { } 92 + } 93 + 94 + @external 95 + namespace `pub`.leaflet.blocks.horizontalRule { 96 + model Main { } 97 + } 98 + 99 + @external 100 + namespace `pub`.leaflet.blocks.bskyPost { 101 + model Main { } 102 + }
+14 -1
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/publication.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace `pub`.leaflet.publication { 4 - @doc("Record declaring a publication") 4 + /** Record declaring a publication */ 5 5 @rec("tid") 6 6 model Main { 7 7 @maxLength(2000) ··· 34 34 accentText?: (`pub`.leaflet.theme.color.Rgba | `pub`.leaflet.theme.color.Rgb | unknown); 35 35 } 36 36 } 37 + 38 + // --- Externals --- 39 + 40 + @external 41 + namespace `pub`.leaflet.theme.color { 42 + model Rgba { } 43 + model Rgb { } 44 + } 45 + 46 + @external 47 + namespace `pub`.leaflet.theme.backgroundImage { 48 + model Main { } 49 + }
+10 -10
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/richtext/facet.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace `pub`.leaflet.richtext.facet { 4 - @doc("Annotation of a sub-string within rich text.") 4 + /** Annotation of a sub-string within rich text. */ 5 5 model Main { 6 6 @required index: ByteSlice; 7 7 @required features: (Link | Code | Highlight | Underline | Strikethrough | Id | Bold | Italic | unknown)[]; 8 8 } 9 9 10 - @doc("Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.") 10 + /** Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets. */ 11 11 model ByteSlice { 12 12 @minValue(0) 13 13 @required ··· 18 18 byteEnd: integer; 19 19 } 20 20 21 - @doc("Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.") 21 + /** Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL. */ 22 22 model Link { 23 23 @required uri: uri; 24 24 } 25 25 26 - @doc("Facet feature for inline code.") 26 + /** Facet feature for inline code. */ 27 27 model Code {} 28 28 29 - @doc("Facet feature for highlighted text.") 29 + /** Facet feature for highlighted text. */ 30 30 model Highlight {} 31 31 32 - @doc("Facet feature for underline markup") 32 + /** Facet feature for underline markup */ 33 33 model Underline {} 34 34 35 - @doc("Facet feature for strikethrough markup") 35 + /** Facet feature for strikethrough markup */ 36 36 model Strikethrough {} 37 37 38 - @doc("Facet feature for an identifier. Used for linking to a segment") 38 + /** Facet feature for an identifier. Used for linking to a segment */ 39 39 model Id { 40 40 id?: string; 41 41 } 42 42 43 - @doc("Facet feature for bold text") 43 + /** Facet feature for bold text */ 44 44 model Bold {} 45 45 46 - @doc("Facet feature for italic text") 46 + /** Facet feature for italic text */ 47 47 model Italic {} 48 48 }
+7 -7
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/actor/profile.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.actor.profile { 4 - @doc("A declaration of a Tangled account profile.") 4 + /** A declaration of a Tangled account profile. */ 5 5 @rec("literal:self") 6 6 model Main { 7 - @doc("Free-form profile description text.") 7 + /** Free-form profile description text. */ 8 8 @maxGraphemes(256) 9 9 @maxLength(2560) 10 10 description?: string; ··· 17 17 @maxItems(2) 18 18 stats?: StatValue[]; 19 19 20 - @doc("Include link to this account on Bluesky.") 20 + /** Include link to this account on Bluesky. */ 21 21 @required 22 22 bluesky: boolean; 23 23 24 - @doc("Free-form location text.") 24 + /** Free-form location text. */ 25 25 @maxGraphemes(40) 26 26 @maxLength(400) 27 27 location?: string; 28 28 29 - @doc("Any ATURI, it is up to appviews to validate these fields.") 29 + /** Any ATURI, it is up to appviews to validate these fields. */ 30 30 @minItems(0) 31 31 @maxItems(6) 32 32 pinnedRepositories?: atUri[]; 33 33 } 34 34 35 - @doc("Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.") 35 + /** Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too. */ 36 36 @inline 37 37 scalar LinkItem extends uri; 38 38 39 - @doc("Vanity stats.") 39 + /** Vanity stats. */ 40 40 @closed 41 41 @inline 42 42 union StatValue {
+7 -7
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/git/refUpdate.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.git.refUpdate { 4 - @doc("An update to a git repository, emitted by knots.") 4 + /** An update to a git repository, emitted by knots. */ 5 5 @rec("tid") 6 6 model Main { 7 - @doc("Ref being updated") 7 + /** Ref being updated */ 8 8 @maxGraphemes(256) 9 9 @maxLength(2560) 10 10 @required 11 11 ref: string; 12 12 13 - @doc("did of the user that pushed this ref") 13 + /** did of the user that pushed this ref */ 14 14 @required 15 15 committerDid: did; 16 16 17 - @doc("did of the owner of the repo") 17 + /** did of the owner of the repo */ 18 18 @required 19 19 repoDid: did; 20 20 21 - @doc("name of the repo") 21 + /** name of the repo */ 22 22 @required 23 23 repoName: string; 24 24 25 - @doc("old SHA of this ref") 25 + /** old SHA of this ref */ 26 26 @minLength(40) 27 27 @maxLength(40) 28 28 @required 29 29 oldSha: string; 30 30 31 - @doc("new SHA of this ref") 31 + /** new SHA of this ref */ 32 32 @minLength(40) 33 33 @maxLength(40) 34 34 @required
+8 -8
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/knot/listKeys.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.knot.listKeys { 4 - @doc("List all public keys stored in the knot server") 4 + /** List all public keys stored in the knot server */ 5 5 @query 6 6 @errors(InternalServerError) 7 7 op main( 8 - @doc("Maximum number of keys to return") 8 + /** Maximum number of keys to return */ 9 9 @minValue(1) 10 10 @maxValue(1000) 11 11 limit?: int32 = 100, 12 12 13 - @doc("Pagination cursor") 13 + /** Pagination cursor */ 14 14 cursor?: string 15 15 ): { 16 16 @required keys: PublicKey[]; 17 17 18 - @doc("Pagination cursor for next page") 18 + /** Pagination cursor for next page */ 19 19 cursor?: string; 20 20 }; 21 21 22 22 model PublicKey { 23 - @doc("DID associated with the public key") 23 + /** DID associated with the public key */ 24 24 @required 25 25 did: did; 26 26 27 - @doc("Public key contents") 27 + /** Public key contents */ 28 28 @maxLength(4096) 29 29 @required 30 30 key: string; 31 31 32 - @doc("Key upload timestamp") 32 + /** Key upload timestamp */ 33 33 @required 34 34 createdAt: datetime; 35 35 } 36 36 37 - @doc("Failed to retrieve public keys") 37 + /** Failed to retrieve public keys */ 38 38 model InternalServerError {} 39 39 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/knot/member.tsp
··· 5 5 model Main { 6 6 @required subject: did; 7 7 8 - @doc("domain that this member now belongs to") 8 + /** domain that this member now belongs to */ 9 9 @required 10 10 domain: string; 11 11
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/knot/version.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.knot.version { 4 - @doc("Get the version of a knot") 4 + /** Get the version of a knot */ 5 5 @query 6 6 op main(): { 7 7 @required version: string;
+8 -8
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/label/definition.tsp
··· 3 3 namespace sh.tangled.label.definition { 4 4 @rec("any") 5 5 model Main { 6 - @doc("The display name of this label.") 6 + /** The display name of this label. */ 7 7 @minGraphemes(1) 8 8 @maxGraphemes(40) 9 9 @required 10 10 name: string; 11 11 12 - @doc("The type definition of this label. Appviews may allow sorting for certain types.") 12 + /** The type definition of this label. Appviews may allow sorting for certain types. */ 13 13 @required 14 14 valueType: ValueType; 15 15 16 - @doc("The areas of the repo this label may apply to, eg.: sh.tangled.repo.issue. Appviews may choose to respect this.") 16 + /** The areas of the repo this label may apply to, eg.: sh.tangled.repo.issue. Appviews may choose to respect this. */ 17 17 @required 18 18 scope: nsid[]; 19 19 20 - @doc("The hex value for the background color for the label. Appviews may choose to respect this.") 20 + /** The hex value for the background color for the label. Appviews may choose to respect this. */ 21 21 color?: string; 22 22 23 23 @required createdAt: datetime; 24 24 25 - @doc("Whether this label can be repeated for a given entity, eg.: [reviewer:foo, reviewer:bar]") 25 + /** Whether this label can be repeated for a given entity, eg.: [reviewer:foo, reviewer:bar] */ 26 26 multiple?: boolean; 27 27 } 28 28 ··· 44 44 } 45 45 46 46 model ValueType { 47 - @doc("The concrete type of this label's value.") 47 + /** The concrete type of this label's value. */ 48 48 @required 49 49 type: ConcreteType; 50 50 51 - @doc("An optional constraint that can be applied on string concrete types.") 51 + /** An optional constraint that can be applied on string concrete types. */ 52 52 @required 53 53 format: FormatType; 54 54 55 - @doc("Closed set of values that this label can take.") 55 + /** Closed set of values that this label can take. */ 56 56 `enum`?: string[]; 57 57 } 58 58 }
+29
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/label/op.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.label.`op` { 4 + @rec("tid") 5 + model Main { 6 + @doc("The subject (task, pull or discussion) of this label. Appviews may apply a `scope` check and refuse this op.") 7 + @required 8 + subject: atUri; 9 + 10 + @required 11 + add: Operand[]; 12 + 13 + @required 14 + delete: Operand[]; 15 + 16 + @required 17 + performedAt: datetime; 18 + } 19 + 20 + model Operand { 21 + @doc("ATURI to the label definition") 22 + @required 23 + key: atUri; 24 + 25 + @doc("Stringified value of the label. This is first unstringed by appviews and then interpreted as a concrete value.") 26 + @required 27 + value: string; 28 + } 29 + }
-24
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/label.op.tsp
··· 1 - import "@typelex/emitter"; 2 - 3 - namespace sh.tangled.label.`op` { 4 - @rec("tid") 5 - model Main { 6 - @doc("The subject (task, pull or discussion) of this label. Appviews may apply a `scope` check and refuse this op.") 7 - @required 8 - subject: atUri; 9 - 10 - @required performedAt: datetime; 11 - @required add: Operand[]; 12 - @required delete: Operand[]; 13 - } 14 - 15 - model Operand { 16 - @doc("ATURI to the label definition") 17 - @required 18 - key: atUri; 19 - 20 - @doc("Stringified value of the label. This is first unstringed by appviews and then interpreted as a concrete value.") 21 - @required 22 - value: string; 23 - } 24 - }
+2 -2
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/owner.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.owner { 4 - @doc("Get the owner of a service") 4 + /** Get the owner of a service */ 5 5 @query 6 6 @errors(OwnerNotFound) 7 7 op main(): { 8 8 @required owner: did; 9 9 }; 10 10 11 - @doc("Owner is not set for this service") 11 + /** Owner is not set for this service */ 12 12 model OwnerNotFound {} 13 13 }
+6 -6
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/pipeline/status.tsp
··· 14 14 15 15 @rec("tid") 16 16 model Main { 17 - @doc("ATURI of the pipeline") 17 + /** ATURI of the pipeline */ 18 18 @required 19 19 pipeline: atUri; 20 20 21 - @doc("name of the workflow within this pipeline") 21 + /** name of the workflow within this pipeline */ 22 22 @required 23 23 workflow: atUri; 24 24 25 - @doc("status of the workflow") 25 + /** status of the workflow */ 26 26 @required 27 27 status: WorkflowStatus; 28 28 29 - @doc("time of creation of this status update") 29 + /** time of creation of this status update */ 30 30 @required 31 31 createdAt: datetime; 32 32 33 - @doc("error message if failed") 33 + /** error message if failed */ 34 34 error?: string; 35 35 36 - @doc("exit code if failed") 36 + /** exit code if failed */ 37 37 exitCode?: integer; 38 38 } 39 39 }
+3 -4
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/pipeline.tsp
··· 18 18 model TriggerMetadata { 19 19 @required kind: TriggerKind; 20 20 @required repo: TriggerRepo; 21 - 22 21 push?: PushTriggerData; 23 22 pullRequest?: PullRequestTriggerData; 24 23 manual?: ManualTriggerData; ··· 34 33 model PushTriggerData { 35 34 @required ref: string; 36 35 36 + @required 37 37 @minLength(40) 38 38 @maxLength(40) 39 - @required 40 39 newSha: string; 41 40 41 + @required 42 42 @minLength(40) 43 43 @maxLength(40) 44 - @required 45 44 oldSha: string; 46 45 } 47 46 ··· 49 48 @required sourceBranch: string; 50 49 @required targetBranch: string; 51 50 51 + @required 52 52 @minLength(40) 53 53 @maxLength(40) 54 - @required 55 54 sourceSha: string; 56 55 57 56 @required action: string;
+3 -3
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/publicKey.tsp
··· 3 3 namespace sh.tangled.publicKey { 4 4 @rec("tid") 5 5 model Main { 6 - @doc("public key contents") 6 + /** public key contents */ 7 7 @maxLength(4096) 8 8 @required 9 9 key: string; 10 10 11 - @doc("human-readable name for this key") 11 + /** human-readable name for this key */ 12 12 @required 13 13 name: string; 14 14 15 - @doc("key upload timestamp") 15 + /** key upload timestamp */ 16 16 @required 17 17 createdAt: datetime; 18 18 }
+20
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/addSecret.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.addSecret { 4 + @procedure 5 + @doc("Add a CI secret") 6 + op main(input: { 7 + @required 8 + repo: atUri; 9 + 10 + @required 11 + @maxLength(50) 12 + @minLength(1) 13 + key: string; 14 + 15 + @required 16 + @maxLength(200) 17 + @minLength(1) 18 + value: string; 19 + }): void; 20 + }
+44
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/archive.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.archive { 4 + @query 5 + @encoding("*/*") 6 + @errors(RepoNotFound, RefNotFound, InvalidRequest, ArchiveError) 7 + op main( 8 + @doc("Repository identifier in format 'did:plc:.../repoName'") 9 + @required 10 + repo: string, 11 + 12 + @doc("Git reference (branch, tag, or commit SHA)") 13 + @required 14 + ref: string, 15 + 16 + @doc("Archive format") 17 + format?: ArchiveFormat = "tar.gz", 18 + 19 + @doc("Prefix for files in the archive") 20 + prefix?: string 21 + ): never; 22 + 23 + @closed 24 + @inline 25 + union ArchiveFormat { 26 + "tar", 27 + "zip", 28 + "tar.gz", 29 + "tar.bz2", 30 + "tar.xz" 31 + } 32 + 33 + @doc("Repository not found or access denied") 34 + model RepoNotFound {} 35 + 36 + @doc("Git reference not found") 37 + model RefNotFound {} 38 + 39 + @doc("Invalid request parameters") 40 + model InvalidRequest {} 41 + 42 + @doc("Failed to create archive") 43 + model ArchiveError {} 44 + }
+28
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/artifact.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.artifact { 4 + @rec("tid") 5 + model Main { 6 + @doc("name of the artifact") 7 + @required 8 + name: string; 9 + 10 + @doc("repo that this artifact is being uploaded to") 11 + @required 12 + repo: atUri; 13 + 14 + @doc("hash of the tag object that this artifact is attached to (only annotated tags are supported)") 15 + @required 16 + @minBytes(20) 17 + @maxBytes(20) 18 + tag: bytes; 19 + 20 + @doc("time of creation of this artifact") 21 + @required 22 + createdAt: datetime; 23 + 24 + @doc("the artifact") 25 + @required 26 + artifact: Blob<#["*/*"], 52428800>; 27 + } 28 + }
+100
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/blob.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.blob { 4 + @query 5 + @errors(RepoNotFound, RefNotFound, FileNotFound, InvalidRequest) 6 + op main( 7 + @doc("Repository identifier in format 'did:plc:.../repoName'") 8 + @required 9 + repo: string, 10 + 11 + @doc("Git reference (branch, tag, or commit SHA)") 12 + @required 13 + ref: string, 14 + 15 + @doc("Path to the file within the repository") 16 + @required 17 + path: string, 18 + 19 + @doc("Return raw file content instead of JSON response") 20 + raw?: boolean = false 21 + ): { 22 + @doc("The git reference used") 23 + @required 24 + ref: string; 25 + 26 + @doc("The file path") 27 + @required 28 + path: string; 29 + 30 + @doc("File content (base64 encoded for binary files)") 31 + @required 32 + content: string; 33 + 34 + @doc("Content encoding") 35 + encoding?: ContentEncoding; 36 + 37 + @doc("File size in bytes") 38 + size?: integer; 39 + 40 + @doc("Whether the file is binary") 41 + isBinary?: boolean; 42 + 43 + @doc("MIME type of the file") 44 + mimeType?: string; 45 + 46 + lastCommit?: LastCommit; 47 + }; 48 + 49 + @closed 50 + @inline 51 + union ContentEncoding { 52 + "utf-8", 53 + "base64" 54 + } 55 + 56 + model LastCommit { 57 + @doc("Commit hash") 58 + @required 59 + hash: string; 60 + 61 + @doc("Short commit hash") 62 + shortHash?: string; 63 + 64 + @doc("Commit message") 65 + @required 66 + message: string; 67 + 68 + author?: Signature; 69 + 70 + @doc("Commit timestamp") 71 + @required 72 + when: datetime; 73 + } 74 + 75 + model Signature { 76 + @doc("Author name") 77 + @required 78 + name: string; 79 + 80 + @doc("Author email") 81 + @required 82 + email: string; 83 + 84 + @doc("Author timestamp") 85 + @required 86 + when: datetime; 87 + } 88 + 89 + @doc("Repository not found or access denied") 90 + model RepoNotFound {} 91 + 92 + @doc("Git reference not found") 93 + model RefNotFound {} 94 + 95 + @doc("File not found at the specified path") 96 + model FileNotFound {} 97 + 98 + @doc("Invalid request parameters") 99 + model InvalidRequest {} 100 + }
+61
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/branch.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.branch { 4 + @query 5 + @errors(RepoNotFound, BranchNotFound, InvalidRequest) 6 + op main( 7 + @doc("Repository identifier in format 'did:plc:.../repoName'") 8 + @required 9 + repo: string, 10 + 11 + @doc("Branch name to get information for") 12 + @required 13 + name: string 14 + ): { 15 + @doc("Branch name") 16 + @required 17 + name: string; 18 + 19 + @doc("Latest commit hash on this branch") 20 + @required 21 + hash: string; 22 + 23 + @doc("Short commit hash") 24 + shortHash?: string; 25 + 26 + @doc("Timestamp of latest commit") 27 + @required 28 + when: datetime; 29 + 30 + @doc("Latest commit message") 31 + message?: string; 32 + 33 + author?: Signature; 34 + 35 + @doc("Whether this is the default branch") 36 + isDefault?: boolean; 37 + }; 38 + 39 + model Signature { 40 + @doc("Author name") 41 + @required 42 + name: string; 43 + 44 + @doc("Author email") 45 + @required 46 + email: string; 47 + 48 + @doc("Author timestamp") 49 + @required 50 + when: datetime; 51 + } 52 + 53 + @doc("Repository not found or access denied") 54 + model RepoNotFound {} 55 + 56 + @doc("Branch not found") 57 + model BranchNotFound {} 58 + 59 + @doc("Invalid request parameters") 60 + model InvalidRequest {} 61 + }
+26
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/branches.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.branches { 4 + @query 5 + @encoding("*/*") 6 + @errors(RepoNotFound, InvalidRequest) 7 + op main( 8 + @doc("Repository identifier in format 'did:plc:.../repoName'") 9 + @required 10 + repo: string, 11 + 12 + @doc("Maximum number of branches to return") 13 + @minValue(1) 14 + @maxValue(100) 15 + limit?: integer = 50, 16 + 17 + @doc("Pagination cursor") 18 + cursor?: string 19 + ): never; 20 + 21 + @doc("Repository not found or access denied") 22 + model RepoNotFound {} 23 + 24 + @doc("Invalid request parameters") 25 + model InvalidRequest {} 26 + }
+16
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/collaborator.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.collaborator { 4 + @rec("tid") 5 + model Main { 6 + @required 7 + subject: did; 8 + 9 + @doc("repo to add this user to") 10 + @required 11 + repo: atUri; 12 + 13 + @required 14 + createdAt: datetime; 15 + } 16 + }
+32
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/compare.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.compare { 4 + @query 5 + @encoding("*/*") 6 + @errors(RepoNotFound, RevisionNotFound, InvalidRequest, CompareError) 7 + op main( 8 + @doc("Repository identifier in format 'did:plc:.../repoName'") 9 + @required 10 + repo: string, 11 + 12 + @doc("First revision (commit, branch, or tag)") 13 + @required 14 + rev1: string, 15 + 16 + @doc("Second revision (commit, branch, or tag)") 17 + @required 18 + rev2: string 19 + ): never; 20 + 21 + @doc("Repository not found or access denied") 22 + model RepoNotFound {} 23 + 24 + @doc("One or both revisions not found") 25 + model RevisionNotFound {} 26 + 27 + @doc("Invalid request parameters") 28 + model InvalidRequest {} 29 + 30 + @doc("Failed to compare revisions") 31 + model CompareError {} 32 + }
+17
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/create.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.create { 4 + @procedure 5 + @doc("Create a new repository") 6 + op main(input: { 7 + @doc("Rkey of the repository record") 8 + @required 9 + rkey: string; 10 + 11 + @doc("Default branch to push to") 12 + defaultBranch?: string; 13 + 14 + @doc("A source URL to clone from, populate this when forking or importing a repository.") 15 + source?: string; 16 + }): void; 17 + }
+19
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/delete.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.delete { 4 + @procedure 5 + @doc("Delete a repository") 6 + op main(input: { 7 + @doc("DID of the repository owner") 8 + @required 9 + did: did; 10 + 11 + @doc("Name of the repository to delete") 12 + @required 13 + name: string; 14 + 15 + @doc("Rkey of the repository record") 16 + @required 17 + rkey: string; 18 + }): void; 19 + }
+25
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/diff.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.diff { 4 + @query 5 + @encoding("*/*") 6 + @errors(RepoNotFound, RefNotFound, InvalidRequest) 7 + op main( 8 + @doc("Repository identifier in format 'did:plc:.../repoName'") 9 + @required 10 + repo: string, 11 + 12 + @doc("Git reference (branch, tag, or commit SHA)") 13 + @required 14 + ref: string 15 + ): never; 16 + 17 + @doc("Repository not found or access denied") 18 + model RepoNotFound {} 19 + 20 + @doc("Git reference not found") 21 + model RefNotFound {} 22 + 23 + @doc("Invalid request parameters") 24 + model InvalidRequest {} 25 + }
+31
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/forkStatus.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.forkStatus { 4 + @procedure 5 + @doc("Check fork status relative to upstream source") 6 + op main(input: { 7 + @doc("DID of the fork owner") 8 + @required 9 + did: did; 10 + 11 + @doc("Name of the forked repository") 12 + @required 13 + name: string; 14 + 15 + @doc("Source repository URL") 16 + @required 17 + source: string; 18 + 19 + @doc("Branch to check status for") 20 + @required 21 + branch: string; 22 + 23 + @doc("Hidden ref to use for comparison") 24 + @required 25 + hiddenRef: string; 26 + }): { 27 + @doc("Fork status: 0=UpToDate, 1=FastForwardable, 2=Conflict, 3=MissingBranch") 28 + @required 29 + status: integer; 30 + }; 31 + }
+23
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/forkSync.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.forkSync { 4 + @procedure 5 + @doc("Sync a forked repository with its upstream source") 6 + op main(input: { 7 + @doc("DID of the fork owner") 8 + @required 9 + did: did; 10 + 11 + @doc("AT-URI of the source repository") 12 + @required 13 + source: atUri; 14 + 15 + @doc("Name of the forked repository") 16 + @required 17 + name: string; 18 + 19 + @doc("Branch to sync") 20 + @required 21 + branch: string; 22 + }): void; 23 + }
+51
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/getDefaultBranch.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.getDefaultBranch { 4 + @query 5 + @errors(RepoNotFound, InvalidRequest) 6 + op main( 7 + @doc("Repository identifier in format 'did:plc:.../repoName'") 8 + @required 9 + repo: string 10 + ): { 11 + @doc("Default branch name") 12 + @required 13 + name: string; 14 + 15 + @doc("Latest commit hash on default branch") 16 + @required 17 + hash: string; 18 + 19 + @doc("Short commit hash") 20 + shortHash?: string; 21 + 22 + @doc("Timestamp of latest commit") 23 + @required 24 + when: datetime; 25 + 26 + @doc("Latest commit message") 27 + message?: string; 28 + 29 + author?: Signature; 30 + }; 31 + 32 + model Signature { 33 + @doc("Author name") 34 + @required 35 + name: string; 36 + 37 + @doc("Author email") 38 + @required 39 + email: string; 40 + 41 + @doc("Author timestamp") 42 + @required 43 + when: datetime; 44 + } 45 + 46 + @doc("Repository not found or access denied") 47 + model RepoNotFound {} 48 + 49 + @doc("Invalid request parameters") 50 + model InvalidRequest {} 51 + }
+29
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/hiddenRef.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.hiddenRef { 4 + @procedure 5 + @doc("Create a hidden ref in a repository") 6 + op main(input: { 7 + @doc("AT-URI of the repository") 8 + @required 9 + repo: atUri; 10 + 11 + @doc("Fork reference name") 12 + @required 13 + forkRef: string; 14 + 15 + @doc("Remote reference name") 16 + @required 17 + remoteRef: string; 18 + }): { 19 + @doc("Whether the hidden ref was created successfully") 20 + @required 21 + success: boolean; 22 + 23 + @doc("The created hidden ref name") 24 + ref?: string; 25 + 26 + @doc("Error message if creation failed") 27 + error?: string; 28 + }; 29 + }
-11
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/issue.tsp
··· 1 - import "@typelex/emitter"; 2 - 3 - namespace sh.tangled.repo.issue { 4 - @rec("tid") 5 - model Main { 6 - @required repo: atUri; 7 - @required title: string; 8 - body?: string; 9 - @required createdAt: datetime; 10 - } 11 - }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/state/closed.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.repo.issue.state.closed { 4 - @doc("closed issue") 4 + /** closed issue */ 5 5 @token 6 6 model Main {} 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/state/open.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.repo.issue.state.open { 4 - @doc("open issue") 4 + /** open issue */ 5 5 @token 6 6 model Main {} 7 7 }
+13 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/state.tsp
··· 5 5 model Main { 6 6 @required issue: atUri; 7 7 8 - @doc("state of the issue") 8 + /** state of the issue */ 9 9 @required 10 10 state: "sh.tangled.repo.issue.state.open" | "sh.tangled.repo.issue.state.closed" | string = "sh.tangled.repo.issue.state.open"; 11 11 } 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace sh.tangled.repo.issue.state.open { 18 + @token model Main { } 19 + } 20 + 21 + @external 22 + namespace sh.tangled.repo.issue.state.closed { 23 + @token model Main { } 24 + }
+11
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.issue { 4 + @rec("tid") 5 + model Main { 6 + @required repo: atUri; 7 + @required title: string; 8 + body?: string; 9 + @required createdAt: datetime; 10 + } 11 + }
+59
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/languages.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.languages { 4 + @query 5 + @errors(RepoNotFound, RefNotFound, InvalidRequest) 6 + op main( 7 + @doc("Repository identifier in format 'did:plc:.../repoName'") 8 + @required 9 + repo: string, 10 + 11 + @doc("Git reference (branch, tag, or commit SHA)") 12 + ref?: string = "HEAD" 13 + ): { 14 + @doc("The git reference used") 15 + @required 16 + ref: string; 17 + 18 + @required 19 + languages: Language[]; 20 + 21 + @doc("Total size of all analyzed files in bytes") 22 + totalSize?: integer; 23 + 24 + @doc("Total number of files analyzed") 25 + totalFiles?: integer; 26 + }; 27 + 28 + model Language { 29 + @doc("Programming language name") 30 + @required 31 + name: string; 32 + 33 + @doc("Total size of files in this language (bytes)") 34 + @required 35 + size: integer; 36 + 37 + @doc("Percentage of total codebase (0-100)") 38 + @required 39 + percentage: integer; 40 + 41 + @doc("Number of files in this language") 42 + fileCount?: integer; 43 + 44 + @doc("Hex color code for this language") 45 + color?: string; 46 + 47 + @doc("File extensions associated with this language") 48 + extensions?: string[]; 49 + } 50 + 51 + @doc("Repository not found or access denied") 52 + model RepoNotFound {} 53 + 54 + @doc("Git reference not found") 55 + model RefNotFound {} 56 + 57 + @doc("Invalid request parameters") 58 + model InvalidRequest {} 59 + }
+28
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/listSecrets.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.listSecrets { 4 + @query 5 + op main( 6 + @required 7 + repo: atUri 8 + ): { 9 + @required 10 + secrets: Secret[]; 11 + }; 12 + 13 + model Secret { 14 + @required 15 + repo: atUri; 16 + 17 + @required 18 + @maxLength(50) 19 + @minLength(1) 20 + key: string; 21 + 22 + @required 23 + createdAt: datetime; 24 + 25 + @required 26 + createdBy: did; 27 + } 28 + }
+39
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/log.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.log { 4 + @query 5 + @encoding("*/*") 6 + @errors(RepoNotFound, RefNotFound, PathNotFound, InvalidRequest) 7 + op main( 8 + @doc("Repository identifier in format 'did:plc:.../repoName'") 9 + @required 10 + repo: string, 11 + 12 + @doc("Git reference (branch, tag, or commit SHA)") 13 + @required 14 + ref: string, 15 + 16 + @doc("Path to filter commits by") 17 + path?: string = "", 18 + 19 + @doc("Maximum number of commits to return") 20 + @minValue(1) 21 + @maxValue(100) 22 + limit?: integer = 50, 23 + 24 + @doc("Pagination cursor (commit SHA)") 25 + cursor?: string 26 + ): never; 27 + 28 + @doc("Repository not found or access denied") 29 + model RepoNotFound {} 30 + 31 + @doc("Git reference not found") 32 + model RefNotFound {} 33 + 34 + @doc("Path not found in repository") 35 + model PathNotFound {} 36 + 37 + @doc("Invalid request parameters") 38 + model InvalidRequest {} 39 + }
+35
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/merge.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.merge { 4 + @procedure 5 + @doc("Merge a patch into a repository branch") 6 + op main(input: { 7 + @doc("DID of the repository owner") 8 + @required 9 + did: did; 10 + 11 + @doc("Name of the repository") 12 + @required 13 + name: string; 14 + 15 + @doc("Patch content to merge") 16 + @required 17 + patch: string; 18 + 19 + @doc("Target branch to merge into") 20 + @required 21 + branch: string; 22 + 23 + @doc("Author name for the merge commit") 24 + authorName?: string; 25 + 26 + @doc("Author email for the merge commit") 27 + authorEmail?: string; 28 + 29 + @doc("Additional commit message body") 30 + commitBody?: string; 31 + 32 + @doc("Merge commit message") 33 + commitMessage?: string; 34 + }): void; 35 + }
+41
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/mergeCheck.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.mergeCheck { 4 + /** Check if a merge is possible between two branches */ 5 + @procedure 6 + op main( 7 + input: { 8 + /** DID of the repository owner */ 9 + @required did: did, 10 + 11 + /** Name of the repository */ 12 + @required name: string, 13 + 14 + /** Patch or pull request to check for merge conflicts */ 15 + @required patch: string, 16 + 17 + /** Target branch to merge into */ 18 + @required branch: string, 19 + } 20 + ): { 21 + /** Whether the merge has conflicts */ 22 + @required is_conflicted: boolean; 23 + 24 + /** List of files with merge conflicts */ 25 + conflicts?: ConflictInfo[]; 26 + 27 + /** Additional message about the merge check */ 28 + message?: string; 29 + 30 + /** Error message if check failed */ 31 + error?: string; 32 + }; 33 + 34 + model ConflictInfo { 35 + /** Name of the conflicted file */ 36 + @required filename: string; 37 + 38 + /** Reason for the conflict */ 39 + @required reason: string; 40 + } 41 + }
-33
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/pull.tsp
··· 1 - import "@typelex/emitter"; 2 - 3 - namespace sh.tangled.repo.pull { 4 - @rec("tid") 5 - model Main { 6 - @required target: Target; 7 - @required title: string; 8 - 9 - body?: string; 10 - 11 - @required patch: string; 12 - 13 - source?: Source; 14 - 15 - @required createdAt: datetime; 16 - } 17 - 18 - model Target { 19 - @required repo: atUri; 20 - @required branch: string; 21 - } 22 - 23 - model Source { 24 - @required branch: string; 25 - 26 - @minLength(40) 27 - @maxLength(40) 28 - @required 29 - sha: string; 30 - 31 - repo?: atUri; 32 - } 33 - }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status/closed.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.repo.pull.status.closed { 4 - @doc("closed pull request") 4 + /** closed pull request */ 5 5 @token 6 6 model Main {} 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status/merged.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.repo.pull.status.merged { 4 - @doc("merged pull request") 4 + /** merged pull request */ 5 5 @token 6 6 model Main {} 7 7 }
+1 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status/open.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace sh.tangled.repo.pull.status.open { 4 - @doc("open pull request") 4 + /** open pull request */ 5 5 @token 6 6 model Main {} 7 7 }
+18 -1
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status.tsp
··· 5 5 model Main { 6 6 @required pull: atUri; 7 7 8 - @doc("status of the pull request") 8 + /** status of the pull request */ 9 9 @required 10 10 status: "sh.tangled.repo.pull.status.open" | "sh.tangled.repo.pull.status.closed" | "sh.tangled.repo.pull.status.merged" | string = "sh.tangled.repo.pull.status.open"; 11 11 } 12 12 } 13 + 14 + // --- Externals --- 15 + 16 + @external 17 + namespace sh.tangled.repo.pull.status.open { 18 + @token model Main { } 19 + } 20 + 21 + @external 22 + namespace sh.tangled.repo.pull.status.closed { 23 + @token model Main { } 24 + } 25 + 26 + @external 27 + namespace sh.tangled.repo.pull.status.merged { 28 + @token model Main { } 29 + }
+33
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.pull { 4 + @rec("tid") 5 + model Main { 6 + @required target: Target; 7 + @required title: string; 8 + 9 + body?: string; 10 + 11 + @required patch: string; 12 + 13 + source?: Source; 14 + 15 + @required createdAt: datetime; 16 + } 17 + 18 + model Target { 19 + @required repo: atUri; 20 + @required branch: string; 21 + } 22 + 23 + model Source { 24 + @required branch: string; 25 + 26 + @required 27 + @minLength(40) 28 + @maxLength(40) 29 + sha: string; 30 + 31 + repo?: atUri; 32 + } 33 + }
+16
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/removeSecret.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.removeSecret { 4 + /** Remove a CI secret */ 5 + @procedure 6 + op main( 7 + input: { 8 + @required repo: atUri, 9 + 10 + @required 11 + @minLength(1) 12 + @maxLength(50) 13 + key: string, 14 + } 15 + ): void; 16 + }
+13
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/setDefaultBranch.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.setDefaultBranch { 4 + @procedure 5 + @doc("Set the default branch for a repository") 6 + op main(input: { 7 + @required 8 + repo: atUri; 9 + 10 + @required 11 + defaultBranch: string; 12 + }): void; 13 + }
+25
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/tags.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.tags { 4 + @query 5 + @errors(RepoNotFound, InvalidRequest) 6 + @encoding("*/*") 7 + op main( 8 + /** Repository identifier in format 'did:plc:.../repoName' */ 9 + @required repo: string, 10 + 11 + /** Maximum number of tags to return */ 12 + @minValue(1) 13 + @maxValue(100) 14 + limit?: integer = 50, 15 + 16 + /** Pagination cursor */ 17 + cursor?: string, 18 + ): void; 19 + 20 + /** Repository not found or access denied */ 21 + model RepoNotFound {} 22 + 23 + /** Invalid request parameters */ 24 + model InvalidRequest {} 25 + }
+80
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/tree.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo.tree { 4 + @query 5 + @errors(RepoNotFound, RefNotFound, PathNotFound, InvalidRequest) 6 + op main( 7 + /** Repository identifier in format 'did:plc:.../repoName' */ 8 + @required repo: string, 9 + 10 + /** Git reference (branch, tag, or commit SHA) */ 11 + @required ref: string, 12 + 13 + /** Path within the repository tree */ 14 + path?: string = "", 15 + ): { 16 + /** The git reference used */ 17 + @required ref: string; 18 + 19 + /** The parent path in the tree */ 20 + parent?: string; 21 + 22 + /** Parent directory path */ 23 + dotdot?: string; 24 + 25 + /** Readme for this file tree */ 26 + readme?: Readme; 27 + 28 + @required files: TreeEntry[]; 29 + }; 30 + 31 + model Readme { 32 + /** Name of the readme file */ 33 + @required filename: string; 34 + 35 + /** Contents of the readme file */ 36 + @required contents: string; 37 + } 38 + 39 + model TreeEntry { 40 + /** Relative file or directory name */ 41 + @required name: string; 42 + 43 + /** File mode */ 44 + @required mode: string; 45 + 46 + /** File size in bytes */ 47 + @required size: integer; 48 + 49 + /** Whether this entry is a file */ 50 + @required is_file: boolean; 51 + 52 + /** Whether this entry is a directory/subtree */ 53 + @required is_subtree: boolean; 54 + 55 + last_commit?: LastCommit; 56 + } 57 + 58 + model LastCommit { 59 + /** Commit hash */ 60 + @required hash: string; 61 + 62 + /** Commit message */ 63 + @required message: string; 64 + 65 + /** Commit timestamp */ 66 + @required when: datetime; 67 + } 68 + 69 + /** Repository not found or access denied */ 70 + model RepoNotFound {} 71 + 72 + /** Git reference not found */ 73 + model RefNotFound {} 74 + 75 + /** Path not found in repository tree */ 76 + model PathNotFound {} 77 + 78 + /** Invalid request parameters */ 79 + model InvalidRequest {} 80 + }
+30
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.repo { 4 + @rec("tid") 5 + model Main { 6 + @doc("name of the repo") 7 + @required 8 + name: string; 9 + 10 + @doc("knot where the repo was created") 11 + @required 12 + knot: string; 13 + 14 + @doc("CI runner to send jobs to and receive results from") 15 + spindle?: string; 16 + 17 + @minGraphemes(1) 18 + @maxGraphemes(140) 19 + description?: string; 20 + 21 + @doc("source of the repo") 22 + source?: uri; 23 + 24 + @doc("List of labels that this repo subscribes to") 25 + labels?: atUri[]; 26 + 27 + @required 28 + createdAt: datetime; 29 + } 30 + }
+16
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/spindle/member.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.spindle.member { 4 + @rec("tid") 5 + model Main { 6 + @required 7 + subject: did; 8 + 9 + @doc("spindle instance that the subject is now a member of") 10 + @required 11 + instance: string; 12 + 13 + @required 14 + createdAt: datetime; 15 + } 16 + }
+8
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/spindle.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace sh.tangled.spindle { 4 + @rec("any") 5 + model Main { 6 + @required createdAt: datetime; 7 + } 8 + }
-10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/issue/closed.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.issue.state.closed", 4 - "defs": { 5 - "main": { 6 - "type": "token", 7 - "description": "closed issue" 8 - } 9 - } 10 - }
-35
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/issue/comment.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.issue.comment", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "issue", 12 - "body", 13 - "createdAt" 14 - ], 15 - "properties": { 16 - "issue": { 17 - "type": "string", 18 - "format": "at-uri" 19 - }, 20 - "body": { 21 - "type": "string" 22 - }, 23 - "createdAt": { 24 - "type": "string", 25 - "format": "datetime" 26 - }, 27 - "replyTo": { 28 - "type": "string", 29 - "format": "at-uri" 30 - } 31 - } 32 - } 33 - } 34 - } 35 - }
-30
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/issue/issue.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.issue", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": ["repo", "title", "createdAt"], 11 - "properties": { 12 - "repo": { 13 - "type": "string", 14 - "format": "at-uri" 15 - }, 16 - "title": { 17 - "type": "string" 18 - }, 19 - "body": { 20 - "type": "string" 21 - }, 22 - "createdAt": { 23 - "type": "string", 24 - "format": "datetime" 25 - } 26 - } 27 - } 28 - } 29 - } 30 - }
-10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/issue/open.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.issue.state.open", 4 - "defs": { 5 - "main": { 6 - "type": "token", 7 - "description": "open issue" 8 - } 9 - } 10 - }
-32
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/issue/state.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.issue.state", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "issue", 12 - "state" 13 - ], 14 - "properties": { 15 - "issue": { 16 - "type": "string", 17 - "format": "at-uri" 18 - }, 19 - "state": { 20 - "type": "string", 21 - "description": "state of the issue", 22 - "knownValues": [ 23 - "sh.tangled.repo.issue.state.open", 24 - "sh.tangled.repo.issue.state.closed" 25 - ], 26 - "default": "sh.tangled.repo.issue.state.open" 27 - } 28 - } 29 - } 30 - } 31 - } 32 - }
-22
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/knot/knot.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.knot", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "any", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "createdAt" 12 - ], 13 - "properties": { 14 - "createdAt": { 15 - "type": "string", 16 - "format": "datetime" 17 - } 18 - } 19 - } 20 - } 21 - } 22 - }
+22
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/knot.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.knot", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "any", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "createdAt" 12 + ], 13 + "properties": { 14 + "createdAt": { 15 + "type": "string", 16 + "format": "datetime" 17 + } 18 + } 19 + } 20 + } 21 + } 22 + }
-205
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pipeline/pipeline.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.pipeline", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "triggerMetadata", 12 - "workflows" 13 - ], 14 - "properties": { 15 - "triggerMetadata": { 16 - "type": "ref", 17 - "ref": "#triggerMetadata" 18 - }, 19 - "workflows": { 20 - "type": "array", 21 - "items": { 22 - "type": "ref", 23 - "ref": "#workflow" 24 - } 25 - } 26 - } 27 - } 28 - }, 29 - "triggerMetadata": { 30 - "type": "object", 31 - "required": [ 32 - "kind", 33 - "repo" 34 - ], 35 - "properties": { 36 - "kind": { 37 - "type": "string", 38 - "enum": [ 39 - "push", 40 - "pull_request", 41 - "manual" 42 - ] 43 - }, 44 - "repo": { 45 - "type": "ref", 46 - "ref": "#triggerRepo" 47 - }, 48 - "push": { 49 - "type": "ref", 50 - "ref": "#pushTriggerData" 51 - }, 52 - "pullRequest": { 53 - "type": "ref", 54 - "ref": "#pullRequestTriggerData" 55 - }, 56 - "manual": { 57 - "type": "ref", 58 - "ref": "#manualTriggerData" 59 - } 60 - } 61 - }, 62 - "triggerRepo": { 63 - "type": "object", 64 - "required": [ 65 - "knot", 66 - "did", 67 - "repo", 68 - "defaultBranch" 69 - ], 70 - "properties": { 71 - "knot": { 72 - "type": "string" 73 - }, 74 - "did": { 75 - "type": "string", 76 - "format": "did" 77 - }, 78 - "repo": { 79 - "type": "string" 80 - }, 81 - "defaultBranch": { 82 - "type": "string" 83 - } 84 - } 85 - }, 86 - "pushTriggerData": { 87 - "type": "object", 88 - "required": [ 89 - "ref", 90 - "newSha", 91 - "oldSha" 92 - ], 93 - "properties": { 94 - "ref": { 95 - "type": "string" 96 - }, 97 - "newSha": { 98 - "type": "string", 99 - "minLength": 40, 100 - "maxLength": 40 101 - }, 102 - "oldSha": { 103 - "type": "string", 104 - "minLength": 40, 105 - "maxLength": 40 106 - } 107 - } 108 - }, 109 - "pullRequestTriggerData": { 110 - "type": "object", 111 - "required": [ 112 - "sourceBranch", 113 - "targetBranch", 114 - "sourceSha", 115 - "action" 116 - ], 117 - "properties": { 118 - "sourceBranch": { 119 - "type": "string" 120 - }, 121 - "targetBranch": { 122 - "type": "string" 123 - }, 124 - "sourceSha": { 125 - "type": "string", 126 - "minLength": 40, 127 - "maxLength": 40 128 - }, 129 - "action": { 130 - "type": "string" 131 - } 132 - } 133 - }, 134 - "manualTriggerData": { 135 - "type": "object", 136 - "properties": { 137 - "inputs": { 138 - "type": "array", 139 - "items": { 140 - "type": "ref", 141 - "ref": "#pair" 142 - } 143 - } 144 - } 145 - }, 146 - "workflow": { 147 - "type": "object", 148 - "required": [ 149 - "name", 150 - "engine", 151 - "clone", 152 - "raw" 153 - ], 154 - "properties": { 155 - "name": { 156 - "type": "string" 157 - }, 158 - "engine": { 159 - "type": "string" 160 - }, 161 - "clone": { 162 - "type": "ref", 163 - "ref": "#cloneOpts" 164 - }, 165 - "raw": { 166 - "type": "string" 167 - } 168 - } 169 - }, 170 - "cloneOpts": { 171 - "type": "object", 172 - "required": [ 173 - "skip", 174 - "depth", 175 - "submodules" 176 - ], 177 - "properties": { 178 - "skip": { 179 - "type": "boolean" 180 - }, 181 - "depth": { 182 - "type": "integer" 183 - }, 184 - "submodules": { 185 - "type": "boolean" 186 - } 187 - } 188 - }, 189 - "pair": { 190 - "type": "object", 191 - "required": [ 192 - "key", 193 - "value" 194 - ], 195 - "properties": { 196 - "key": { 197 - "type": "string" 198 - }, 199 - "value": { 200 - "type": "string" 201 - } 202 - } 203 - } 204 - } 205 - }
+205
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pipeline.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.pipeline", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "triggerMetadata", 12 + "workflows" 13 + ], 14 + "properties": { 15 + "triggerMetadata": { 16 + "type": "ref", 17 + "ref": "#triggerMetadata" 18 + }, 19 + "workflows": { 20 + "type": "array", 21 + "items": { 22 + "type": "ref", 23 + "ref": "#workflow" 24 + } 25 + } 26 + } 27 + } 28 + }, 29 + "triggerMetadata": { 30 + "type": "object", 31 + "required": [ 32 + "kind", 33 + "repo" 34 + ], 35 + "properties": { 36 + "kind": { 37 + "type": "string", 38 + "enum": [ 39 + "push", 40 + "pull_request", 41 + "manual" 42 + ] 43 + }, 44 + "repo": { 45 + "type": "ref", 46 + "ref": "#triggerRepo" 47 + }, 48 + "push": { 49 + "type": "ref", 50 + "ref": "#pushTriggerData" 51 + }, 52 + "pullRequest": { 53 + "type": "ref", 54 + "ref": "#pullRequestTriggerData" 55 + }, 56 + "manual": { 57 + "type": "ref", 58 + "ref": "#manualTriggerData" 59 + } 60 + } 61 + }, 62 + "triggerRepo": { 63 + "type": "object", 64 + "required": [ 65 + "knot", 66 + "did", 67 + "repo", 68 + "defaultBranch" 69 + ], 70 + "properties": { 71 + "knot": { 72 + "type": "string" 73 + }, 74 + "did": { 75 + "type": "string", 76 + "format": "did" 77 + }, 78 + "repo": { 79 + "type": "string" 80 + }, 81 + "defaultBranch": { 82 + "type": "string" 83 + } 84 + } 85 + }, 86 + "pushTriggerData": { 87 + "type": "object", 88 + "required": [ 89 + "ref", 90 + "newSha", 91 + "oldSha" 92 + ], 93 + "properties": { 94 + "ref": { 95 + "type": "string" 96 + }, 97 + "newSha": { 98 + "type": "string", 99 + "minLength": 40, 100 + "maxLength": 40 101 + }, 102 + "oldSha": { 103 + "type": "string", 104 + "minLength": 40, 105 + "maxLength": 40 106 + } 107 + } 108 + }, 109 + "pullRequestTriggerData": { 110 + "type": "object", 111 + "required": [ 112 + "sourceBranch", 113 + "targetBranch", 114 + "sourceSha", 115 + "action" 116 + ], 117 + "properties": { 118 + "sourceBranch": { 119 + "type": "string" 120 + }, 121 + "targetBranch": { 122 + "type": "string" 123 + }, 124 + "sourceSha": { 125 + "type": "string", 126 + "minLength": 40, 127 + "maxLength": 40 128 + }, 129 + "action": { 130 + "type": "string" 131 + } 132 + } 133 + }, 134 + "manualTriggerData": { 135 + "type": "object", 136 + "properties": { 137 + "inputs": { 138 + "type": "array", 139 + "items": { 140 + "type": "ref", 141 + "ref": "#pair" 142 + } 143 + } 144 + } 145 + }, 146 + "workflow": { 147 + "type": "object", 148 + "required": [ 149 + "name", 150 + "engine", 151 + "clone", 152 + "raw" 153 + ], 154 + "properties": { 155 + "name": { 156 + "type": "string" 157 + }, 158 + "engine": { 159 + "type": "string" 160 + }, 161 + "clone": { 162 + "type": "ref", 163 + "ref": "#cloneOpts" 164 + }, 165 + "raw": { 166 + "type": "string" 167 + } 168 + } 169 + }, 170 + "cloneOpts": { 171 + "type": "object", 172 + "required": [ 173 + "skip", 174 + "depth", 175 + "submodules" 176 + ], 177 + "properties": { 178 + "skip": { 179 + "type": "boolean" 180 + }, 181 + "depth": { 182 + "type": "integer" 183 + }, 184 + "submodules": { 185 + "type": "boolean" 186 + } 187 + } 188 + }, 189 + "pair": { 190 + "type": "object", 191 + "required": [ 192 + "key", 193 + "value" 194 + ], 195 + "properties": { 196 + "key": { 197 + "type": "string" 198 + }, 199 + "value": { 200 + "type": "string" 201 + } 202 + } 203 + } 204 + } 205 + }
-10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/closed.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull.status.closed", 4 - "defs": { 5 - "main": { 6 - "type": "token", 7 - "description": "closed pull request" 8 - } 9 - } 10 - }
-31
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/comment.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull.comment", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "pull", 12 - "body", 13 - "createdAt" 14 - ], 15 - "properties": { 16 - "pull": { 17 - "type": "string", 18 - "format": "at-uri" 19 - }, 20 - "body": { 21 - "type": "string" 22 - }, 23 - "createdAt": { 24 - "type": "string", 25 - "format": "datetime" 26 - } 27 - } 28 - } 29 - } 30 - } 31 - }
-10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/merged.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull.status.merged", 4 - "defs": { 5 - "main": { 6 - "type": "token", 7 - "description": "merged pull request" 8 - } 9 - } 10 - }
-10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/open.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull.status.open", 4 - "defs": { 5 - "main": { 6 - "type": "token", 7 - "description": "open pull request" 8 - } 9 - } 10 - }
-79
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/pull.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "target", 12 - "title", 13 - "patch", 14 - "createdAt" 15 - ], 16 - "properties": { 17 - "target": { 18 - "type": "ref", 19 - "ref": "#target" 20 - }, 21 - "title": { 22 - "type": "string" 23 - }, 24 - "body": { 25 - "type": "string" 26 - }, 27 - "patch": { 28 - "type": "string" 29 - }, 30 - "source": { 31 - "type": "ref", 32 - "ref": "#source" 33 - }, 34 - "createdAt": { 35 - "type": "string", 36 - "format": "datetime" 37 - } 38 - } 39 - } 40 - }, 41 - "target": { 42 - "type": "object", 43 - "required": [ 44 - "repo", 45 - "branch" 46 - ], 47 - "properties": { 48 - "repo": { 49 - "type": "string", 50 - "format": "at-uri" 51 - }, 52 - "branch": { 53 - "type": "string" 54 - } 55 - } 56 - }, 57 - "source": { 58 - "type": "object", 59 - "required": [ 60 - "branch", 61 - "sha" 62 - ], 63 - "properties": { 64 - "branch": { 65 - "type": "string" 66 - }, 67 - "sha": { 68 - "type": "string", 69 - "minLength": 40, 70 - "maxLength": 40 71 - }, 72 - "repo": { 73 - "type": "string", 74 - "format": "at-uri" 75 - } 76 - } 77 - } 78 - } 79 - }
-33
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/pulls/state.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.pull.status", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "pull", 12 - "status" 13 - ], 14 - "properties": { 15 - "pull": { 16 - "type": "string", 17 - "format": "at-uri" 18 - }, 19 - "status": { 20 - "type": "string", 21 - "description": "status of the pull request", 22 - "knownValues": [ 23 - "sh.tangled.repo.pull.status.open", 24 - "sh.tangled.repo.pull.status.closed", 25 - "sh.tangled.repo.pull.status.merged" 26 - ], 27 - "default": "sh.tangled.repo.pull.status.open" 28 - } 29 - } 30 - } 31 - } 32 - } 33 - }
+1 -2
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/archive.json
··· 29 29 } 30 30 }, 31 31 "output": { 32 - "encoding": "*/*", 33 - "description": "Binary archive data" 32 + "encoding": "*/*" 34 33 }, 35 34 "errors": [ 36 35 {
+1 -2
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/compare.json
··· 23 23 } 24 24 }, 25 25 "output": { 26 - "encoding": "*/*", 27 - "description": "Compare output in application/json" 26 + "encoding": "*/*" 28 27 }, 29 28 "errors": [ 30 29 {
-29
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/defaultBranch.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo.setDefaultBranch", 4 - "defs": { 5 - "main": { 6 - "type": "procedure", 7 - "description": "Set the default branch for a repository", 8 - "input": { 9 - "encoding": "application/json", 10 - "schema": { 11 - "type": "object", 12 - "required": [ 13 - "repo", 14 - "defaultBranch" 15 - ], 16 - "properties": { 17 - "repo": { 18 - "type": "string", 19 - "format": "at-uri" 20 - }, 21 - "defaultBranch": { 22 - "type": "string" 23 - } 24 - } 25 - } 26 - } 27 - } 28 - } 29 - }
+35
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/issue/comment.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.issue.comment", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "issue", 12 + "body", 13 + "createdAt" 14 + ], 15 + "properties": { 16 + "issue": { 17 + "type": "string", 18 + "format": "at-uri" 19 + }, 20 + "body": { 21 + "type": "string" 22 + }, 23 + "createdAt": { 24 + "type": "string", 25 + "format": "datetime" 26 + }, 27 + "replyTo": { 28 + "type": "string", 29 + "format": "at-uri" 30 + } 31 + } 32 + } 33 + } 34 + } 35 + }
+10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/issue/state/closed.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.issue.state.closed", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "closed issue" 8 + } 9 + } 10 + }
+10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/issue/state/open.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.issue.state.open", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "open issue" 8 + } 9 + } 10 + }
+32
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/issue/state.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.issue.state", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "issue", 12 + "state" 13 + ], 14 + "properties": { 15 + "issue": { 16 + "type": "string", 17 + "format": "at-uri" 18 + }, 19 + "state": { 20 + "type": "string", 21 + "description": "state of the issue", 22 + "knownValues": [ 23 + "sh.tangled.repo.issue.state.open", 24 + "sh.tangled.repo.issue.state.closed" 25 + ], 26 + "default": "sh.tangled.repo.issue.state.open" 27 + } 28 + } 29 + } 30 + } 31 + } 32 + }
+30
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/issue.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.issue", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": ["repo", "title", "createdAt"], 11 + "properties": { 12 + "repo": { 13 + "type": "string", 14 + "format": "at-uri" 15 + }, 16 + "title": { 17 + "type": "string" 18 + }, 19 + "body": { 20 + "type": "string" 21 + }, 22 + "createdAt": { 23 + "type": "string", 24 + "format": "datetime" 25 + } 26 + } 27 + } 28 + } 29 + } 30 + }
+31
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull/comment.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull.comment", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "pull", 12 + "body", 13 + "createdAt" 14 + ], 15 + "properties": { 16 + "pull": { 17 + "type": "string", 18 + "format": "at-uri" 19 + }, 20 + "body": { 21 + "type": "string" 22 + }, 23 + "createdAt": { 24 + "type": "string", 25 + "format": "datetime" 26 + } 27 + } 28 + } 29 + } 30 + } 31 + }
+10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull/status/closed.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull.status.closed", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "closed pull request" 8 + } 9 + } 10 + }
+10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull/status/merged.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull.status.merged", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "merged pull request" 8 + } 9 + } 10 + }
+10
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull/status/open.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull.status.open", 4 + "defs": { 5 + "main": { 6 + "type": "token", 7 + "description": "open pull request" 8 + } 9 + } 10 + }
+33
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull/status.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull.status", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "pull", 12 + "status" 13 + ], 14 + "properties": { 15 + "pull": { 16 + "type": "string", 17 + "format": "at-uri" 18 + }, 19 + "status": { 20 + "type": "string", 21 + "description": "status of the pull request", 22 + "knownValues": [ 23 + "sh.tangled.repo.pull.status.open", 24 + "sh.tangled.repo.pull.status.closed", 25 + "sh.tangled.repo.pull.status.merged" 26 + ], 27 + "default": "sh.tangled.repo.pull.status.open" 28 + } 29 + } 30 + } 31 + } 32 + } 33 + }
+79
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/pull.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.pull", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "target", 12 + "title", 13 + "patch", 14 + "createdAt" 15 + ], 16 + "properties": { 17 + "target": { 18 + "type": "ref", 19 + "ref": "#target" 20 + }, 21 + "title": { 22 + "type": "string" 23 + }, 24 + "body": { 25 + "type": "string" 26 + }, 27 + "patch": { 28 + "type": "string" 29 + }, 30 + "source": { 31 + "type": "ref", 32 + "ref": "#source" 33 + }, 34 + "createdAt": { 35 + "type": "string", 36 + "format": "datetime" 37 + } 38 + } 39 + } 40 + }, 41 + "target": { 42 + "type": "object", 43 + "required": [ 44 + "repo", 45 + "branch" 46 + ], 47 + "properties": { 48 + "repo": { 49 + "type": "string", 50 + "format": "at-uri" 51 + }, 52 + "branch": { 53 + "type": "string" 54 + } 55 + } 56 + }, 57 + "source": { 58 + "type": "object", 59 + "required": [ 60 + "branch", 61 + "sha" 62 + ], 63 + "properties": { 64 + "branch": { 65 + "type": "string" 66 + }, 67 + "sha": { 68 + "type": "string", 69 + "minLength": 40, 70 + "maxLength": 40 71 + }, 72 + "repo": { 73 + "type": "string", 74 + "format": "at-uri" 75 + } 76 + } 77 + } 78 + } 79 + }
-54
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/repo.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.repo", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "name", 12 - "knot", 13 - "createdAt" 14 - ], 15 - "properties": { 16 - "name": { 17 - "type": "string", 18 - "description": "name of the repo" 19 - }, 20 - "knot": { 21 - "type": "string", 22 - "description": "knot where the repo was created" 23 - }, 24 - "spindle": { 25 - "type": "string", 26 - "description": "CI runner to send jobs to and receive results from" 27 - }, 28 - "description": { 29 - "type": "string", 30 - "minGraphemes": 1, 31 - "maxGraphemes": 140 32 - }, 33 - "source": { 34 - "type": "string", 35 - "format": "uri", 36 - "description": "source of the repo" 37 - }, 38 - "labels": { 39 - "type": "array", 40 - "description": "List of labels that this repo subscribes to", 41 - "items": { 42 - "type": "string", 43 - "format": "at-uri" 44 - } 45 - }, 46 - "createdAt": { 47 - "type": "string", 48 - "format": "datetime" 49 - } 50 - } 51 - } 52 - } 53 - } 54 - }
+29
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo/setDefaultBranch.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.setDefaultBranch", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Set the default branch for a repository", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": [ 13 + "repo", 14 + "defaultBranch" 15 + ], 16 + "properties": { 17 + "repo": { 18 + "type": "string", 19 + "format": "at-uri" 20 + }, 21 + "defaultBranch": { 22 + "type": "string" 23 + } 24 + } 25 + } 26 + } 27 + } 28 + } 29 + }
+54
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/repo.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "name", 12 + "knot", 13 + "createdAt" 14 + ], 15 + "properties": { 16 + "name": { 17 + "type": "string", 18 + "description": "name of the repo" 19 + }, 20 + "knot": { 21 + "type": "string", 22 + "description": "knot where the repo was created" 23 + }, 24 + "spindle": { 25 + "type": "string", 26 + "description": "CI runner to send jobs to and receive results from" 27 + }, 28 + "description": { 29 + "type": "string", 30 + "minGraphemes": 1, 31 + "maxGraphemes": 140 32 + }, 33 + "source": { 34 + "type": "string", 35 + "format": "uri", 36 + "description": "source of the repo" 37 + }, 38 + "labels": { 39 + "type": "array", 40 + "description": "List of labels that this repo subscribes to", 41 + "items": { 42 + "type": "string", 43 + "format": "at-uri" 44 + } 45 + }, 46 + "createdAt": { 47 + "type": "string", 48 + "format": "datetime" 49 + } 50 + } 51 + } 52 + } 53 + } 54 + }
-23
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/spindle/spindle.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.spindle", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "any", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "createdAt" 12 - ], 13 - "properties": { 14 - "createdAt": { 15 - "type": "string", 16 - "format": "datetime" 17 - } 18 - } 19 - } 20 - } 21 - } 22 - } 23 -
+23
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/spindle.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.spindle", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "any", 8 + "record": { 9 + "type": "object", 10 + "required": [ 11 + "createdAt" 12 + ], 13 + "properties": { 14 + "createdAt": { 15 + "type": "string", 16 + "format": "datetime" 17 + } 18 + } 19 + } 20 + } 21 + } 22 + } 23 +
-38
packages/emitter/test/integration/lexicon-examples/output/sh/tangled/string/string.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.string", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "key": "tid", 8 - "record": { 9 - "type": "object", 10 - "required": [ 11 - "filename", 12 - "description", 13 - "createdAt", 14 - "contents" 15 - ], 16 - "properties": { 17 - "filename": { 18 - "type": "string", 19 - "maxGraphemes": 140, 20 - "minGraphemes": 1 21 - }, 22 - "description": { 23 - "type": "string", 24 - "maxGraphemes": 280 25 - }, 26 - "createdAt": { 27 - "type": "string", 28 - "format": "datetime" 29 - }, 30 - "contents": { 31 - "type": "string", 32 - "minGraphemes": 1 33 - } 34 - } 35 - } 36 - } 37 - } 38 - }
+12 -20
packages/emitter/test/integration.test.ts
··· 54 54 path.join(scenario, "output"), 55 55 ); 56 56 57 - // Compile all inputs together (for cross-references) 58 - const tspFiles = Object.keys(inputFiles).filter((f) => 59 - f.endsWith(".tsp"), 60 - ); 61 - let emitResult: EmitResult; 62 - 63 - if (tspFiles.length > 0) { 64 - // Create a virtual main.tsp that imports all other files 65 - const mainContent = 66 - 'import "@typelex/emitter";\n' + 67 - tspFiles.map((f) => `import "./${f}";`).join("\n"); 68 - const filesWithMain = { ...inputFiles, "main.tsp": mainContent }; 69 - emitResult = await doEmit(filesWithMain, "main.tsp"); 70 - } else { 71 - emitResult = { files: {}, diagnostics: [], inputFiles: {} }; 72 - } 73 - 74 57 // Generate a test for each expected output 75 58 for (const expectedPath of Object.keys(expectedFiles)) { 76 59 if (!expectedPath.endsWith(".json")) continue; ··· 80 63 const hasInput = Object.keys(inputFiles).includes(inputPath); 81 64 82 65 if (hasInput) { 83 - it(`should emit ${expectedPath}`, function () { 66 + it(`should emit ${expectedPath}`, async function () { 67 + // Compile each file in isolation 68 + const emitResult = await doEmit({ [inputPath]: inputFiles[inputPath] }, inputPath); 69 + 84 70 // Check for compilation errors 85 71 if (emitResult.diagnostics.length > 0) { 86 72 const formattedDiagnostics = emitResult.diagnostics.map((diag) => ··· 91 77 ); 92 78 } 93 79 80 + const normalizedExpectedPath = normalizePathToPosix(expectedPath); 81 + 94 82 assert.ok( 95 83 Object.prototype.hasOwnProperty.call( 96 84 emitResult.files, 97 - expectedPath, 85 + normalizedExpectedPath, 98 86 ), 99 87 `Expected file ${expectedPath} was not produced`, 100 88 ); 101 89 102 - const actual = JSON.parse(emitResult.files[expectedPath]); 90 + const actual = JSON.parse(emitResult.files[normalizedExpectedPath]); 103 91 const expected = JSON.parse(expectedFiles[expectedPath]); 104 92 assert.deepStrictEqual(actual, expected); 105 93 }); ··· 180 168 await walk(dir, ""); 181 169 return result; 182 170 } 171 + 172 + function normalizePathToPosix(thePath: string): string { 173 + return thePath.replaceAll(path.sep, path.posix.sep); 174 + }
+2 -2
packages/emitter/test/spec/basic/input/com/example/allOptionalFields.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.allOptionalFields { 4 - @doc("Object with all optional fields (no required)") 4 + /** Object with all optional fields (no required) */ 5 5 model Main { 6 - @doc("All fields are optional") 6 + /** All fields are optional */ 7 7 field1?: string; 8 8 9 9 field2?: integer;
+2 -2
packages/emitter/test/spec/basic/input/com/example/arrayOfUnions.tsp
··· 2 2 3 3 namespace com.example.arrayOfUnions { 4 4 model Main { 5 - @doc("Array of open union variants") 5 + /** Array of open union variants */ 6 6 @required 7 7 items: (ItemA | ItemB | ItemC | unknown)[]; 8 8 9 - @doc("Array of closed union variants") 9 + /** Array of closed union variants */ 10 10 @maxItems(10) 11 11 operations?: Operations[]; 12 12 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/arrays.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.arrays { 4 - @doc("Array container types") 4 + /** Array container types */ 5 5 model Main { 6 - @doc("Array of strings") 6 + /** Array of strings */ 7 7 stringArray?: string[]; 8 8 9 - @doc("Array with size constraints") 9 + /** Array with size constraints */ 10 10 @minItems(1) 11 11 @maxItems(10) 12 12 arrayWithLength?: integer[]; 13 13 14 - @doc("Array of object references") 14 + /** Array of object references */ 15 15 arrayOfRefs?: Item[]; 16 16 17 - @doc("Array of union types") 17 + /** Array of union types */ 18 18 arrayOfUnion?: (TypeA | TypeB | unknown)[]; 19 19 } 20 20
+6 -6
packages/emitter/test/spec/basic/input/com/example/blobs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.blobs { 4 - @doc("Blob type examples") 4 + /** Blob type examples */ 5 5 model Main { 6 - @doc("Basic blob with no constraints") 6 + /** Basic blob with no constraints */ 7 7 simpleBlob?: Blob; 8 8 9 - @doc("Image blob up to 5MB") 9 + /** Image blob up to 5MB */ 10 10 imageBlob?: Blob<#["image/*"], 5000000>; 11 11 12 - @doc("Specific image types up to 2MB") 12 + /** Specific image types up to 2MB */ 13 13 specificImages?: Blob<#["image/png", "image/jpeg", "image/gif"], 2000000>; 14 14 15 - @doc("Video blob up to 100MB") 15 + /** Video blob up to 100MB */ 16 16 videoBlob?: Blob<#["video/mp4", "video/webm"], 100000000>; 17 17 18 - @doc("Accept any MIME type") 18 + /** Accept any MIME type */ 19 19 anyBlob?: Blob<#["*/*"]>; 20 20 } 21 21 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/booleanConstraints.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.booleanConstraints { 4 - @doc("Boolean field constraints") 4 + /** Boolean field constraints */ 5 5 model Main { 6 - @doc("Boolean with default true") 6 + /** Boolean with default true */ 7 7 defaultTrue?: boolean = true; 8 8 9 - @doc("Boolean with default false") 9 + /** Boolean with default false */ 10 10 defaultFalse?: boolean = false; 11 11 12 - @doc("Constant true value") 12 + /** Constant true value */ 13 13 @readOnly 14 14 constTrue?: boolean = true; 15 15 16 - @doc("Constant false value") 16 + /** Constant false value */ 17 17 @readOnly 18 18 constFalse?: boolean = false; 19 19 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/bytesConstraints.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.bytesConstraints { 4 - @doc("Bytes field constraints") 4 + /** Bytes field constraints */ 5 5 model Main { 6 - @doc("Basic bytes field") 6 + /** Basic bytes field */ 7 7 simpleBytes?: bytes; 8 8 9 - @doc("Bytes with size constraints") 9 + /** Bytes with size constraints */ 10 10 @minBytes(1) 11 11 @maxBytes(1024) 12 12 withLength?: bytes; 13 13 14 - @doc("Bytes with minimum size only") 14 + /** Bytes with minimum size only */ 15 15 @minBytes(32) 16 16 withMinOnly?: bytes; 17 17 18 - @doc("Bytes with maximum size only (5MB)") 18 + /** Bytes with maximum size only (5MB) */ 19 19 @maxBytes(5000000) 20 20 withMaxOnly?: bytes; 21 21 }
+3 -3
packages/emitter/test/spec/basic/input/com/example/cidLinks.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.cidLinks { 4 - @doc("CID link examples") 4 + /** CID link examples */ 5 5 model Main { 6 - @doc("A CID link to content") 6 + /** A CID link to content */ 7 7 contentCid?: cidLink; 8 8 9 - @doc("Link to previous version") 9 + /** Link to previous version */ 10 10 previousVersion?: cidLink; 11 11 } 12 12 }
+7 -7
packages/emitter/test/spec/basic/input/com/example/constValues.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.constValues { 4 - @doc("Tests const values on all supported types") 4 + /** Tests const values on all supported types */ 5 5 model Main { 6 - @doc("Constant string value") 6 + /** Constant string value */ 7 7 @readOnly 8 8 @required 9 9 stringConst: string = "fixed-value"; 10 10 11 - @doc("Constant boolean value (true)") 11 + /** Constant boolean value (true) */ 12 12 @readOnly 13 13 @required 14 14 boolConst: boolean = true; 15 15 16 - @doc("Constant boolean value (false)") 16 + /** Constant boolean value (false) */ 17 17 @readOnly 18 18 falseConst?: boolean = false; 19 19 20 - @doc("Constant integer value") 20 + /** Constant integer value */ 21 21 @readOnly 22 22 @required 23 23 intConst: integer = 42; 24 24 25 - @doc("Constant zero") 25 + /** Constant zero */ 26 26 @readOnly 27 27 zeroConst?: integer = 0; 28 28 29 - @doc("Constant negative integer") 29 + /** Constant negative integer */ 30 30 @readOnly 31 31 negativeConst?: integer = -100; 32 32 }
+3 -3
packages/emitter/test/spec/basic/input/com/example/createRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.createRecord { 4 - @doc("The provided text is invalid") 4 + /** The provided text is invalid */ 5 5 model InvalidText {} 6 6 7 - @doc("Create a new record") 7 + /** Create a new record */ 8 8 @procedure 9 9 @errors(InvalidText) 10 10 op main(input: { 11 - @doc("The text content") 11 + /** The text content */ 12 12 @required 13 13 text: string; 14 14 }): {
+4 -4
packages/emitter/test/spec/basic/input/com/example/datetimeExamples.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.datetimeExamples { 4 - @doc("Datetime format usage examples") 4 + /** Datetime format usage examples */ 5 5 model Main { 6 - @doc("Required datetime field") 6 + /** Required datetime field */ 7 7 @required 8 8 createdAt: datetime; 9 9 10 - @doc("Optional datetime field") 10 + /** Optional datetime field */ 11 11 updatedAt?: datetime; 12 12 13 - @doc("Optional publish time") 13 + /** Optional publish time */ 14 14 publishedAt?: datetime; 15 15 } 16 16 }
+3 -3
packages/emitter/test/spec/basic/input/com/example/deeplyNested.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.deeplyNested { 4 - @doc("Tests deeply nested object structures") 4 + /** Tests deeply nested object structures */ 5 5 model Main { 6 - @doc("Three levels of nesting") 6 + /** Three levels of nesting */ 7 7 @required 8 8 level1: Level1; 9 9 } ··· 21 21 model Level3 { 22 22 @required data: string; 23 23 24 - @doc("Nullable field at deep level") 24 + /** Nullable field at deep level */ 25 25 metadata?: Metadata | null; 26 26 } 27 27
+1 -1
packages/emitter/test/spec/basic/input/com/example/defs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 - @doc("Common definitions used by other lexicons") 3 + /** Common definitions used by other lexicons */ 4 4 namespace com.example.defs { 5 5 @closed 6 6 union StatusEnum {
+1 -1
packages/emitter/test/spec/basic/input/com/example/deleteRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.deleteRecord { 4 - @doc("Delete a record (no output)") 4 + /** Delete a record (no output) */ 5 5 @procedure 6 6 op main(input: { 7 7 @required uri: atUri;
+2 -2
packages/emitter/test/spec/basic/input/com/example/eventStream.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.eventStream { 4 - @doc("Cursor is in the future") 4 + /** Cursor is in the future */ 5 5 model FutureCursor {} 6 6 7 - @doc("Subscription with union message schema") 7 + /** Subscription with union message schema */ 8 8 @subscription 9 9 @errors(FutureCursor) 10 10 op main(cursor?: integer): (Commit | Identity | Account | Handle | Tombstone);
+1 -1
packages/emitter/test/spec/basic/input/com/example/flexibleRecord.tsp
··· 2 2 3 3 namespace com.example.flexibleRecord { 4 4 @rec("any") 5 - @doc("Record with any key type") 5 + /** Record with any key type */ 6 6 model Main { 7 7 data?: string; 8 8 tags?: string[];
+2 -2
packages/emitter/test/spec/basic/input/com/example/getRecord.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.getRecord { 4 - @doc("Retrieve a record by ID") 4 + /** Retrieve a record by ID */ 5 5 @query 6 6 op main( 7 - @doc("The record identifier") 7 + /** The record identifier */ 8 8 @required 9 9 id: string 10 10 ): {
+1 -1
packages/emitter/test/spec/basic/input/com/example/getStatus.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.getStatus { 4 - @doc("Get system status (no parameters)") 4 + /** Get system status (no parameters) */ 5 5 @query 6 6 op main(): { 7 7 @required status: string;
+6 -6
packages/emitter/test/spec/basic/input/com/example/identifierExamples.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.identifierExamples { 4 - @doc("Identifier format examples") 4 + /** Identifier format examples */ 5 5 model Main { 6 - @doc("DID identifier (did:plc:, did:web:, etc)") 6 + /** DID identifier (did:plc:, did:web:, etc) */ 7 7 did?: did; 8 8 9 - @doc("Handle identifier (username.bsky.social)") 9 + /** Handle identifier (username.bsky.social) */ 10 10 handle?: handle; 11 11 12 - @doc("Either a DID or handle") 12 + /** Either a DID or handle */ 13 13 atIdentifier?: atIdentifier; 14 14 15 - @doc("CID as string") 15 + /** CID as string */ 16 16 cidString?: cid; 17 17 18 - @doc("BCP 47 language tag") 18 + /** BCP 47 language tag */ 19 19 languageCode?: language; 20 20 } 21 21 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/integerConstraints.tsp
··· 12 12 13, 13 13 } 14 14 15 - @doc("Integer field constraints") 15 + /** Integer field constraints */ 16 16 model Main { 17 - @doc("Integer between 1 and 100") 17 + /** Integer between 1 and 100 */ 18 18 @minValue(1) 19 19 @maxValue(100) 20 20 withMinMax?: integer; 21 21 22 - @doc("Fibonacci numbers only") 22 + /** Fibonacci numbers only */ 23 23 withEnum?: FibonacciNumbers; 24 24 25 - @doc("Integer with default value") 25 + /** Integer with default value */ 26 26 withDefault?: integer = 42; 27 27 28 - @doc("Constant integer value") 28 + /** Constant integer value */ 29 29 @readOnly 30 30 withConst?: integer = 99; 31 31 }
+6 -6
packages/emitter/test/spec/basic/input/com/example/nestedUnions.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.nestedUnions { 4 - @doc("Tests unions nested within objects and arrays") 4 + /** Tests unions nested within objects and arrays */ 5 5 model Main { 6 - @doc("Object containing a union field") 6 + /** Object containing a union field */ 7 7 @required 8 8 container: Container; 9 9 10 - @doc("Array of objects that contain unions") 10 + /** Array of objects that contain unions */ 11 11 items?: Item[]; 12 12 } 13 13 14 14 model Container { 15 15 @required id: string; 16 16 17 - @doc("Nested union within object") 17 + /** Nested union within object */ 18 18 @required 19 19 payload: (PayloadA | PayloadB | unknown); 20 20 21 - @doc("Nullable ref within object") 21 + /** Nullable ref within object */ 22 22 metadata?: MetadataPublic | null; 23 23 } 24 24 25 25 model Item { 26 26 @required name: string; 27 27 28 - @doc("Union in nested object") 28 + /** Union in nested object */ 29 29 content?: (TextContent | ImageContent | VideoContent | unknown); 30 30 } 31 31
+4 -4
packages/emitter/test/spec/basic/input/com/example/nullType.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.nullType { 4 - @doc("Demonstrates the null primitive type") 4 + /** Demonstrates the null primitive type */ 5 5 model Main { 6 - @doc("Field with explicit null type - always null") 6 + /** Field with explicit null type - always null */ 7 7 @required 8 8 alwaysNull: null; 9 9 10 - @doc("Optional field that can be string or omitted, but not null") 10 + /** Optional field that can be string or omitted, but not null */ 11 11 optionalString?: string; 12 12 13 - @doc("Nullable string - can be omitted, string, or null") 13 + /** Nullable string - can be omitted, string, or null */ 14 14 nullableString?: string | null; 15 15 } 16 16 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/nullableFields.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.nullableFields { 4 - @doc("Demonstrates nullable field semantics") 4 + /** Demonstrates nullable field semantics */ 5 5 model Main { 6 - @doc("Required, cannot be null or omitted") 6 + /** Required, cannot be null or omitted */ 7 7 @required 8 8 requiredField: string; 9 9 10 - @doc("Must be present, but can be null") 10 + /** Must be present, but can be null */ 11 11 nullableRequired?: string | null; 12 12 13 - @doc("Can be omitted, present with value, or present as null") 13 + /** Can be omitted, present with value, or present as null */ 14 14 nullableOptional?: string | null; 15 15 16 - @doc("Can be omitted or present with value, but not null") 16 + /** Can be omitted or present with value, but not null */ 17 17 optionalField?: string; 18 18 } 19 19 }
+4 -4
packages/emitter/test/spec/basic/input/com/example/objects.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.objects { 4 - @doc("Object with various property configurations") 4 + /** Object with various property configurations */ 5 5 model Main { 6 - @doc("This field is required") 6 + /** This field is required */ 7 7 @required 8 8 requiredField: string; 9 9 10 - @doc("This field is optional") 10 + /** This field is optional */ 11 11 optionalField?: string; 12 12 13 - @doc("This field can be null") 13 + /** This field can be null */ 14 14 nullableField?: string | null; 15 15 16 16 nestedObject?: Nested;
+1 -1
packages/emitter/test/spec/basic/input/com/example/outputWithoutSchema.tsp
··· 3 3 namespace com.example.outputWithoutSchema { 4 4 @query 5 5 @encoding("application/json") 6 - @doc("Query with JSON output but no schema defined") 6 + /** Query with JSON output but no schema defined */ 7 7 op main(id?: string): never; 8 8 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/paramsWithArrays.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.paramsWithArrays { 4 - @doc("Tests query parameters with array types") 4 + /** Tests query parameters with array types */ 5 5 @query 6 6 op main( 7 - @doc("Array of string values") 7 + /** Array of string values */ 8 8 tags?: string[], 9 9 10 - @doc("Array of integers") 10 + /** Array of integers */ 11 11 @maxItems(10) 12 12 ids?: integer[], 13 13 14 - @doc("Array of booleans") 14 + /** Array of booleans */ 15 15 flags?: boolean[], 16 16 17 - @doc("Single string param for comparison") 17 + /** Single string param for comparison */ 18 18 cursor?: string 19 19 ): { 20 20 @required count: integer;
+6 -6
packages/emitter/test/spec/basic/input/com/example/primitives.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.primitives { 4 - @doc("All primitive field types") 4 + /** All primitive field types */ 5 5 model Main { 6 - @doc("A boolean field") 6 + /** A boolean field */ 7 7 boolField?: boolean; 8 8 9 - @doc("An integer field") 9 + /** An integer field */ 10 10 intField?: integer; 11 11 12 - @doc("A string field") 12 + /** A string field */ 13 13 stringField?: string; 14 14 15 - @doc("A bytes field") 15 + /** A bytes field */ 16 16 bytesField?: bytes; 17 17 18 - @doc("A CID link field") 18 + /** A CID link field */ 19 19 cidField?: cidLink; 20 20 } 21 21 }
+5 -5
packages/emitter/test/spec/basic/input/com/example/procedureWithBoth.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.procedureWithBoth { 4 - @doc("Procedure with both input body and query parameters") 4 + /** Procedure with both input body and query parameters */ 5 5 @procedure 6 6 op main( 7 7 input: { 8 - @doc("Data to create") 8 + /** Data to create */ 9 9 @required 10 10 data: string; 11 11 12 - @doc("Optional metadata") 12 + /** Optional metadata */ 13 13 metadata?: Metadata; 14 14 }, 15 15 parameters: { 16 - @doc("Whether to validate before creating") 16 + /** Whether to validate before creating */ 17 17 validate?: boolean = true; 18 18 19 - @doc("Target repository") 19 + /** Target repository */ 20 20 @required 21 21 repo: atIdentifier; 22 22 }
+1 -1
packages/emitter/test/spec/basic/input/com/example/profile.tsp
··· 2 2 3 3 namespace com.example.profile { 4 4 @rec("literal:self") 5 - @doc("Record with literal 'self' key") 5 + /** Record with literal 'self' key */ 6 6 model Main { 7 7 @maxGraphemes(64) 8 8 @required
+7 -7
packages/emitter/test/spec/basic/input/com/example/queryParams.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.queryParams { 4 - @doc("Query with various parameter types") 4 + /** Query with various parameter types */ 5 5 @query 6 6 op main( 7 - @doc("A required query parameter") 7 + /** A required query parameter */ 8 8 @required 9 9 requiredParam: string, 10 10 11 - @doc("Optional boolean parameter") 11 + /** Optional boolean parameter */ 12 12 boolParam?: boolean, 13 13 14 - @doc("Optional integer parameter") 14 + /** Optional integer parameter */ 15 15 intParam?: integer, 16 16 17 - @doc("Optional string parameter") 17 + /** Optional string parameter */ 18 18 @maxLength(100) 19 19 stringParam?: string, 20 20 21 - @doc("Array of strings") 21 + /** Array of strings */ 22 22 arrayParam?: string[], 23 23 24 - @doc("Unknown type parameter") 24 + /** Unknown type parameter */ 25 25 unknownParam?: unknown 26 26 ): { 27 27 @required result: string;
+3 -3
packages/emitter/test/spec/basic/input/com/example/record.tsp
··· 2 2 3 3 namespace com.example.`record` { 4 4 @rec("tid") 5 - @doc("A simple record with basic properties") 5 + /** A simple record with basic properties */ 6 6 model Main { 7 - @doc("The text content") 7 + /** The text content */ 8 8 @required 9 9 text: string; 10 10 11 - @doc("When the record was created") 11 + /** When the record was created */ 12 12 @required 13 13 createdAt: datetime; 14 14 }
+3 -3
packages/emitter/test/spec/basic/input/com/example/recordWithNsidKey.tsp
··· 2 2 3 3 namespace com.example.recordWithNsidKey { 4 4 @rec("nsid") 5 - @doc("Record with NSID as the record key") 5 + /** Record with NSID as the record key */ 6 6 model Main { 7 - @doc("The NSID of the associated lexicon") 7 + /** The NSID of the associated lexicon */ 8 8 @required 9 9 nsid: nsid; 10 10 11 - @doc("Optional metadata") 11 + /** Optional metadata */ 12 12 metadata?: string; 13 13 } 14 14 }
+1 -1
packages/emitter/test/spec/basic/input/com/example/refInOutput.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.refInOutput { 4 - @doc("Procedure with ref in output schema") 4 + /** Procedure with ref in output schema */ 5 5 @procedure 6 6 op main(input: InputData): OutputData; 7 7
+4 -4
packages/emitter/test/spec/basic/input/com/example/refs.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.refs { 4 - @doc("Reference examples") 4 + /** Reference examples */ 5 5 model Main { 6 - @doc("Reference to local definition") 6 + /** Reference to local definition */ 7 7 localRef?: LocalDef; 8 8 9 - @doc("Reference to external definition") 9 + /** Reference to external definition */ 10 10 externalRef?: com.example.defs.Metadata; 11 11 12 - @doc("Reference to main definition (no fragment)") 12 + /** Reference to main definition (no fragment) */ 13 13 mainRef?: com.example.`record`.Main; 14 14 } 15 15
+8 -8
packages/emitter/test/spec/basic/input/com/example/stringConstraints.tsp
··· 9 9 "archived", 10 10 } 11 11 12 - @doc("String field constraints") 12 + /** String field constraints */ 13 13 model Main { 14 - @doc("String with byte length constraints") 14 + /** String with byte length constraints */ 15 15 @minLength(1) 16 16 @maxLength(100) 17 17 withLength?: string; 18 18 19 - @doc("String with grapheme cluster length constraints") 19 + /** String with grapheme cluster length constraints */ 20 20 @minGraphemes(1) 21 21 @maxGraphemes(50) 22 22 withGraphemes?: string; 23 23 24 - @doc("String with both byte and grapheme constraints") 24 + /** String with both byte and grapheme constraints */ 25 25 @minLength(1) 26 26 @maxLength(300) 27 27 @minGraphemes(1) 28 28 @maxGraphemes(100) 29 29 withBothLengths?: string; 30 30 31 - @doc("Closed enum of allowed values") 31 + /** Closed enum of allowed values */ 32 32 withEnum?: StatusEnum; 33 33 34 - @doc("Open set of suggested values") 34 + /** Open set of suggested values */ 35 35 withKnownValues?: "en" | "es" | "fr" | "de" | "ja" | string; 36 36 37 - @doc("String with default value") 37 + /** String with default value */ 38 38 withDefault?: string = "hello"; 39 39 40 - @doc("Constant string value") 40 + /** Constant string value */ 41 41 @readOnly 42 42 withConst?: string = "fixed-value"; 43 43 }
+12 -12
packages/emitter/test/spec/basic/input/com/example/stringFormats.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.stringFormats { 4 - @doc("All string format types") 4 + /** All string format types */ 5 5 model Main { 6 - @doc("Handle or DID") 6 + /** Handle or DID */ 7 7 atIdentifier?: atIdentifier; 8 8 9 - @doc("AT-URI format") 9 + /** AT-URI format */ 10 10 atUri?: atUri; 11 11 12 - @doc("CID in string format") 12 + /** CID in string format */ 13 13 cid?: cid; 14 14 15 - @doc("ISO 8601 datetime with timezone") 15 + /** ISO 8601 datetime with timezone */ 16 16 datetime?: datetime; 17 17 18 - @doc("DID identifier") 18 + /** DID identifier */ 19 19 did?: did; 20 20 21 - @doc("Handle identifier") 21 + /** Handle identifier */ 22 22 handle?: handle; 23 23 24 - @doc("Namespaced identifier") 24 + /** Namespaced identifier */ 25 25 nsid?: nsid; 26 26 27 - @doc("Timestamp identifier") 27 + /** Timestamp identifier */ 28 28 tid?: tid; 29 29 30 - @doc("Record key (any syntax)") 30 + /** Record key (any syntax) */ 31 31 recordKey?: recordKey; 32 32 33 - @doc("Generic URI (RFC 3986)") 33 + /** Generic URI (RFC 3986) */ 34 34 uri?: uri; 35 35 36 - @doc("IETF BCP 47 language tag") 36 + /** IETF BCP 47 language tag */ 37 37 language?: language; 38 38 } 39 39 }
+2 -2
packages/emitter/test/spec/basic/input/com/example/subscribeRecords.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.subscribeRecords { 4 - @doc("Subscribe to record updates") 4 + /** Subscribe to record updates */ 5 5 @subscription 6 6 op main( 7 - @doc("Optional cursor for resuming") 7 + /** Optional cursor for resuming */ 8 8 cursor?: integer 9 9 ): (Record | Delete); 10 10
+4 -4
packages/emitter/test/spec/basic/input/com/example/subscriptionWithErrors.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.subscriptionWithErrors { 4 - @doc("Subscription with error definitions") 4 + /** Subscription with error definitions */ 5 5 @subscription 6 6 @errors(ConsumerTooSlow, InvalidCursor) 7 7 op main( 8 - @doc("Optional cursor for resuming") 8 + /** Optional cursor for resuming */ 9 9 cursor?: integer 10 10 ): (Event | Status | Info); 11 11 ··· 25 25 message?: string; 26 26 } 27 27 28 - @doc("Indicates the consumer is not keeping up with the event stream") 28 + /** Indicates the consumer is not keeping up with the event stream */ 29 29 model ConsumerTooSlow {} 30 30 31 - @doc("The provided cursor is invalid or expired") 31 + /** The provided cursor is invalid or expired */ 32 32 model InvalidCursor {} 33 33 }
+1 -1
packages/emitter/test/spec/basic/input/com/example/tidRecord.tsp
··· 2 2 3 3 namespace com.example.tidRecord { 4 4 @rec("tid") 5 - @doc("Record with TID key type") 5 + /** Record with TID key type */ 6 6 model Main { 7 7 @required 8 8 content: string;
+4 -4
packages/emitter/test/spec/basic/input/com/example/tokens.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.tokens { 4 - @doc("Token usage example") 4 + /** Token usage example */ 5 5 model Main { 6 6 @required status: Active; 7 7 } 8 8 9 - @doc("Indicates an active state") 9 + /** Indicates an active state */ 10 10 @token 11 11 model Active {} 12 12 13 - @doc("Indicates an inactive state") 13 + /** Indicates an inactive state */ 14 14 @token 15 15 model Inactive {} 16 16 17 - @doc("Indicates a pending state") 17 + /** Indicates a pending state */ 18 18 @token 19 19 model Pending {} 20 20 }
+2 -2
packages/emitter/test/spec/basic/input/com/example/unionClosed.tsp
··· 9 9 Delete, 10 10 } 11 11 12 - @doc("Closed union example") 12 + /** Closed union example */ 13 13 model Main { 14 - @doc("Closed union - no more variants allowed") 14 + /** Closed union - no more variants allowed */ 15 15 @required 16 16 action: Action; 17 17 }
+2 -2
packages/emitter/test/spec/basic/input/com/example/unionEmpty.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.unionEmpty { 4 - @doc("Empty open union (similar to unknown but requires $type)") 4 + /** Empty open union (similar to unknown but requires $type) */ 5 5 model Main { 6 - @doc("Empty open union - any object with $type allowed") 6 + /** Empty open union - any object with $type allowed */ 7 7 data?: (never | unknown); 8 8 } 9 9 }
+2 -2
packages/emitter/test/spec/basic/input/com/example/unionOpen.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.unionOpen { 4 - @doc("Open union example (default)") 4 + /** Open union example (default) */ 5 5 model Main { 6 - @doc("Open union - more variants can be added in future") 6 + /** Open union - more variants can be added in future */ 7 7 @required 8 8 item: TypeA | TypeB | TypeC | unknown; 9 9 }
-22
packages/emitter/test/spec/basic/input/com/example/unionWithTokens.tsp
··· 1 - import "@typelex/emitter"; 2 - 3 - namespace com.example.unionWithTokens { 4 - @doc("Tests union with token references") 5 - model Main { 6 - @doc("Reason can be a known token or unknown type") 7 - @required 8 - reason: (ReasonSpam | ReasonViolation | ReasonMisleading | unknown); 9 - } 10 - 11 - @doc("Spam: frequent unwanted promotion") 12 - @token 13 - model ReasonSpam {} 14 - 15 - @doc("Direct violation of rules") 16 - @token 17 - model ReasonViolation {} 18 - 19 - @doc("Misleading or deceptive content") 20 - @token 21 - model ReasonMisleading {} 22 - }
+3 -3
packages/emitter/test/spec/basic/input/com/example/unknown.tsp
··· 3 3 using TypeSpec.Reflection; 4 4 5 5 namespace com.example.`unknown` { 6 - @doc("Unknown type usage") 6 + /** Unknown type usage */ 7 7 model Main { 8 - @doc("Any object data without validation") 8 + /** Any object data without validation */ 9 9 metadata?: unknown; 10 10 11 - @doc("Optional unknown field") 11 + /** Optional unknown field */ 12 12 optionalMetadata?: unknown; 13 13 } 14 14 }
+6 -6
packages/emitter/test/spec/basic/input/com/example/uriExamples.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.uriExamples { 4 - @doc("Various URI format examples") 4 + /** Various URI format examples */ 5 5 model Main { 6 - @doc("AT protocol URI") 6 + /** AT protocol URI */ 7 7 atUri?: atUri; 8 8 9 - @doc("Generic URI (https, did, ipfs, etc)") 9 + /** Generic URI (https, did, ipfs, etc) */ 10 10 genericUri?: uri; 11 11 12 - @doc("Record key identifier") 12 + /** Record key identifier */ 13 13 recordKey?: recordKey; 14 14 15 - @doc("Namespaced identifier") 15 + /** Namespaced identifier */ 16 16 nsid?: nsid; 17 17 18 - @doc("Timestamp identifier") 18 + /** Timestamp identifier */ 19 19 tid?: tid; 20 20 } 21 21 }
+3 -3
packages/emitter/test/spec/basic/input/com/example/withErrors.tsp
··· 1 1 import "@typelex/emitter"; 2 2 3 3 namespace com.example.withErrors { 4 - @doc("The provided value is invalid") 4 + /** The provided value is invalid */ 5 5 model InvalidValue {} 6 6 7 - @doc("Too many requests") 7 + /** Too many requests */ 8 8 model RateLimitExceeded {} 9 9 10 10 model Unauthorized {} 11 11 12 - @doc("Procedure with error definitions") 12 + /** Procedure with error definitions */ 13 13 @procedure 14 14 @errors(InvalidValue, RateLimitExceeded, Unauthorized) 15 15 op main(input: {
+21
packages/emitter/test/spec/basic/output/com/example/other.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.example.other", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "properties": {} 8 + }, 9 + "someDef": { 10 + "type": "object", 11 + "required": [ 12 + "value" 13 + ], 14 + "properties": { 15 + "value": { 16 + "type": "string" 17 + } 18 + } 19 + } 20 + } 21 + }
-34
packages/emitter/test/spec/basic/output/com/example/unionWithTokens.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "com.example.unionWithTokens", 4 - "defs": { 5 - "main": { 6 - "type": "object", 7 - "description": "Tests union with token references", 8 - "required": ["reason"], 9 - "properties": { 10 - "reason": { 11 - "type": "union", 12 - "description": "Reason can be a known token or unknown type", 13 - "refs": [ 14 - "#reasonSpam", 15 - "#reasonViolation", 16 - "#reasonMisleading" 17 - ] 18 - } 19 - } 20 - }, 21 - "reasonSpam": { 22 - "type": "token", 23 - "description": "Spam: frequent unwanted promotion" 24 - }, 25 - "reasonViolation": { 26 - "type": "token", 27 - "description": "Direct violation of rules" 28 - }, 29 - "reasonMisleading": { 30 - "type": "token", 31 - "description": "Misleading or deceptive content" 32 - } 33 - } 34 - }
+8
packages/emitter/test/spec/external/input/test/external.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + @external 4 + namespace test.external { 5 + model Main { } 6 + 7 + model AlsoNotEmitted { } 8 + }
+7
packages/emitter/test/spec/external/input/test/normal.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + namespace test.normal { 4 + model Main { 5 + name?: string; 6 + } 7 + }
+14
packages/emitter/test/spec/external/output/test/normal.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "test.normal", 4 + "defs": { 5 + "main": { 6 + "type": "object", 7 + "properties": { 8 + "name": { 9 + "type": "string" 10 + } 11 + } 12 + } 13 + } 14 + }
+32 -4
packages/emitter/test/spec.test.ts
··· 64 64 // Create a virtual main.tsp that imports all other files 65 65 const mainContent = 66 66 'import "@typelex/emitter";\n' + 67 - tspFiles.map((f) => `import "./${f}";`).join("\n"); 67 + tspFiles.map((f) => `import "./${normalizePathToPosix(f)}";`).join("\n"); 68 68 const filesWithMain = { ...inputFiles, "main.tsp": mainContent }; 69 69 emitResult = await doEmit(filesWithMain, "main.tsp"); 70 70 } else { ··· 91 91 ); 92 92 } 93 93 94 + const normalizedExpectedPath = normalizePathToPosix(expectedPath); 95 + 94 96 assert.ok( 95 97 Object.prototype.hasOwnProperty.call( 96 98 emitResult.files, 97 - expectedPath, 99 + normalizedExpectedPath, 98 100 ), 99 101 `Expected file ${expectedPath} was not produced`, 100 102 ); 101 103 102 - const actual = JSON.parse(emitResult.files[expectedPath]); 104 + const actual = JSON.parse(emitResult.files[normalizedExpectedPath]); 103 105 const expected = JSON.parse(expectedFiles[expectedPath]); 104 106 assert.deepStrictEqual(actual, expected); 105 107 }); 106 108 } else { 107 - it.skip(`TODO: ${expectedPath} (add ${inputPath})`, function () {}); 109 + it(`should emit ${expectedPath}`, function () { 110 + assert.fail( 111 + `Expected output file ${expectedPath} has no corresponding input file ${inputPath}. ` + 112 + `Either add the input file or remove the expected output.` 113 + ); 114 + }); 108 115 } 109 116 } 117 + 118 + // Check for unexpected emitted files 119 + it("should not emit unexpected files", function () { 120 + const emittedFiles = Object.keys(emitResult.files).filter(f => f.endsWith(".json")); 121 + const expectedPaths = Object.keys(expectedFiles) 122 + .filter(f => f.endsWith(".json")) 123 + .map(normalizePathToPosix); 124 + 125 + const unexpected = emittedFiles.filter(f => !expectedPaths.includes(f)); 126 + 127 + if (unexpected.length > 0) { 128 + assert.fail( 129 + `Unexpected files were emitted: ${unexpected.join(", ")}. ` + 130 + `Either add expected output files or ensure these should not be emitted.` 131 + ); 132 + } 133 + }); 110 134 }); 111 135 } 112 136 }); ··· 180 204 await walk(dir, ""); 181 205 return result; 182 206 } 207 + 208 + function normalizePathToPosix(thePath: string): string { 209 + return thePath.replaceAll(path.sep, path.posix.sep); 210 + }
+4 -4
packages/example/package.json
··· 4 4 "private": true, 5 5 "type": "module", 6 6 "scripts": { 7 - "build": "pnpm run build:typelex && pnpm run build:codegen", 8 - "build:typelex": "tsp compile typelex/main.tsp", 9 - "build:codegen": "lex gen-server --yes ./src lexicon/app/example/*.json" 7 + "build": "pnpm run build:lexicons && pnpm run build:codegen", 8 + "build:lexicons": "typelex compile xyz.statusphere.*", 9 + "build:codegen": "lex gen-server --yes ./src lexicons/xyz/statusphere/*.json" 10 10 }, 11 11 "dependencies": { 12 12 "@atproto/lex-cli": "^0.9.5", 13 13 "@atproto/xrpc-server": "^0.9.5", 14 - "@typespec/compiler": "^1.4.0", 14 + "@typelex/cli": "workspace:*", 15 15 "@typelex/emitter": "workspace:*" 16 16 }, 17 17 "devDependencies": {
+45 -6
packages/example/src/index.ts
··· 10 10 createServer as createXrpcServer, 11 11 } from '@atproto/xrpc-server' 12 12 import { schemas } from './lexicons.js' 13 + import * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' 14 + import * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' 15 + import * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' 13 16 14 17 export function createServer(options?: XrpcOptions): Server { 15 18 return new Server(options) ··· 17 20 18 21 export class Server { 19 22 xrpc: XrpcServer 20 - app: AppNS 23 + xyz: XyzNS 21 24 22 25 constructor(options?: XrpcOptions) { 23 26 this.xrpc = createXrpcServer(schemas, options) 24 - this.app = new AppNS(this) 27 + this.xyz = new XyzNS(this) 25 28 } 26 29 } 27 30 28 - export class AppNS { 31 + export class XyzNS { 29 32 _server: Server 30 - example: AppExampleNS 33 + statusphere: XyzStatusphereNS 31 34 32 35 constructor(server: Server) { 33 36 this._server = server 34 - this.example = new AppExampleNS(server) 37 + this.statusphere = new XyzStatusphereNS(server) 35 38 } 36 39 } 37 40 38 - export class AppExampleNS { 41 + export class XyzStatusphereNS { 39 42 _server: Server 40 43 41 44 constructor(server: Server) { 42 45 this._server = server 46 + } 47 + 48 + getStatuses<A extends Auth = void>( 49 + cfg: MethodConfigOrHandler< 50 + A, 51 + XyzStatusphereGetStatuses.QueryParams, 52 + XyzStatusphereGetStatuses.HandlerInput, 53 + XyzStatusphereGetStatuses.HandlerOutput 54 + >, 55 + ) { 56 + const nsid = 'xyz.statusphere.getStatuses' // @ts-ignore 57 + return this._server.xrpc.method(nsid, cfg) 58 + } 59 + 60 + getUser<A extends Auth = void>( 61 + cfg: MethodConfigOrHandler< 62 + A, 63 + XyzStatusphereGetUser.QueryParams, 64 + XyzStatusphereGetUser.HandlerInput, 65 + XyzStatusphereGetUser.HandlerOutput 66 + >, 67 + ) { 68 + const nsid = 'xyz.statusphere.getUser' // @ts-ignore 69 + return this._server.xrpc.method(nsid, cfg) 70 + } 71 + 72 + sendStatus<A extends Auth = void>( 73 + cfg: MethodConfigOrHandler< 74 + A, 75 + XyzStatusphereSendStatus.QueryParams, 76 + XyzStatusphereSendStatus.HandlerInput, 77 + XyzStatusphereSendStatus.HandlerOutput 78 + >, 79 + ) { 80 + const nsid = 'xyz.statusphere.sendStatus' // @ts-ignore 81 + return this._server.xrpc.method(nsid, cfg) 43 82 } 44 83 }
+100 -153
packages/example/src/lexicons.ts
··· 10 10 import { type $Typed, is$typed, maybe$typed } from './util.js' 11 11 12 12 export const schemaDict = { 13 - AppExampleDefs: { 13 + XyzStatusphereDefs: { 14 14 lexicon: 1, 15 - id: 'app.example.defs', 15 + id: 'xyz.statusphere.defs', 16 16 defs: { 17 - postRef: { 17 + statusView: { 18 18 type: 'object', 19 19 properties: { 20 20 uri: { 21 21 type: 'string', 22 - description: 'AT URI of the post', 22 + format: 'at-uri', 23 23 }, 24 - cid: { 24 + status: { 25 25 type: 'string', 26 - description: 'CID of the post', 26 + maxLength: 32, 27 + minLength: 1, 28 + maxGraphemes: 1, 27 29 }, 28 - }, 29 - description: 'Reference to a post', 30 - required: ['uri', 'cid'], 31 - }, 32 - replyRef: { 33 - type: 'object', 34 - properties: { 35 - root: { 36 - type: 'ref', 37 - ref: 'lex:app.example.defs#postRef', 38 - description: 'Root post in the thread', 30 + createdAt: { 31 + type: 'string', 32 + format: 'datetime', 39 33 }, 40 - parent: { 34 + profile: { 41 35 type: 'ref', 42 - ref: 'lex:app.example.defs#postRef', 43 - description: 'Direct parent post being replied to', 36 + ref: 'lex:xyz.statusphere.defs#profileView', 44 37 }, 45 38 }, 46 - description: 'Reference to a parent post in a reply chain', 47 - required: ['root', 'parent'], 39 + required: ['uri', 'status', 'createdAt', 'profile'], 48 40 }, 49 - entity: { 41 + profileView: { 50 42 type: 'object', 51 43 properties: { 52 - start: { 53 - type: 'integer', 54 - description: 'Start index in text', 55 - }, 56 - end: { 57 - type: 'integer', 58 - description: 'End index in text', 59 - }, 60 - type: { 44 + did: { 61 45 type: 'string', 62 - description: 'Entity type', 46 + format: 'did', 63 47 }, 64 - value: { 48 + handle: { 65 49 type: 'string', 66 - description: 'Entity value (handle, URL, or tag)', 50 + format: 'handle', 67 51 }, 68 52 }, 69 - description: 'Text entity (mention, link, or tag)', 70 - required: ['start', 'end', 'type', 'value'], 71 - }, 72 - notificationType: { 73 - type: 'string', 74 - knownValues: ['like', 'repost', 'follow', 'mention', 'reply'], 75 - description: 'Type of notification', 53 + required: ['did', 'handle'], 76 54 }, 77 55 }, 78 56 }, 79 - AppExampleFollow: { 57 + XyzStatusphereGetStatuses: { 80 58 lexicon: 1, 81 - id: 'app.example.follow', 59 + id: 'xyz.statusphere.getStatuses', 82 60 defs: { 83 61 main: { 84 - type: 'record', 85 - key: 'tid', 86 - record: { 87 - type: 'object', 62 + type: 'query', 63 + description: 'Get a list of the most recent statuses on the network.', 64 + parameters: { 65 + type: 'params', 88 66 properties: { 89 - subject: { 90 - type: 'string', 91 - description: 'DID of the account being followed', 92 - }, 93 - createdAt: { 94 - type: 'string', 95 - format: 'datetime', 96 - description: 'When the follow was created', 67 + limit: { 68 + type: 'integer', 69 + minimum: 1, 70 + maximum: 100, 71 + default: 50, 97 72 }, 98 73 }, 99 - required: ['subject', 'createdAt'], 100 74 }, 101 - description: 'A follow relationship', 102 - }, 103 - }, 104 - }, 105 - AppExampleLike: { 106 - lexicon: 1, 107 - id: 'app.example.like', 108 - defs: { 109 - main: { 110 - type: 'record', 111 - key: 'tid', 112 - record: { 113 - type: 'object', 114 - properties: { 115 - subject: { 116 - type: 'ref', 117 - ref: 'lex:app.example.defs#postRef', 118 - description: 'Post being liked', 75 + output: { 76 + encoding: 'application/json', 77 + schema: { 78 + type: 'object', 79 + properties: { 80 + statuses: { 81 + type: 'array', 82 + items: { 83 + type: 'ref', 84 + ref: 'lex:xyz.statusphere.defs#statusView', 85 + }, 86 + }, 119 87 }, 120 - createdAt: { 121 - type: 'string', 122 - format: 'datetime', 123 - description: 'When the like was created', 124 - }, 88 + required: ['statuses'], 125 89 }, 126 - required: ['subject', 'createdAt'], 127 90 }, 128 - description: 'A like on a post', 129 91 }, 130 92 }, 131 93 }, 132 - AppExamplePost: { 94 + XyzStatusphereGetUser: { 133 95 lexicon: 1, 134 - id: 'app.example.post', 96 + id: 'xyz.statusphere.getUser', 135 97 defs: { 136 98 main: { 137 - type: 'record', 138 - key: 'tid', 139 - record: { 140 - type: 'object', 141 - properties: { 142 - text: { 143 - type: 'string', 144 - description: 'Post text content', 145 - }, 146 - createdAt: { 147 - type: 'string', 148 - format: 'datetime', 149 - description: 'Creation timestamp', 150 - }, 151 - langs: { 152 - type: 'array', 153 - items: { 154 - type: 'string', 99 + type: 'query', 100 + description: "Get the current user's profile and status.", 101 + output: { 102 + encoding: 'application/json', 103 + schema: { 104 + type: 'object', 105 + properties: { 106 + profile: { 107 + type: 'ref', 108 + ref: 'lex:app.bsky.actor.defs#profileView', 155 109 }, 156 - description: 'Languages the post is written in', 157 - }, 158 - entities: { 159 - type: 'array', 160 - items: { 110 + status: { 161 111 type: 'ref', 162 - ref: 'lex:app.example.defs#entity', 112 + ref: 'lex:xyz.statusphere.defs#statusView', 163 113 }, 164 - description: 'Referenced entities in the post', 165 114 }, 166 - reply: { 167 - type: 'ref', 168 - ref: 'lex:app.example.defs#replyRef', 169 - description: 'Post the user is replying to', 170 - }, 115 + required: ['profile'], 171 116 }, 172 - required: ['text', 'createdAt'], 173 117 }, 174 - description: 'A post in the feed', 175 118 }, 176 119 }, 177 120 }, 178 - AppExampleProfile: { 121 + XyzStatusphereSendStatus: { 179 122 lexicon: 1, 180 - id: 'app.example.profile', 123 + id: 'xyz.statusphere.sendStatus', 181 124 defs: { 182 125 main: { 183 - type: 'record', 184 - key: 'self', 185 - record: { 186 - type: 'object', 187 - properties: { 188 - displayName: { 189 - type: 'string', 190 - description: 'Display name', 191 - }, 192 - description: { 193 - type: 'string', 194 - description: 'Profile description', 126 + type: 'procedure', 127 + description: 'Send a status into the ATmosphere.', 128 + input: { 129 + encoding: 'application/json', 130 + schema: { 131 + type: 'object', 132 + properties: { 133 + status: { 134 + type: 'string', 135 + maxLength: 32, 136 + minLength: 1, 137 + maxGraphemes: 1, 138 + }, 195 139 }, 196 - avatar: { 197 - type: 'string', 198 - description: 'Profile avatar image', 140 + required: ['status'], 141 + }, 142 + }, 143 + output: { 144 + encoding: 'application/json', 145 + schema: { 146 + type: 'object', 147 + properties: { 148 + status: { 149 + type: 'ref', 150 + ref: 'lex:xyz.statusphere.defs#statusView', 151 + }, 199 152 }, 200 - banner: { 201 - type: 'string', 202 - description: 'Profile banner image', 203 - }, 153 + required: ['status'], 204 154 }, 205 155 }, 206 - description: 'User profile information', 207 156 }, 208 157 }, 209 158 }, 210 - AppExampleRepost: { 159 + XyzStatusphereStatus: { 211 160 lexicon: 1, 212 - id: 'app.example.repost', 161 + id: 'xyz.statusphere.status', 213 162 defs: { 214 163 main: { 215 164 type: 'record', ··· 217 166 record: { 218 167 type: 'object', 219 168 properties: { 220 - subject: { 221 - type: 'ref', 222 - ref: 'lex:app.example.defs#postRef', 223 - description: 'Post being reposted', 169 + status: { 170 + type: 'string', 171 + maxLength: 32, 172 + minLength: 1, 173 + maxGraphemes: 1, 224 174 }, 225 175 createdAt: { 226 176 type: 'string', 227 177 format: 'datetime', 228 - description: 'When the repost was created', 229 178 }, 230 179 }, 231 - required: ['subject', 'createdAt'], 180 + required: ['status', 'createdAt'], 232 181 }, 233 - description: 'A repost of another post', 234 182 }, 235 183 }, 236 184 }, ··· 267 215 } 268 216 269 217 export const ids = { 270 - AppExampleDefs: 'app.example.defs', 271 - AppExampleFollow: 'app.example.follow', 272 - AppExampleLike: 'app.example.like', 273 - AppExamplePost: 'app.example.post', 274 - AppExampleProfile: 'app.example.profile', 275 - AppExampleRepost: 'app.example.repost', 218 + XyzStatusphereDefs: 'xyz.statusphere.defs', 219 + XyzStatusphereGetStatuses: 'xyz.statusphere.getStatuses', 220 + XyzStatusphereGetUser: 'xyz.statusphere.getUser', 221 + XyzStatusphereSendStatus: 'xyz.statusphere.sendStatus', 222 + XyzStatusphereStatus: 'xyz.statusphere.status', 276 223 } as const
-79
packages/example/src/types/app/example/defs.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - 9 - const is$typed = _is$typed, 10 - validate = _validate 11 - const id = 'app.example.defs' 12 - 13 - /** Reference to a post */ 14 - export interface PostRef { 15 - $type?: 'app.example.defs#postRef' 16 - /** AT URI of the post */ 17 - uri: string 18 - /** CID of the post */ 19 - cid: string 20 - } 21 - 22 - const hashPostRef = 'postRef' 23 - 24 - export function isPostRef<V>(v: V) { 25 - return is$typed(v, id, hashPostRef) 26 - } 27 - 28 - export function validatePostRef<V>(v: V) { 29 - return validate<PostRef & V>(v, id, hashPostRef) 30 - } 31 - 32 - /** Reference to a parent post in a reply chain */ 33 - export interface ReplyRef { 34 - $type?: 'app.example.defs#replyRef' 35 - root: PostRef 36 - parent: PostRef 37 - } 38 - 39 - const hashReplyRef = 'replyRef' 40 - 41 - export function isReplyRef<V>(v: V) { 42 - return is$typed(v, id, hashReplyRef) 43 - } 44 - 45 - export function validateReplyRef<V>(v: V) { 46 - return validate<ReplyRef & V>(v, id, hashReplyRef) 47 - } 48 - 49 - /** Text entity (mention, link, or tag) */ 50 - export interface Entity { 51 - $type?: 'app.example.defs#entity' 52 - /** Start index in text */ 53 - start: number 54 - /** End index in text */ 55 - end: number 56 - /** Entity type */ 57 - type: string 58 - /** Entity value (handle, URL, or tag) */ 59 - value: string 60 - } 61 - 62 - const hashEntity = 'entity' 63 - 64 - export function isEntity<V>(v: V) { 65 - return is$typed(v, id, hashEntity) 66 - } 67 - 68 - export function validateEntity<V>(v: V) { 69 - return validate<Entity & V>(v, id, hashEntity) 70 - } 71 - 72 - /** Type of notification */ 73 - export type NotificationType = 74 - | 'like' 75 - | 'repost' 76 - | 'follow' 77 - | 'mention' 78 - | 'reply' 79 - | (string & {})
-30
packages/example/src/types/app/example/follow.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - 9 - const is$typed = _is$typed, 10 - validate = _validate 11 - const id = 'app.example.follow' 12 - 13 - export interface Record { 14 - $type: 'app.example.follow' 15 - /** DID of the account being followed */ 16 - subject: string 17 - /** When the follow was created */ 18 - createdAt: string 19 - [k: string]: unknown 20 - } 21 - 22 - const hashRecord = 'main' 23 - 24 - export function isRecord<V>(v: V) { 25 - return is$typed(v, id, hashRecord) 26 - } 27 - 28 - export function validateRecord<V>(v: V) { 29 - return validate<Record & V>(v, id, hashRecord, true) 30 - }
-30
packages/example/src/types/app/example/like.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as AppExampleDefs from './defs.js' 9 - 10 - const is$typed = _is$typed, 11 - validate = _validate 12 - const id = 'app.example.like' 13 - 14 - export interface Record { 15 - $type: 'app.example.like' 16 - subject: AppExampleDefs.PostRef 17 - /** When the like was created */ 18 - createdAt: string 19 - [k: string]: unknown 20 - } 21 - 22 - const hashRecord = 'main' 23 - 24 - export function isRecord<V>(v: V) { 25 - return is$typed(v, id, hashRecord) 26 - } 27 - 28 - export function validateRecord<V>(v: V) { 29 - return validate<Record & V>(v, id, hashRecord, true) 30 - }
-36
packages/example/src/types/app/example/post.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as AppExampleDefs from './defs.js' 9 - 10 - const is$typed = _is$typed, 11 - validate = _validate 12 - const id = 'app.example.post' 13 - 14 - export interface Record { 15 - $type: 'app.example.post' 16 - /** Post text content */ 17 - text: string 18 - /** Creation timestamp */ 19 - createdAt: string 20 - /** Languages the post is written in */ 21 - langs?: string[] 22 - /** Referenced entities in the post */ 23 - entities?: AppExampleDefs.Entity[] 24 - reply?: AppExampleDefs.ReplyRef 25 - [k: string]: unknown 26 - } 27 - 28 - const hashRecord = 'main' 29 - 30 - export function isRecord<V>(v: V) { 31 - return is$typed(v, id, hashRecord) 32 - } 33 - 34 - export function validateRecord<V>(v: V) { 35 - return validate<Record & V>(v, id, hashRecord, true) 36 - }
-34
packages/example/src/types/app/example/profile.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - 9 - const is$typed = _is$typed, 10 - validate = _validate 11 - const id = 'app.example.profile' 12 - 13 - export interface Record { 14 - $type: 'app.example.profile' 15 - /** Display name */ 16 - displayName?: string 17 - /** Profile description */ 18 - description?: string 19 - /** Profile avatar image */ 20 - avatar?: string 21 - /** Profile banner image */ 22 - banner?: string 23 - [k: string]: unknown 24 - } 25 - 26 - const hashRecord = 'main' 27 - 28 - export function isRecord<V>(v: V) { 29 - return is$typed(v, id, hashRecord) 30 - } 31 - 32 - export function validateRecord<V>(v: V) { 33 - return validate<Record & V>(v, id, hashRecord, true) 34 - }
-30
packages/example/src/types/app/example/repost.ts
··· 1 - /** 2 - * GENERATED CODE - DO NOT MODIFY 3 - */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as AppExampleDefs from './defs.js' 9 - 10 - const is$typed = _is$typed, 11 - validate = _validate 12 - const id = 'app.example.repost' 13 - 14 - export interface Record { 15 - $type: 'app.example.repost' 16 - subject: AppExampleDefs.PostRef 17 - /** When the repost was created */ 18 - createdAt: string 19 - [k: string]: unknown 20 - } 21 - 22 - const hashRecord = 'main' 23 - 24 - export function isRecord<V>(v: V) { 25 - return is$typed(v, id, hashRecord) 26 - } 27 - 28 - export function validateRecord<V>(v: V) { 29 - return validate<Record & V>(v, id, hashRecord, true) 30 - }
+45
packages/example/src/types/xyz/statusphere/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + 9 + const is$typed = _is$typed, 10 + validate = _validate 11 + const id = 'xyz.statusphere.defs' 12 + 13 + export interface StatusView { 14 + $type?: 'xyz.statusphere.defs#statusView' 15 + uri: string 16 + status: string 17 + createdAt: string 18 + profile: ProfileView 19 + } 20 + 21 + const hashStatusView = 'statusView' 22 + 23 + export function isStatusView<V>(v: V) { 24 + return is$typed(v, id, hashStatusView) 25 + } 26 + 27 + export function validateStatusView<V>(v: V) { 28 + return validate<StatusView & V>(v, id, hashStatusView) 29 + } 30 + 31 + export interface ProfileView { 32 + $type?: 'xyz.statusphere.defs#profileView' 33 + did: string 34 + handle: string 35 + } 36 + 37 + const hashProfileView = 'profileView' 38 + 39 + export function isProfileView<V>(v: V) { 40 + return is$typed(v, id, hashProfileView) 41 + } 42 + 43 + export function validateProfileView<V>(v: V) { 44 + return validate<ProfileView & V>(v, id, hashProfileView) 45 + }
+36
packages/example/src/types/xyz/statusphere/getStatuses.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as XyzStatusphereDefs from './defs.js' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'xyz.statusphere.getStatuses' 13 + 14 + export type QueryParams = { 15 + limit: number 16 + } 17 + export type InputSchema = undefined 18 + 19 + export interface OutputSchema { 20 + statuses: XyzStatusphereDefs.StatusView[] 21 + } 22 + 23 + export type HandlerInput = void 24 + 25 + export interface HandlerSuccess { 26 + encoding: 'application/json' 27 + body: OutputSchema 28 + headers?: { [key: string]: string } 29 + } 30 + 31 + export interface HandlerError { 32 + status: number 33 + message?: string 34 + } 35 + 36 + export type HandlerOutput = HandlerError | HandlerSuccess
+36
packages/example/src/types/xyz/statusphere/getUser.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as AppBskyActorDefs from '../../app/bsky/actor/defs.js' 9 + import type * as XyzStatusphereDefs from './defs.js' 10 + 11 + const is$typed = _is$typed, 12 + validate = _validate 13 + const id = 'xyz.statusphere.getUser' 14 + 15 + export type QueryParams = {} 16 + export type InputSchema = undefined 17 + 18 + export interface OutputSchema { 19 + profile: AppBskyActorDefs.ProfileView 20 + status?: XyzStatusphereDefs.StatusView 21 + } 22 + 23 + export type HandlerInput = void 24 + 25 + export interface HandlerSuccess { 26 + encoding: 'application/json' 27 + body: OutputSchema 28 + headers?: { [key: string]: string } 29 + } 30 + 31 + export interface HandlerError { 32 + status: number 33 + message?: string 34 + } 35 + 36 + export type HandlerOutput = HandlerError | HandlerSuccess
+40
packages/example/src/types/xyz/statusphere/sendStatus.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as XyzStatusphereDefs from './defs.js' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'xyz.statusphere.sendStatus' 13 + 14 + export type QueryParams = {} 15 + 16 + export interface InputSchema { 17 + status: string 18 + } 19 + 20 + export interface OutputSchema { 21 + status: XyzStatusphereDefs.StatusView 22 + } 23 + 24 + export interface HandlerInput { 25 + encoding: 'application/json' 26 + body: InputSchema 27 + } 28 + 29 + export interface HandlerSuccess { 30 + encoding: 'application/json' 31 + body: OutputSchema 32 + headers?: { [key: string]: string } 33 + } 34 + 35 + export interface HandlerError { 36 + status: number 37 + message?: string 38 + } 39 + 40 + export type HandlerOutput = HandlerError | HandlerSuccess
+28
packages/example/src/types/xyz/statusphere/status.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + 9 + const is$typed = _is$typed, 10 + validate = _validate 11 + const id = 'xyz.statusphere.status' 12 + 13 + export interface Record { 14 + $type: 'xyz.statusphere.status' 15 + status: string 16 + createdAt: string 17 + [k: string]: unknown 18 + } 19 + 20 + const hashRecord = 'main' 21 + 22 + export function isRecord<V>(v: V) { 23 + return is$typed(v, id, hashRecord) 24 + } 25 + 26 + export function validateRecord<V>(v: V) { 27 + return validate<Record & V>(v, id, hashRecord, true) 28 + }
-5
packages/example/tspconfig.yaml
··· 1 - emit: 2 - - "@typelex/emitter" 3 - options: 4 - "@typelex/emitter": 5 - output-dir: "./lexicon"
+235
packages/example/typelex/externals.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + // Generated by typelex 4 + // This file is auto-generated. Do not edit manually. 5 + 6 + @external 7 + namespace app.bsky.actor.defs { 8 + model AdultContentPref { } 9 + model BskyAppProgressGuide { } 10 + model BskyAppStatePref { } 11 + model ContentLabelPref { } 12 + model FeedViewPref { } 13 + model HiddenPostsPref { } 14 + model InterestsPref { } 15 + model KnownFollowers { } 16 + model LabelerPrefItem { } 17 + model LabelersPref { } 18 + model MutedWord { } 19 + model MutedWordsPref { } 20 + model MutedWordTarget { } 21 + model Nux { } 22 + model PersonalDetailsPref { } 23 + model PostInteractionSettingsPref { } 24 + model ProfileAssociated { } 25 + model ProfileAssociatedChat { } 26 + model ProfileView { } 27 + model ProfileViewBasic { } 28 + model ProfileViewDetailed { } 29 + model SavedFeed { } 30 + model SavedFeedsPref { } 31 + model SavedFeedsPrefV2 { } 32 + model ThreadViewPref { } 33 + model ViewerState { } 34 + } 35 + 36 + @external 37 + namespace app.bsky.actor.profile { 38 + model Main { } 39 + } 40 + 41 + @external 42 + namespace app.bsky.embed.defs { 43 + model AspectRatio { } 44 + } 45 + 46 + @external 47 + namespace app.bsky.embed.external { 48 + model External { } 49 + model Main { } 50 + model View { } 51 + model ViewExternal { } 52 + } 53 + 54 + @external 55 + namespace app.bsky.embed.images { 56 + model Image { } 57 + model Main { } 58 + model View { } 59 + model ViewImage { } 60 + } 61 + 62 + @external 63 + namespace app.bsky.embed.`record` { 64 + model Main { } 65 + model View { } 66 + model ViewBlocked { } 67 + model ViewDetached { } 68 + model ViewNotFound { } 69 + model ViewRecord { } 70 + } 71 + 72 + @external 73 + namespace app.bsky.embed.recordWithMedia { 74 + model Main { } 75 + model View { } 76 + } 77 + 78 + @external 79 + namespace app.bsky.embed.video { 80 + model Caption { } 81 + model Main { } 82 + model View { } 83 + } 84 + 85 + @external 86 + namespace app.bsky.feed.defs { 87 + model BlockedAuthor { } 88 + model BlockedPost { } 89 + @token model ClickthroughAuthor { } 90 + @token model ClickthroughEmbed { } 91 + @token model ClickthroughItem { } 92 + @token model ClickthroughReposter { } 93 + @token model ContentModeUnspecified { } 94 + @token model ContentModeVideo { } 95 + model FeedViewPost { } 96 + model GeneratorView { } 97 + model GeneratorViewerState { } 98 + model Interaction { } 99 + @token model InteractionLike { } 100 + @token model InteractionQuote { } 101 + @token model InteractionReply { } 102 + @token model InteractionRepost { } 103 + @token model InteractionSeen { } 104 + @token model InteractionShare { } 105 + model NotFoundPost { } 106 + model PostView { } 107 + model ReasonPin { } 108 + model ReasonRepost { } 109 + model ReplyRef { } 110 + @token model RequestLess { } 111 + @token model RequestMore { } 112 + model SkeletonFeedPost { } 113 + model SkeletonReasonPin { } 114 + model SkeletonReasonRepost { } 115 + model ThreadContext { } 116 + model ThreadgateView { } 117 + model ThreadViewPost { } 118 + model ViewerState { } 119 + } 120 + 121 + @external 122 + namespace app.bsky.feed.postgate { 123 + model DisableRule { } 124 + model Main { } 125 + } 126 + 127 + @external 128 + namespace app.bsky.feed.threadgate { 129 + model FollowerRule { } 130 + model FollowingRule { } 131 + model ListRule { } 132 + model Main { } 133 + model MentionRule { } 134 + } 135 + 136 + @external 137 + namespace app.bsky.graph.defs { 138 + @token model Curatelist { } 139 + model ListItemView { } 140 + model ListPurpose { } 141 + model ListView { } 142 + model ListViewBasic { } 143 + model ListViewerState { } 144 + @token model Modlist { } 145 + model NotFoundActor { } 146 + @token model Referencelist { } 147 + model Relationship { } 148 + model StarterPackView { } 149 + model StarterPackViewBasic { } 150 + } 151 + 152 + @external 153 + namespace app.bsky.labeler.defs { 154 + model LabelerPolicies { } 155 + model LabelerView { } 156 + model LabelerViewDetailed { } 157 + model LabelerViewerState { } 158 + } 159 + 160 + @external 161 + namespace app.bsky.richtext.facet { 162 + model ByteSlice { } 163 + model Link { } 164 + model Main { } 165 + model Mention { } 166 + model Tag { } 167 + } 168 + 169 + @external 170 + namespace com.atproto.label.defs { 171 + model Label { } 172 + model LabelValue { } 173 + model LabelValueDefinition { } 174 + model LabelValueDefinitionStrings { } 175 + model SelfLabel { } 176 + model SelfLabels { } 177 + } 178 + 179 + @external 180 + namespace com.atproto.repo.applyWrites { 181 + model Create { } 182 + model CreateResult { } 183 + model Delete { } 184 + model DeleteResult { } 185 + model Update { } 186 + model UpdateResult { } 187 + } 188 + 189 + @external 190 + namespace com.atproto.repo.createRecord { 191 + } 192 + 193 + @external 194 + namespace com.atproto.repo.defs { 195 + model CommitMeta { } 196 + } 197 + 198 + @external 199 + namespace com.atproto.repo.deleteRecord { 200 + } 201 + 202 + @external 203 + namespace com.atproto.repo.describeRepo { 204 + } 205 + 206 + @external 207 + namespace com.atproto.repo.getRecord { 208 + } 209 + 210 + @external 211 + namespace com.atproto.repo.importRepo { 212 + } 213 + 214 + @external 215 + namespace com.atproto.repo.listMissingBlobs { 216 + model RecordBlob { } 217 + } 218 + 219 + @external 220 + namespace com.atproto.repo.listRecords { 221 + model Record { } 222 + } 223 + 224 + @external 225 + namespace com.atproto.repo.putRecord { 226 + } 227 + 228 + @external 229 + namespace com.atproto.repo.strongRef { 230 + model Main { } 231 + } 232 + 233 + @external 234 + namespace com.atproto.repo.uploadBlob { 235 + }
+46 -122
packages/example/typelex/main.tsp
··· 1 1 import "@typelex/emitter"; 2 - 3 - // Example showing typelex as source of truth for atproto lexicons 4 - 5 - // ============ Common Types ============ 6 - 7 - namespace app.example.defs { 8 - @doc("Type of notification") 9 - union notificationType { 10 - string, 11 - 12 - Like: "like", 13 - Repost: "repost", 14 - Follow: "follow", 15 - Mention: "mention", 16 - Reply: "reply", 17 - } 18 - 19 - @doc("Reference to a post") 20 - model PostRef { 21 - @doc("AT URI of the post") 22 - @required 23 - uri: string; 24 - 25 - @doc("CID of the post") 26 - @required 27 - cid: string; 28 - } 29 - 30 - @doc("Reference to a parent post in a reply chain") 31 - model ReplyRef { 32 - @doc("Root post in the thread") 33 - @required 34 - root: PostRef; 35 - 36 - @doc("Direct parent post being replied to") 37 - @required 38 - parent: PostRef; 39 - } 40 - 41 - @doc("Text entity (mention, link, or tag)") 42 - model Entity { 43 - @doc("Start index in text") 44 - @required 45 - start: int32; 2 + import "./externals.tsp"; 46 3 47 - @doc("End index in text") 48 - @required 49 - end: int32; 4 + namespace xyz.statusphere.defs { 5 + model StatusView { 6 + @required uri: atUri; 50 7 51 - @doc("Entity type") 52 8 @required 53 - type: string; 9 + @minLength(1) 10 + @maxGraphemes(1) 11 + @maxLength(32) 12 + status: string; 54 13 55 - @doc("Entity value (handle, URL, or tag)") 56 - @required 57 - value: string; 14 + @required createdAt: datetime; 15 + @required profile: ProfileView; 58 16 } 59 - } 60 17 61 - // ============ Records ============ 62 - 63 - namespace app.example.post { 64 - @rec("tid") 65 - @doc("A post in the feed") 66 - model Main { 67 - @doc("Post text content") 68 - @required 69 - text: string; 70 - 71 - @doc("Creation timestamp") 72 - @required 73 - createdAt: datetime; 74 - 75 - @doc("Languages the post is written in") 76 - langs?: string[]; 77 - 78 - @doc("Referenced entities in the post") 79 - entities?: app.example.defs.Entity[]; 80 - 81 - @doc("Post the user is replying to") 82 - reply?: app.example.defs.ReplyRef; 18 + model ProfileView { 19 + @required did: did; 20 + @required handle: handle; 83 21 } 84 22 } 85 23 86 - namespace app.example.follow { 24 + namespace xyz.statusphere.status { 87 25 @rec("tid") 88 - @doc("A follow relationship") 89 26 model Main { 90 - @doc("DID of the account being followed") 91 27 @required 92 - subject: string; 28 + @minLength(1) 29 + @maxGraphemes(1) 30 + @maxLength(32) 31 + status: string; 93 32 94 - @doc("When the follow was created") 95 - @required 96 - createdAt: datetime; 33 + @required createdAt: datetime; 97 34 } 98 35 } 99 36 100 - namespace app.example.like { 101 - @rec("tid") 102 - @doc("A like on a post") 103 - model Main { 104 - @doc("Post being liked") 105 - @required 106 - subject: app.example.defs.PostRef; 107 - 108 - @doc("When the like was created") 109 - @required 110 - createdAt: datetime; 111 - } 37 + namespace xyz.statusphere.sendStatus { 38 + @procedure 39 + @doc("Send a status into the ATmosphere.") 40 + op main( 41 + input: { 42 + @required 43 + @minLength(1) 44 + @maxGraphemes(1) 45 + @maxLength(32) 46 + status: string; 47 + }, 48 + ): { 49 + @required status: xyz.statusphere.defs.StatusView; 50 + }; 112 51 } 113 52 114 - namespace app.example.repost { 115 - @rec("tid") 116 - @doc("A repost of another post") 117 - model Main { 118 - @doc("Post being reposted") 119 - @required 120 - subject: app.example.defs.PostRef; 121 - 122 - @doc("When the repost was created") 123 - @required 124 - createdAt: datetime; 125 - } 53 + namespace xyz.statusphere.getStatuses { 54 + @query 55 + @doc("Get a list of the most recent statuses on the network.") 56 + op main(@minValue(1) @maxValue(100) limit?: integer = 50): { 57 + @required statuses: xyz.statusphere.defs.StatusView[]; 58 + }; 126 59 } 127 60 128 - namespace app.example.profile { 129 - @rec("self") 130 - @doc("User profile information") 131 - model Main { 132 - @doc("Display name") 133 - displayName?: string; 134 - 135 - @doc("Profile description") 136 - description?: string; 137 - 138 - @doc("Profile avatar image") 139 - avatar?: string; 140 - 141 - @doc("Profile banner image") 142 - banner?: string; 143 - } 61 + namespace xyz.statusphere.getUser { 62 + @query 63 + @doc("Get the current user's profile and status.") 64 + op main(): { 65 + @required profile: app.bsky.actor.defs.ProfileView; 66 + status?: xyz.statusphere.defs.StatusView; 67 + }; 144 68 }
+2 -3
packages/playground/package.json
··· 4 4 "private": true, 5 5 "type": "module", 6 6 "scripts": { 7 - "build:samples": "node samples/build.js", 8 - "dev": "npm run build:samples && vite", 9 - "build": "npm run build:samples && vite build", 7 + "dev": "vite", 8 + "build": "vite build", 10 9 "preview": "vite preview" 11 10 }, 12 11 "dependencies": {
-52
packages/playground/samples/build.js
··· 1 - // @ts-check 2 - import { writeFileSync, mkdirSync } from "fs"; 3 - import { dirname, resolve, join } from "path"; 4 - import { fileURLToPath } from "url"; 5 - import { lexicons, bundleLexicon } from "./index.js"; 6 - 7 - const __dirname = dirname(fileURLToPath(import.meta.url)); 8 - const outputDir = resolve(__dirname, "dist"); 9 - 10 - // Create output directory 11 - mkdirSync(outputDir, { recursive: true }); 12 - 13 - // Write each bundled lexicon to disk 14 - const samplesList = {}; 15 - 16 - // Add the default sample first 17 - const defaultSample = "app.bsky.feed.like"; 18 - if (lexicons.has(defaultSample)) { 19 - const lexicon = lexicons.get(defaultSample); 20 - const bundled = bundleLexicon(defaultSample); 21 - const filename = `${defaultSample}.tsp`; 22 - const filepath = join(outputDir, filename); 23 - 24 - writeFileSync(filepath, bundled); 25 - 26 - samplesList[defaultSample] = { 27 - filename: `samples/dist/${filename}`, 28 - preferredEmitter: "@typelex/emitter", 29 - }; 30 - } 31 - 32 - // Add the rest 33 - for (const [namespace, lexicon] of lexicons) { 34 - if (namespace === defaultSample) continue; // Already added 35 - 36 - const bundled = bundleLexicon(namespace); 37 - const filename = `${namespace}.tsp`; 38 - const filepath = join(outputDir, filename); 39 - 40 - writeFileSync(filepath, bundled); 41 - 42 - samplesList[namespace] = { 43 - filename: `samples/dist/${filename}`, 44 - preferredEmitter: "@typelex/emitter", 45 - }; 46 - } 47 - 48 - // Write the samples index 49 - const samplesIndex = `export default ${JSON.stringify(samplesList, null, 2)};`; 50 - writeFileSync(join(outputDir, "samples.js"), samplesIndex); 51 - 52 - console.log(`Wrote ${Object.keys(samplesList).length} bundled samples to disk`);
+28 -105
packages/playground/samples/index.js
··· 6 6 const __dirname = dirname(fileURLToPath(import.meta.url)); 7 7 8 8 // Get all tsp files 9 - function getAllTspFiles(dir, baseDir = dir) { 9 + function getAllFiles(dir, baseDir = dir) { 10 10 const files = []; 11 11 const entries = readdirSync(dir); 12 12 ··· 15 15 const stat = statSync(fullPath); 16 16 17 17 if (stat.isDirectory()) { 18 - files.push(...getAllTspFiles(fullPath, baseDir)); 18 + files.push(...getAllFiles(fullPath, baseDir)); 19 19 } else if (entry.endsWith(".tsp")) { 20 20 files.push(relative(baseDir, fullPath)); 21 21 } ··· 24 24 return files.sort(); 25 25 } 26 26 27 - // Extract dependencies from a file 28 - function extractDependencies(content) { 29 - const deps = new Set(); 30 - // Match namespace references like "com.atproto.label.defs.Label" or "com.atproto.repo.strongRef.Main" 31 - // Pattern: word.word.word... followed by dot and identifier starting with capital letter 32 - const pattern = 33 - /\b([a-z]+(?:\.[a-z]+)+(?:\.[a-z][a-zA-Z]*)*)\.[A-Z][a-zA-Z]*/g; 34 - const withoutDeclaration = content.replace(/namespace\s+[a-z.]+\s*\{/, ""); 27 + const integrationDir = join(__dirname, "../../emitter/test/integration"); 35 28 36 - const matches = withoutDeclaration.matchAll(pattern); 37 - for (const match of matches) { 38 - deps.add(match[1]); 39 - } 29 + // Get all test suite directories 30 + const testSuites = readdirSync(integrationDir).filter((name) => { 31 + const fullPath = join(integrationDir, name); 32 + return statSync(fullPath).isDirectory() && !name.startsWith("."); 33 + }); 40 34 41 - return Array.from(deps); 42 - } 35 + // Load all lexicons from test suites 36 + const lexicons = new Map(); // namespace -> { file, content, suite } 43 37 44 - const atprotoInputDir = join( 45 - __dirname, 46 - "../../emitter/test/integration/atproto/input", 47 - ); 48 - const lexiconExamplesDir = join( 49 - __dirname, 50 - "../../emitter/test/integration/lexicon-examples/input", 51 - ); 38 + for (const suite of testSuites) { 39 + const inputDir = join(integrationDir, suite, "input"); 40 + const inputFiles = getAllFiles(inputDir).filter((f) => f.endsWith(".tsp")); 52 41 53 - const atprotoFiles = getAllTspFiles(atprotoInputDir); 54 - const lexiconExampleFiles = getAllTspFiles(lexiconExamplesDir); 42 + for (const file of inputFiles) { 43 + const fullPath = join(inputDir, file); 44 + const content = readFileSync(fullPath, "utf-8"); 45 + const namespace = file.replace(/\.tsp$/, "").replace(/\//g, "."); 55 46 56 - // Build dependency graph 57 - const lexicons = new Map(); // namespace -> { file, content, deps } 58 - 59 - // Process atproto files 60 - for (const file of atprotoFiles) { 61 - const fullPath = join(atprotoInputDir, file); 62 - const content = readFileSync(fullPath, "utf-8"); 63 - const namespace = file.replace(/\.tsp$/, "").replace(/\//g, "."); 64 - const deps = extractDependencies(content); 65 - 66 - lexicons.set(namespace, { file: `atproto/${file}`, content, deps }); 67 - } 68 - 69 - // Process lexicon-examples files 70 - for (const file of lexiconExampleFiles) { 71 - const fullPath = join(lexiconExamplesDir, file); 72 - const content = readFileSync(fullPath, "utf-8"); 73 - const namespace = file.replace(/\.tsp$/, "").replace(/\//g, "."); 74 - const deps = extractDependencies(content); 75 - 76 - lexicons.set(namespace, { file: `examples/${file}`, content, deps }); 77 - } 78 - 79 - // Recursively collect all dependencies (topological sort) 80 - function collectDependencies( 81 - namespace, 82 - collected = new Set(), 83 - visiting = new Set(), 84 - ) { 85 - if (collected.has(namespace)) return; 86 - if (visiting.has(namespace)) return; // circular dependency 87 - 88 - const lexicon = lexicons.get(namespace); 89 - if (!lexicon) return; 90 - 91 - visiting.add(namespace); 92 - 93 - // First collect all dependencies 94 - for (const dep of lexicon.deps) { 95 - collectDependencies(dep, collected, visiting); 47 + lexicons.set(namespace, { file, content, suite, fullPath }); 96 48 } 97 - 98 - visiting.delete(namespace); 99 - collected.add(namespace); 100 49 } 101 50 102 - // Bundle a lexicon with all its dependencies 103 - function bundleLexicon(namespace) { 104 - const collected = new Set(); 105 - collectDependencies(namespace, collected); 106 - 107 - // Put the main lexicon FIRST, then its dependencies 108 - const mainLexicon = lexicons.get(namespace); 109 - const deps = Array.from(collected).filter((ns) => ns !== namespace); 110 - 111 - let bundled = 'import "@typelex/emitter";\n\n'; 112 - 113 - // Main lexicon first (so it shows in the playground) 114 - if (mainLexicon) { 115 - const contentWithoutImport = mainLexicon.content.replace( 116 - /^import "@typelex\/emitter";\s*\n/, 117 - "", 118 - ); 119 - bundled += `// ${mainLexicon.file}\n${contentWithoutImport}\n`; 120 - } 121 - 122 - // Then dependencies 123 - for (const ns of deps) { 124 - const lexicon = lexicons.get(ns); 125 - if (!lexicon) continue; 126 - 127 - const contentWithoutImport = lexicon.content.replace( 128 - /^import "@typelex\/emitter";\s*\n/, 129 - "", 130 - ); 131 - bundled += `// ${lexicon.file}\n${contentWithoutImport}\n`; 132 - } 133 - 134 - return bundled; 51 + // Build samples list for playground 52 + const samplesList = {}; 53 + for (const [namespace, lexicon] of lexicons) { 54 + samplesList[namespace] = { 55 + filename: relative(join(__dirname, ".."), lexicon.fullPath), 56 + preferredEmitter: "@typelex/emitter", 57 + }; 135 58 } 136 59 137 - // Export for build script 138 - export { lexicons, bundleLexicon }; 60 + export { lexicons }; 61 + export default samplesList; 139 62 140 - console.log(`Loaded ${lexicons.size} lexicons for bundling`); 63 + console.log(`Loaded ${lexicons.size} lexicons`);
+3 -2
packages/playground/vite.config.ts
··· 1 1 import { definePlaygroundViteConfig } from "@typespec/playground/vite"; 2 2 import { defineConfig } from "vite"; 3 - import samples from "./samples/dist/samples.js"; 3 + import samples from "./samples/index.js"; 4 4 5 5 const playgroundConfig = definePlaygroundViteConfig({ 6 6 defaultEmitter: "@typelex/emitter", 7 7 libraries: ["@typespec/compiler", "@typelex/emitter"], 8 8 samples, 9 9 links: { 10 - documentationUrl: "https://tangled.org/@danabra.mov/typelex", 10 + documentationUrl: 11 + "https://tangled.org/@danabra.mov/typelex/blob/main/DOCS.md", 11 12 }, 12 13 }); 13 14
packages/website/public/og.png

This is a binary file and will not be displayed.

+142 -51
packages/website/src/pages/index.astro
··· 1 1 --- 2 - import Playground from '../components/Playground'; 3 2 import { highlightCode } from '../utils/shiki'; 4 3 import { compileToJson } from '../utils/compile'; 5 4 import { createPlaygroundUrl } from '../utils/playground-url'; ··· 130 129 <head> 131 130 <meta charset="utf-8" /> 132 131 <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 133 - <meta name="viewport" content="width=device-width" /> 132 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 134 133 <meta name="generator" content={Astro.generator} /> 135 134 <title>typelex โ€“ An experimental TypeSpec syntax for Lexicon</title> 135 + <meta name="description" content="An experimental TypeSpec syntax for AT Protocol Lexicons. Write Lexicons in a more readable syntax using TypeSpec." /> 136 + 137 + <!-- Open Graph / Facebook --> 138 + <meta property="og:type" content="website" /> 139 + <meta property="og:url" content="https://typelex.org/" /> 140 + <meta property="og:title" content="typelex โ€“ An experimental TypeSpec syntax for Lexicon" /> 141 + <meta property="og:description" content="An experimental TypeSpec syntax for AT Protocol Lexicons. Write Lexicons in a more readable syntax using TypeSpec." /> 142 + <meta property="og:image" content="https://typelex.org/og.png" /> 143 + 144 + <!-- Twitter --> 145 + <meta property="twitter:card" content="summary_large_image" /> 146 + <meta property="twitter:url" content="https://typelex.org/" /> 147 + <meta property="twitter:title" content="typelex โ€“ An experimental TypeSpec syntax for Lexicon" /> 148 + <meta property="twitter:description" content="An experimental TypeSpec syntax for AT Protocol Lexicons. Write Lexicons in a more readable syntax using TypeSpec." /> 149 + <meta property="twitter:image" content="https://typelex.org/og.png" /> 136 150 </head> 137 151 <body> 138 - <div class="container"> 152 + <main class="container"> 139 153 <header> 140 154 <h1>typelex</h1> 141 155 <p class="tagline">An experimental <a href="https://typespec.io" target="_blank" rel="noopener noreferrer">TypeSpec</a> syntax for <a href="https://atproto.com/specs/lexicon" target="_blank" rel="noopener noreferrer">Lexicon</a></p> 142 156 143 - <div class="hero-comparison"> 157 + <figure class="hero-comparison"> 144 158 <div class="comparison-content"> 145 159 <div class="hero-panel"> 146 - <h3 class="hero-header"> 160 + <p class="hero-header"> 147 161 Typelex 148 162 <a href={createPlaygroundUrl(`import "@typelex/emitter"; 149 163 ··· 164 178 <path d="M3 5.5C3 4.67157 3.67157 4 4.5 4H5C5.27614 4 5.5 4.22386 5.5 4.5C5.5 4.77614 5.27614 5 5 5H4.5C4.22386 5 4 5.22386 4 5.5V11.5C4 11.7761 4.22386 12 4.5 12H10.5C10.7761 12 11 11.7761 11 11.5V11C11 10.7239 11.2239 10.5 11.5 10.5C11.7761 10.5 12 10.7239 12 11V11.5C12 12.3284 11.3284 13 10.5 13H4.5C3.67157 13 3 12.3284 3 11.5V5.5Z" fill="currentColor"/> 165 179 </svg> 166 180 </a> 167 - </h3> 181 + </p> 168 182 <div class="hero-code" set:html={await highlightCode(`import "@typelex/emitter"; 169 183 170 184 namespace app.bsky.actor.profile { ··· 181 195 }`, 'typespec')} /> 182 196 </div> 183 197 <div class="hero-panel"> 184 - <h3 class="hero-header"> 198 + <p class="hero-header"> 185 199 Lexicon 186 - </h3> 200 + </p> 187 201 <div class="hero-code" set:html={await highlightCode(stringify({ 188 202 "lexicon": 1, 189 203 "id": "app.bsky.actor.profile", ··· 211 225 }, { maxLength: 50 }), 'json')} /> 212 226 </div> 213 227 </div> 214 - </div> 228 + </figure> 215 229 216 230 <p class="hero-description"> 217 231 Typelex lets you write AT <a target="_blank" href="https://atproto.com/specs/lexicon">Lexicons</a> in a more readable syntax. <br /> 218 232 It uses <a target="_blank" href="https://typespec.io/">TypeSpec</a> for syntax, adding conventions for Lexicons. 219 233 </p> 220 234 221 - <div class="hero-actions"> 235 + <nav class="hero-actions"> 222 236 <a href="#install" class="install-cta">Try It</a> 223 - <a href="https://tangled.org/@danabra.mov/typlex" target="_blank" rel="noopener noreferrer" class="star-btn"> 224 - Read Documentation 237 + <a href="https://tangled.org/@danabra.mov/typelex/blob/main/DOCS.md" target="_blank" rel="noopener noreferrer" class="star-btn"> 238 + Read Docs 225 239 </a> 226 - </div> 240 + </nav> 227 241 </header> 228 242 229 - <div class="separator"></div> 243 + <hr class="separator" /> 230 244 231 245 {highlighted.map(({ title, typelexHtml, lexiconHtml, playgroundUrl }) => ( 232 246 <section> 233 247 <h2>{title}</h2> 234 - <div class="comparison"> 248 + <figure class="comparison"> 235 249 <div class="comparison-content"> 236 250 <div class="code-panel"> 237 - <h3 class="code-header"> 251 + <p class="code-header"> 238 252 Typelex 239 253 <a href={playgroundUrl} target="_blank" rel="noopener noreferrer" class="code-playground-link" aria-label="Open in playground"> 240 254 <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> ··· 242 256 <path d="M3 5.5C3 4.67157 3.67157 4 4.5 4H5C5.27614 4 5.5 4.22386 5.5 4.5C5.5 4.77614 5.27614 5 5 5H4.5C4.22386 5 4 5.22386 4 5.5V11.5C4 11.7761 4.22386 12 4.5 12H10.5C10.7761 12 11 11.7761 11 11.5V11C11 10.7239 11.2239 10.5 11.5 10.5C11.7761 10.5 12 10.7239 12 11V11.5C12 12.3284 11.3284 13 10.5 13H4.5C3.67157 13 3 12.3284 3 11.5V5.5Z" fill="currentColor"/> 243 257 </svg> 244 258 </a> 245 - </h3> 259 + </p> 246 260 <div class="code-block" set:html={typelexHtml} /> 247 261 </div> 248 262 <div class="code-panel"> 249 - <h3 class="code-header"> 263 + <p class="code-header"> 250 264 Lexicon 251 - </h3> 265 + </p> 252 266 <div class="code-block" set:html={lexiconHtml} /> 253 267 </div> 254 268 </div> 255 - </div> 269 + </figure> 256 270 </section> 257 271 ))} 258 272 259 - <div class="separator"></div> 273 + <hr class="separator" /> 260 274 261 275 <section class="install-section" id="install"> 262 276 <h2>Install</h2> 263 277 <div class="install-grid"> 264 278 <div class="install-notice"> 265 - <p class="notice-text">This is an early-stage experiment. There are bugs. You must verify the output!</p> 279 + <p class="notice-text">This is an early-stage experiment. Design and syntax may change.</p> 266 280 </div> 267 281 <div class="install-step playground-step"> 268 282 <div class="step-number">0</div> ··· 279 293 <div class="step-number">1</div> 280 294 <div class="step-content"> 281 295 <h3>Install packages</h3> 282 - <div class="install-box" set:html={await highlightCode('npm install -D @typespec/compiler @typelex/emitter', 'bash')} /> 296 + <figure class="install-box" set:html={await highlightCode('npm install -D @typespec/compiler @typelex/emitter', 'bash')} /> 283 297 </div> 284 298 </div> 285 299 286 300 <div class="install-step"> 287 301 <div class="step-number">2</div> 288 302 <div class="step-content"> 289 - <h3>Add <a href="https://typespec.io/docs/handbook/configuration/configuration/" target="_blank" rel="noopener noreferrer">tspconfig.yaml</a></h3> 290 - <div class="install-box" set:html={await highlightCode(`emit: 303 + <h3>Create <code>typelex/main.tsp</code></h3> 304 + <figure class="install-box install-box-with-link"> 305 + <a href={createPlaygroundUrl(`import "@typelex/emitter"; 306 + 307 + namespace com.example.actor.profile { 308 + /** My profile. */ 309 + @rec("literal:self") 310 + model Main { 311 + /** Free-form profile description.*/ 312 + @maxGraphemes(256) 313 + description?: string; 314 + } 315 + }`)} target="_blank" rel="noopener noreferrer" class="install-playground-link" aria-label="Open in playground"> 316 + <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 317 + <path d="M6.5 3.5C6.5 3.22386 6.72386 3 7 3H13C13.2761 3 13.5 3.22386 13.5 3.5V9.5C13.5 9.77614 13.2761 10 13 10C12.7239 10 12.5 9.77614 12.5 9.5V4.70711L6.85355 10.3536C6.65829 10.5488 6.34171 10.5488 6.14645 10.3536C5.95118 10.1583 5.95118 9.84171 6.14645 9.64645L11.7929 4H7C6.72386 4 6.5 3.77614 6.5 3.5Z" fill="currentColor"/> 318 + <path d="M3 5.5C3 4.67157 3.67157 4 4.5 4H5C5.27614 4 5.5 4.22386 5.5 4.5C5.5 4.77614 5.27614 5 5 5H4.5C4.22386 5 4 5.22386 4 5.5V11.5C4 11.7761 4.22386 12 4.5 12H10.5C10.7761 12 11 11.7761 11 11.5V11C11 10.7239 11.2239 10.5 11.5 10.5C11.7761 10.5 12 10.7239 12 11V11.5C12 12.3284 11.3284 13 10.5 13H4.5C3.67157 13 3 12.3284 3 11.5V5.5Z" fill="currentColor"/> 319 + </svg> 320 + </a> 321 + <div set:html={await highlightCode(`import "@typelex/emitter"; 322 + 323 + namespace com.example.actor.profile { 324 + /** My profile. */ 325 + @rec("literal:self") 326 + model Main { 327 + /** Free-form profile description.*/ 328 + @maxGraphemes(256) 329 + description?: string; 330 + } 331 + }`, 'typespec')} /> 332 + </figure> 333 + </div> 334 + <p class="step-description">Or grab any example Lexicon <a target=_blank href="https://playground.typelex.org/">from the Playground</a>.</p> 335 + </div> 336 + 337 + <div class="install-step"> 338 + <div class="step-number">3</div> 339 + <div class="step-content"> 340 + <h3>Create <code><a href="https://typespec.io/docs/handbook/configuration/configuration/" target="_blank" rel="noopener noreferrer">tspconfig.yaml</a></code></h3> 341 + <figure class="install-box" set:html={await highlightCode(`emit: 291 342 - "@typelex/emitter" 292 343 options: 293 344 "@typelex/emitter": 294 - output-dir: "./lexicon"`, 'yaml')} /> 345 + output-dir: "./lexicons"`, 'yaml')} /> 295 346 </div> 296 347 </div> 297 348 298 349 <div class="install-step"> 299 - <div class="step-number">3</div> 350 + <div class="step-number">4</div> 300 351 <div class="step-content"> 301 - <h3>Configure scripts</h3> 302 - <div class="install-box" set:html={await highlightCode(`{ 352 + <h3>Add a build script to <code>package.json</code></h3> 353 + <figure class="install-box" set:html={await highlightCode(`{ 303 354 "scripts": { 304 - "build": "npm run build:lexicon && npm run build:codegen", 305 - "build:lexicon": "tsp compile . && tsp format **/*.tsp", 306 - "build:codegen": "lex gen-api --yes ./src lexicon/app/example/*.json" 355 + // ... 356 + "build:lexicons": "tsp compile typelex/main.tsp" 307 357 } 308 358 }`, 'json')} /> 309 359 </div> 310 360 </div> 311 361 312 362 <div class="install-step"> 313 - <div class="step-number">4</div> 363 + <div class="step-number">5</div> 364 + <div class="step-content"> 365 + <h3>Generate Lexicon files</h3> 366 + <figure class="install-box" set:html={await highlightCode(`npm run build:lexicons`, 'bash')} /> 367 + <p class="step-description">Lexicon files will be generated in the <code>output-dir</code> from your <code>tspconfig.yaml</code> config.</p> 368 + </div> 369 + </div> 370 + 371 + <div class="install-step"> 372 + <div class="step-number">6</div> 314 373 <div class="step-content"> 315 374 <h3>Set up VS Code</h3> 316 375 <p class="step-description">Install the <a href="https://typespec.io/docs/introduction/editor/vscode/" target="_blank" rel="noopener noreferrer">TypeSpec for VS Code extension</a> for syntax highlighting and IntelliSense.</p> ··· 318 377 </div> 319 378 320 379 <div class="install-step"> 321 - <div class="step-number">5</div> 380 + <div class="step-number">7</div> 322 381 <div class="step-content"> 323 382 <h3>Read the docs</h3> 324 - <p class="step-description">Check out the <a href="https://tangled.org/@danabra.mov/typlex" target="_blank" rel="noopener noreferrer">documentation</a> to learn more about Typelex syntax and features.</p> 383 + <p class="step-description">Check out the <a href="https://tangled.org/@danabra.mov/typelex/blob/main/DOCS.md" target="_blank" rel="noopener noreferrer">documentation</a> to learn more.</p> 325 384 </div> 326 385 </div> 327 386 </div> 328 387 </section> 329 - 330 - <div class="separator"></div> 388 + 389 + <hr class="separator" /> 331 390 332 391 <footer> 333 392 <p>This is my personal hobby project and is not affiliated with AT or endorsed by anyone.</p> 334 393 <p>Who knows if this is a good idea?</p> 335 394 </footer> 336 - </div> 395 + </main> 337 396 338 397 <script> 339 398 document.addEventListener('DOMContentLoaded', () => { ··· 438 497 439 498 header { 440 499 text-align: center; 441 - padding: 3rem 1rem 2rem; 500 + padding: 3rem 1rem 0.5rem; 442 501 margin: 0 auto; 443 502 } 444 503 ··· 589 648 590 649 .hero-actions { 591 650 display: flex; 592 - flex-direction: column; 593 - gap: 1rem; 651 + flex-direction: row; 652 + flex-wrap: wrap; 653 + gap: 0.75rem; 594 654 margin-top: 2.5rem; 595 655 align-items: center; 656 + justify-content: center; 596 657 } 597 658 598 659 @media (min-width: 640px) { 599 660 .hero-actions { 600 - flex-direction: row; 601 - justify-content: center; 602 661 gap: 1rem; 603 662 } 604 663 } ··· 710 769 height: 1px; 711 770 background: linear-gradient(to right, transparent, #e2e8f0 20%, #e2e8f0 80%, transparent); 712 771 margin: 4rem 0; 772 + border: none; 713 773 } 714 774 715 775 @media (min-width: 768px) { ··· 736 796 737 797 .install-notice { 738 798 padding: 1rem 1.5rem; 739 - background: #fef3c7; 740 - border: 1px solid #fbbf24; 799 + background: linear-gradient(135deg, #fefce8 0%, #fef3c7 100%); 800 + border: 2px solid #f59e0b; 741 801 border-radius: 10px; 742 - text-align: center; 802 + text-align: left; 743 803 } 744 804 745 805 .notice-text { 746 806 margin: 0; 747 807 font-size: 1.0625rem; 748 808 line-height: 1.5; 749 - font-weight: 600; 809 + font-weight: 700; 750 810 color: #92400e; 751 811 } 752 812 ··· 842 902 background: #1e1b29; 843 903 border-radius: 8px; 844 904 overflow: hidden; 905 + margin: 0; 906 + } 907 + 908 + .install-box-with-link { 909 + position: relative; 910 + } 911 + 912 + .install-playground-link { 913 + position: absolute; 914 + top: 0.75rem; 915 + right: 0.75rem; 916 + z-index: 10; 917 + display: inline-flex; 918 + align-items: center; 919 + justify-content: center; 920 + color: #94a3b8; 921 + transition: all 0.2s ease; 922 + text-decoration: none; 923 + opacity: 0.4; 924 + padding: 0.125rem; 925 + } 926 + 927 + .install-playground-link:hover { 928 + color: #c7d2fe; 929 + opacity: 1; 930 + } 931 + 932 + .install-playground-link svg { 933 + width: 1rem; 934 + height: 1rem; 845 935 } 846 936 847 937 .install-box + .step-description { ··· 857 947 858 948 .install-box code { 859 949 font-family: 'Monaco', 'Menlo', monospace; 860 - font-size: 0.8125rem !important; 950 + font-size: 0.75rem !important; 861 951 line-height: 1.6; 862 952 } 863 953 ··· 874 964 875 965 .install-notice { 876 966 padding: 1.5rem 2rem; 967 + border-width: 2px; 877 968 } 878 969 879 970 .notice-text { ··· 898 989 } 899 990 900 991 .install-box code { 901 - font-size: 0.875rem !important; 992 + font-size: 0.8125rem !important; 902 993 } 903 994 } 904 995 ··· 1070 1161 .code-block code, 1071 1162 .hero-code code { 1072 1163 font-family: 'Monaco', 'Menlo', monospace; 1073 - font-size: 0.8125rem !important; 1164 + font-size: 0.75rem !important; 1074 1165 line-height: 1.6; 1075 1166 white-space: pre; 1076 1167 text-align: left; ··· 1079 1170 @media (min-width: 768px) { 1080 1171 .code-block code, 1081 1172 .hero-code code { 1082 - font-size: 0.9375rem !important; 1173 + font-size: 0.875rem !important; 1083 1174 } 1084 1175 } 1085 1176
+46 -3
pnpm-lock.yaml
··· 12 12 specifier: ^5.0.0 13 13 version: 5.9.3 14 14 15 + packages/cli: 16 + dependencies: 17 + '@typelex/emitter': 18 + specifier: ^0.2.0 19 + version: 0.2.0(@typespec/compiler@1.4.0(@types/node@20.19.19)) 20 + '@typespec/compiler': 21 + specifier: ^1.4.0 22 + version: 1.4.0(@types/node@20.19.19) 23 + yargs: 24 + specifier: ^18.0.0 25 + version: 18.0.0 26 + devDependencies: 27 + '@types/node': 28 + specifier: ^20.0.0 29 + version: 20.19.19 30 + '@types/yargs': 31 + specifier: ^17.0.33 32 + version: 17.0.33 33 + typescript: 34 + specifier: ^5.0.0 35 + version: 5.9.3 36 + 15 37 packages/emitter: 16 38 dependencies: 17 39 '@typespec/compiler': ··· 48 70 '@atproto/xrpc-server': 49 71 specifier: ^0.9.5 50 72 version: 0.9.5 73 + '@typelex/cli': 74 + specifier: workspace:* 75 + version: link:../cli 51 76 '@typelex/emitter': 52 77 specifier: workspace:* 53 78 version: link:../emitter 54 - '@typespec/compiler': 55 - specifier: ^1.4.0 56 - version: 1.4.0(@types/node@20.19.19) 57 79 devDependencies: 58 80 typescript: 59 81 specifier: ^5.0.0 ··· 1645 1667 '@ts-morph/common@0.25.0': 1646 1668 resolution: {integrity: sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==} 1647 1669 1670 + '@typelex/emitter@0.2.0': 1671 + resolution: {integrity: sha512-4Iw6VAnd9nCFGOkJcu9utWdmu9ZyPeAb1QX/B7KerGBmfc2FuIDqgZZ/mZ6c56atcZd62pb2oYF/3RgSFhEsoQ==} 1672 + peerDependencies: 1673 + '@typespec/compiler': ^1.4.0 1674 + 1648 1675 '@types/babel__core@7.20.5': 1649 1676 resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 1650 1677 ··· 1705 1732 1706 1733 '@types/unist@3.0.3': 1707 1734 resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} 1735 + 1736 + '@types/yargs-parser@21.0.3': 1737 + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} 1738 + 1739 + '@types/yargs@17.0.33': 1740 + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} 1708 1741 1709 1742 '@typespec/asset-emitter@0.74.0': 1710 1743 resolution: {integrity: sha512-DWIdlSNhRgBeZ8exfqubfUn0H6mRg4gr0s7zLTdBMUEDHL3Yh0ljnRPkd8AXTZhoW3maTFT69loWTrqx09T5oQ==} ··· 7411 7444 path-browserify: 1.0.1 7412 7445 tinyglobby: 0.2.15 7413 7446 7447 + '@typelex/emitter@0.2.0(@typespec/compiler@1.4.0(@types/node@20.19.19))': 7448 + dependencies: 7449 + '@typespec/compiler': 1.4.0(@types/node@20.19.19) 7450 + 7414 7451 '@types/babel__core@7.20.5': 7415 7452 dependencies: 7416 7453 '@babel/parser': 7.28.4 ··· 7482 7519 csstype: 3.1.3 7483 7520 7484 7521 '@types/unist@3.0.3': {} 7522 + 7523 + '@types/yargs-parser@21.0.3': {} 7524 + 7525 + '@types/yargs@17.0.33': 7526 + dependencies: 7527 + '@types/yargs-parser': 21.0.3 7485 7528 7486 7529 '@typespec/asset-emitter@0.74.0(@typespec/compiler@1.4.0(@types/node@20.19.19))': 7487 7530 dependencies: