From 1fa3bcef3eeee1670c75a3ec949e960ceb42b4b6 Mon Sep 17 00:00:00 2001 From: Brad Estey Date: Sat, 26 Sep 2026 21:03:52 -0400 Subject: [PATCH] Add some limits. --- README.md | 64 ++++++++++--------- src/constants.ts | 10 +++ src/lexicons/work.sequential.chapter.ts | 15 +++-- src/lexicons/work.sequential.cover.ts | 7 +- src/lexicons/work.sequential.defs.ts | 9 ++- src/lexicons/work.sequential.graph.list.ts | 5 +- .../work.sequential.graph.subscription.ts | 2 +- src/lexicons/work.sequential.page.ts | 6 +- src/lexicons/work.sequential.series.ts | 7 +- src/lexicons/work.sequential.slice.ts | 6 +- src/lexicons/work.sequential.strip.ts | 8 ++- 11 files changed, 95 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 8ed353d..32ec6c8 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ All formats support localized images and text, alt text for every image, and wor Many values are open-ended `string`s rather than enums, so each AppView can support its own set of values. +Every list and free-form string has a maximum length, set generously in `src/constants.ts`. Raising a limit later is backward compatible; lowering one or adding a new one is not. + ## Installation Requires Node 22.18 or later and pnpm. @@ -77,7 +79,7 @@ series (format: strip) └── panels[] (inline) ``` -A parent record holds an ordered list of AT-URIs to its children. Each child points back to its parent with an AT-URI. Covers use the same pattern as slices and pages, so adding a variant cover means creating a `cover` record and adding its URI to `chapter.covers`. +A parent record holds an ordered list of AT-URIs to its children. Each child points back to its parent with an AT-URI. AT-URIs stored in records should always use the DID form (`at://did:plc:…/…`), never a handle: handles can change, and a handle-based link breaks when they do. The schema can't enforce this, so AppViews should resolve handles to DIDs before writing. Covers use the same pattern as slices and pages, so adding a variant cover means creating a `cover` record and adding its URI to `chapter.covers`. Strips are the exception. A long-running strip can have thousands of entries, and keeping them all in one ever-growing list on the series record would be fragile: every new strip rewrites the whole list, and PDSes limit record size. Strips only point to their series, and AppViews order them by `createdAt`. @@ -202,10 +204,10 @@ A comic series. | Property | Type | Required | Description | | --- | --- | --- | --- | -| `title` | array of `defs#localizedTitle` (min 1) | yes | Localized title of the series. | +| `title` | array of `defs#localizedTitle` (min 1, max 100) | yes | Localized title of the series. | | `banner` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | Banner image. 3:1 aspect ratio (e.g. 3000×1000). | | `ogImage` | blob (jpeg/png, max 4 MiB) | yes | Open Graph share image. 1.91:1 aspect ratio (e.g. 1200×630). JPEG or PNG only, because many link preview crawlers don't support newer formats. | -| `description` | array of `series#localizedDescription` (min 1) | yes | Localized short description of the series. | +| `description` | array of `series#localizedDescription` (min 1, max 100) | yes | Localized short description of the series. | | `createdAt` | string (datetime) | yes | When the series was created. | | `categories` | array of `defs#category` (min 1, max 3) | yes | Categories, most relevant first. | | `tags` | array of `defs#tag` (max 10) | no | Free-form tags for discovery. | @@ -214,7 +216,7 @@ A comic series. | `status` | string | no | Publication status. Known values: `ongoing`, `completed`, `hiatus`, `cancelled`. | | `nativeLanguage` | string (language) | no | BCP-47 tag of the language the series was created in, e.g. `ja`. Readers fall back to it when their language isn't available. | | `labels` | `com.atproto.label.defs#selfLabels` | no | Self-labels and content warnings for the series. See [Labels](#labels). | -| `chapters` | array of at-uri | no | Ordered list of chapters. Not used by the `strip` format. | +| `chapters` | array of at-uri (max 5000) | no | Ordered list of chapters. Not used by the `strip` format. | #### `series#localizedDescription` @@ -227,13 +229,13 @@ A chapter in a series. Use `slices` or `pages`, whichever matches the series `fo | Property | Type | Required | Description | | --- | --- | --- | --- | | `series` | string (at-uri) | yes | AT-URI of the parent series. | -| `title` | array of `defs#localizedTitle` | no | Localized chapter title or number. | -| `description` | array of `defs#localizedDescription` | no | Localized short description of the chapter. | +| `title` | array of `defs#localizedTitle` (max 100) | no | Localized chapter title or number. | +| `description` | array of `defs#localizedDescription` (max 100) | no | Localized short description of the chapter. | | `createdAt` | string (datetime) | yes | When the chapter was published. | -| `covers` | array of at-uri (min 1) | yes | Ordered `cover` records. The first is the primary cover and the rest are variants. | -| `slices` | array of at-uri | no | Ordered `slice` records (vertical format). | -| `pages` | array of at-uri | no | Ordered `page` records (page format). | -| `credits` | array of `defs#credit` | no | Credits for this chapter. | +| `covers` | array of at-uri (min 1, max 500) | yes | Ordered `cover` records. The first is the primary cover and the rest are variants. | +| `slices` | array of at-uri (max 1000) | no | Ordered `slice` records (vertical format). | +| `pages` | array of at-uri (max 1000) | no | Ordered `page` records (page format). | +| `credits` | array of `defs#credit` (max 100) | no | Credits for this chapter. | ### `work.sequential.cover` @@ -243,11 +245,11 @@ A cover for a chapter: either the primary cover or a variant. Put the same art w | --- | --- | --- | --- | | `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | | `label` | string (max 100 graphemes) | no | Name that tells this cover apart from others, e.g. `Cover A`, `1:25 Incentive`. | -| `images` | array of `defs#localizedImage` (min 1) | yes | Localized final images. | -| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the cover. | -| `versions` | array of `defs#version` | no | Other versions of the artwork: production stages or alternate treatments. | +| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | +| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the cover. | +| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | | `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the cover. | -| `credits` | array of `defs#credit` | no | Cover credits, when they differ from the chapter's (e.g. a guest variant artist). | +| `credits` | array of `defs#credit` (max 100) | no | Cover credits, when they differ from the chapter's (e.g. a guest variant artist). | ### `work.sequential.slice` @@ -256,9 +258,9 @@ A slice of a vertically formatted comic. | Property | Type | Required | Description | | --- | --- | --- | --- | | `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | -| `images` | array of `defs#localizedImage` (min 1) | yes | Localized final images. | -| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the slice. | -| `versions` | array of `defs#version` | no | Other versions of the artwork: production stages or alternate treatments. | +| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | +| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the slice. | +| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | | `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the slice. | ### `work.sequential.page` @@ -268,11 +270,11 @@ A page of a page-format comic. | Property | Type | Required | Description | | --- | --- | --- | --- | | `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | -| `images` | array of `defs#localizedImage` (min 1) | yes | Localized final images. | -| `versions` | array of `defs#version` | no | Other versions of the artwork: production stages or alternate treatments. | +| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | +| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | | `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the page. | | `span` | integer (min 1, default 1) | no | Number of printed pages the image covers: 1 for a single page, 2 for a spread, 3+ for a gatefold or foldout. | -| `panels` | array of `defs#panel` | no | Panels in reading order. Alt text for pages lives on panels. | +| `panels` | array of `defs#panel` (max 100) | no | Panels in reading order. Alt text for pages lives on panels. | ### `work.sequential.strip` @@ -281,14 +283,14 @@ A comic strip. Strips belong directly to a series, with no chapters. Each strip | Property | Type | Required | Description | | --- | --- | --- | --- | | `series` | string (at-uri) | yes | AT-URI of the parent series. | -| `title` | array of `defs#localizedTitle` | no | Localized strip title or number. | +| `title` | array of `defs#localizedTitle` (max 100) | no | Localized strip title or number. | | `tags` | array of `defs#tag` (max 10) | no | Free-form tags for this strip. | | `createdAt` | string (datetime) | yes | When the strip was published. Strips are ordered by this date. | -| `images` | array of `defs#localizedImage` (min 1) | yes | Localized final images. | -| `versions` | array of `defs#version` | no | Other versions of the artwork: production stages or alternate treatments. | +| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | +| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | | `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the strip. | -| `panels` | array of `defs#panel` | no | Panels in reading order. Alt text for strips lives on panels. | -| `credits` | array of `defs#credit` | no | Credits for this strip. | +| `panels` | array of `defs#panel` (max 100) | no | Panels in reading order. Alt text for strips lives on panels. | +| `credits` | array of `defs#credit` (max 100) | no | Credits for this strip. | ### `work.sequential.graph.list` @@ -296,8 +298,8 @@ A curated reading list or reading order. See [Reading Lists](#reading-lists). | Property | Type | Required | Description | | --- | --- | --- | --- | -| `title` | array of `defs#localizedTitle` (min 1) | yes | Localized title of the list. | -| `description` | array of `defs#localizedDescription` | no | Localized description of the list. | +| `title` | array of `defs#localizedTitle` (min 1, max 100) | yes | Localized title of the list. | +| `description` | array of `defs#localizedDescription` (max 100) | no | Localized description of the list. | | `createdAt` | string (datetime) | yes | When the list was created. | | `items` | array of at-uri (max 1000) | no | Ordered `chapter` or `strip` records, in the curator's order. For a reading order, this is the order to read them in. | @@ -321,7 +323,7 @@ A panel on a page or strip. It carries the panel's alt text and, optionally, how | Property | Type | Required | Description | | --- | --- | --- | --- | | `bounds` | `defs#bounds` | no | Where the panel sits on the image. | -| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the panel. | +| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the panel. | | `background` | `defs#rgba` | no | Fills the viewport around the panel. Opacity `0` keeps the surrounding page visible, `100` hides it behind a solid color. Fully transparent when omitted. | | `mask` | blob (png, max 4 MiB) | no | Black and white mask of the panel's exact shape, covering the panel's `bounds`. White shows, black hides. Ignored without `bounds`. | @@ -382,7 +384,7 @@ Another version of the same artwork, such as a production stage or an alternate | Property | Type | Required | Description | | --- | --- | --- | --- | | `image` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | The version image. | -| `kind` | string | yes | What this version shows. AppViews may define their own kinds. | +| `kind` | string (max 64 graphemes) | yes | What this version shows. AppViews may define their own kinds. | **Example kinds:** @@ -402,11 +404,11 @@ Another version of the same artwork, such as a production stage or an alternate | --- | --- | --- | --- | | `displayName` | string (max 100 graphemes) | yes | Display name of the credited person. | | `did` | string (did) | no | DID of the credited person, if they have one. | -| `role` | string | yes | e.g. `writer`, `penciler`, `inker`, `colorist`, `letterer`, `cover`, `editor`, `translator`, `other`. | +| `role` | string (max 64 graphemes) | yes | e.g. `writer`, `penciler`, `inker`, `colorist`, `letterer`, `cover`, `editor`, `translator`, `other`. | #### `defs#category` -A category for a series. AppViews may define their own (e.g. `action`, `comedy`, `drama`, `fantasy`, `horror`, `mystery`, `romance`, `sciFi`, `sliceOfLife`, `superhero`). `(string)` +A category for a series. AppViews may define their own (e.g. `action`, `comedy`, `drama`, `fantasy`, `horror`, `mystery`, `romance`, `sciFi`, `sliceOfLife`, `superhero`). Max 64 graphemes. `(string)` #### `defs#tag` diff --git a/src/constants.ts b/src/constants.ts index 8993135..1eb1780 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,13 @@ export const MiB = 1024 * 1024 export const IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/avif'] + +// Array caps. Raising a cap later is backward compatible; lowering one is not. +export const MAX_LANGUAGES = 100 +export const MAX_CREDITS = 100 +export const MAX_PANELS = 100 +export const MAX_VERSIONS = 50 +export const MAX_COVERS = 500 +export const MAX_SLICES = 1000 +export const MAX_PAGES = 1000 +export const MAX_CHAPTERS = 5000 diff --git a/src/lexicons/work.sequential.chapter.ts b/src/lexicons/work.sequential.chapter.ts index cfbbb7e..d0355b8 100644 --- a/src/lexicons/work.sequential.chapter.ts +++ b/src/lexicons/work.sequential.chapter.ts @@ -1,4 +1,5 @@ import { lx } from 'prototypey' +import { MAX_COVERS, MAX_CREDITS, MAX_LANGUAGES, MAX_PAGES, MAX_SLICES } from '../constants.ts' export const workSequentialChapter = lx.lexicon('work.sequential.chapter', { main: lx.record({ @@ -7,12 +8,14 @@ export const workSequentialChapter = lx.lexicon('work.sequential.chapter', { series: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI of the parent series.' + description: 'AT-URI of the parent series. Use the DID form (at://did:...), not a handle.' }), title: lx.array(lx.ref('work.sequential.defs#localizedTitle'), { + maxLength: MAX_LANGUAGES, description: 'Localized chapter title or number.' }), description: lx.array(lx.ref('work.sequential.defs#localizedDescription'), { + maxLength: MAX_LANGUAGES, description: 'Localized short description of the chapter.' }), createdAt: lx.string({ @@ -23,21 +26,25 @@ export const workSequentialChapter = lx.lexicon('work.sequential.chapter', { covers: lx.array(lx.string({ format: 'at-uri' }), { + maxLength: MAX_COVERS, minLength: 1, required: true, - description: 'Ordered list of work.sequential.cover records. The first is the primary cover; the rest are variants.' + description: 'Ordered list of work.sequential.cover records. The first is the primary cover; the rest are variants. Use the DID form (at://did:...), not a handle.' }), slices: lx.array(lx.string({ format: 'at-uri' }), { - description: 'Ordered list of work.sequential.slice records. Used when the series format is \'vertical\'.' + maxLength: MAX_SLICES, + description: 'Ordered list of work.sequential.slice records. Used when the series format is \'vertical\'. Use the DID form (at://did:...), not a handle.' }), pages: lx.array(lx.string({ format: 'at-uri' }), { - description: 'Ordered list of work.sequential.page records. Used when the series format is \'page\'.' + maxLength: MAX_PAGES, + description: 'Ordered list of work.sequential.page records. Used when the series format is \'page\'. Use the DID form (at://did:...), not a handle.' }), credits: lx.array(lx.ref('work.sequential.defs#credit'), { + maxLength: MAX_CREDITS, description: 'The credits for the people who worked on this chapter.' }) }), diff --git a/src/lexicons/work.sequential.cover.ts b/src/lexicons/work.sequential.cover.ts index dcd614d..92515f8 100644 --- a/src/lexicons/work.sequential.cover.ts +++ b/src/lexicons/work.sequential.cover.ts @@ -1,4 +1,5 @@ import { lx } from 'prototypey' +import { MAX_CREDITS, MAX_LANGUAGES, MAX_VERSIONS } from '../constants.ts' export const workSequentialCover = lx.lexicon('work.sequential.cover', { main: lx.record({ @@ -7,7 +8,7 @@ export const workSequentialCover = lx.lexicon('work.sequential.cover', { chapter: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI of the parent chapter.' + description: 'AT-URI of the parent chapter. Use the DID form (at://did:...), not a handle.' }), label: lx.string({ maxLength: 1000, @@ -15,20 +16,24 @@ export const workSequentialCover = lx.lexicon('work.sequential.cover', { description: 'Name distinguishing this cover from others on the chapter (e.g. \'Cover A\', \'Cover B\', \'1:25 Incentive\').' }), images: lx.array(lx.ref('work.sequential.defs#localizedImage'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized final images for this cover.' }), alt: lx.array(lx.ref('work.sequential.defs#localizedAlt'), { + maxLength: MAX_LANGUAGES, description: 'Localized alt text for the cover: describes the art and transcribes the title, cover text and any lettering.' }), versions: lx.array(lx.ref('work.sequential.defs#version'), { + maxLength: MAX_VERSIONS, description: 'Other versions of the cover\'s artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.' }), aspectRatio: lx.ref('work.sequential.defs#aspectRatio', { description: 'The aspect ratio of the cover.' }), credits: lx.array(lx.ref('work.sequential.defs#credit'), { + maxLength: MAX_CREDITS, description: 'The credits for the people who worked on this cover, when they differ from the chapter\'s.' }) }), diff --git a/src/lexicons/work.sequential.defs.ts b/src/lexicons/work.sequential.defs.ts index ff47cfa..8f466b4 100644 --- a/src/lexicons/work.sequential.defs.ts +++ b/src/lexicons/work.sequential.defs.ts @@ -1,5 +1,5 @@ import { lx } from 'prototypey' -import { MiB, IMAGE_TYPES } from '../constants.ts' +import { MiB, IMAGE_TYPES, MAX_LANGUAGES } from '../constants.ts' export const workSequentialDefs = lx.lexicon('work.sequential.defs', { localizedImage: lx.object({ @@ -61,6 +61,8 @@ export const workSequentialDefs = lx.lexicon('work.sequential.defs', { description: 'The version image.' }), kind: lx.string({ + maxLength: 640, + maxGraphemes: 64, required: true, description: 'What this version shows. AppViews may define their own kinds.' }) @@ -87,11 +89,15 @@ export const workSequentialDefs = lx.lexicon('work.sequential.defs', { format: 'did' }), role: lx.string({ + maxLength: 640, + maxGraphemes: 64, required: true, description: 'The role of the credit. AppViews may define their own roles.' }) }), category: lx.string({ + maxLength: 640, + maxGraphemes: 64, description: 'A category for a series. AppViews may define their own categories.' }), tag: lx.string({ @@ -104,6 +110,7 @@ export const workSequentialDefs = lx.lexicon('work.sequential.defs', { description: 'Where the panel sits on the image. Optional, so alt text can be added without mapping panel positions.' }), alt: lx.array(lx.ref('work.sequential.defs#localizedAlt'), { + maxLength: MAX_LANGUAGES, description: 'Localized alt text for this panel: describes the art and transcribes the dialogue, captions and sound effects.' }), background: lx.ref('#rgba', { diff --git a/src/lexicons/work.sequential.graph.list.ts b/src/lexicons/work.sequential.graph.list.ts index 81ba00d..95ebbe0 100644 --- a/src/lexicons/work.sequential.graph.list.ts +++ b/src/lexicons/work.sequential.graph.list.ts @@ -1,15 +1,18 @@ import { lx } from 'prototypey' +import { MAX_LANGUAGES } from '../constants.ts' export const workSequentialGraphList = lx.lexicon('work.sequential.graph.list', { main: lx.record({ key: 'tid', record: lx.object({ title: lx.array(lx.ref('work.sequential.defs#localizedTitle'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized title of the list.' }), description: lx.array(lx.ref('work.sequential.defs#localizedDescription'), { + maxLength: MAX_LANGUAGES, description: 'Localized description of the list.' }), createdAt: lx.string({ @@ -21,7 +24,7 @@ export const workSequentialGraphList = lx.lexicon('work.sequential.graph.list', format: 'at-uri' }), { maxLength: 1000, - description: 'AT-URIs of work.sequential.chapter or work.sequential.strip records, in the order the curator chose. For a reading order, this is the order to read them in.' + description: 'AT-URIs of work.sequential.chapter or work.sequential.strip records, in the order the curator chose. For a reading order, this is the order to read them in. Use the DID form (at://did:...), not a handle.' }) }), description: 'A curated reading list or reading order. Anyone can create one in their own repo, and items can point to any creator\'s chapters or strips.' diff --git a/src/lexicons/work.sequential.graph.subscription.ts b/src/lexicons/work.sequential.graph.subscription.ts index 6a7ba79..27e7492 100644 --- a/src/lexicons/work.sequential.graph.subscription.ts +++ b/src/lexicons/work.sequential.graph.subscription.ts @@ -7,7 +7,7 @@ export const workSequentialGraphSubscription = lx.lexicon('work.sequential.graph series: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI reference to the series record being subscribed to (ex: at://did:plc:abc123/work.sequential.series/xyz789).' + description: 'AT-URI reference to the series record being subscribed to (ex: at://did:plc:abc123/work.sequential.series/xyz789). Use the DID form (at://did:...), not a handle.' }), createdAt: lx.string({ format: 'datetime', diff --git a/src/lexicons/work.sequential.page.ts b/src/lexicons/work.sequential.page.ts index a275127..a033287 100644 --- a/src/lexicons/work.sequential.page.ts +++ b/src/lexicons/work.sequential.page.ts @@ -1,4 +1,5 @@ import { lx } from 'prototypey' +import { MAX_LANGUAGES, MAX_PANELS, MAX_VERSIONS } from '../constants.ts' export const workSequentialPage = lx.lexicon('work.sequential.page', { main: lx.record({ @@ -7,14 +8,16 @@ export const workSequentialPage = lx.lexicon('work.sequential.page', { chapter: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI of the parent chapter.' + description: 'AT-URI of the parent chapter. Use the DID form (at://did:...), not a handle.' }), images: lx.array(lx.ref('work.sequential.defs#localizedImage'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized final images for this page.' }), versions: lx.array(lx.ref('work.sequential.defs#version'), { + maxLength: MAX_VERSIONS, description: 'Other versions of the page\'s artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.' }), aspectRatio: lx.ref('work.sequential.defs#aspectRatio', { @@ -26,6 +29,7 @@ export const workSequentialPage = lx.lexicon('work.sequential.page', { description: 'Number of printed pages this image covers. 1 for a single page, 2 for a two-page spread, 3 or more for a gatefold or foldout.' }), panels: lx.array(lx.ref('work.sequential.defs#panel'), { + maxLength: MAX_PANELS, description: 'Panels on this page, in reading order. Alt text for pages lives on panels.' }) }), diff --git a/src/lexicons/work.sequential.series.ts b/src/lexicons/work.sequential.series.ts index 32c629d..0abb76f 100644 --- a/src/lexicons/work.sequential.series.ts +++ b/src/lexicons/work.sequential.series.ts @@ -1,11 +1,12 @@ import { lx } from 'prototypey' -import { MiB, IMAGE_TYPES } from '../constants.ts' +import { MiB, IMAGE_TYPES, MAX_CHAPTERS, MAX_LANGUAGES } from '../constants.ts' export const workSequentialSeries = lx.lexicon('work.sequential.series', { main: lx.record({ key: 'tid', record: lx.object({ title: lx.array(lx.ref('work.sequential.defs#localizedTitle'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized title of the series.' @@ -23,6 +24,7 @@ export const workSequentialSeries = lx.lexicon('work.sequential.series', { description: 'The open graph share image of the series. 1.91:1 aspect ratio. (e.g. 1200x630 pixels) JPEG or PNG only, because many link preview crawlers don\'t support newer formats.' }), description: lx.array(lx.ref('#localizedDescription'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized short description of the series.' @@ -66,7 +68,8 @@ export const workSequentialSeries = lx.lexicon('work.sequential.series', { chapters: lx.array(lx.string({ format: 'at-uri' }), { - description: 'Ordered list of chapters in this series. Not used by the \'strip\' format.' + maxLength: MAX_CHAPTERS, + description: 'Ordered list of chapters in this series. Not used by the \'strip\' format. Use the DID form (at://did:...), not a handle.' }) }), description: 'A comic series.' diff --git a/src/lexicons/work.sequential.slice.ts b/src/lexicons/work.sequential.slice.ts index c9ee79f..ab47e5a 100644 --- a/src/lexicons/work.sequential.slice.ts +++ b/src/lexicons/work.sequential.slice.ts @@ -1,4 +1,5 @@ import { lx } from 'prototypey' +import { MAX_LANGUAGES, MAX_VERSIONS } from '../constants.ts' export const workSequentialSlice = lx.lexicon('work.sequential.slice', { main: lx.record({ @@ -7,17 +8,20 @@ export const workSequentialSlice = lx.lexicon('work.sequential.slice', { chapter: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI of the parent chapter.' + description: 'AT-URI of the parent chapter. Use the DID form (at://did:...), not a handle.' }), images: lx.array(lx.ref('work.sequential.defs#localizedImage'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized final images for this slice.' }), alt: lx.array(lx.ref('work.sequential.defs#localizedAlt'), { + maxLength: MAX_LANGUAGES, description: 'Localized alt text for the slice: describes the art and transcribes the dialogue, captions and sound effects.' }), versions: lx.array(lx.ref('work.sequential.defs#version'), { + maxLength: MAX_VERSIONS, description: 'Other versions of the slice\'s artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.' }), aspectRatio: lx.ref('work.sequential.defs#aspectRatio', { diff --git a/src/lexicons/work.sequential.strip.ts b/src/lexicons/work.sequential.strip.ts index 228f5eb..ce12551 100644 --- a/src/lexicons/work.sequential.strip.ts +++ b/src/lexicons/work.sequential.strip.ts @@ -1,4 +1,5 @@ import { lx } from 'prototypey' +import { MAX_CREDITS, MAX_LANGUAGES, MAX_PANELS, MAX_VERSIONS } from '../constants.ts' export const workSequentialStrip = lx.lexicon('work.sequential.strip', { main: lx.record({ @@ -7,9 +8,10 @@ export const workSequentialStrip = lx.lexicon('work.sequential.strip', { series: lx.string({ format: 'at-uri', required: true, - description: 'AT-URI of the parent series.' + description: 'AT-URI of the parent series. Use the DID form (at://did:...), not a handle.' }), title: lx.array(lx.ref('work.sequential.defs#localizedTitle'), { + maxLength: MAX_LANGUAGES, description: 'Localized strip title or number.' }), tags: lx.array(lx.ref('work.sequential.defs#tag'), { @@ -22,20 +24,24 @@ export const workSequentialStrip = lx.lexicon('work.sequential.strip', { description: 'The date and time the strip was published. Strips are ordered by this date. Drafts are kept by the AppView and only written to the repo when published.' }), images: lx.array(lx.ref('work.sequential.defs#localizedImage'), { + maxLength: MAX_LANGUAGES, minLength: 1, required: true, description: 'Localized final images for this strip.' }), versions: lx.array(lx.ref('work.sequential.defs#version'), { + maxLength: MAX_VERSIONS, description: 'Other versions of the strip\'s artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.' }), aspectRatio: lx.ref('work.sequential.defs#aspectRatio', { description: 'The aspect ratio of the strip.' }), panels: lx.array(lx.ref('work.sequential.defs#panel'), { + maxLength: MAX_PANELS, description: 'Panels in this strip, in reading order. Alt text for strips lives on panels.' }), credits: lx.array(lx.ref('work.sequential.defs#credit'), { + maxLength: MAX_CREDITS, description: 'The credits for the people who worked on this strip.' }) }), -- 2.54.0