@nodaro/shared 2.24.0 → 2.27.0
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.
- package/dist/index.cjs +599 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +786 -308
- package/dist/index.d.ts +786 -308
- package/dist/index.js +560 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/llm-models.test.ts +4 -2
- package/src/__tests__/node-preset-extract.test.ts +11 -0
- package/src/__tests__/scene3d.test.ts +482 -0
- package/src/__tests__/studio-transient.test.ts +205 -0
- package/src/index.ts +12 -8
- package/src/llm-models.ts +10 -0
- package/src/model-constants.ts +5 -0
- package/src/node-mappable-fields.ts +2 -0
- package/src/node-preset-extract.ts +8 -2
- package/src/scene3d-edit.ts +311 -0
- package/src/scene3d.ts +615 -0
- package/src/studio-transient.ts +124 -0
- package/src/studio-production-wire.ts +0 -344
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts of `settings.studio` that must not leave the owner's account.
|
|
3
|
+
*
|
|
4
|
+
* A shared production is read by anyone with the link. Three things inside the
|
|
5
|
+
* document are the OWNER'S working state and nobody else's business:
|
|
6
|
+
*
|
|
7
|
+
* - `trash` — the recycle bin, which holds every shot, still and clip they
|
|
8
|
+
* deleted, with prompts and urls intact. A share viewer receiving the bin is
|
|
9
|
+
* the sharpest of the three: it hands out work the owner explicitly threw away.
|
|
10
|
+
* - the in-flight job markers — `pendingClips` / `pendingStills` per shot, and
|
|
11
|
+
* `pendingMusic` / `pendingDraft` on the document. A viewer cannot land any
|
|
12
|
+
* of them and does not own them; all they carry across is job ids.
|
|
13
|
+
* - `freecutDraftUrl` — an unsaved editor draft.
|
|
14
|
+
*
|
|
15
|
+
* They do NOT all live at the same level, and that is the whole reason this
|
|
16
|
+
* file exists rather than one array: the writer puts `trash` and
|
|
17
|
+
* `freecutDraftUrl` on `settings.studio` itself, and puts the per-shot markers
|
|
18
|
+
* on the `settings.studio.shots[]` entry. A strip that walked only the top
|
|
19
|
+
* level would pass its own test and still hand a share viewer every marker in
|
|
20
|
+
* the production.
|
|
21
|
+
*
|
|
22
|
+
* It lives in `@nodaro/shared` because two independent readers need the SAME
|
|
23
|
+
* list: the public share read (which is the reason the list exists) and the
|
|
24
|
+
* production writer's own bundle projection. A second copy of a list like this
|
|
25
|
+
* does not stay equal — it goes one key stale and the stale side is the one
|
|
26
|
+
* that publishes.
|
|
27
|
+
*
|
|
28
|
+
* This is a plain JSON walker on purpose. `settings` is a free-form column that
|
|
29
|
+
* a client owns end to end; the projection reads the keys it must drop and
|
|
30
|
+
* nothing else, so it never needs — and must never grow — a dependency on
|
|
31
|
+
* whatever writes the rest of the document.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `settings.studio`'s OWN transient keys.
|
|
36
|
+
*
|
|
37
|
+
* The per-shot pending lists are on this list as well as the shot one on
|
|
38
|
+
* purpose: nothing writes them here today, and a stray one from an older
|
|
39
|
+
* client — or from a client that is not the studio editor at all — still must
|
|
40
|
+
* not ride out to a viewer.
|
|
41
|
+
*/
|
|
42
|
+
export const STUDIO_TRANSIENT_KEYS = [
|
|
43
|
+
"trash",
|
|
44
|
+
"pendingStills",
|
|
45
|
+
"pendingClips",
|
|
46
|
+
// The two markers that genuinely DO live at this level: a soundtrack render
|
|
47
|
+
// and a story-planning run in flight. Same rule as the per-shot pair — they
|
|
48
|
+
// name jobs on the owner's account and nobody else can land them.
|
|
49
|
+
"pendingMusic",
|
|
50
|
+
"pendingDraft",
|
|
51
|
+
"freecutDraftUrl",
|
|
52
|
+
] as const
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* ...and a SHOT entry's, which is where the per-shot markers actually are.
|
|
56
|
+
*
|
|
57
|
+
* `pendingClip` (singular) is the pre-concurrent-markers shape; the editor's
|
|
58
|
+
* reader still migrates it on parse, so a row can still be carrying one and it
|
|
59
|
+
* is still in-flight state.
|
|
60
|
+
*/
|
|
61
|
+
export const STUDIO_SHOT_TRANSIENT_KEYS = ["pendingClips", "pendingClip", "pendingStills"] as const
|
|
62
|
+
|
|
63
|
+
function withoutKeys(
|
|
64
|
+
source: Record<string, unknown>,
|
|
65
|
+
drop: ReadonlyArray<string>,
|
|
66
|
+
): Record<string, unknown> {
|
|
67
|
+
const kept: Record<string, unknown> = {}
|
|
68
|
+
for (const [key, value] of Object.entries(source)) {
|
|
69
|
+
if (drop.includes(key)) continue
|
|
70
|
+
kept[key] = value
|
|
71
|
+
}
|
|
72
|
+
return kept
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `settings.studio.shots` with every shot's in-flight markers removed.
|
|
77
|
+
*
|
|
78
|
+
* Returns the SAME array when no shot carried one, so an idle production's
|
|
79
|
+
* share read allocates nothing. Anything that is not a shot-shaped object rides
|
|
80
|
+
* through untouched: this runs on whatever is in the column, and a projection
|
|
81
|
+
* that threw on an unexpected row would take the share read down with it.
|
|
82
|
+
*/
|
|
83
|
+
function stripShots(value: unknown): unknown {
|
|
84
|
+
if (!Array.isArray(value)) return value
|
|
85
|
+
let changed = false
|
|
86
|
+
const out = value.map((entry) => {
|
|
87
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return entry
|
|
88
|
+
const shot = entry as Record<string, unknown>
|
|
89
|
+
if (!STUDIO_SHOT_TRANSIENT_KEYS.some((key) => key in shot)) return entry
|
|
90
|
+
changed = true
|
|
91
|
+
return withoutKeys(shot, STUDIO_SHOT_TRANSIENT_KEYS)
|
|
92
|
+
})
|
|
93
|
+
return changed ? out : value
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A production's `settings` with the owner's working state removed.
|
|
98
|
+
*
|
|
99
|
+
* Copy-on-write, and structurally: it rebuilds the objects without those keys
|
|
100
|
+
* rather than deleting from the caller's, so the stored row is untouched. A
|
|
101
|
+
* `settings` with no `studio` comes back unchanged — this is a studio concern,
|
|
102
|
+
* and a workflow that is not a production has nothing here to strip.
|
|
103
|
+
*
|
|
104
|
+
* Takes and returns `unknown` because the column is free-form and every caller
|
|
105
|
+
* already holds it as whatever its own layer calls JSON; narrowing here would
|
|
106
|
+
* only move the cast one line up.
|
|
107
|
+
*/
|
|
108
|
+
export function stripStudioTransientSettings(settings: unknown): unknown {
|
|
109
|
+
if (!settings || typeof settings !== "object") return settings
|
|
110
|
+
const studio = (settings as { studio?: unknown }).studio
|
|
111
|
+
if (!studio || typeof studio !== "object" || Array.isArray(studio)) return settings
|
|
112
|
+
|
|
113
|
+
const source = studio as Record<string, unknown>
|
|
114
|
+
const kept = withoutKeys(source, STUDIO_TRANSIENT_KEYS)
|
|
115
|
+
const shots = stripShots(source.shots)
|
|
116
|
+
if (source.shots !== undefined) kept.shots = shots
|
|
117
|
+
|
|
118
|
+
// Nothing to drop at either level — hand back the original object so an
|
|
119
|
+
// ordinary share read allocates nothing.
|
|
120
|
+
if (Object.keys(kept).length === Object.keys(source).length && shots === source.shots) {
|
|
121
|
+
return settings
|
|
122
|
+
}
|
|
123
|
+
return { ...(settings as Record<string, unknown>), studio: kept }
|
|
124
|
+
}
|
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The wire contract of `/v1/studio/productions` — TYPES ONLY.
|
|
3
|
-
*
|
|
4
|
-
* A studio production is a Nodaro workflow whose `settings.studio` holds the
|
|
5
|
-
* shots. Its CODE — the codec that reads and writes that document, the plan
|
|
6
|
-
* format, the catalogs and the reducers — lives in `@nodaro/studio-production`,
|
|
7
|
-
* which is FSL-licensed. What lives here is the ENVELOPE the routes return and
|
|
8
|
-
* the bodies they take, because the SDK is typed against this package and an
|
|
9
|
-
* SDK caller has to know the shape of a reply.
|
|
10
|
-
*
|
|
11
|
-
* The document's own sub-objects (the cast, the looks, the scene plan, the
|
|
12
|
-
* cuts) are therefore named JSON aliases rather than re-declared shapes. That
|
|
13
|
-
* is deliberate on both counts:
|
|
14
|
-
*
|
|
15
|
-
* - **Named**, not one anonymous `unknown`, so a `.d.ts` reader can still see
|
|
16
|
-
* which field is which and the SDK's surface documents itself.
|
|
17
|
-
* - **Not re-declared**, because a second definition of the document is exactly
|
|
18
|
-
* the disagreement this contract exists to end — and publishing the studio's
|
|
19
|
-
* domain types under Apache would be an irrevocable grant of code that was
|
|
20
|
-
* deliberately placed one tier down.
|
|
21
|
-
*
|
|
22
|
-
* `@nodaro/studio-production` narrows every one of them to its real type and
|
|
23
|
-
* pins the narrowed view against this one at build time, so the two cannot
|
|
24
|
-
* drift apart in silence. A consumer that wants the narrow types depends on
|
|
25
|
-
* that package; a consumer that only reads the wire uses these.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* How a result is ADDRESSED: its job id when it has one, its url otherwise.
|
|
30
|
-
*
|
|
31
|
-
* Never a position. An index is meaningless the moment another writer inserts
|
|
32
|
-
* a result — and two writers is the normal case here, since an agent and the
|
|
33
|
-
* editor hold the same production open. Uploaded and hand-attached media have
|
|
34
|
-
* no job, which is why the url is the fallback rather than the key.
|
|
35
|
-
*/
|
|
36
|
-
export type ResultKey = string
|
|
37
|
-
|
|
38
|
-
// ── the document's own vocabulary, as JSON ──────────────────────────────────
|
|
39
|
-
// Each alias names the `@nodaro/studio-production` type that defines it.
|
|
40
|
-
|
|
41
|
-
/** `LookSelectionMap` — cinematic picks by dimension key. */
|
|
42
|
-
export type StudioLookMapJson = Record<string, unknown>
|
|
43
|
-
/** `Cast` — the production's roles, keyed by role slug. */
|
|
44
|
-
export type StudioCastJson = Record<string, unknown>
|
|
45
|
-
/** `CastLookMap` — which view of each actor a scene pins. */
|
|
46
|
-
export type StudioCastLookMapJson = Record<string, unknown>
|
|
47
|
-
/** `ProductionFolder` — a named timeline folder. */
|
|
48
|
-
export type StudioFolderJson = Record<string, unknown>
|
|
49
|
-
/** `StoryboardSettings` — the Storyboard tab's persisted state (`brief` lives here). */
|
|
50
|
-
export type StudioStoryboardJson = Record<string, unknown>
|
|
51
|
-
/** `ProductionMusic` — the rendered soundtrack muxed over the export. */
|
|
52
|
-
export type StudioMusicJson = Record<string, unknown>
|
|
53
|
-
/** `PlanMusic` — the soundtrack PLAN (prompt + pickers), kept beside the track. */
|
|
54
|
-
export type StudioMusicPlanJson = Record<string, unknown>
|
|
55
|
-
/** `ProductionCut` — one exported cut of the film. */
|
|
56
|
-
export type StudioCutJson = Record<string, unknown>
|
|
57
|
-
/** `ScenePlan` — a scene's authored framing / motion / voice, before it renders. */
|
|
58
|
-
export type StudioPlanJson = Record<string, unknown>
|
|
59
|
-
/** `ShotBeat` — one timed motion window inside a scene. */
|
|
60
|
-
export type StudioBeatJson = Record<string, unknown>
|
|
61
|
-
/** `ShotTransition` — how a scene's last frames go out. */
|
|
62
|
-
export type StudioTransitionJson = Record<string, unknown>
|
|
63
|
-
/** `ShotVoice` — the scene's generated voiceover. */
|
|
64
|
-
export type StudioVoiceJson = Record<string, unknown>
|
|
65
|
-
/** `DirectionFields` / `SubjectFields` — platform catalog ids carried on a result. */
|
|
66
|
-
export type StudioIdFieldsJson = Record<string, unknown>
|
|
67
|
-
/** `ConnectedReference` — a bound `@`-entity chip. */
|
|
68
|
-
export type StudioReferenceJson = Record<string, unknown>
|
|
69
|
-
/** `TrashedItem` — one deleted shot, still or clip, restorable by id. */
|
|
70
|
-
export type StudioTrashItemJson = Record<string, unknown>
|
|
71
|
-
|
|
72
|
-
// ── results ────────────────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* One generated STILL, with the context that regenerates it.
|
|
76
|
-
*
|
|
77
|
-
* Result histories ACCUMULATE — a generate appends, it never replaces — so a
|
|
78
|
-
* shot's stills are every framing candidate it has ever had, and each one
|
|
79
|
-
* carries what it was made with. That is what makes "go back to the second
|
|
80
|
-
* one" a read rather than a re-run.
|
|
81
|
-
*/
|
|
82
|
-
export interface StudioResultView {
|
|
83
|
-
key: ResultKey
|
|
84
|
-
url: string
|
|
85
|
-
jobId?: string
|
|
86
|
-
name?: string
|
|
87
|
-
prompt?: string
|
|
88
|
-
negativePrompt?: string
|
|
89
|
-
provider?: string
|
|
90
|
-
referenceImageUrls?: string[]
|
|
91
|
-
references?: StudioReferenceJson[]
|
|
92
|
-
aspectRatio?: string
|
|
93
|
-
resolution?: string
|
|
94
|
-
/** The look layers this generation was sent with — film, scene, then the shot's own. */
|
|
95
|
-
filmLook?: StudioLookMapJson
|
|
96
|
-
sceneLook?: StudioLookMapJson
|
|
97
|
-
look?: StudioLookMapJson
|
|
98
|
-
subject?: StudioIdFieldsJson
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* One generated CLIP, with the frames it was animated from.
|
|
103
|
-
*
|
|
104
|
-
* The frames matter more here than they look: selecting a past clip restores
|
|
105
|
-
* the start and end frames THAT clip was made from, which is why they are
|
|
106
|
-
* stored per result rather than read off the shot.
|
|
107
|
-
*/
|
|
108
|
-
export interface StudioClipResultView {
|
|
109
|
-
key: ResultKey
|
|
110
|
-
url: string
|
|
111
|
-
jobId?: string
|
|
112
|
-
name?: string
|
|
113
|
-
prompt?: string
|
|
114
|
-
provider?: string
|
|
115
|
-
negativePrompt?: string
|
|
116
|
-
duration?: number
|
|
117
|
-
/** The frames THIS clip was animated from — restored with it, never derived. */
|
|
118
|
-
startFrameUrl?: string
|
|
119
|
-
endFrameUrl?: string
|
|
120
|
-
referenceImageUrls?: string[]
|
|
121
|
-
references?: StudioReferenceJson[]
|
|
122
|
-
beats?: StudioBeatJson[]
|
|
123
|
-
scenePrompt?: string
|
|
124
|
-
endTransition?: StudioTransitionJson
|
|
125
|
-
filmLook?: StudioLookMapJson
|
|
126
|
-
sceneLook?: StudioLookMapJson
|
|
127
|
-
look?: StudioLookMapJson
|
|
128
|
-
subject?: StudioIdFieldsJson
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* A framing batch that is STILL RUNNING, with everything needed to land it.
|
|
133
|
-
*
|
|
134
|
-
* A marker rather than a promise: any client — or none — can finish the job,
|
|
135
|
-
* because the context that turns a finished job into a result is written down
|
|
136
|
-
* on the production instead of living in the browser tab that started it.
|
|
137
|
-
*/
|
|
138
|
-
export interface PendingStillView {
|
|
139
|
-
jobId: string
|
|
140
|
-
batchId?: string
|
|
141
|
-
provider?: string
|
|
142
|
-
prompt?: string
|
|
143
|
-
count?: number
|
|
144
|
-
startedAt?: string
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** An animate that is still running. The clip mirror of {@link PendingStillView}. */
|
|
148
|
-
export interface PendingClipView {
|
|
149
|
-
jobId: string
|
|
150
|
-
provider?: string
|
|
151
|
-
prompt?: string
|
|
152
|
-
startedAt?: string
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ── the shot ───────────────────────────────────────────────────────────────
|
|
156
|
-
|
|
157
|
-
/** A shot's framed STILL: the active frame, and (at `detail: "full"`) its history. */
|
|
158
|
-
export interface StudioStillView {
|
|
159
|
-
nodeId: string
|
|
160
|
-
provider: string
|
|
161
|
-
prompt: string
|
|
162
|
-
active: ResultKey | null
|
|
163
|
-
activeUrl: string
|
|
164
|
-
count: number
|
|
165
|
-
/** Cinematic direction as PLATFORM catalog ids — never baked hint text. */
|
|
166
|
-
direction?: StudioIdFieldsJson
|
|
167
|
-
subject?: StudioIdFieldsJson
|
|
168
|
-
/** Present only at `detail: "full"` — a list read returns counts, not histories. */
|
|
169
|
-
results?: StudioResultView[]
|
|
170
|
-
pending: PendingStillView[]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** A shot's animated CLIP. Independent of the still: deleting one never touches the other. */
|
|
174
|
-
export interface StudioClipView {
|
|
175
|
-
nodeId: string
|
|
176
|
-
provider: string
|
|
177
|
-
prompt: string
|
|
178
|
-
duration?: number
|
|
179
|
-
active: ResultKey | null
|
|
180
|
-
activeUrl: string
|
|
181
|
-
count: number
|
|
182
|
-
direction?: StudioIdFieldsJson
|
|
183
|
-
/** The voice this clip was revoiced into, when it was. */
|
|
184
|
-
revoicedVoiceId?: string
|
|
185
|
-
revoicedVoiceName?: string
|
|
186
|
-
/** Present only at `detail: "full"`. */
|
|
187
|
-
results?: StudioClipResultView[]
|
|
188
|
-
pending: PendingClipView[]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** One shot, in timeline order. */
|
|
192
|
-
export interface StudioShotView {
|
|
193
|
-
id: string
|
|
194
|
-
index: number
|
|
195
|
-
name?: string
|
|
196
|
-
folderId?: string
|
|
197
|
-
still?: StudioStillView
|
|
198
|
-
clip?: StudioClipView
|
|
199
|
-
/** Explicit and sticky: selecting a still never moves them. */
|
|
200
|
-
startFrame?: string
|
|
201
|
-
endFrame?: string
|
|
202
|
-
directingReferences?: {
|
|
203
|
-
images?: string[]
|
|
204
|
-
videos?: string[]
|
|
205
|
-
audio?: string[]
|
|
206
|
-
}
|
|
207
|
-
plan?: StudioPlanJson
|
|
208
|
-
scenePrompt?: string
|
|
209
|
-
beats?: StudioBeatJson[]
|
|
210
|
-
endTransition?: StudioTransitionJson
|
|
211
|
-
look?: StudioLookMapJson
|
|
212
|
-
castLook?: StudioCastLookMapJson
|
|
213
|
-
voice?: StudioVoiceJson
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ── the production ─────────────────────────────────────────────────────────
|
|
217
|
-
|
|
218
|
-
/** What is in flight, at a glance — the reason a `get` can reconcile before it reads. */
|
|
219
|
-
export interface StudioPendingView {
|
|
220
|
-
stills: number
|
|
221
|
-
clips: number
|
|
222
|
-
music: boolean
|
|
223
|
-
draft: { jobId: string; mode: "replace" | "append" } | null
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** The bin: a count always, the items only at `detail: "full"`. */
|
|
227
|
-
export interface StudioTrashView {
|
|
228
|
-
count: number
|
|
229
|
-
items?: StudioTrashItemJson[]
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** The read shape of every `/v1/studio/productions` route and every studio MCP tool. */
|
|
233
|
-
export interface StudioProductionView {
|
|
234
|
-
id: string
|
|
235
|
-
name: string
|
|
236
|
-
version: number
|
|
237
|
-
updatedAt: string
|
|
238
|
-
thumbnailUrl: string | null
|
|
239
|
-
shared: boolean
|
|
240
|
-
archived: boolean
|
|
241
|
-
film?: StudioLookMapJson
|
|
242
|
-
cast?: StudioCastJson
|
|
243
|
-
folders: StudioFolderJson[]
|
|
244
|
-
storyboard?: StudioStoryboardJson
|
|
245
|
-
music?: StudioMusicJson
|
|
246
|
-
musicPlan?: StudioMusicPlanJson
|
|
247
|
-
cuts: StudioCutJson[]
|
|
248
|
-
trash: StudioTrashView
|
|
249
|
-
pending: StudioPendingView
|
|
250
|
-
/** In timeline order. */
|
|
251
|
-
shots: StudioShotView[]
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** A dashboard row — what a list returns, with no shot bodies at all. */
|
|
255
|
-
export interface StudioProductionSummary {
|
|
256
|
-
id: string
|
|
257
|
-
name: string
|
|
258
|
-
version: number
|
|
259
|
-
updatedAt: string
|
|
260
|
-
thumbnailUrl: string | null
|
|
261
|
-
shared: boolean
|
|
262
|
-
archived: boolean
|
|
263
|
-
shotCount: number
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ── request / response bodies ───────────────────────────────────────────────
|
|
267
|
-
// Phase 0's five routes. The operation, generation and lifecycle bodies land
|
|
268
|
-
// with the routes that take them, so this file never describes a route the
|
|
269
|
-
// platform does not serve.
|
|
270
|
-
|
|
271
|
-
/** `GET …/skill` — the authoring skill, rendered from the package at request time. */
|
|
272
|
-
export interface StudioSkillResponse {
|
|
273
|
-
/** SKILL.md — the authoring guide. */
|
|
274
|
-
skill: string
|
|
275
|
-
/** references/catalog.md — every picker, model and enum, in full. */
|
|
276
|
-
catalog: string
|
|
277
|
-
/** schema.json — the strict JSON Schema a plan is validated against. */
|
|
278
|
-
schema: Record<string, unknown>
|
|
279
|
-
/** The operating guide: the tool map, the loops, the rules. */
|
|
280
|
-
operating: string
|
|
281
|
-
/** The catalog versions the three were rendered from. */
|
|
282
|
-
generatedFrom: { prompts: string; shared: string }
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/** One thing wrong with a plan, addressed at the field that is wrong. */
|
|
286
|
-
export interface StudioPlanIssue {
|
|
287
|
-
path: string
|
|
288
|
-
message: string
|
|
289
|
-
hint?: string
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/** `POST …/validate` — free, persists nothing, and resolves against the caller's library. */
|
|
293
|
-
export interface StudioValidatePlanRequest {
|
|
294
|
-
plan: Record<string, unknown>
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export interface StudioValidatePlanResponse {
|
|
298
|
-
valid: boolean
|
|
299
|
-
errors: StudioPlanIssue[]
|
|
300
|
-
warnings: StudioPlanIssue[]
|
|
301
|
-
summary?: {
|
|
302
|
-
name?: string
|
|
303
|
-
scenes: number
|
|
304
|
-
shots: number
|
|
305
|
-
cast: number
|
|
306
|
-
/** Cast entries that matched a row in the caller's library. */
|
|
307
|
-
bound: number
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** `GET …?limit&cursor` — the caller's "Studio" project, archived and hidden filtered. */
|
|
312
|
-
export interface StudioListProductionsResponse {
|
|
313
|
-
data: StudioProductionSummary[]
|
|
314
|
-
nextCursor?: string
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/** `POST …` — a new production, optionally landed from a plan in the same call. */
|
|
318
|
-
export interface StudioCreateProductionRequest {
|
|
319
|
-
name?: string
|
|
320
|
-
plan?: Record<string, unknown>
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/** `POST …/:id/import` — add a plan's scenes to a production that already exists. */
|
|
324
|
-
export interface StudioImportPlanRequest {
|
|
325
|
-
plan: Record<string, unknown>
|
|
326
|
-
mode?: "append"
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/** What an import did, in the words a receipt would use. */
|
|
330
|
-
export interface StudioImportSummary {
|
|
331
|
-
shotsAdded: number
|
|
332
|
-
castEnrolled: number
|
|
333
|
-
/** Cast entries that resolved to a row in the caller's library. */
|
|
334
|
-
castBound: number
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export interface StudioProductionResponse {
|
|
338
|
-
production: StudioProductionView
|
|
339
|
-
warnings?: StudioPlanIssue[]
|
|
340
|
-
summary?: StudioImportSummary
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/** How much of a production a read returns. */
|
|
344
|
-
export type StudioProductionDetail = "summary" | "full"
|