+3
-3
README.md
+3
-3
README.md
···
23
23
## Project Structure
24
24
25
25
```
26
-
lexicons/
26
+
/
27
27
├── src/
28
28
│ └── lexicons/ # TypeScript lexicon definitions (source)
29
29
│ ├── site.standard.document.ts
···
31
31
│ ├── site.standard.publication.ts
32
32
│ ├── site.standard.theme.basic.ts
33
33
│ └── site.standard.theme.color.ts
34
-
└── schemas/ # Generated JSON schemas
34
+
└── out/ # Generated JSON schemas
35
35
├── site.standard.document.json
36
36
├── site.standard.graph.subscription.json
37
37
├── site.standard.publication.json
···
44
44
- [Standard.site](https://standard.site/) - Full specification and documentation
45
45
- [AT Protocol](https://atproto.com/) - The underlying protocol
46
46
- [Lexicon Documentation](https://atproto.com/specs/lexicon) - AT Protocol lexicon spec
47
-
- [Prototypey](https://tangled.org/tylur.dev/prototypey) - AT Protocol lexicon typescript toolkit
47
+
- [Prototypey](https://github.com/tylersayshi/prototypey) - AT Protocol lexicon typescript toolkit
48
48
49
49
## License
50
50
+2
-2
package.json
+2
-2
package.json
···
4
4
"type": "module",
5
5
"private": true,
6
6
"scripts": {
7
-
"lexicon:emit": "bunx prototypey gen-emit ./schemas ./src/lexicons/**/*.ts",
8
-
"lexicon:import": "bunx prototypey gen-from-json ./src/lexicons ./schemas/**/*.json",
7
+
"lexicon:emit": "bunx prototypey gen-emit ./out ./src/lexicons/**/*.ts",
8
+
"lexicon:import": "bunx prototypey gen-from-json ./src/lexicons ./out/**/*.json",
9
9
"lexicon:publish": "bun run scripts/publish.ts"
10
10
},
11
11
"devDependencies": {
+1
src/constants.ts
+1
src/constants.ts
···
1
+
export const MB = 1000000; // 1MB
+61
src/lexicons/site.standard.document.ts
+61
src/lexicons/site.standard.document.ts
···
1
+
import { lx } from 'prototypey'
2
+
import { MB } from '../constants.ts'
3
+
4
+
export const siteStandardDocument = lx.lexicon('site.standard.document', {
5
+
main: lx.record({
6
+
key: 'tid',
7
+
type: 'record',
8
+
record: lx.object({
9
+
site: lx.string({
10
+
required: true,
11
+
format: 'uri',
12
+
description: 'Points to a publication record (at://) or a publication url (https://) for loose documents. Avoid trailing slashes.'
13
+
}),
14
+
path: lx.string({
15
+
description: 'Combine with site or publication url to construct a canonical URL to the document. Prepend with a leading slash.'
16
+
}),
17
+
title: lx.string({
18
+
required: true,
19
+
maxLength: 1280,
20
+
maxGraphemes: 128,
21
+
description: 'Title of the document.'
22
+
}),
23
+
description: lx.string({
24
+
maxLength: 3000,
25
+
maxGraphemes: 300,
26
+
description: 'A brief description or excerpt from the document.'
27
+
}),
28
+
coverImage: lx.blob({
29
+
maxSize: 1 * MB,
30
+
accept: ['image/*'],
31
+
description: 'Image to used for thumbnail or cover image. Less than 1MB is size.'
32
+
}),
33
+
content: lx.union([], {
34
+
closed: false,
35
+
description: 'Open union used to define the record\'s content. Each entry must specify a $type and may be extended with other lexicons to support additional content formats.'
36
+
}),
37
+
textContent: lx.string({
38
+
description: 'Plaintext representation of the documents contents. Should not contain markdown or other formatting.'
39
+
}),
40
+
bskyPostRef: lx.ref('com.atproto.repo.strongRef', {
41
+
description: 'Strong reference to a Bluesky post. Useful to keep track of comments off-platform.'
42
+
}),
43
+
tags: lx.array({
44
+
type: 'string',
45
+
}, {
46
+
maxLength: 100,
47
+
description: 'Array of strings used to tag or categorize the document. Avoid prepending tags with hashtags.'
48
+
}),
49
+
publishedAt: lx.string({
50
+
required: true,
51
+
format: 'datetime',
52
+
description: 'Timestamp of the documents publish time.'
53
+
}),
54
+
updatedAt: lx.string({
55
+
format: 'datetime',
56
+
description: 'Timestamp of the documents last edit.'
57
+
})
58
+
}),
59
+
description: 'A document record representing a published article, blog post, or other content. Documents can belong to a publication or exist independently.'
60
+
})
61
+
})
+16
src/lexicons/site.standard.graph.subscription.ts
+16
src/lexicons/site.standard.graph.subscription.ts
···
1
+
import { lx } from 'prototypey'
2
+
3
+
export const siteStandardGraphSubscription = lx.lexicon('site.standard.graph.subscription', {
4
+
main: lx.record({
5
+
key: 'tid',
6
+
type: 'record',
7
+
record: lx.object({
8
+
publication: lx.string({
9
+
required: true,
10
+
format: 'at-uri',
11
+
description: 'AT-URI reference to the publication record being subscribed to (ex: at://did:plc:abc123/site.standard.publication/xyz789).'
12
+
})
13
+
}),
14
+
description: 'Record declaring a subscription to a publication.'
15
+
})
16
+
})
+47
src/lexicons/site.standard.publication.ts
+47
src/lexicons/site.standard.publication.ts
···
1
+
import { lx } from 'prototypey'
2
+
import { siteStandardThemeBasic } from './site.standard.theme.basic.ts'
3
+
import { MB } from '../constants.ts'
4
+
5
+
export const siteStandardPublication = lx.lexicon('site.standard.publication', {
6
+
main: lx.record({
7
+
key: 'tid',
8
+
type: 'record',
9
+
record: lx.object({
10
+
url: lx.string({
11
+
required: true,
12
+
format: 'uri',
13
+
description: 'Base publication url (ex: https://standard.site). The canonical document URL is formed by combining this value with the document path.'
14
+
}),
15
+
icon: lx.blob({
16
+
maxSize: 1 * MB,
17
+
accept: ['image/*'],
18
+
description: 'Square image to identify the publication. Should be at least 256x256.'
19
+
}),
20
+
name: lx.string({
21
+
required: true,
22
+
maxLength: 1280,
23
+
maxGraphemes: 128,
24
+
description: 'Name of the publication.'
25
+
}),
26
+
description: lx.string({
27
+
maxLength: 3000,
28
+
maxGraphemes: 300,
29
+
description: 'Brief description of the publication.'
30
+
}),
31
+
basicTheme: lx.ref(siteStandardThemeBasic.json.id, {
32
+
description: 'Simplified publication theme for tools and apps to utilize when displaying content.'
33
+
}),
34
+
preferences: lx.ref('#preferences', {
35
+
description: 'Object containing platform specific preferences (with a few shared properties).'
36
+
})
37
+
}),
38
+
description: 'A publication record representing a blog, website, or content platform. Publications serve as containers for documents and define the overall branding and settings.'
39
+
}),
40
+
preferences: lx.object({
41
+
showInDiscover: lx.boolean({
42
+
default: true,
43
+
description: 'Boolean which decides whether the publication should appear in discovery feeds.'
44
+
}),
45
+
description: 'Platform-specific preferences for the publication, including discovery and visibility settings.'
46
+
})
47
+
})
+28
src/lexicons/site.standard.theme.basic.ts
+28
src/lexicons/site.standard.theme.basic.ts
···
1
+
import { lx } from 'prototypey'
2
+
import { siteStandardThemeColor } from './site.standard.theme.color.ts'
3
+
4
+
export const siteStandardThemeBasic = lx.lexicon('site.standard.theme.basic', {
5
+
main: lx.record({
6
+
key: 'tid',
7
+
type: 'record',
8
+
record: lx.object({
9
+
background: lx.union([siteStandardThemeColor.json.id + '#rgb'], {
10
+
required: true,
11
+
description: 'Color used for content background.'
12
+
}),
13
+
foreground: lx.union([siteStandardThemeColor.json.id + '#rgb'], {
14
+
required: true,
15
+
description: 'Color used for content text.'
16
+
}),
17
+
accent: lx.union([siteStandardThemeColor.json.id + '#rgb'], {
18
+
required: true,
19
+
description: 'Color used for links and button backgrounds.'
20
+
}),
21
+
accentForeground: lx.union([siteStandardThemeColor.json.id + '#rgb'], {
22
+
required: true,
23
+
description: 'Color used for button text.'
24
+
})
25
+
}),
26
+
description: 'A simplified theme definition for publications, providing basic color customization for content display across different platforms and applications.'
27
+
})
28
+
})
+43
src/lexicons/site.standard.theme.color.ts
+43
src/lexicons/site.standard.theme.color.ts
···
1
+
import { lx } from 'prototypey'
2
+
3
+
export const siteStandardThemeColor = lx.lexicon('site.standard.theme.color', {
4
+
rgb: lx.object({
5
+
r: lx.integer({
6
+
required: true,
7
+
minimum: 0,
8
+
maximum: 255
9
+
}),
10
+
g: lx.integer({
11
+
required: true,
12
+
minimum: 0,
13
+
maximum: 255
14
+
}),
15
+
b: lx.integer({
16
+
required: true,
17
+
minimum: 0,
18
+
maximum: 255
19
+
})
20
+
}),
21
+
rgba: lx.object({
22
+
r: lx.integer({
23
+
required: true,
24
+
minimum: 0,
25
+
maximum: 255
26
+
}),
27
+
g: lx.integer({
28
+
required: true,
29
+
minimum: 0,
30
+
maximum: 255
31
+
}),
32
+
b: lx.integer({
33
+
required: true,
34
+
minimum: 0,
35
+
maximum: 255
36
+
}),
37
+
a: lx.integer({
38
+
required: true,
39
+
minimum: 0,
40
+
maximum: 100
41
+
})
42
+
}),
43
+
})