@sudobility/screenwriter_types 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,312 +0,0 @@
1
- /**
2
- * Production packets (spec 11 §7, spec 05 §6.22), slice B10. Schemas are the contract MCP and generators consume
3
- * (`scenePacketSchema` and friends); the API validates its own output against them in tests.
4
- * Asset lists are always empty until slice B11 (assets) lands.
5
- */
6
- import { z } from 'zod';
7
- export const PACKET_VERSION = 1;
8
- /**
9
- * `?snapshotId=&include=` shared by the four packet routes. `include` is a comma list added to the defaults (`assets` and
10
- * `script` are on by default; `neighbors` and `pages` are opt-in; `-script` and `-assets` switch a default off).
11
- */
12
- export const PACKET_INCLUDE_FLAGS = [
13
- 'assets',
14
- 'assetUrls',
15
- 'script',
16
- 'neighbors',
17
- 'pages',
18
- ];
19
- export const packetQuerySchema = z.object({
20
- snapshotId: z.string().min(1).optional(),
21
- include: z
22
- .preprocess((v) => typeof v === 'string'
23
- ? v
24
- .split(',')
25
- .map((s) => s.trim())
26
- .filter(Boolean)
27
- : v, z.array(z.enum([
28
- 'assets',
29
- 'assetUrls=original',
30
- 'script',
31
- 'neighbors',
32
- 'pages',
33
- '-assets',
34
- '-script',
35
- ])))
36
- .optional(),
37
- });
38
- /** 2 MB JSON cap (spec 11 §7.1): beyond it script text is truncated per element with `truncated: true`. */
39
- export const PACKET_MAX_BYTES = 2 * 1024 * 1024;
40
- const envelope = (kind, body) => z.object({
41
- packetVersion: z.literal(PACKET_VERSION),
42
- kind: z.literal(kind),
43
- documentId: z.string(),
44
- documentTitle: z.string(),
45
- snapshotId: z.string().nullable(),
46
- templateId: z.string(),
47
- language: z.string(),
48
- generatedAt: z.string(),
49
- /** `packetHash` of the packet without `generatedAt`, `hash` and signed-URL fields (spec 11 §4.2). */
50
- hash: z.string(),
51
- truncated: z.boolean().optional(),
52
- body,
53
- });
54
- const assetSummary = z
55
- .object({
56
- assetId: z.string(),
57
- role: z.string(),
58
- kind: z.string(),
59
- title: z.string(),
60
- })
61
- .passthrough();
62
- const pages = z
63
- .object({ start: z.string(), end: z.string(), eighths: z.number().int() })
64
- .nullable();
65
- export const packetElementSchema = z.union([
66
- z.object({
67
- id: z.string(),
68
- type: z.literal('character'),
69
- text: z.string(),
70
- speakerEntityId: z.string().nullable(),
71
- extension: z.string().nullable(),
72
- }),
73
- z.object({
74
- id: z.string(),
75
- type: z.literal('parenthetical'),
76
- text: z.string(),
77
- speakerEntityId: z.string().nullable(),
78
- }),
79
- z.object({
80
- id: z.string(),
81
- type: z.literal('dialogue'),
82
- text: z.string(),
83
- speakerEntityId: z.string().nullable(),
84
- dual: z.boolean(),
85
- lineIndex: z.number().int(),
86
- }),
87
- // every other role: sceneHeading, action, transition, shot, normal, ...
88
- z.object({
89
- id: z.string(),
90
- type: z
91
- .string()
92
- .refine((t) => !['character', 'parenthetical', 'dialogue'].includes(t)),
93
- text: z.string(),
94
- }),
95
- ]);
96
- const shotSummary = z.object({
97
- id: z.string(),
98
- number: z.string().nullable(),
99
- label: z.string().nullable(),
100
- status: z.string(),
101
- camera: z.record(z.string(), z.unknown()),
102
- estimatedDurationMs: z.number().nullable(),
103
- description: z.string().nullable(),
104
- elementIds: z.array(z.string()),
105
- contentHash: z.string(),
106
- assets: z.array(assetSummary),
107
- });
108
- const sceneRef = z.object({
109
- id: z.string(),
110
- number: z.string().nullable(),
111
- heading: z.string(),
112
- synopsis: z.string(),
113
- });
114
- const sceneHeader = z.object({
115
- id: z.string(),
116
- number: z.string().nullable(),
117
- ordinal: z.number().int(),
118
- omitted: z.boolean(),
119
- heading: z.object({
120
- raw: z.string(),
121
- intro: z.string().nullable(),
122
- location: z.string().nullable(),
123
- time: z.string().nullable(),
124
- }),
125
- locationEntityId: z.string().nullable(),
126
- synopsis: z.string(),
127
- color: z.string().nullable(),
128
- pages,
129
- estimatedDurationMs: z.number(),
130
- folders: z.array(z.object({ id: z.string(), kind: z.string(), title: z.string() })),
131
- contentHash: z.string(),
132
- scriptDay: z.string().nullable(),
133
- });
134
- const locationBlock = z
135
- .object({
136
- entityId: z.string(),
137
- name: z.string(),
138
- fields: z.record(z.string(), z.unknown()),
139
- description: z.string(),
140
- assets: z.array(assetSummary),
141
- })
142
- .nullable();
143
- export const scenePacketBodySchema = z.object({
144
- scene: sceneHeader,
145
- script: z.array(packetElementSchema),
146
- cast: z.array(z.object({
147
- entityId: z.string(),
148
- name: z.string(),
149
- speaking: z.boolean(),
150
- lineCount: z.number().int(),
151
- wordCount: z.number().int(),
152
- firstAppearance: z.boolean(),
153
- summary: z.string(),
154
- fields: z.record(z.string(), z.unknown()),
155
- traits: z.record(z.string(), z.union([z.string(), z.number()])),
156
- assets: z.array(assetSummary),
157
- })),
158
- location: locationBlock,
159
- elements: z.array(z.object({
160
- entityId: z.string(),
161
- kind: z.string(),
162
- name: z.string(),
163
- description: z.string(),
164
- tagCount: z.number().int(),
165
- assets: z.array(assetSummary),
166
- })),
167
- shots: z.array(shotSummary),
168
- notes: z.array(z.object({
169
- id: z.string(),
170
- type: z.string(),
171
- text: z.string(),
172
- anchorElementId: z.string().nullable(),
173
- })),
174
- sceneAssets: z.array(assetSummary),
175
- neighbors: z
176
- .object({ previous: sceneRef.nullable(), next: sceneRef.nullable() })
177
- .optional(),
178
- });
179
- export const scenePacketSchema = envelope('scene', scenePacketBodySchema);
180
- export const characterPacketBodySchema = z.object({
181
- character: z.object({
182
- id: z.string(),
183
- name: z.string(),
184
- aliases: z.array(z.string()),
185
- description: z.string(),
186
- fields: z.record(z.string(), z.unknown()),
187
- traits: z.record(z.string(), z.union([z.string(), z.number()])),
188
- attributes: z.record(z.string(), z.unknown()),
189
- contentHash: z.string(),
190
- }),
191
- ages: z.array(z.object({
192
- id: z.string(),
193
- label: z.string(),
194
- ageValue: z.number().nullable(),
195
- description: z.string(),
196
- skeleton: z.array(z.object({
197
- section: z.string().nullable(),
198
- label: z.string(),
199
- answer: z.string(),
200
- })),
201
- appearances: z.array(z.object({
202
- id: z.string(),
203
- label: z.string(),
204
- description: z.string(),
205
- assetIds: z.array(z.string()),
206
- })),
207
- sceneCount: z.number().int(),
208
- })),
209
- skeleton: z.array(z.object({
210
- section: z.string().nullable(),
211
- label: z.string(),
212
- answer: z.string(),
213
- })),
214
- stats: z.object({
215
- sceneCount: z.number().int(),
216
- speakingSceneCount: z.number().int(),
217
- lineCount: z.number().int(),
218
- wordCount: z.number().int(),
219
- firstSceneId: z.string().nullable(),
220
- lastSceneId: z.string().nullable(),
221
- }),
222
- scenes: z.array(z.object({
223
- id: z.string(),
224
- number: z.string().nullable(),
225
- heading: z.string(),
226
- speaking: z.boolean(),
227
- lineCount: z.number().int(),
228
- ageId: z.string().nullable(),
229
- appearanceId: z.string().nullable(),
230
- })),
231
- sampleDialogue: z.array(z.object({ elementId: z.string(), sceneId: z.string(), text: z.string() })),
232
- relationships: z.array(z.object({
233
- entityId: z.string(),
234
- name: z.string(),
235
- sharedSceneCount: z.number().int(),
236
- })),
237
- props: z.array(z.object({ entityId: z.string(), name: z.string() })),
238
- assets: z.array(assetSummary),
239
- });
240
- export const characterPacketSchema = envelope('character', characterPacketBodySchema);
241
- export const locationPacketBodySchema = z.object({
242
- location: z.object({
243
- id: z.string(),
244
- name: z.string(),
245
- aliases: z.array(z.string()),
246
- description: z.string(),
247
- fields: z.record(z.string(), z.unknown()),
248
- parent: z.object({ id: z.string(), name: z.string() }).nullable(),
249
- children: z.array(z.object({ id: z.string(), name: z.string() })),
250
- contentHash: z.string(),
251
- }),
252
- versions: z.array(z.object({
253
- id: z.string(),
254
- label: z.string(),
255
- description: z.string(),
256
- setting: z.string().nullable(),
257
- sceneCount: z.number().int(),
258
- })),
259
- scenes: z.array(z.object({
260
- id: z.string(),
261
- number: z.string().nullable(),
262
- heading: z.string(),
263
- time: z.string().nullable(),
264
- pages,
265
- cast: z.array(z.string()),
266
- versionId: z.string().nullable(),
267
- })),
268
- totals: z.object({
269
- sceneCount: z.number().int(),
270
- eighths: z.number().int(),
271
- timesOfDay: z.record(z.string(), z.number().int()),
272
- }),
273
- setElements: z.array(z.object({ entityId: z.string(), kind: z.string(), name: z.string() })),
274
- assets: z.array(assetSummary),
275
- });
276
- export const locationPacketSchema = envelope('location', locationPacketBodySchema);
277
- export const shotPacketBodySchema = z.object({
278
- shot: shotSummary.extend({
279
- sceneId: z.string(),
280
- range: z
281
- .object({
282
- startElementId: z.string(),
283
- endElementId: z.string(),
284
- text: z.string(),
285
- })
286
- .nullable(),
287
- }),
288
- scene: z.object({
289
- id: z.string(),
290
- number: z.string().nullable(),
291
- heading: z.object({
292
- raw: z.string(),
293
- intro: z.string().nullable(),
294
- location: z.string().nullable(),
295
- time: z.string().nullable(),
296
- }),
297
- locationEntityId: z.string().nullable(),
298
- contentHash: z.string(),
299
- }),
300
- script: z.array(packetElementSchema),
301
- subjects: z.array(z.object({
302
- entityId: z.string(),
303
- kind: z.string(),
304
- name: z.string(),
305
- fields: z.record(z.string(), z.unknown()),
306
- assets: z.array(assetSummary),
307
- })),
308
- location: locationBlock,
309
- previousShot: shotSummary.nullable(),
310
- nextShot: shotSummary.nullable(),
311
- });
312
- export const shotPacketSchema = envelope('shot', shotPacketBodySchema);
@@ -1,75 +0,0 @@
1
- import { z } from 'zod';
2
- import { DOCUMENT_KINDS } from '@sudobility/writing_core';
3
- export { DOCUMENT_KINDS };
4
- export const documentKindSchema = z.enum(DOCUMENT_KINDS);
5
- export const SORT_OPTIONS = ['updated', 'name', 'created'];
6
- /** Spec 05 §6.26: a `series` project orders its episodes by `(season, episode)` (F-COL-006). */
7
- export const PROJECT_KINDS = ['feature', 'series', 'play', 'other'];
8
- export const projectKindSchema = z.enum(PROJECT_KINDS);
9
- /** Document labels (F-COL-006): at most 20 per document, each 1-40 characters. */
10
- export const DOCUMENT_LABELS_MAX = 20;
11
- export const DOCUMENT_LABEL_MAX_CHARS = 40;
12
- export const documentLabelsSchema = z
13
- .array(z.string().trim().min(1).max(DOCUMENT_LABEL_MAX_CHARS))
14
- .max(DOCUMENT_LABELS_MAX);
15
- export const projectCreateSchema = z.object({
16
- name: z.string().trim().min(1).max(200),
17
- description: z.string().max(2000).optional(),
18
- coverAssetId: z.string().optional(),
19
- color: z.string().max(32).optional(),
20
- kind: projectKindSchema.optional(),
21
- logline: z.string().max(2000).optional(),
22
- });
23
- export const projectUpdateSchema = z.object({
24
- name: z.string().trim().min(1).max(200).optional(),
25
- description: z.string().max(2000).nullable().optional(),
26
- coverAssetId: z.string().nullable().optional(),
27
- color: z.string().max(32).nullable().optional(),
28
- kind: projectKindSchema.optional(),
29
- logline: z.string().max(2000).nullable().optional(),
30
- workspaceId: z.string().optional(),
31
- });
32
- export const projectListQuerySchema = z.object({
33
- trashed: z
34
- .enum(['true', 'false'])
35
- .transform((v) => v === 'true')
36
- .optional(),
37
- q: z.string().optional(),
38
- sort: z.enum(SORT_OPTIONS).optional(),
39
- });
40
- // ─── Documents (spec 05 §6.7) ───────────────────────────────────────────────
41
- export const documentCreateSchema = z.object({
42
- /** Client-generated `doc_...`; creation is idempotent on it. */
43
- id: z.string().startsWith('doc_').optional(),
44
- title: z.string().trim().min(1).max(200),
45
- kind: documentKindSchema,
46
- folderId: z.string().optional(),
47
- templateId: z.string().optional(),
48
- templateVersion: z.number().int().positive().optional(),
49
- language: z.string().max(16).optional(),
50
- labels: documentLabelsSchema.optional(),
51
- season: z.number().int().min(0).max(9999).optional(),
52
- episode: z.number().int().min(0).max(99999).optional(),
53
- });
54
- export const documentUpdateSchema = z.object({
55
- title: z.string().trim().min(1).max(200).optional(),
56
- folderId: z.string().nullable().optional(),
57
- position: z.number().optional(),
58
- color: z.string().max(32).nullable().optional(),
59
- language: z.string().max(16).optional(),
60
- logline: z.string().max(2000).nullable().optional(),
61
- excludeFromAi: z.boolean().optional(),
62
- labels: documentLabelsSchema.optional(),
63
- season: z.number().int().min(0).max(9999).nullable().optional(),
64
- episode: z.number().int().min(0).max(99999).nullable().optional(),
65
- });
66
- export const documentListQuerySchema = z.object({
67
- trashed: z
68
- .enum(['true', 'false'])
69
- .transform((v) => v === 'true')
70
- .optional(),
71
- folderId: z.string().optional(),
72
- kind: documentKindSchema.optional(),
73
- /** `episode` = `(season, episode)` with unnumbered documents last; the default for a `series` project. */
74
- order: z.enum(['position', 'episode']).optional(),
75
- });
@@ -1,25 +0,0 @@
1
- import { z } from 'zod';
2
- /** `POST /public/purchases/handoff/redeem`: exchanges a handoff token for a Firebase custom token (same uid), so the web page signs in without a password. */
3
- export const purchaseHandoffRedeemSchema = z.object({
4
- token: z.string().min(1),
5
- });
6
- // ---- Telemetry (spec 05 §6.24) ----
7
- export const telemetryEventSchema = z.object({
8
- name: z.string().min(1).max(120),
9
- ts: z.string(),
10
- /** Allow-listed, flat properties only: no script text, no free-form nesting. */
11
- properties: z
12
- .record(z.string(), z.union([z.string().max(500), z.number(), z.boolean(), z.null()]))
13
- .optional(),
14
- });
15
- export const clientErrorReportSchema = z.object({
16
- message: z.string().min(1).max(2000),
17
- stack: z.string().max(8000).optional(),
18
- context: z
19
- .record(z.string(), z.union([z.string().max(500), z.number(), z.boolean(), z.null()]))
20
- .optional(),
21
- });
22
- export const telemetryRequestSchema = z.object({
23
- events: z.array(telemetryEventSchema).max(100),
24
- errors: z.array(clientErrorReportSchema).max(20).optional(),
25
- });
@@ -1,118 +0,0 @@
1
- /**
2
- * Projection-backed reads (spec 05 §6.7.1, §6.7.2, §6.12, §6.14, §6.15, §6.17), slice B10.
3
- * The Y.Doc is canonical; these are the shapes the API returns for entities, tags, notes, beats, the Bin,
4
- * revisions, tracked changes, alternates, title page, statistics, shots, locators and search.
5
- */
6
- import { z } from 'zod';
7
- import { cursorQuerySchema } from '../api/pagination.js';
8
- // ─── ?source= ───────────────────────────────────────────────────────────────
9
- /** Query encoding of a `SourceRef` (spec 05 §6.7.1): `live`, `snapshot:<snapshotId>` or `version:<versionId>`. */
10
- export const sourceParamSchema = z
11
- .string()
12
- .regex(/^(live|snapshot:[A-Za-z0-9_-]+|version:[A-Za-z0-9_-]+)$/, 'source must be live, snapshot:<id> or version:<id>');
13
- /** JSON-body source on a document route: the document is implied by the route (spec 03 `SourceRef` without `documentId`). */
14
- export const docSourceSchema = z.discriminatedUnion('kind', [
15
- z.object({ kind: z.literal('live') }),
16
- z.object({ kind: z.literal('snapshot'), snapshotId: z.string().min(1) }),
17
- z.object({ kind: z.literal('version'), versionId: z.string().min(1) }),
18
- ]);
19
- export const sourceQuerySchema = z.object({
20
- source: sourceParamSchema.optional(),
21
- });
22
- export function sourceParamOf(ref) {
23
- if (!ref || ref.kind === 'live')
24
- return undefined;
25
- return ref.kind === 'snapshot'
26
- ? `snapshot:${ref.snapshotId}`
27
- : `version:${ref.versionId}`;
28
- }
29
- // ─── Entities (spec 05 §6.12) ───────────────────────────────────────────────
30
- export const entityListQuerySchema = cursorQuerySchema.extend({
31
- kind: z.string().min(1).optional(),
32
- q: z.string().min(1).max(200).optional(),
33
- source: sourceParamSchema.optional(),
34
- });
35
- export const entityUsageQuerySchema = z.object({
36
- variantId: z.string().min(1).optional(),
37
- source: sourceParamSchema.optional(),
38
- });
39
- export const entityDialogueQuerySchema = z.object({
40
- sceneIds: z.string().optional(),
41
- source: sourceParamSchema.optional(),
42
- });
43
- export const tagsQuerySchema = z.object({
44
- sceneId: z.string().optional(),
45
- categoryId: z.string().optional(),
46
- entityId: z.string().optional(),
47
- source: sourceParamSchema.optional(),
48
- });
49
- export const notesQuerySchema = z.object({
50
- sceneId: z.string().optional(),
51
- type: z.string().optional(),
52
- status: z.enum(['open', 'resolved']).optional(),
53
- source: sourceParamSchema.optional(),
54
- });
55
- export const changesQuerySchema = z.object({
56
- authorId: z.string().optional(),
57
- sceneId: z.string().optional(),
58
- source: sourceParamSchema.optional(),
59
- });
60
- export const fountainQuerySchema = z.object({
61
- sceneIds: z.string().optional(),
62
- fromSceneId: z.string().optional(),
63
- source: sourceParamSchema.optional(),
64
- });
65
- /** `GET /documents/:did/fountain` truncates at this many bytes and sets `X-Next-From-Scene`. */
66
- export const FOUNTAIN_MAX_BYTES = 1_000_000;
67
- // ─── Locators (spec 11 §1.3, spec 05 §6.7.2) ────────────────────────────────
68
- export const LOCATOR_KINDS = [
69
- 'scene',
70
- 'element',
71
- 'shot',
72
- 'entity',
73
- 'page',
74
- ];
75
- export const RESOLVE_BATCH_MAX = 200;
76
- export const resolveBatchSchema = z.object({
77
- locators: z.array(z.string().min(1).max(500)).min(1).max(RESOLVE_BATCH_MAX),
78
- snapshotId: z.string().min(1).optional(),
79
- });
80
- /** `GET /documents/:did/resolve?ref=&snapshotId=`. */
81
- export const resolveOneQuerySchema = z.object({
82
- ref: z.string().min(1).max(500),
83
- snapshotId: z.string().min(1).optional(),
84
- });
85
- // ─── Search (spec 05 §6.17) ─────────────────────────────────────────────────
86
- export const SEARCH_HIT_TYPES = [
87
- 'document',
88
- 'scene',
89
- 'element',
90
- 'entity',
91
- 'note',
92
- 'asset',
93
- ];
94
- const csv = (v) => typeof v === 'string'
95
- ? v
96
- .split(',')
97
- .map((s) => s.trim())
98
- .filter(Boolean)
99
- : v;
100
- export const searchQuerySchema = cursorQuerySchema.extend({
101
- q: z.string().min(1).max(200),
102
- workspaceId: z.string().min(1).optional(),
103
- projectId: z.string().min(1).optional(),
104
- documentId: z.string().min(1).optional(),
105
- types: z.preprocess(csv, z.array(z.enum(SEARCH_HIT_TYPES)).min(1)).optional(),
106
- styleIds: z.preprocess(csv, z.array(z.string()).min(1)).optional(),
107
- characterId: z.string().min(1).optional(),
108
- language: z.string().min(2).max(35).optional(),
109
- });
110
- /** `GET /documents/:did/search` (in-document find, spec 05 §6.7.1). */
111
- export const documentSearchQuerySchema = z.object({
112
- q: z.string().min(1).max(500),
113
- mode: z.enum(['text', 'regex']).default('text'),
114
- styles: z.preprocess(csv, z.array(z.string()).min(1)).optional(),
115
- characters: z.preprocess(csv, z.array(z.string()).min(1)).optional(),
116
- limit: z.coerce.number().int().min(1).max(100).default(50),
117
- source: sourceParamSchema.optional(),
118
- });