@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,274 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Account and per-user data (slice B13; spec 05 §6.1, §6.1.1, §6.26, §8.1, §17).
4
+ * Everything here belongs to one user: preferences, spelling dictionary, macros, writing sessions, stats, goals, the
5
+ * per-document view state (`my-state`) and stars. All are user-only routes (an API key gets 403 `API_KEY_FORBIDDEN`),
6
+ * except the macro list, which a key may read.
7
+ */
8
+ // ─── user preferences (§6.1.1) ────────────────────────────────────────────────
9
+ export const USER_PREFERENCES_VERSION = 1;
10
+ /** Serialised size cap of `PUT /me/preferences` (`LIMIT_EXCEEDED`). */
11
+ export const USER_PREFERENCES_MAX_BYTES = 256 * 1024;
12
+ export const AUTOCORRECT_MAX_PAIRS = 5000;
13
+ export const RECENT_SEARCHES_MAX = 20;
14
+ export const DEVICE_CLASSES = ['phone', 'tablet', 'desktop'];
15
+ /** A section is an open object: its keys are owned by spec 09/10 features and grow without a types release. */
16
+ const section = z.looseObject({});
17
+ const appearanceSchema = z.looseObject({
18
+ theme: z.enum(['system', 'light', 'dark']).optional(),
19
+ pageTheme: z
20
+ .enum(['paper', 'night', 'midnight', 'sepia', 'custom'])
21
+ .optional(),
22
+ custom: section.optional(),
23
+ density: z.string().max(40).optional(),
24
+ largeIcons: z.boolean().optional(),
25
+ reducedMotion: z.boolean().optional(),
26
+ });
27
+ const keymapSchema = z.looseObject({
28
+ preset: z.enum(['fadewright', 'finalDraft', 'fadeIn']).optional(),
29
+ overrides: z.record(z.string(), z.string().nullable()).optional(),
30
+ byPlatform: section.optional(),
31
+ });
32
+ const autocorrectSchema = z.looseObject({
33
+ options: section.optional(),
34
+ pairs: z
35
+ .array(z.object({ from: z.string().min(1).max(200), to: z.string().max(200) }))
36
+ .max(AUTOCORRECT_MAX_PAIRS)
37
+ .optional(),
38
+ });
39
+ const languageSchema = z.looseObject({
40
+ scriptDefault: z.string().max(35).optional(),
41
+ installedDictionaries: z.array(z.string().max(35)).max(50).optional(),
42
+ ignoreLists: section.optional(),
43
+ lookups: z
44
+ .array(z.object({ label: z.string().max(80), url: z.string().max(500) }))
45
+ .max(50)
46
+ .optional(),
47
+ });
48
+ const tableReadSchema = z.looseObject({
49
+ narratorVoiceByLanguage: z.record(z.string(), section).optional(),
50
+ rate: z.number().optional(),
51
+ actors: z
52
+ .array(z.looseObject({ id: z.string(), name: z.string() }))
53
+ .max(200)
54
+ .optional(),
55
+ });
56
+ /**
57
+ * `UserPreferences`: one JSON document per user, closed at the top level (an unknown top-level key is `VALIDATION`),
58
+ * open inside each section. `version` is `USER_PREFERENCES_VERSION`; a higher version from a newer client is stored as is.
59
+ */
60
+ export const userPreferencesSchema = z.strictObject({
61
+ version: z.number().int().min(1).max(1000).default(USER_PREFERENCES_VERSION),
62
+ appearance: appearanceSchema.optional(),
63
+ editor: section.optional(),
64
+ deviceClasses: z
65
+ .strictObject({
66
+ phone: section.optional(),
67
+ tablet: section.optional(),
68
+ desktop: section.optional(),
69
+ })
70
+ .optional(),
71
+ keymap: keymapSchema.optional(),
72
+ autocorrect: autocorrectSchema.optional(),
73
+ language: languageSchema.optional(),
74
+ tableRead: tableReadSchema.optional(),
75
+ toolbar: section.optional(),
76
+ recentSearches: z
77
+ .array(z.string().max(500))
78
+ .max(RECENT_SEARCHES_MAX)
79
+ .optional(),
80
+ revisionColourSets: z.array(section).max(100).optional(),
81
+ offline: section.optional(),
82
+ });
83
+ export const USER_PREFERENCES_KEYS = [
84
+ 'version',
85
+ 'appearance',
86
+ 'editor',
87
+ 'deviceClasses',
88
+ 'keymap',
89
+ 'autocorrect',
90
+ 'language',
91
+ 'tableRead',
92
+ 'toolbar',
93
+ 'recentSearches',
94
+ 'revisionColourSets',
95
+ 'offline',
96
+ ];
97
+ /** `PUT /me/preferences`: a whole-document replace; `baseUpdatedAt` is the `updatedAt` of the last GET/PUT. */
98
+ export const userPreferencesPutSchema = userPreferencesSchema.extend({
99
+ baseUpdatedAt: z.string().min(1).max(40),
100
+ });
101
+ // ─── dictionary ───────────────────────────────────────────────────────────────
102
+ export const DICTIONARY_MAX_WORDS = 50_000;
103
+ const dictionaryWord = z.string().trim().min(1).max(100);
104
+ export const dictionaryUpdateSchema = z.object({
105
+ add: z.array(dictionaryWord).max(5000).optional(),
106
+ remove: z.array(dictionaryWord).max(5000).optional(),
107
+ });
108
+ // ─── macros ───────────────────────────────────────────────────────────────────
109
+ export const USER_MACRO_MAX = 500;
110
+ export const MACRO_TRIGGER_KINDS = ['shortcut', 'alias'];
111
+ const macroOptionFields = {
112
+ smartReplace: z.boolean(),
113
+ confirm: z.boolean(),
114
+ wordOnly: z.boolean(),
115
+ matchCase: z.boolean(),
116
+ activeInStyles: z.array(z.string().max(80)).max(100),
117
+ };
118
+ /** Create: every option defaulted. */
119
+ const macroOptionsSchema = z.object({
120
+ smartReplace: macroOptionFields.smartReplace.default(false),
121
+ confirm: macroOptionFields.confirm.default(false),
122
+ wordOnly: macroOptionFields.wordOnly.default(true),
123
+ matchCase: macroOptionFields.matchCase.default(false),
124
+ activeInStyles: macroOptionFields.activeInStyles.default([]),
125
+ });
126
+ /** Patch: no defaults (a default would overwrite the stored value of an option the request did not mention). */
127
+ const macroOptionsPatchSchema = z.object(macroOptionFields).partial();
128
+ export const userMacroSchema = z.object({
129
+ name: z.string().trim().min(1).max(120),
130
+ trigger: z.object({
131
+ kind: z.enum(MACRO_TRIGGER_KINDS),
132
+ value: z.string().trim().min(1).max(80),
133
+ }),
134
+ insertText: z.string().max(10_000),
135
+ setElementStyle: z.string().max(80).nullable().optional(),
136
+ nextElementStyle: z.string().max(80).nullable().optional(),
137
+ options: macroOptionsSchema.prefault({}),
138
+ });
139
+ /** `PATCH /me/macros/:mid`: any subset; `options` and `trigger` merge into the stored ones. */
140
+ export const userMacroPatchSchema = z.object({
141
+ name: userMacroSchema.shape.name.optional(),
142
+ trigger: z
143
+ .object({
144
+ kind: z.enum(MACRO_TRIGGER_KINDS).optional(),
145
+ value: userMacroSchema.shape.trigger.shape.value.optional(),
146
+ })
147
+ .optional(),
148
+ insertText: userMacroSchema.shape.insertText.optional(),
149
+ setElementStyle: userMacroSchema.shape.setElementStyle,
150
+ nextElementStyle: userMacroSchema.shape.nextElementStyle,
151
+ options: macroOptionsPatchSchema.optional(),
152
+ });
153
+ // ─── writing sessions, stats, goals ───────────────────────────────────────────
154
+ export const WRITING_GOAL_KINDS = [
155
+ 'wordsPerDay',
156
+ 'pagesPerDay',
157
+ 'minutesPerDay',
158
+ 'wordsPerWeek',
159
+ 'documentTarget',
160
+ ];
161
+ export const WRITING_GOALS_MAX = 50;
162
+ export const WRITING_STATS_GRANULARITIES = [
163
+ 'day',
164
+ 'week',
165
+ 'month',
166
+ 'year',
167
+ ];
168
+ const isoInstant = z.iso.datetime({ offset: true });
169
+ /**
170
+ * `POST /me/writing-sessions`. `clientSessionId` is the row id (`wss_...`, made by the client) and makes the upload
171
+ * idempotent: a repeat is accepted and changes nothing, so a device may retry and may upload offline sessions later.
172
+ */
173
+ export const writingSessionSchema = z
174
+ .object({
175
+ clientSessionId: z
176
+ .string()
177
+ .min(8)
178
+ .max(64)
179
+ .regex(/^[A-Za-z0-9_-]+$/),
180
+ documentId: z.string().min(1).max(100).nullable().optional(),
181
+ startedAt: isoInstant,
182
+ endedAt: isoInstant,
183
+ activeSeconds: z.number().int().min(0).max(86_400),
184
+ wordsAdded: z.number().int().min(0).max(1_000_000),
185
+ wordsRemoved: z.number().int().min(0).max(1_000_000),
186
+ netPagesEighths: z.number().int().min(-100_000).max(100_000),
187
+ sprint: z
188
+ .object({
189
+ targetWords: z.number().int().min(1).max(1_000_000).optional(),
190
+ targetMinutes: z.number().int().min(1).max(1440).optional(),
191
+ })
192
+ .optional(),
193
+ })
194
+ .refine((s) => Date.parse(s.endedAt) >= Date.parse(s.startedAt), {
195
+ message: 'endedAt is before startedAt',
196
+ path: ['endedAt'],
197
+ });
198
+ const localDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
199
+ /** `GET /me/writing-stats`: dates are calendar days in the user's time zone (`users.time_zone`). Defaults: the last 30 days by day. */
200
+ export const writingStatsQuerySchema = z.object({
201
+ from: localDate.optional(),
202
+ to: localDate.optional(),
203
+ granularity: z.enum(WRITING_STATS_GRANULARITIES).default('day'),
204
+ documentId: z.string().min(1).max(100).optional(),
205
+ });
206
+ export const writingGoalInputSchema = z
207
+ .object({
208
+ id: z
209
+ .string()
210
+ .min(4)
211
+ .max(64)
212
+ .regex(/^[A-Za-z0-9_-]+$/)
213
+ .optional(),
214
+ kind: z.enum(WRITING_GOAL_KINDS),
215
+ documentId: z.string().min(1).max(100).nullable().optional(),
216
+ target: z.number().int().min(1).max(10_000_000),
217
+ daysOfWeek: z.array(z.number().int().min(0).max(6)).max(7).optional(),
218
+ idleTimeoutSec: z.number().int().min(5).max(3600).optional(),
219
+ })
220
+ .refine((g) => g.kind !== 'documentTarget' || !!g.documentId, {
221
+ message: 'documentTarget needs documentId',
222
+ path: ['documentId'],
223
+ });
224
+ /** `PUT /me/writing-goals`: the whole list. A goal missing from it is deleted; one without `id` is created. `daysOfWeek`: 0 = Sunday. */
225
+ /** No `.max` here on purpose: more than `WRITING_GOALS_MAX` is 409 `LIMIT_EXCEEDED` from the server, not `VALIDATION`. */
226
+ export const writingGoalsPutSchema = z.object({
227
+ goals: z.array(writingGoalInputSchema),
228
+ });
229
+ // ─── export, delete, restore ──────────────────────────────────────────────────
230
+ export const ACCOUNT_DELETE_CONFIRM = 'DELETE';
231
+ export const accountDeleteSchema = z.object({
232
+ confirm: z.literal(ACCOUNT_DELETE_CONFIRM),
233
+ });
234
+ /** `account.export` job input: none. The output is `fadewright-export.zip`. */
235
+ export const accountExportInputSchema = z.object({}).default({});
236
+ export const ACCOUNT_EXPORT_FILENAME = 'fadewright-export.zip';
237
+ // ─── per-user document state and stars (§6.26) ────────────────────────────────
238
+ export const DOCUMENT_VIEW_STATE_MAX_BYTES = 64 * 1024;
239
+ const deviceViewSchema = z.looseObject({
240
+ view: z.string().max(40).optional(),
241
+ zoom: z.number().optional(),
242
+ invisibles: z.boolean().optional(),
243
+ ruler: z.boolean().optional(),
244
+ });
245
+ /**
246
+ * Cross-device view state of one document for one user: Navigator tabs and columns, split layout, view mode and zoom per
247
+ * device class. Caret, selection, undo history and the Beat Board viewport stay device-local. Every key optional: the
248
+ * client merges per top-level key.
249
+ */
250
+ export const documentViewStateSchema = z.strictObject({
251
+ navigator: z
252
+ .looseObject({
253
+ tabs: z.array(z.looseObject({})).max(50).optional(),
254
+ activeTab: z.string().max(80).nullable().optional(),
255
+ })
256
+ .optional(),
257
+ layout: z
258
+ .looseObject({
259
+ split: z.union([z.string().max(40), z.number(), z.null()]).optional(),
260
+ panes: z.array(z.unknown()).max(20).optional(),
261
+ })
262
+ .optional(),
263
+ views: z
264
+ .strictObject({
265
+ phone: deviceViewSchema.optional(),
266
+ tablet: deviceViewSchema.optional(),
267
+ desktop: deviceViewSchema.optional(),
268
+ })
269
+ .optional(),
270
+ });
271
+ /** `PUT`: the whole state; `baseUpdatedAt` is the `updatedAt` last read (null or absent when the GET returned `{}`). */
272
+ export const documentViewStatePutSchema = documentViewStateSchema.extend({
273
+ baseUpdatedAt: z.string().min(1).max(40).nullable().optional(),
274
+ });
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ import { JOB_STATUSES } from '../jobs/index';
3
+ /**
4
+ * Admin routes (spec 05 §6.23): `/api/v1/admin/*` requires `siteAdmin` on a user principal (never an API key).
5
+ * Every call writes `audit_log`.
6
+ */
7
+ export interface AdminUserLookup {
8
+ id: string;
9
+ email: string | null;
10
+ emailVerified: boolean;
11
+ displayName: string | null;
12
+ siteAdmin: boolean;
13
+ createdAt: string;
14
+ lastSeenAt: string | null;
15
+ deletionScheduledFor: string | null;
16
+ deletedAt: string | null;
17
+ }
18
+ export declare const adminJobListQuerySchema: z.ZodObject<{
19
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
20
+ cursor: z.ZodOptional<z.ZodString>;
21
+ status: z.ZodOptional<z.ZodEnum<{
22
+ cancelled: "cancelled";
23
+ queued: "queued";
24
+ running: "running";
25
+ succeeded: "succeeded";
26
+ failed: "failed";
27
+ }>>;
28
+ kind: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strip>;
30
+ export interface AdminJobListQuery {
31
+ limit?: number;
32
+ cursor?: string;
33
+ status?: (typeof JOB_STATUSES)[number];
34
+ kind?: string;
35
+ }
36
+ /** `PATCH /admin/job-kinds/:kind`: a sparse patch onto `job_kind_config`. */
37
+ export declare const adminJobKindPatchSchema: z.ZodObject<{
38
+ enabled: z.ZodOptional<z.ZodBoolean>;
39
+ maxAttempts: z.ZodOptional<z.ZodNumber>;
40
+ maxRetries: z.ZodOptional<z.ZodNumber>;
41
+ maxConcurrentPerUser: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
42
+ maxConcurrentGlobal: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
43
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
44
+ }, z.core.$strip>;
45
+ export type AdminJobKindPatchRequest = z.infer<typeof adminJobKindPatchSchema>;
46
+ export interface AdminJobKind {
47
+ kind: string;
48
+ enabled: boolean;
49
+ adapter: string;
50
+ lane: string;
51
+ billable: boolean;
52
+ maxAttempts: number;
53
+ maxRetries: number;
54
+ maxConcurrentPerUser: number | null;
55
+ maxConcurrentGlobal: number | null;
56
+ timeoutMs: number;
57
+ updatedAt: string;
58
+ }
59
+ /** `POST /admin/jobs/:id/refund`: an optional partial amount; defaults to the job's full `chargedCredits`. */
60
+ export declare const adminJobRefundSchema: z.ZodObject<{
61
+ credits: z.ZodOptional<z.ZodNumber>;
62
+ }, z.core.$strip>;
63
+ export type AdminJobRefundRequest = z.infer<typeof adminJobRefundSchema>;
64
+ export interface AdminJobRefundResponse {
65
+ refundedCredits: number;
66
+ balance: number;
67
+ }
68
+ /** `POST /admin/users/:uid/restore`. */
69
+ export interface AdminUserRestoreResponse {
70
+ restored: true;
71
+ }
72
+ /**
73
+ * `DELETE /admin/users/:uid`: schedules an IMMEDIATE deletion (no grace period) and revokes the user's API keys and
74
+ * share links — the same bounded action `DELETE /me` takes, admin-triggered. **Not** a destructive data purge: no
75
+ * `system.purge` job exists anywhere in this codebase yet to actually delete a user's workspaces/documents/content,
76
+ * and `workspaces.created_by` is `ON DELETE RESTRICT`, so a raw row delete cannot work without first reassigning
77
+ * or deleting every workspace the person created — real work for that job, not this route.
78
+ */
79
+ export interface AdminUserPurgeResponse {
80
+ deletionScheduledFor: string;
81
+ }
82
+ /** `GET /admin/documents/:did/meta`: metadata only, never content. */
83
+ export interface AdminDocumentMeta {
84
+ id: string;
85
+ title: string;
86
+ kind: string;
87
+ projectId: string;
88
+ workspaceId: string;
89
+ ownerEmail: string | null;
90
+ epoch: number;
91
+ schemaVersion: number;
92
+ excludeFromAi: boolean;
93
+ createdAt: string;
94
+ trashedAt: string | null;
95
+ }
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import { cursorQuerySchema } from '../api/pagination';
3
+ import { JOB_STATUSES } from '../jobs/index';
4
+ export const adminJobListQuerySchema = cursorQuerySchema.extend({
5
+ status: z.enum(JOB_STATUSES).optional(),
6
+ kind: z.string().optional(),
7
+ });
8
+ /** `PATCH /admin/job-kinds/:kind`: a sparse patch onto `job_kind_config`. */
9
+ export const adminJobKindPatchSchema = z.object({
10
+ enabled: z.boolean().optional(),
11
+ maxAttempts: z.number().int().min(1).max(10).optional(),
12
+ maxRetries: z.number().int().min(0).max(10).optional(),
13
+ maxConcurrentPerUser: z.number().int().min(1).nullable().optional(),
14
+ maxConcurrentGlobal: z.number().int().min(1).nullable().optional(),
15
+ timeoutMs: z.number().int().min(1000).optional(),
16
+ });
17
+ /** `POST /admin/jobs/:id/refund`: an optional partial amount; defaults to the job's full `chargedCredits`. */
18
+ export const adminJobRefundSchema = z.object({
19
+ credits: z.number().int().positive().optional(),
20
+ });