@sudobility/screenwriter_types 0.1.1

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.
Files changed (65) hide show
  1. package/CLAUDE.md +50 -0
  2. package/README.md +34 -0
  3. package/dist/account/index.d.ts +440 -0
  4. package/dist/account/index.js +274 -0
  5. package/dist/admin/index.d.ts +95 -0
  6. package/dist/admin/index.js +20 -0
  7. package/dist/ai/index.d.ts +564 -0
  8. package/dist/ai/index.js +179 -0
  9. package/dist/api/envelope.d.ts +33 -0
  10. package/dist/api/envelope.js +140 -0
  11. package/dist/api/pagination.d.ts +7 -0
  12. package/dist/api/pagination.js +9 -0
  13. package/dist/api/routes.d.ts +273 -0
  14. package/dist/api/routes.js +306 -0
  15. package/dist/assets/index.d.ts +722 -0
  16. package/dist/assets/index.js +452 -0
  17. package/dist/collab/index.d.ts +214 -0
  18. package/dist/collab/index.js +211 -0
  19. package/dist/commands/index.d.ts +92 -0
  20. package/dist/commands/index.js +20 -0
  21. package/dist/credits/index.d.ts +26 -0
  22. package/dist/credits/index.js +7 -0
  23. package/dist/formats/index.d.ts +87 -0
  24. package/dist/formats/index.js +26 -0
  25. package/dist/ids/index.d.ts +2 -0
  26. package/dist/ids/index.js +2 -0
  27. package/dist/index.d.ts +33 -0
  28. package/dist/index.js +33 -0
  29. package/dist/io/index.d.ts +395 -0
  30. package/dist/io/index.js +161 -0
  31. package/dist/jobs/index.d.ts +278 -0
  32. package/dist/jobs/index.js +138 -0
  33. package/dist/keys/index.d.ts +53 -0
  34. package/dist/keys/index.js +22 -0
  35. package/dist/lifecycle/index.d.ts +263 -0
  36. package/dist/lifecycle/index.js +148 -0
  37. package/dist/me/index.d.ts +35 -0
  38. package/dist/me/index.js +18 -0
  39. package/dist/packets/index.d.ts +887 -0
  40. package/dist/packets/index.js +312 -0
  41. package/dist/projects/index.d.ts +201 -0
  42. package/dist/projects/index.js +75 -0
  43. package/dist/public/index.d.ts +95 -0
  44. package/dist/public/index.js +25 -0
  45. package/dist/reads/index.d.ts +387 -0
  46. package/dist/reads/index.js +118 -0
  47. package/dist/reports/index.d.ts +168 -0
  48. package/dist/reports/index.js +466 -0
  49. package/dist/sharing/index.d.ts +256 -0
  50. package/dist/sharing/index.js +91 -0
  51. package/dist/sync/codec.d.ts +7 -0
  52. package/dist/sync/codec.js +91 -0
  53. package/dist/sync/index.d.ts +382 -0
  54. package/dist/sync/index.js +262 -0
  55. package/dist/sync/permissions.d.ts +22 -0
  56. package/dist/sync/permissions.js +11 -0
  57. package/dist/templates/index.d.ts +1699 -0
  58. package/dist/templates/index.js +70 -0
  59. package/dist/tenancy/index.d.ts +116 -0
  60. package/dist/tenancy/index.js +102 -0
  61. package/dist/test/index.d.ts +8 -0
  62. package/dist/test/index.js +88 -0
  63. package/dist/versions/index.d.ts +335 -0
  64. package/dist/versions/index.js +107 -0
  65. package/package.json +62 -0
@@ -0,0 +1,312 @@
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);
@@ -0,0 +1,201 @@
1
+ import { z } from 'zod';
2
+ import { DOCUMENT_KINDS } from '@sudobility/writing_core';
3
+ import type { DocumentKind } from '@sudobility/writing_core';
4
+ import type { Role } from '../tenancy/index.js';
5
+ export { DOCUMENT_KINDS };
6
+ export type { DocumentKind };
7
+ export declare const documentKindSchema: z.ZodEnum<{
8
+ outline: "outline";
9
+ script: "script";
10
+ bible: "bible";
11
+ treatment: "treatment";
12
+ other: "other";
13
+ }>;
14
+ export declare const SORT_OPTIONS: readonly ["updated", "name", "created"];
15
+ /** Spec 05 §6.26: a `series` project orders its episodes by `(season, episode)` (F-COL-006). */
16
+ export declare const PROJECT_KINDS: readonly ["feature", "series", "play", "other"];
17
+ export type ProjectKind = (typeof PROJECT_KINDS)[number];
18
+ export declare const projectKindSchema: z.ZodEnum<{
19
+ other: "other";
20
+ feature: "feature";
21
+ series: "series";
22
+ play: "play";
23
+ }>;
24
+ /** Document labels (F-COL-006): at most 20 per document, each 1-40 characters. */
25
+ export declare const DOCUMENT_LABELS_MAX = 20;
26
+ export declare const DOCUMENT_LABEL_MAX_CHARS = 40;
27
+ export declare const documentLabelsSchema: z.ZodArray<z.ZodString>;
28
+ export interface ProjectSummary {
29
+ id: string;
30
+ workspaceId: string;
31
+ name: string;
32
+ description: string | null;
33
+ color: string | null;
34
+ coverAssetId: string | null;
35
+ kind: ProjectKind;
36
+ logline: string | null;
37
+ documentCount: number;
38
+ createdAt: string;
39
+ updatedAt: string;
40
+ trashedAt: string | null;
41
+ }
42
+ export interface DocumentMeta {
43
+ id: string;
44
+ projectId: string;
45
+ workspaceId: string;
46
+ folderId: string | null;
47
+ kind: DocumentKind;
48
+ title: string;
49
+ logline: string | null;
50
+ color: string | null;
51
+ labels: string[];
52
+ /** Series projects: episodes order by `(season, episode)`. */
53
+ season: number | null;
54
+ episode: number | null;
55
+ position: number;
56
+ language: string;
57
+ templateId: string | null;
58
+ templateVersion: number | null;
59
+ epoch: number;
60
+ excludeFromAi: boolean;
61
+ locked: boolean;
62
+ pageCount: number;
63
+ sceneCount: number;
64
+ wordCount: number;
65
+ parentSnapshotId: string | null;
66
+ createdBy: string | null;
67
+ createdAt: string;
68
+ updatedAt: string;
69
+ lastEditedAt: string | null;
70
+ trashedAt: string | null;
71
+ }
72
+ export interface ProjectFolder {
73
+ id: string;
74
+ projectId: string;
75
+ parentFolderId: string | null;
76
+ name: string;
77
+ position: number;
78
+ }
79
+ /** `GET /projects/:pid` */
80
+ export interface Project extends ProjectSummary {
81
+ role: Role;
82
+ folders: ProjectFolder[];
83
+ documents: DocumentMeta[];
84
+ }
85
+ export declare const projectCreateSchema: z.ZodObject<{
86
+ name: z.ZodString;
87
+ description: z.ZodOptional<z.ZodString>;
88
+ coverAssetId: z.ZodOptional<z.ZodString>;
89
+ color: z.ZodOptional<z.ZodString>;
90
+ kind: z.ZodOptional<z.ZodEnum<{
91
+ other: "other";
92
+ feature: "feature";
93
+ series: "series";
94
+ play: "play";
95
+ }>>;
96
+ logline: z.ZodOptional<z.ZodString>;
97
+ }, z.core.$strip>;
98
+ export type ProjectCreateRequest = z.infer<typeof projectCreateSchema>;
99
+ export declare const projectUpdateSchema: z.ZodObject<{
100
+ name: z.ZodOptional<z.ZodString>;
101
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
102
+ coverAssetId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
103
+ color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
104
+ kind: z.ZodOptional<z.ZodEnum<{
105
+ other: "other";
106
+ feature: "feature";
107
+ series: "series";
108
+ play: "play";
109
+ }>>;
110
+ logline: z.ZodOptional<z.ZodNullable<z.ZodString>>;
111
+ workspaceId: z.ZodOptional<z.ZodString>;
112
+ }, z.core.$strip>;
113
+ export type ProjectUpdateRequest = z.infer<typeof projectUpdateSchema>;
114
+ export declare const projectListQuerySchema: z.ZodObject<{
115
+ trashed: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
116
+ true: "true";
117
+ false: "false";
118
+ }>, z.ZodTransform<boolean, "true" | "false">>>;
119
+ q: z.ZodOptional<z.ZodString>;
120
+ sort: z.ZodOptional<z.ZodEnum<{
121
+ name: "name";
122
+ updated: "updated";
123
+ created: "created";
124
+ }>>;
125
+ }, z.core.$strip>;
126
+ export type ProjectListQuery = z.infer<typeof projectListQuerySchema>;
127
+ export declare const documentCreateSchema: z.ZodObject<{
128
+ id: z.ZodOptional<z.ZodString>;
129
+ title: z.ZodString;
130
+ kind: z.ZodEnum<{
131
+ outline: "outline";
132
+ script: "script";
133
+ bible: "bible";
134
+ treatment: "treatment";
135
+ other: "other";
136
+ }>;
137
+ folderId: z.ZodOptional<z.ZodString>;
138
+ templateId: z.ZodOptional<z.ZodString>;
139
+ templateVersion: z.ZodOptional<z.ZodNumber>;
140
+ language: z.ZodOptional<z.ZodString>;
141
+ labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
142
+ season: z.ZodOptional<z.ZodNumber>;
143
+ episode: z.ZodOptional<z.ZodNumber>;
144
+ }, z.core.$strip>;
145
+ export type DocumentCreateRequest = z.infer<typeof documentCreateSchema>;
146
+ export declare const documentUpdateSchema: z.ZodObject<{
147
+ title: z.ZodOptional<z.ZodString>;
148
+ folderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
149
+ position: z.ZodOptional<z.ZodNumber>;
150
+ color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
151
+ language: z.ZodOptional<z.ZodString>;
152
+ logline: z.ZodOptional<z.ZodNullable<z.ZodString>>;
153
+ excludeFromAi: z.ZodOptional<z.ZodBoolean>;
154
+ labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
155
+ season: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
156
+ episode: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
157
+ }, z.core.$strip>;
158
+ export type DocumentUpdateRequest = z.infer<typeof documentUpdateSchema>;
159
+ export declare const documentListQuerySchema: z.ZodObject<{
160
+ trashed: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
161
+ true: "true";
162
+ false: "false";
163
+ }>, z.ZodTransform<boolean, "true" | "false">>>;
164
+ folderId: z.ZodOptional<z.ZodString>;
165
+ kind: z.ZodOptional<z.ZodEnum<{
166
+ outline: "outline";
167
+ script: "script";
168
+ bible: "bible";
169
+ treatment: "treatment";
170
+ other: "other";
171
+ }>>;
172
+ order: z.ZodOptional<z.ZodEnum<{
173
+ episode: "episode";
174
+ position: "position";
175
+ }>>;
176
+ }, z.core.$strip>;
177
+ export type DocumentListQuery = z.infer<typeof documentListQuerySchema>;
178
+ /** Style row in `DocumentDetail.template` (`code` = shortcut digit, `enter`/`tab` = next style ids). */
179
+ export interface DocumentTemplateStyle {
180
+ id: string;
181
+ name: string;
182
+ code: number | null;
183
+ enter: string | null;
184
+ tab: string | null;
185
+ }
186
+ export interface DocumentTemplateInfo {
187
+ id: string;
188
+ version: number;
189
+ name: string;
190
+ layoutMode: 'flow' | 'panels';
191
+ styles: DocumentTemplateStyle[];
192
+ }
193
+ /** `GET /documents/:did` (metadata plus live-state facts, no content). */
194
+ export interface DocumentDetail extends DocumentMeta {
195
+ role: Role;
196
+ schemaVersion: number;
197
+ template: DocumentTemplateInfo | null;
198
+ }
199
+ export interface TrashResult {
200
+ trashedAt: string;
201
+ }
@@ -0,0 +1,75 @@
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
+ });
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Public routes (spec 05 §6.21, B18): no session needed. `/public/templates[/:key]` reuse `TemplateSummary`
4
+ * (templates/index.ts) and writing_core's `TemplateJSON` for the full body.
5
+ */
6
+ /** `GET /public/config` (R31): the single client-policy route. Clients below `minClientVersion` show the update
7
+ * prompt; a call from one is answered `CLIENT_TOO_OLD` (426), read from the `X-Client-Version` request header. */
8
+ export interface PublicConfig {
9
+ uiLanguages: string[];
10
+ scriptLanguages: string[];
11
+ assetLimits: Record<string, unknown>;
12
+ maxImportBytes: number;
13
+ importFormats: string[];
14
+ exportFormats: string[];
15
+ aiTasks: {
16
+ task: string;
17
+ pricingBasis: string;
18
+ }[];
19
+ aiConsentVersion: number;
20
+ syncProtocolVersion: number;
21
+ docSchemaVersion: number;
22
+ minClientVersion: {
23
+ web: string;
24
+ ios: string;
25
+ android: string;
26
+ macos: string;
27
+ windows: string;
28
+ };
29
+ namesDbVersion: number;
30
+ features: {
31
+ ocr: boolean;
32
+ grammar: boolean;
33
+ push: boolean;
34
+ };
35
+ }
36
+ /** `GET /public/names-db/:version` (spec 09): served gzipped with a far-future immutable Cache-Control. */
37
+ export interface NamesDbEntry {
38
+ name: string;
39
+ gender?: 'f' | 'm' | 'u';
40
+ origins: string[];
41
+ }
42
+ export interface NamesDbResponse {
43
+ version: number;
44
+ names: NamesDbEntry[];
45
+ }
46
+ /** `GET /public/health/deep`: readiness, no secrets. */
47
+ export interface DeepHealthResponse {
48
+ db: 'ok' | 'error';
49
+ storage: 'ok' | 'unconfigured' | 'error';
50
+ ai: 'ok' | 'unconfigured';
51
+ email: 'ok' | 'unconfigured';
52
+ }
53
+ /** `GET /public/watermarked/:exportId/:token`: the recipient's download page payload (never the file itself). */
54
+ export interface WatermarkedDownloadInfo {
55
+ documentTitle: string;
56
+ senderName: string;
57
+ url: string;
58
+ expiresAt: string;
59
+ }
60
+ /** `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. */
61
+ export declare const purchaseHandoffRedeemSchema: z.ZodObject<{
62
+ token: z.ZodString;
63
+ }, z.core.$strip>;
64
+ export type PurchaseHandoffRedeemRequest = z.infer<typeof purchaseHandoffRedeemSchema>;
65
+ export interface PurchaseHandoffRedeemResponse {
66
+ customToken: string;
67
+ }
68
+ export declare const telemetryEventSchema: z.ZodObject<{
69
+ name: z.ZodString;
70
+ ts: z.ZodString;
71
+ properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
72
+ }, z.core.$strip>;
73
+ export type TelemetryEvent = z.infer<typeof telemetryEventSchema>;
74
+ export declare const clientErrorReportSchema: z.ZodObject<{
75
+ message: z.ZodString;
76
+ stack: z.ZodOptional<z.ZodString>;
77
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
78
+ }, z.core.$strip>;
79
+ export type ClientErrorReport = z.infer<typeof clientErrorReportSchema>;
80
+ export declare const telemetryRequestSchema: z.ZodObject<{
81
+ events: z.ZodArray<z.ZodObject<{
82
+ name: z.ZodString;
83
+ ts: z.ZodString;
84
+ properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
85
+ }, z.core.$strip>>;
86
+ errors: z.ZodOptional<z.ZodArray<z.ZodObject<{
87
+ message: z.ZodString;
88
+ stack: z.ZodOptional<z.ZodString>;
89
+ context: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
90
+ }, z.core.$strip>>>;
91
+ }, z.core.$strip>;
92
+ export type TelemetryRequest = z.infer<typeof telemetryRequestSchema>;
93
+ export interface TelemetryResponse {
94
+ accepted: boolean;
95
+ }