Shared lexicon schemas for long-form publishing on AT Protocol. Uses typescript to json via prototypey.

Compare changes

Choose any two refs to compare.

+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
··· 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 + export const MB = 1000000; // 1MB
+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
··· 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
··· 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
··· 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
··· 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 + })