+12
CHANGELOG.md
+12
CHANGELOG.md
+11
-34
CLAUDE.md
+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/`).
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:
4
5
-
- read `DOCS.md`!!! it's the main guideline
6
7
-
- `../typespec/packages` has a bunch of other emitters (so you can check how they're implemented and common patterns)
8
9
-
- `../typespec/website/src/content` has typescpec docs (which you might find very useful)
10
11
-
- `../atproto-website/src/app/[locale]/specs/lexicon` contains lexicon spec (where we want to reach feature parity), and most importantly
12
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).
14
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.
24
25
good luck! i believe in you
26
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
···
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
3
+
you have the following resources you're encouraged to explore on your own whenever they seem relevant to the task:
4
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.
8
9
+
as a part of your workflow, you will:
10
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!
12
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).
14
15
+
- we also have a playground in `packages/playground` and website in `packages/website`.
16
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.
18
19
good luck! i believe in you
20
21
+
and read the `DOCS.md` (and `*.tsp` files in this repo)!!! they're your most important sources of information.
+24
-3
DOCS.md
+24
-3
DOCS.md
···
238
239
### External Stubs
240
241
-
If you don't have TypeSpec definitions for external Lexicons, you can stub them out:
242
243
```typescript
244
import "@typelex/emitter";
···
250
}
251
252
// Empty stub (like .d.ts in TypeScript)
253
namespace com.atproto.label.defs {
254
model SelfLabels { }
255
}
256
```
257
258
-
You could collect stubs in one file and import them:
259
260
```typescript
261
import "@typelex/emitter";
···
268
}
269
```
270
271
-
You'll want to replace the stubbed lexicons in the output folder with their real JSON before running codegen.
272
273
### Inline Models
274
···
238
239
### External Stubs
240
241
+
If you don't have TypeSpec definitions for external Lexicons, you can stub them out using the `@external` decorator:
242
243
```typescript
244
import "@typelex/emitter";
···
250
}
251
252
// Empty stub (like .d.ts in TypeScript)
253
+
@external
254
namespace com.atproto.label.defs {
255
model SelfLabels { }
256
}
257
```
258
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";
···
271
}
272
```
273
274
+
Then in `atproto-stubs.tsp`:
275
+
276
+
```typescript
277
+
import "@typelex/emitter";
278
+
279
+
@external
280
+
namespace com.atproto.label.defs {
281
+
model SelfLabels { }
282
+
}
283
+
284
+
@external
285
+
namespace com.atproto.repo.defs {
286
+
model StrongRef { }
287
+
@token model SomeToken { } // Note: Tokens still need @token
288
+
}
289
+
// ... more stubs
290
+
```
291
+
292
+
You'll want to ensure the real JSON for external Lexicons is available before running codegen.
293
294
### Inline Models
295
+5
-3
README.md
+5
-3
README.md
+2
-1
package.json
+2
-1
package.json
···
10
"example": "pnpm --filter @typelex/example build",
11
"playground": "pnpm --filter @typelex/playground dev",
12
"validate": "pnpm build && pnpm run validate-lexicons && pnpm test",
13
+
"validate-lexicons": "node scripts/validate-lexicons.js",
14
+
"cli": "pnpm --filter @typelex/cli"
15
},
16
"repository": {
17
"type": "git",
+66
packages/cli/README.md
+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
+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
+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
+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
packages/cli/src/index.ts
···
···
1
+
export { compileCommand } from "./commands/compile.js";
+38
packages/cli/src/utils/ensure-imports.ts
+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
+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
+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
+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
+4
packages/cli/typelex/externals.tsp
+15
packages/emitter/lib/decorators.tsp
+15
packages/emitter/lib/decorators.tsp
···
1
import "../dist/tsp-index.js";
2
3
+
using TypeSpec.Reflection;
4
+
5
/**
6
* Specifies the maximum number of graphemes (user-perceived characters) allowed.
7
* Used alongside maxLength for proper Unicode text handling.
···
161
* ```
162
*/
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);
+3
-2
packages/emitter/package.json
+3
-2
packages/emitter/package.json
···
1
{
2
"name": "@typelex/emitter",
3
-
"version": "0.1.4",
4
"description": "TypeSpec emitter for ATProto Lexicon definitions",
5
"main": "dist/index.js",
6
"type": "module",
···
27
"test:ci": "npm run build && vitest run",
28
"test:watch": "tsc-watch --onSuccess \"vitest run\"",
29
"clean": "rm -rf dist",
30
-
"watch": "tsc --watch"
31
},
32
"keywords": [
33
"typespec",
···
1
{
2
"name": "@typelex/emitter",
3
+
"version": "0.2.0",
4
"description": "TypeSpec emitter for ATProto Lexicon definitions",
5
"main": "dist/index.js",
6
"type": "module",
···
27
"test:ci": "npm run build && vitest run",
28
"test:watch": "tsc-watch --onSuccess \"vitest run\"",
29
"clean": "rm -rf dist",
30
+
"watch": "tsc --watch",
31
+
"prepublishOnly": "npm test"
32
},
33
"keywords": [
34
"typespec",
+23
packages/emitter/src/decorators.ts
+23
packages/emitter/src/decorators.ts
···
24
const inlineKey = Symbol("inline");
25
const maxBytesKey = Symbol("maxBytes");
26
const minBytesKey = Symbol("minBytes");
27
28
/**
29
* @maxBytes decorator for maximum length of bytes type
···
294
export function isReadOnly(program: Program, target: Type): boolean {
295
return program.stateSet(readOnlyKey).has(target);
296
}
···
24
const inlineKey = Symbol("inline");
25
const maxBytesKey = Symbol("maxBytes");
26
const minBytesKey = Symbol("minBytes");
27
+
const externalKey = Symbol("external");
28
29
/**
30
* @maxBytes decorator for maximum length of bytes type
···
295
export function isReadOnly(program: Program, target: Type): boolean {
296
return program.stateSet(readOnlyKey).has(target);
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
+
}
+86
-15
packages/emitter/src/emitter.ts
+86
-15
packages/emitter/src/emitter.ts
···
67
isInline,
68
getMaxBytes,
69
getMinBytes,
70
} from "./decorators.js";
71
72
export interface EmitterOptions {
···
117
if (!fullName || fullName.startsWith("TypeSpec")) {
118
for (const [_, childNs] of ns.namespaces) {
119
this.processNamespace(childNs);
120
}
121
return;
122
}
···
369
return;
370
}
371
372
-
// Only string enums can be added as defs
373
// Union refs (type: "union") must be inlined at usage sites
374
if (unionDef.type === "string" && (unionDef.knownValues || unionDef.enum)) {
375
const defName = name.charAt(0).toLowerCase() + name.slice(1);
···
380
code: "union-refs-not-allowed-as-def",
381
severity: "error",
382
message:
383
-
`Named unions of model references cannot be defined as standalone defs. ` +
384
-
`Use @inline to inline them at usage sites, or use string enums instead.`,
385
target: union,
386
});
387
}
···
461
private unionToLexiconProperty(
462
unionType: Union,
463
prop?: ModelProperty,
464
): LexObjectProperty | null {
465
const variants = this.parseUnionVariants(unionType);
466
···
503
(variants.stringLiterals.length > 0 &&
504
!variants.hasStringType &&
505
variants.unionRefs.length === 0 &&
506
isClosed(this.program, unionType))
507
) {
508
const isClosedUnion = isClosed(this.program, unionType);
···
514
const minLength = getMinLength(this.program, unionType);
515
const maxGraphemes = getMaxGraphemes(this.program, unionType);
516
const minGraphemes = getMinGraphemes(this.program, unionType);
517
return {
518
type: "string",
519
-
[isClosedUnion ? "enum" : "knownValues"]: variants.stringLiterals,
520
...(propDesc && { description: propDesc }),
521
...(defaultValue !== undefined &&
522
typeof defaultValue === "string" && { default: defaultValue }),
···
529
530
// Model reference union (including empty union with unknown)
531
if (variants.unionRefs.length > 0 || variants.hasUnknown) {
532
-
if (variants.stringLiterals.length > 0) {
533
this.program.reportDiagnostic({
534
code: "union-mixed-refs-literals",
535
severity: "error",
536
message:
537
-
`Union contains both model references and string literals. Lexicon unions must be either: ` +
538
-
`(1) model references only (type: "union"), ` +
539
-
`(2) string literals + string type (type: "string" with knownValues), or ` +
540
`(3) integer literals + integer type (type: "integer" with knownValues). ` +
541
`Separate these into distinct fields or nested unions.`,
542
target: unionType,
···
609
const stringLiterals: string[] = [];
610
const numericLiterals: number[] = [];
611
const booleanLiterals: boolean[] = [];
612
let hasStringType = false;
613
let hasUnknown = false;
614
615
for (const variant of unionType.variants.values()) {
616
switch (variant.type.kind) {
617
case "Model":
618
-
const ref = this.getModelReference(variant.type as Model);
619
-
if (ref) unionRefs.push(ref);
620
break;
621
case "String":
622
stringLiterals.push((variant.type as StringLiteral).value);
···
641
}
642
}
643
644
const isStringEnum =
645
-
stringLiterals.length > 0 && hasStringType && unionRefs.length === 0;
646
647
return {
648
unionRefs,
649
stringLiterals,
650
numericLiterals,
651
booleanLiterals,
652
hasStringType,
653
hasUnknown,
654
isStringEnum,
···
1194
}
1195
}
1196
1197
-
const unionDef = this.unionToLexiconProperty(unionType, prop);
1198
if (!unionDef) return null;
1199
1200
// Inherit description from union if no prop description and union is @inline
···
1395
entity: Model | Union,
1396
name: string | undefined,
1397
namespace: Namespace | undefined,
1398
): string | null {
1399
if (!name || !namespace || namespace.name === "TypeSpec") return null;
1400
···
1414
});
1415
}
1416
1417
-
// Local reference (same namespace)
1418
if (
1419
this.currentLexiconId === namespaceName ||
1420
this.currentLexiconId === `${namespaceName}.defs`
···
1427
return namespaceName;
1428
}
1429
1430
return `${namespaceName}#${defName}`;
1431
}
1432
1433
-
private getModelReference(model: Model): string | null {
1434
-
return this.getReference(model, model.name, model.namespace);
1435
}
1436
1437
private getUnionReference(union: Union): string | null {
···
67
isInline,
68
getMaxBytes,
69
getMinBytes,
70
+
isExternal,
71
} from "./decorators.js";
72
73
export interface EmitterOptions {
···
118
if (!fullName || fullName.startsWith("TypeSpec")) {
119
for (const [_, childNs] of ns.namespaces) {
120
this.processNamespace(childNs);
121
+
}
122
+
return;
123
+
}
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
}
···
386
return;
387
}
388
389
+
// Only string enums (including token refs) can be added as defs
390
// Union refs (type: "union") must be inlined at usage sites
391
if (unionDef.type === "string" && (unionDef.knownValues || unionDef.enum)) {
392
const defName = name.charAt(0).toLowerCase() + name.slice(1);
···
397
code: "union-refs-not-allowed-as-def",
398
severity: "error",
399
message:
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.`,
402
target: union,
403
});
404
}
···
478
private unionToLexiconProperty(
479
unionType: Union,
480
prop?: ModelProperty,
481
+
isDefining?: boolean,
482
): LexObjectProperty | null {
483
const variants = this.parseUnionVariants(unionType);
484
···
521
(variants.stringLiterals.length > 0 &&
522
!variants.hasStringType &&
523
variants.unionRefs.length === 0 &&
524
+
variants.knownValueRefs.length === 0 &&
525
isClosed(this.program, unionType))
526
) {
527
const isClosedUnion = isClosed(this.program, unionType);
···
533
const minLength = getMinLength(this.program, unionType);
534
const maxGraphemes = getMaxGraphemes(this.program, unionType);
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
+
543
return {
544
type: "string",
545
+
[isClosedUnion ? "enum" : "knownValues"]: allKnownValues,
546
...(propDesc && { description: propDesc }),
547
...(defaultValue !== undefined &&
548
typeof defaultValue === "string" && { default: defaultValue }),
···
555
556
// Model reference union (including empty union with unknown)
557
if (variants.unionRefs.length > 0 || variants.hasUnknown) {
558
+
if (
559
+
variants.stringLiterals.length > 0 ||
560
+
variants.knownValueRefs.length > 0
561
+
) {
562
this.program.reportDiagnostic({
563
code: "union-mixed-refs-literals",
564
severity: "error",
565
message:
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 ` +
569
`(3) integer literals + integer type (type: "integer" with knownValues). ` +
570
`Separate these into distinct fields or nested unions.`,
571
target: unionType,
···
638
const stringLiterals: string[] = [];
639
const numericLiterals: number[] = [];
640
const booleanLiterals: boolean[] = [];
641
+
const tokenModels: Model[] = [];
642
let hasStringType = false;
643
let hasUnknown = false;
644
645
for (const variant of unionType.variants.values()) {
646
switch (variant.type.kind) {
647
case "Model":
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
+
}
656
break;
657
case "String":
658
stringLiterals.push((variant.type as StringLiteral).value);
···
677
}
678
}
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
+
697
const isStringEnum =
698
+
(stringLiterals.length > 0 || knownValueRefs.length > 0) &&
699
+
hasStringType &&
700
+
unionRefs.length === 0;
701
702
return {
703
unionRefs,
704
stringLiterals,
705
numericLiterals,
706
booleanLiterals,
707
+
knownValueRefs,
708
hasStringType,
709
hasUnknown,
710
isStringEnum,
···
1250
}
1251
}
1252
1253
+
const unionDef = this.unionToLexiconProperty(unionType, prop, isDefining);
1254
if (!unionDef) return null;
1255
1256
// Inherit description from union if no prop description and union is @inline
···
1451
entity: Model | Union,
1452
name: string | undefined,
1453
namespace: Namespace | undefined,
1454
+
fullyQualified = false,
1455
): string | null {
1456
if (!name || !namespace || namespace.name === "TypeSpec") return null;
1457
···
1471
});
1472
}
1473
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
1480
if (
1481
this.currentLexiconId === namespaceName ||
1482
this.currentLexiconId === `${namespaceName}.defs`
···
1489
return namespaceName;
1490
}
1491
1492
+
// All other refs use fully qualified format
1493
return `${namespaceName}#${defName}`;
1494
}
1495
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
+
);
1506
}
1507
1508
private getUnionReference(union: Union): string | null {
+2
packages/emitter/src/tsp-index.ts
+2
packages/emitter/src/tsp-index.ts
+46
packages/emitter/test/integration/atproto/input/app/bsky/actor/defs.tsp
+46
packages/emitter/test/integration/atproto/input/app/bsky/actor/defs.tsp
···
372
isActive?: boolean;
373
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfile.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfile.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfiles.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getProfiles.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getSuggestions.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/getSuggestions.tsp
+12
packages/emitter/test/integration/atproto/input/app/bsky/actor/profile.tsp
+12
packages/emitter/test/integration/atproto/input/app/bsky/actor/profile.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/putPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/putPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActors.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActors.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActorsTypeahead.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/searchActorsTypeahead.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/status.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/actor/status.tsp
+14
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/defs.tsp
+14
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/defs.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/getBookmarks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/bookmark/getBookmarks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/embed/images.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/embed/images.tsp
+54
packages/emitter/test/integration/atproto/input/app/bsky/embed/record.tsp
+54
packages/emitter/test/integration/atproto/input/app/bsky/embed/record.tsp
···
74
detached: boolean = true;
75
}
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
+
}
+26
packages/emitter/test/integration/atproto/input/app/bsky/embed/recordWithMedia.tsp
+26
packages/emitter/test/integration/atproto/input/app/bsky/embed/recordWithMedia.tsp
···
26
);
27
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/embed/video.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/embed/video.tsp
+49
packages/emitter/test/integration/atproto/input/app/bsky/feed/defs.tsp
+49
packages/emitter/test/integration/atproto/input/app/bsky/feed/defs.tsp
···
246
@token
247
model InteractionShare {}
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
+
}
+18
packages/emitter/test/integration/atproto/input/app/bsky/feed/generator.tsp
+18
packages/emitter/test/integration/atproto/input/app/bsky/feed/generator.tsp
···
30
@required createdAt: datetime;
31
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorLikes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getActorLikes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getAuthorFeed.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getAuthorFeed.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeed.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeed.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerator.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerator.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerators.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedGenerators.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getFeedSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getLikes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getLikes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getListFeed.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getListFeed.tsp
+10
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPostThread.tsp
+10
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPostThread.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPosts.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getPosts.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getQuotes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getQuotes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getRepostedBy.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getRepostedBy.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getSuggestedFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getSuggestedFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getTimeline.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/getTimeline.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/like.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/like.tsp
+42
packages/emitter/test/integration/atproto/input/app/bsky/feed/post.tsp
+42
packages/emitter/test/integration/atproto/input/app/bsky/feed/post.tsp
···
74
@maxGraphemes(64)
75
@maxLength(640)
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/repost.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/repost.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/searchPosts.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/searchPosts.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/sendInteractions.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/feed/sendInteractions.tsp
+23
packages/emitter/test/integration/atproto/input/app/bsky/graph/defs.tsp
+23
packages/emitter/test/integration/atproto/input/app/bsky/graph/defs.tsp
···
139
followedBy?: atUri;
140
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getActorStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getActorStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getBlocks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getBlocks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollowers.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollowers.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollows.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getFollows.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getKnownFollowers.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getKnownFollowers.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getList.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getList.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListBlocks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListBlocks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListMutes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListMutes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getLists.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getLists.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListsWithMembership.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getListsWithMembership.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getMutes.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getMutes.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getRelationships.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getRelationships.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPack.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPack.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacks.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacksWithMembership.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/graph/getStarterPacksWithMembership.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getSuggestedFollowsByActor.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/getSuggestedFollowsByActor.tsp
+17
packages/emitter/test/integration/atproto/input/app/bsky/graph/list.tsp
+17
packages/emitter/test/integration/atproto/input/app/bsky/graph/list.tsp
···
27
@required createdAt: datetime;
28
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/searchStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/searchStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/starterpack.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/graph/starterpack.tsp
+20
packages/emitter/test/integration/atproto/input/app/bsky/labeler/defs.tsp
+20
packages/emitter/test/integration/atproto/input/app/bsky/labeler/defs.tsp
···
54
labelValueDefinitions?: com.atproto.label.defs.LabelValueDefinition[];
55
}
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
+
}
+8
packages/emitter/test/integration/atproto/input/app/bsky/labeler/getServices.tsp
+8
packages/emitter/test/integration/atproto/input/app/bsky/labeler/getServices.tsp
+18
packages/emitter/test/integration/atproto/input/app/bsky/labeler/service.tsp
+18
packages/emitter/test/integration/atproto/input/app/bsky/labeler/service.tsp
···
20
subjectCollections?: nsid[];
21
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/getPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/getPreferences.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/listActivitySubscriptions.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/listActivitySubscriptions.tsp
+12
packages/emitter/test/integration/atproto/input/app/bsky/notification/listNotifications.tsp
+12
packages/emitter/test/integration/atproto/input/app/bsky/notification/listNotifications.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/putActivitySubscription.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/notification/putActivitySubscription.tsp
+10
packages/emitter/test/integration/atproto/input/app/bsky/notification/putPreferencesV2.tsp
+10
packages/emitter/test/integration/atproto/input/app/bsky/notification/putPreferencesV2.tsp
···
21
@required preferences: app.bsky.notification.defs.Preferences;
22
};
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
+
}
+13
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/defs.tsp
+13
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/defs.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getAgeAssuranceState.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getAgeAssuranceState.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPopularFeedGenerators.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPopularFeedGenerators.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadOtherV2.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadOtherV2.tsp
+15
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadV2.tsp
+15
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getPostThreadV2.tsp
···
55
);
56
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedFeeds.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedStarterPacks.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedUsers.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestedUsers.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestionsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getSuggestionsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendingTopics.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendingTopics.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrends.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrends.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/getTrendsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/initAgeAssurance.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/initAgeAssurance.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchActorsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchActorsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchPostsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchPostsSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchStarterPacksSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/unspecced/searchStarterPacksSkeleton.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/video/getJobStatus.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/video/getJobStatus.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/video/uploadVideo.tsp
+7
packages/emitter/test/integration/atproto/input/app/bsky/video/uploadVideo.tsp
+14
packages/emitter/test/integration/atproto/input/chat/bsky/actor/defs.tsp
+14
packages/emitter/test/integration/atproto/input/chat/bsky/actor/defs.tsp
···
20
verification?: app.bsky.actor.defs.VerificationState;
21
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/addReaction.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/addReaction.tsp
+18
packages/emitter/test/integration/atproto/input/chat/bsky/convo/defs.tsp
+18
packages/emitter/test/integration/atproto/input/chat/bsky/convo/defs.tsp
···
139
@required reaction: ReactionView;
140
}
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
packages/emitter/test/integration/atproto/input/chat/bsky/convo/deleteMessageForSelf.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoAvailability.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoAvailability.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoForMembers.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getConvoForMembers.tsp
+16
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getLog.tsp
+16
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getLog.tsp
···
21
)[];
22
};
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
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/getMessages.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/listConvos.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/listConvos.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/muteConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/muteConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/removeReaction.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/removeReaction.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessage.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessage.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessageBatch.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/convo/sendMessageBatch.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/unmuteConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/unmuteConvo.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/updateRead.tsp
+7
packages/emitter/test/integration/atproto/input/chat/bsky/convo/updateRead.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/moderation/getMessageContext.tsp
+8
packages/emitter/test/integration/atproto/input/chat/bsky/moderation/getMessageContext.tsp
+8
packages/emitter/test/integration/atproto/input/com/atproto/admin/defs.tsp
+8
packages/emitter/test/integration/atproto/input/com/atproto/admin/defs.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfo.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfo.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfos.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getAccountInfos.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getInviteCodes.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/getInviteCodes.tsp
+14
packages/emitter/test/integration/atproto/input/com/atproto/admin/getSubjectStatus.tsp
+14
packages/emitter/test/integration/atproto/input/com/atproto/admin/getSubjectStatus.tsp
···
20
deactivated?: com.atproto.admin.defs.StatusAttr;
21
};
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
+
}
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/searchAccounts.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/admin/searchAccounts.tsp
+14
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateSubjectStatus.tsp
+14
packages/emitter/test/integration/atproto/input/com/atproto/admin/updateSubjectStatus.tsp
···
26
takedown?: com.atproto.admin.defs.StatusAttr;
27
};
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
+
}
+7
packages/emitter/test/integration/atproto/input/com/atproto/identity/refreshIdentity.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/identity/refreshIdentity.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/identity/resolveIdentity.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/identity/resolveIdentity.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/label/queryLabels.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/label/queryLabels.tsp
+8
packages/emitter/test/integration/atproto/input/com/atproto/label/subscribeLabels.tsp
+8
packages/emitter/test/integration/atproto/input/com/atproto/label/subscribeLabels.tsp
+17
packages/emitter/test/integration/atproto/input/com/atproto/moderation/createReport.tsp
+17
packages/emitter/test/integration/atproto/input/com/atproto/moderation/createReport.tsp
···
50
meta?: unknown;
51
}
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
+
}
+51
packages/emitter/test/integration/atproto/input/com/atproto/moderation/defs.tsp
+51
packages/emitter/test/integration/atproto/input/com/atproto/moderation/defs.tsp
···
93
string,
94
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/applyWrites.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/applyWrites.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/createRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/createRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/deleteRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/deleteRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/putRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/repo/putRecord.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/server/getAccountInviteCodes.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/server/getAccountInviteCodes.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/sync/getHostStatus.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/sync/getHostStatus.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/sync/listHosts.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/sync/listHosts.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/temp/fetchLabels.tsp
+7
packages/emitter/test/integration/atproto/input/com/atproto/temp/fetchLabels.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/createTemplate.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/createTemplate.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/listTemplates.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/listTemplates.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/updateTemplate.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/communication/updateTemplate.tsp
+34
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/defs.tsp
+34
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/defs.tsp
···
601
@token
602
model TimelineEventPlcTombstone {}
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
+
}
+40
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/emitEvent.tsp
+40
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/emitEvent.tsp
···
54
externalId?: string;
55
}): tools.ozone.moderation.defs.ModEventView;
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
+
}
+39
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getAccountTimeline.tsp
+39
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getAccountTimeline.tsp
···
55
@required count: integer;
56
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getEvent.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getEvent.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecord.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecord.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecords.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRecords.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepo.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepo.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getReporterStats.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getReporterStats.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepos.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getRepos.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getSubjects.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/getSubjects.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryEvents.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryEvents.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryStatuses.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/queryStatuses.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/searchRepos.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/moderation/searchRepos.tsp
+10
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/addRule.tsp
+10
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/addRule.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryEvents.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryEvents.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryRules.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/queryRules.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/removeRule.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/removeRule.tsp
+10
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/updateRule.tsp
+10
packages/emitter/test/integration/atproto/input/tools/ozone/safelink/updateRule.tsp
+16
-7
packages/emitter/test/integration/atproto/input/tools/ozone/server/getConfig.tsp
+16
-7
packages/emitter/test/integration/atproto/input/tools/ozone/server/getConfig.tsp
···
19
}
20
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
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/tools/ozone/set/getValues.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/set/getValues.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/set/querySets.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/set/querySets.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/set/upsertSet.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/set/upsertSet.tsp
+14
-4
packages/emitter/test/integration/atproto/input/tools/ozone/setting/defs.tsp
+14
-4
packages/emitter/test/integration/atproto/input/tools/ozone/setting/defs.tsp
···
16
updatedAt?: datetime;
17
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"
23
| string;
24
25
@required scope: "instance" | "personal" | string;
···
27
@required lastUpdatedBy: did;
28
}
29
}
···
16
updatedAt?: datetime;
17
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
23
| string;
24
25
@required scope: "instance" | "personal" | string;
···
27
@required lastUpdatedBy: did;
28
}
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
+
}
+7
packages/emitter/test/integration/atproto/input/tools/ozone/setting/listOptions.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/setting/listOptions.tsp
+19
-4
packages/emitter/test/integration/atproto/input/tools/ozone/setting/upsertOption.tsp
+19
-4
packages/emitter/test/integration/atproto/input/tools/ozone/setting/upsertOption.tsp
···
14
description?: string;
15
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"
21
| string;
22
}): {
23
@required option: tools.ozone.setting.defs.Option;
24
};
25
}
···
14
description?: string;
15
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
21
| string;
22
}): {
23
@required option: tools.ozone.setting.defs.Option;
24
};
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
+
}
+7
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findCorrelation.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findCorrelation.tsp
+12
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findRelatedAccounts.tsp
+12
packages/emitter/test/integration/atproto/input/tools/ozone/signature/findRelatedAccounts.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/signature/searchAccounts.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/signature/searchAccounts.tsp
+17
-7
packages/emitter/test/integration/atproto/input/tools/ozone/team/addMember.tsp
+17
-7
packages/emitter/test/integration/atproto/input/tools/ozone/team/addMember.tsp
···
11
@required did: did;
12
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
}): tools.ozone.team.defs.Member;
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
+
}
+7
packages/emitter/test/integration/atproto/input/tools/ozone/team/defs.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/team/defs.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/team/listMembers.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/team/listMembers.tsp
+17
-7
packages/emitter/test/integration/atproto/input/tools/ozone/team/updateMember.tsp
+17
-7
packages/emitter/test/integration/atproto/input/tools/ozone/team/updateMember.tsp
···
11
@required did: did;
12
disabled?: boolean;
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
-
);
21
}): tools.ozone.team.defs.Member;
22
}
···
11
@required did: did;
12
disabled?: boolean;
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
}): tools.ozone.team.defs.Member;
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
+
}
+8
packages/emitter/test/integration/atproto/input/tools/ozone/verification/defs.tsp
+8
packages/emitter/test/integration/atproto/input/tools/ozone/verification/defs.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/verification/grantVerifications.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/verification/grantVerifications.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/verification/listVerifications.tsp
+7
packages/emitter/test/integration/atproto/input/tools/ozone/verification/listVerifications.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/defs.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/defs.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfo.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfo.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfos.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getAccountInfos.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getInviteCodes.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getInviteCodes.tsp
+14
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getSubjectStatus.tsp
+14
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/getSubjectStatus.tsp
···
20
deactivated?: com.atproto.admin.defs.StatusAttr;
21
};
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
+
}
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/searchAccounts.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/searchAccounts.tsp
+14
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateSubjectStatus.tsp
+14
packages/emitter/test/integration/lexicon-examples/input/com/atproto/admin/updateSubjectStatus.tsp
···
26
takedown?: com.atproto.admin.defs.StatusAttr;
27
};
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
+
}
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/refreshIdentity.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/refreshIdentity.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/resolveIdentity.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/identity/resolveIdentity.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/queryLabels.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/queryLabels.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/subscribeLabels.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/label/subscribeLabels.tsp
+17
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/createReport.tsp
+17
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/createReport.tsp
···
50
meta?: unknown;
51
}
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
+
}
+51
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/defs.tsp
+51
packages/emitter/test/integration/lexicon-examples/input/com/atproto/moderation/defs.tsp
···
102
string,
103
}
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
+
}
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/applyWrites.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/applyWrites.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/createRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/createRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/deleteRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/deleteRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/putRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/repo/putRecord.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/getAccountInviteCodes.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/server/getAccountInviteCodes.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getHostStatus.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/getHostStatus.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listHosts.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/sync/listHosts.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/fetchLabels.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/com/atproto/temp/fetchLabels.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/blockquote.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/blockquote.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/bskyPost.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/bskyPost.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/header.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/header.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/text.tsp
+7
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/text.tsp
+17
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/unorderedList.tsp
+17
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/blocks/unorderedList.tsp
···
10
children?: ListItem[];
11
}
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
+
}
+12
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/comment.tsp
+12
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/comment.tsp
+12
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/document.tsp
+12
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/document.tsp
···
23
@required pages: (`pub`.leaflet.pages.linearDocument.Main | unknown)[];
24
}
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
+
}
+57
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/pages/linearDocument.tsp
+57
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/pages/linearDocument.tsp
···
43
@required offset: integer;
44
}
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
+
}
+13
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/publication.tsp
+13
packages/emitter/test/integration/lexicon-examples/input/pub/leaflet/publication.tsp
···
34
accentText?: (`pub`.leaflet.theme.color.Rgba | `pub`.leaflet.theme.color.Rgb | unknown);
35
}
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
+
}
+12
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/state.tsp
+12
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/issue/state.tsp
···
10
state: "sh.tangled.repo.issue.state.open" | "sh.tangled.repo.issue.state.closed" | string = "sh.tangled.repo.issue.state.open";
11
}
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
+
}
+17
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status.tsp
+17
packages/emitter/test/integration/lexicon-examples/input/sh/tangled/repo/pull/status.tsp
···
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
}
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
+
}
+12
-20
packages/emitter/test/integration.test.ts
+12
-20
packages/emitter/test/integration.test.ts
···
54
path.join(scenario, "output"),
55
);
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
// Generate a test for each expected output
75
for (const expectedPath of Object.keys(expectedFiles)) {
76
if (!expectedPath.endsWith(".json")) continue;
···
80
const hasInput = Object.keys(inputFiles).includes(inputPath);
81
82
if (hasInput) {
83
-
it(`should emit ${expectedPath}`, function () {
84
// Check for compilation errors
85
if (emitResult.diagnostics.length > 0) {
86
const formattedDiagnostics = emitResult.diagnostics.map((diag) =>
···
91
);
92
}
93
94
assert.ok(
95
Object.prototype.hasOwnProperty.call(
96
emitResult.files,
97
-
expectedPath,
98
),
99
`Expected file ${expectedPath} was not produced`,
100
);
101
102
-
const actual = JSON.parse(emitResult.files[expectedPath]);
103
const expected = JSON.parse(expectedFiles[expectedPath]);
104
assert.deepStrictEqual(actual, expected);
105
});
···
180
await walk(dir, "");
181
return result;
182
}
···
54
path.join(scenario, "output"),
55
);
56
57
// Generate a test for each expected output
58
for (const expectedPath of Object.keys(expectedFiles)) {
59
if (!expectedPath.endsWith(".json")) continue;
···
63
const hasInput = Object.keys(inputFiles).includes(inputPath);
64
65
if (hasInput) {
66
+
it(`should emit ${expectedPath}`, async function () {
67
+
// Compile each file in isolation
68
+
const emitResult = await doEmit({ [inputPath]: inputFiles[inputPath] }, inputPath);
69
+
70
// Check for compilation errors
71
if (emitResult.diagnostics.length > 0) {
72
const formattedDiagnostics = emitResult.diagnostics.map((diag) =>
···
77
);
78
}
79
80
+
const normalizedExpectedPath = normalizePathToPosix(expectedPath);
81
+
82
assert.ok(
83
Object.prototype.hasOwnProperty.call(
84
emitResult.files,
85
+
normalizedExpectedPath,
86
),
87
`Expected file ${expectedPath} was not produced`,
88
);
89
90
+
const actual = JSON.parse(emitResult.files[normalizedExpectedPath]);
91
const expected = JSON.parse(expectedFiles[expectedPath]);
92
assert.deepStrictEqual(actual, expected);
93
});
···
168
await walk(dir, "");
169
return result;
170
}
171
+
172
+
function normalizePathToPosix(thePath: string): string {
173
+
return thePath.replaceAll(path.sep, path.posix.sep);
174
+
}
-22
packages/emitter/test/spec/basic/input/com/example/unionWithTokens.tsp
-22
packages/emitter/test/spec/basic/input/com/example/unionWithTokens.tsp
···
1
-
import "@typelex/emitter";
2
-
3
-
namespace com.example.unionWithTokens {
4
-
/** Tests union with token references */
5
-
model Main {
6
-
/** Reason can be a known token or unknown type */
7
-
@required
8
-
reason: (ReasonSpam | ReasonViolation | ReasonMisleading | unknown);
9
-
}
10
-
11
-
/** Spam: frequent unwanted promotion */
12
-
@token
13
-
model ReasonSpam {}
14
-
15
-
/** Direct violation of rules */
16
-
@token
17
-
model ReasonViolation {}
18
-
19
-
/** Misleading or deceptive content */
20
-
@token
21
-
model ReasonMisleading {}
22
-
}
···
+21
packages/emitter/test/spec/basic/output/com/example/other.json
+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
-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
+8
packages/emitter/test/spec/external/input/test/external.tsp
+7
packages/emitter/test/spec/external/input/test/normal.tsp
+7
packages/emitter/test/spec/external/input/test/normal.tsp
+14
packages/emitter/test/spec/external/output/test/normal.json
+14
packages/emitter/test/spec/external/output/test/normal.json
+32
-4
packages/emitter/test/spec.test.ts
+32
-4
packages/emitter/test/spec.test.ts
···
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 {
···
91
);
92
}
93
94
assert.ok(
95
Object.prototype.hasOwnProperty.call(
96
emitResult.files,
97
-
expectedPath,
98
),
99
`Expected file ${expectedPath} was not produced`,
100
);
101
102
-
const actual = JSON.parse(emitResult.files[expectedPath]);
103
const expected = JSON.parse(expectedFiles[expectedPath]);
104
assert.deepStrictEqual(actual, expected);
105
});
106
} else {
107
-
it.skip(`TODO: ${expectedPath} (add ${inputPath})`, function () {});
108
}
109
}
110
});
111
}
112
});
···
180
await walk(dir, "");
181
return result;
182
}
···
64
// Create a virtual main.tsp that imports all other files
65
const mainContent =
66
'import "@typelex/emitter";\n' +
67
+
tspFiles.map((f) => `import "./${normalizePathToPosix(f)}";`).join("\n");
68
const filesWithMain = { ...inputFiles, "main.tsp": mainContent };
69
emitResult = await doEmit(filesWithMain, "main.tsp");
70
} else {
···
91
);
92
}
93
94
+
const normalizedExpectedPath = normalizePathToPosix(expectedPath);
95
+
96
assert.ok(
97
Object.prototype.hasOwnProperty.call(
98
emitResult.files,
99
+
normalizedExpectedPath,
100
),
101
`Expected file ${expectedPath} was not produced`,
102
);
103
104
+
const actual = JSON.parse(emitResult.files[normalizedExpectedPath]);
105
const expected = JSON.parse(expectedFiles[expectedPath]);
106
assert.deepStrictEqual(actual, expected);
107
});
108
} else {
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
+
});
115
}
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
+
});
134
});
135
}
136
});
···
204
await walk(dir, "");
205
return result;
206
}
207
+
208
+
function normalizePathToPosix(thePath: string): string {
209
+
return thePath.replaceAll(path.sep, path.posix.sep);
210
+
}
+3
-3
packages/example/package.json
+3
-3
packages/example/package.json
···
5
"type": "module",
6
"scripts": {
7
"build": "pnpm run build:lexicons && pnpm run build:codegen",
8
-
"build:lexicons": "tsp compile typelex/main.tsp",
9
-
"build:codegen": "lex gen-server --yes ./src lexicons/app/example/*.json"
10
},
11
"dependencies": {
12
"@atproto/lex-cli": "^0.9.5",
13
"@atproto/xrpc-server": "^0.9.5",
14
-
"@typespec/compiler": "^1.4.0",
15
"@typelex/emitter": "workspace:*"
16
},
17
"devDependencies": {
···
5
"type": "module",
6
"scripts": {
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
},
11
"dependencies": {
12
"@atproto/lex-cli": "^0.9.5",
13
"@atproto/xrpc-server": "^0.9.5",
14
+
"@typelex/cli": "workspace:*",
15
"@typelex/emitter": "workspace:*"
16
},
17
"devDependencies": {
+45
-6
packages/example/src/index.ts
+45
-6
packages/example/src/index.ts
···
10
createServer as createXrpcServer,
11
} from '@atproto/xrpc-server'
12
import { schemas } from './lexicons.js'
13
14
export function createServer(options?: XrpcOptions): Server {
15
return new Server(options)
···
17
18
export class Server {
19
xrpc: XrpcServer
20
-
app: AppNS
21
22
constructor(options?: XrpcOptions) {
23
this.xrpc = createXrpcServer(schemas, options)
24
-
this.app = new AppNS(this)
25
}
26
}
27
28
-
export class AppNS {
29
_server: Server
30
-
example: AppExampleNS
31
32
constructor(server: Server) {
33
this._server = server
34
-
this.example = new AppExampleNS(server)
35
}
36
}
37
38
-
export class AppExampleNS {
39
_server: Server
40
41
constructor(server: Server) {
42
this._server = server
43
}
44
}
···
10
createServer as createXrpcServer,
11
} from '@atproto/xrpc-server'
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'
16
17
export function createServer(options?: XrpcOptions): Server {
18
return new Server(options)
···
20
21
export class Server {
22
xrpc: XrpcServer
23
+
xyz: XyzNS
24
25
constructor(options?: XrpcOptions) {
26
this.xrpc = createXrpcServer(schemas, options)
27
+
this.xyz = new XyzNS(this)
28
}
29
}
30
31
+
export class XyzNS {
32
_server: Server
33
+
statusphere: XyzStatusphereNS
34
35
constructor(server: Server) {
36
this._server = server
37
+
this.statusphere = new XyzStatusphereNS(server)
38
}
39
}
40
41
+
export class XyzStatusphereNS {
42
_server: Server
43
44
constructor(server: Server) {
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)
82
}
83
}
+100
-153
packages/example/src/lexicons.ts
+100
-153
packages/example/src/lexicons.ts
···
10
import { type $Typed, is$typed, maybe$typed } from './util.js'
11
12
export const schemaDict = {
13
-
AppExampleDefs: {
14
lexicon: 1,
15
-
id: 'app.example.defs',
16
defs: {
17
-
postRef: {
18
type: 'object',
19
properties: {
20
uri: {
21
type: 'string',
22
-
description: 'AT URI of the post',
23
},
24
-
cid: {
25
type: 'string',
26
-
description: 'CID of the post',
27
},
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',
39
},
40
-
parent: {
41
type: 'ref',
42
-
ref: 'lex:app.example.defs#postRef',
43
-
description: 'Direct parent post being replied to',
44
},
45
},
46
-
description: 'Reference to a parent post in a reply chain',
47
-
required: ['root', 'parent'],
48
},
49
-
entity: {
50
type: 'object',
51
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: {
61
type: 'string',
62
-
description: 'Entity type',
63
},
64
-
value: {
65
type: 'string',
66
-
description: 'Entity value (handle, URL, or tag)',
67
},
68
},
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',
76
},
77
},
78
},
79
-
AppExampleFollow: {
80
lexicon: 1,
81
-
id: 'app.example.follow',
82
defs: {
83
main: {
84
-
type: 'record',
85
-
key: 'tid',
86
-
record: {
87
-
type: 'object',
88
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',
97
},
98
},
99
-
required: ['subject', 'createdAt'],
100
},
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',
119
},
120
-
createdAt: {
121
-
type: 'string',
122
-
format: 'datetime',
123
-
description: 'When the like was created',
124
-
},
125
},
126
-
required: ['subject', 'createdAt'],
127
},
128
-
description: 'A like on a post',
129
},
130
},
131
},
132
-
AppExamplePost: {
133
lexicon: 1,
134
-
id: 'app.example.post',
135
defs: {
136
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',
155
},
156
-
description: 'Languages the post is written in',
157
-
},
158
-
entities: {
159
-
type: 'array',
160
-
items: {
161
type: 'ref',
162
-
ref: 'lex:app.example.defs#entity',
163
},
164
-
description: 'Referenced entities in the post',
165
},
166
-
reply: {
167
-
type: 'ref',
168
-
ref: 'lex:app.example.defs#replyRef',
169
-
description: 'Post the user is replying to',
170
-
},
171
},
172
-
required: ['text', 'createdAt'],
173
},
174
-
description: 'A post in the feed',
175
},
176
},
177
},
178
-
AppExampleProfile: {
179
lexicon: 1,
180
-
id: 'app.example.profile',
181
defs: {
182
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',
195
},
196
-
avatar: {
197
-
type: 'string',
198
-
description: 'Profile avatar image',
199
},
200
-
banner: {
201
-
type: 'string',
202
-
description: 'Profile banner image',
203
-
},
204
},
205
},
206
-
description: 'User profile information',
207
},
208
},
209
},
210
-
AppExampleRepost: {
211
lexicon: 1,
212
-
id: 'app.example.repost',
213
defs: {
214
main: {
215
type: 'record',
···
217
record: {
218
type: 'object',
219
properties: {
220
-
subject: {
221
-
type: 'ref',
222
-
ref: 'lex:app.example.defs#postRef',
223
-
description: 'Post being reposted',
224
},
225
createdAt: {
226
type: 'string',
227
format: 'datetime',
228
-
description: 'When the repost was created',
229
},
230
},
231
-
required: ['subject', 'createdAt'],
232
},
233
-
description: 'A repost of another post',
234
},
235
},
236
},
···
267
}
268
269
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',
276
} as const
···
10
import { type $Typed, is$typed, maybe$typed } from './util.js'
11
12
export const schemaDict = {
13
+
XyzStatusphereDefs: {
14
lexicon: 1,
15
+
id: 'xyz.statusphere.defs',
16
defs: {
17
+
statusView: {
18
type: 'object',
19
properties: {
20
uri: {
21
type: 'string',
22
+
format: 'at-uri',
23
},
24
+
status: {
25
type: 'string',
26
+
maxLength: 32,
27
+
minLength: 1,
28
+
maxGraphemes: 1,
29
},
30
+
createdAt: {
31
+
type: 'string',
32
+
format: 'datetime',
33
},
34
+
profile: {
35
type: 'ref',
36
+
ref: 'lex:xyz.statusphere.defs#profileView',
37
},
38
},
39
+
required: ['uri', 'status', 'createdAt', 'profile'],
40
},
41
+
profileView: {
42
type: 'object',
43
properties: {
44
+
did: {
45
type: 'string',
46
+
format: 'did',
47
},
48
+
handle: {
49
type: 'string',
50
+
format: 'handle',
51
},
52
},
53
+
required: ['did', 'handle'],
54
},
55
},
56
},
57
+
XyzStatusphereGetStatuses: {
58
lexicon: 1,
59
+
id: 'xyz.statusphere.getStatuses',
60
defs: {
61
main: {
62
+
type: 'query',
63
+
description: 'Get a list of the most recent statuses on the network.',
64
+
parameters: {
65
+
type: 'params',
66
properties: {
67
+
limit: {
68
+
type: 'integer',
69
+
minimum: 1,
70
+
maximum: 100,
71
+
default: 50,
72
},
73
},
74
},
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
+
},
87
},
88
+
required: ['statuses'],
89
},
90
},
91
},
92
},
93
},
94
+
XyzStatusphereGetUser: {
95
lexicon: 1,
96
+
id: 'xyz.statusphere.getUser',
97
defs: {
98
main: {
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',
109
},
110
+
status: {
111
type: 'ref',
112
+
ref: 'lex:xyz.statusphere.defs#statusView',
113
},
114
},
115
+
required: ['profile'],
116
},
117
},
118
},
119
},
120
},
121
+
XyzStatusphereSendStatus: {
122
lexicon: 1,
123
+
id: 'xyz.statusphere.sendStatus',
124
defs: {
125
main: {
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
+
},
139
},
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
+
},
152
},
153
+
required: ['status'],
154
},
155
},
156
},
157
},
158
},
159
+
XyzStatusphereStatus: {
160
lexicon: 1,
161
+
id: 'xyz.statusphere.status',
162
defs: {
163
main: {
164
type: 'record',
···
166
record: {
167
type: 'object',
168
properties: {
169
+
status: {
170
+
type: 'string',
171
+
maxLength: 32,
172
+
minLength: 1,
173
+
maxGraphemes: 1,
174
},
175
createdAt: {
176
type: 'string',
177
format: 'datetime',
178
},
179
},
180
+
required: ['status', 'createdAt'],
181
},
182
},
183
},
184
},
···
215
}
216
217
export const ids = {
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',
223
} as const
-79
packages/example/src/types/app/example/defs.ts
-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
-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
-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
-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
-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
-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
+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
+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
+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
+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
+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
-5
packages/example/tspconfig.yaml
+235
packages/example/typelex/externals.tsp
+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
+46
-122
packages/example/typelex/main.tsp
···
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;
46
47
-
@doc("End index in text")
48
-
@required
49
-
end: int32;
50
51
-
@doc("Entity type")
52
@required
53
-
type: string;
54
55
-
@doc("Entity value (handle, URL, or tag)")
56
-
@required
57
-
value: string;
58
}
59
-
}
60
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;
83
}
84
}
85
86
-
namespace app.example.follow {
87
@rec("tid")
88
-
@doc("A follow relationship")
89
model Main {
90
-
@doc("DID of the account being followed")
91
@required
92
-
subject: string;
93
94
-
@doc("When the follow was created")
95
-
@required
96
-
createdAt: datetime;
97
}
98
}
99
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
-
}
112
}
113
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
-
}
126
}
127
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
-
}
144
}
···
1
import "@typelex/emitter";
2
+
import "./externals.tsp";
3
4
+
namespace xyz.statusphere.defs {
5
+
model StatusView {
6
+
@required uri: atUri;
7
8
@required
9
+
@minLength(1)
10
+
@maxGraphemes(1)
11
+
@maxLength(32)
12
+
status: string;
13
14
+
@required createdAt: datetime;
15
+
@required profile: ProfileView;
16
}
17
18
+
model ProfileView {
19
+
@required did: did;
20
+
@required handle: handle;
21
}
22
}
23
24
+
namespace xyz.statusphere.status {
25
@rec("tid")
26
model Main {
27
@required
28
+
@minLength(1)
29
+
@maxGraphemes(1)
30
+
@maxLength(32)
31
+
status: string;
32
33
+
@required createdAt: datetime;
34
}
35
}
36
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
+
};
51
}
52
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
+
};
59
}
60
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
+
};
68
}
+2
-3
packages/playground/package.json
+2
-3
packages/playground/package.json
-33
packages/playground/samples/build.js
-33
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
-
for (const [namespace, lexicon] of lexicons) {
17
-
const bundled = bundleLexicon(namespace);
18
-
const filename = `${namespace}.tsp`;
19
-
const filepath = join(outputDir, filename);
20
-
21
-
writeFileSync(filepath, bundled);
22
-
23
-
samplesList[namespace] = {
24
-
filename: `samples/dist/${filename}`,
25
-
preferredEmitter: "@typelex/emitter",
26
-
};
27
-
}
28
-
29
-
// Write the samples index
30
-
const samplesIndex = `export default ${JSON.stringify(samplesList, null, 2)};`;
31
-
writeFileSync(join(outputDir, "samples.js"), samplesIndex);
32
-
33
-
console.log(`Wrote ${Object.keys(samplesList).length} bundled samples to disk`);
···
+28
-105
packages/playground/samples/index.js
+28
-105
packages/playground/samples/index.js
···
6
const __dirname = dirname(fileURLToPath(import.meta.url));
7
8
// Get all tsp files
9
-
function getAllTspFiles(dir, baseDir = dir) {
10
const files = [];
11
const entries = readdirSync(dir);
12
···
15
const stat = statSync(fullPath);
16
17
if (stat.isDirectory()) {
18
-
files.push(...getAllTspFiles(fullPath, baseDir));
19
} else if (entry.endsWith(".tsp")) {
20
files.push(relative(baseDir, fullPath));
21
}
···
24
return files.sort();
25
}
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*\{/, "");
35
36
-
const matches = withoutDeclaration.matchAll(pattern);
37
-
for (const match of matches) {
38
-
deps.add(match[1]);
39
-
}
40
41
-
return Array.from(deps);
42
-
}
43
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
-
);
52
53
-
const atprotoFiles = getAllTspFiles(atprotoInputDir);
54
-
const lexiconExampleFiles = getAllTspFiles(lexiconExamplesDir);
55
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);
96
}
97
-
98
-
visiting.delete(namespace);
99
-
collected.add(namespace);
100
}
101
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;
135
}
136
137
-
// Export for build script
138
-
export { lexicons, bundleLexicon };
139
140
-
console.log(`Loaded ${lexicons.size} lexicons for bundling`);
···
6
const __dirname = dirname(fileURLToPath(import.meta.url));
7
8
// Get all tsp files
9
+
function getAllFiles(dir, baseDir = dir) {
10
const files = [];
11
const entries = readdirSync(dir);
12
···
15
const stat = statSync(fullPath);
16
17
if (stat.isDirectory()) {
18
+
files.push(...getAllFiles(fullPath, baseDir));
19
} else if (entry.endsWith(".tsp")) {
20
files.push(relative(baseDir, fullPath));
21
}
···
24
return files.sort();
25
}
26
27
+
const integrationDir = join(__dirname, "../../emitter/test/integration");
28
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
+
});
34
35
+
// Load all lexicons from test suites
36
+
const lexicons = new Map(); // namespace -> { file, content, suite }
37
38
+
for (const suite of testSuites) {
39
+
const inputDir = join(integrationDir, suite, "input");
40
+
const inputFiles = getAllFiles(inputDir).filter((f) => f.endsWith(".tsp"));
41
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, ".");
46
47
+
lexicons.set(namespace, { file, content, suite, fullPath });
48
}
49
}
50
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
+
};
58
}
59
60
+
export { lexicons };
61
+
export default samplesList;
62
63
+
console.log(`Loaded ${lexicons.size} lexicons`);
+1
-1
packages/playground/vite.config.ts
+1
-1
packages/playground/vite.config.ts
+10
-11
packages/website/src/pages/index.astro
+10
-11
packages/website/src/pages/index.astro
···
1
---
2
-
import Playground from '../components/Playground';
3
import { highlightCode } from '../utils/shiki';
4
import { compileToJson } from '../utils/compile';
5
import { createPlaygroundUrl } from '../utils/playground-url';
···
236
<nav class="hero-actions">
237
<a href="#install" class="install-cta">Try It</a>
238
<a href="https://tangled.org/@danabra.mov/typelex/blob/main/DOCS.md" target="_blank" rel="noopener noreferrer" class="star-btn">
239
-
Read Documentation
240
</a>
241
</nav>
242
</header>
···
277
<h2>Install</h2>
278
<div class="install-grid">
279
<div class="install-notice">
280
-
<p class="notice-text">This is an early-stage experiment. It's probably buggy as hell.</p>
281
</div>
282
<div class="install-step playground-step">
283
<div class="step-number">0</div>
···
498
499
header {
500
text-align: center;
501
-
padding: 3rem 1rem 2rem;
502
margin: 0 auto;
503
}
504
···
649
650
.hero-actions {
651
display: flex;
652
-
flex-direction: column;
653
-
gap: 1rem;
654
margin-top: 2.5rem;
655
align-items: center;
656
}
657
658
@media (min-width: 640px) {
659
.hero-actions {
660
-
flex-direction: row;
661
-
justify-content: center;
662
gap: 1rem;
663
}
664
}
···
797
798
.install-notice {
799
padding: 1rem 1.5rem;
800
-
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
801
-
border: 2px solid #ef4444;
802
border-radius: 10px;
803
text-align: left;
804
}
···
808
font-size: 1.0625rem;
809
line-height: 1.5;
810
font-weight: 700;
811
-
color: #991b1b;
812
}
813
814
.install-grid {
···
1
---
2
import { highlightCode } from '../utils/shiki';
3
import { compileToJson } from '../utils/compile';
4
import { createPlaygroundUrl } from '../utils/playground-url';
···
235
<nav class="hero-actions">
236
<a href="#install" class="install-cta">Try It</a>
237
<a href="https://tangled.org/@danabra.mov/typelex/blob/main/DOCS.md" target="_blank" rel="noopener noreferrer" class="star-btn">
238
+
Read Docs
239
</a>
240
</nav>
241
</header>
···
276
<h2>Install</h2>
277
<div class="install-grid">
278
<div class="install-notice">
279
+
<p class="notice-text">This is an early-stage experiment. Design and syntax may change.</p>
280
</div>
281
<div class="install-step playground-step">
282
<div class="step-number">0</div>
···
497
498
header {
499
text-align: center;
500
+
padding: 3rem 1rem 0.5rem;
501
margin: 0 auto;
502
}
503
···
648
649
.hero-actions {
650
display: flex;
651
+
flex-direction: row;
652
+
flex-wrap: wrap;
653
+
gap: 0.75rem;
654
margin-top: 2.5rem;
655
align-items: center;
656
+
justify-content: center;
657
}
658
659
@media (min-width: 640px) {
660
.hero-actions {
661
gap: 1rem;
662
}
663
}
···
796
797
.install-notice {
798
padding: 1rem 1.5rem;
799
+
background: linear-gradient(135deg, #fefce8 0%, #fef3c7 100%);
800
+
border: 2px solid #f59e0b;
801
border-radius: 10px;
802
text-align: left;
803
}
···
807
font-size: 1.0625rem;
808
line-height: 1.5;
809
font-weight: 700;
810
+
color: #92400e;
811
}
812
813
.install-grid {
+46
-3
pnpm-lock.yaml
+46
-3
pnpm-lock.yaml
···
12
specifier: ^5.0.0
13
version: 5.9.3
14
15
packages/emitter:
16
dependencies:
17
'@typespec/compiler':
···
48
'@atproto/xrpc-server':
49
specifier: ^0.9.5
50
version: 0.9.5
51
'@typelex/emitter':
52
specifier: workspace:*
53
version: link:../emitter
54
-
'@typespec/compiler':
55
-
specifier: ^1.4.0
56
-
version: 1.4.0(@types/node@20.19.19)
57
devDependencies:
58
typescript:
59
specifier: ^5.0.0
···
1645
'@ts-morph/common@0.25.0':
1646
resolution: {integrity: sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==}
1647
1648
'@types/babel__core@7.20.5':
1649
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
1650
···
1705
1706
'@types/unist@3.0.3':
1707
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
1708
1709
'@typespec/asset-emitter@0.74.0':
1710
resolution: {integrity: sha512-DWIdlSNhRgBeZ8exfqubfUn0H6mRg4gr0s7zLTdBMUEDHL3Yh0ljnRPkd8AXTZhoW3maTFT69loWTrqx09T5oQ==}
···
7411
path-browserify: 1.0.1
7412
tinyglobby: 0.2.15
7413
7414
'@types/babel__core@7.20.5':
7415
dependencies:
7416
'@babel/parser': 7.28.4
···
7482
csstype: 3.1.3
7483
7484
'@types/unist@3.0.3': {}
7485
7486
'@typespec/asset-emitter@0.74.0(@typespec/compiler@1.4.0(@types/node@20.19.19))':
7487
dependencies:
···
12
specifier: ^5.0.0
13
version: 5.9.3
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
+
37
packages/emitter:
38
dependencies:
39
'@typespec/compiler':
···
70
'@atproto/xrpc-server':
71
specifier: ^0.9.5
72
version: 0.9.5
73
+
'@typelex/cli':
74
+
specifier: workspace:*
75
+
version: link:../cli
76
'@typelex/emitter':
77
specifier: workspace:*
78
version: link:../emitter
79
devDependencies:
80
typescript:
81
specifier: ^5.0.0
···
1667
'@ts-morph/common@0.25.0':
1668
resolution: {integrity: sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==}
1669
1670
+
'@typelex/emitter@0.2.0':
1671
+
resolution: {integrity: sha512-4Iw6VAnd9nCFGOkJcu9utWdmu9ZyPeAb1QX/B7KerGBmfc2FuIDqgZZ/mZ6c56atcZd62pb2oYF/3RgSFhEsoQ==}
1672
+
peerDependencies:
1673
+
'@typespec/compiler': ^1.4.0
1674
+
1675
'@types/babel__core@7.20.5':
1676
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
1677
···
1732
1733
'@types/unist@3.0.3':
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==}
1741
1742
'@typespec/asset-emitter@0.74.0':
1743
resolution: {integrity: sha512-DWIdlSNhRgBeZ8exfqubfUn0H6mRg4gr0s7zLTdBMUEDHL3Yh0ljnRPkd8AXTZhoW3maTFT69loWTrqx09T5oQ==}
···
7444
path-browserify: 1.0.1
7445
tinyglobby: 0.2.15
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
+
7451
'@types/babel__core@7.20.5':
7452
dependencies:
7453
'@babel/parser': 7.28.4
···
7519
csstype: 3.1.3
7520
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
7528
7529
'@typespec/asset-emitter@0.74.0(@typespec/compiler@1.4.0(@types/node@20.19.19))':
7530
dependencies: