@oeave/bakery3 0.0.0-stage → 0.1.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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +562 -2
  3. package/dist/animation-B1h0Ryvj.d.ts +97 -0
  4. package/dist/bake/index.d.ts +369 -0
  5. package/dist/bake/index.js +9 -0
  6. package/dist/bake/index.js.map +1 -0
  7. package/dist/bake-DaOlenyx.d.ts +1357 -0
  8. package/dist/catalog/index.d.ts +100 -0
  9. package/dist/catalog/index.js +154 -0
  10. package/dist/catalog/index.js.map +1 -0
  11. package/dist/catalog.gen-BM-aNf7n.d.ts +733 -0
  12. package/dist/chunk-25ZXSVQ3.js +1107 -0
  13. package/dist/chunk-25ZXSVQ3.js.map +1 -0
  14. package/dist/chunk-3G5QL4F4.js +145 -0
  15. package/dist/chunk-3G5QL4F4.js.map +1 -0
  16. package/dist/chunk-4E5VV4QY.js +12 -0
  17. package/dist/chunk-4E5VV4QY.js.map +1 -0
  18. package/dist/chunk-54WJDN5R.js +3286 -0
  19. package/dist/chunk-54WJDN5R.js.map +1 -0
  20. package/dist/chunk-6NYR73Y7.js +832 -0
  21. package/dist/chunk-6NYR73Y7.js.map +1 -0
  22. package/dist/chunk-6YVQANMW.js +1463 -0
  23. package/dist/chunk-6YVQANMW.js.map +1 -0
  24. package/dist/chunk-AF5BIELI.js +70 -0
  25. package/dist/chunk-AF5BIELI.js.map +1 -0
  26. package/dist/chunk-APBOWWSV.js +92 -0
  27. package/dist/chunk-APBOWWSV.js.map +1 -0
  28. package/dist/chunk-ARMTTZXZ.js +1157 -0
  29. package/dist/chunk-ARMTTZXZ.js.map +1 -0
  30. package/dist/chunk-CX4WTVGH.js +4492 -0
  31. package/dist/chunk-CX4WTVGH.js.map +1 -0
  32. package/dist/chunk-HQTUFVMM.js +1412 -0
  33. package/dist/chunk-HQTUFVMM.js.map +1 -0
  34. package/dist/chunk-MD2U6S4N.js +1952 -0
  35. package/dist/chunk-MD2U6S4N.js.map +1 -0
  36. package/dist/chunk-S4AJTLLN.js +23 -0
  37. package/dist/chunk-S4AJTLLN.js.map +1 -0
  38. package/dist/chunk-TGFNQBKC.js +551 -0
  39. package/dist/chunk-TGFNQBKC.js.map +1 -0
  40. package/dist/chunk-V3QINEEG.js +87 -0
  41. package/dist/chunk-V3QINEEG.js.map +1 -0
  42. package/dist/chunk-W6WQ5UP7.js +91 -0
  43. package/dist/chunk-W6WQ5UP7.js.map +1 -0
  44. package/dist/chunk-WBIAFIHV.js +346 -0
  45. package/dist/chunk-WBIAFIHV.js.map +1 -0
  46. package/dist/chunk-ZEBVAIJJ.js +156 -0
  47. package/dist/chunk-ZEBVAIJJ.js.map +1 -0
  48. package/dist/devtools/index.d.ts +536 -0
  49. package/dist/devtools/index.js +15 -0
  50. package/dist/devtools/index.js.map +1 -0
  51. package/dist/environments/index.d.ts +93 -0
  52. package/dist/environments/index.js +382 -0
  53. package/dist/environments/index.js.map +1 -0
  54. package/dist/hotspots/index.d.ts +111 -0
  55. package/dist/hotspots/index.js +285 -0
  56. package/dist/hotspots/index.js.map +1 -0
  57. package/dist/index.d.ts +944 -0
  58. package/dist/index.js +15 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/node/index.cjs +5115 -0
  61. package/dist/node/index.cjs.map +1 -0
  62. package/dist/node/index.d.cts +4862 -0
  63. package/dist/node/index.d.ts +576 -0
  64. package/dist/node/index.js +1279 -0
  65. package/dist/node/index.js.map +1 -0
  66. package/dist/prepare-C5qVvaw4.d.ts +112 -0
  67. package/dist/presets/index.d.ts +526 -0
  68. package/dist/presets/index.js +14 -0
  69. package/dist/presets/index.js.map +1 -0
  70. package/dist/r3f/index.d.ts +159 -0
  71. package/dist/r3f/index.js +592 -0
  72. package/dist/r3f/index.js.map +1 -0
  73. package/dist/room-S5TLCK4F.js +9 -0
  74. package/dist/room-S5TLCK4F.js.map +1 -0
  75. package/dist/rooms.gen-vhj6sFW5.d.ts +1434 -0
  76. package/dist/session-YCQS4MRH.js +9 -0
  77. package/dist/session-YCQS4MRH.js.map +1 -0
  78. package/dist/shapes/index.d.ts +222 -0
  79. package/dist/shapes/index.js +836 -0
  80. package/dist/shapes/index.js.map +1 -0
  81. package/dist/testRun-BIPXaVPb.d.ts +1130 -0
  82. package/dist/timeline-ChwgD7bT.d.ts +470 -0
  83. package/dist/tsl/index.d.ts +165 -0
  84. package/dist/tsl/index.js +310 -0
  85. package/dist/tsl/index.js.map +1 -0
  86. package/package.json +170 -4
@@ -0,0 +1,576 @@
1
+ import { Q as QualityPreset, O as OutputSpec, R as RenderSpec, C as ColorSpec } from '../bake-DaOlenyx.js';
2
+ export { F as FrameSequenceIndex } from '../bake-DaOlenyx.js';
3
+ import { B as BatchProgress, R as Recorded, c as Bakery3Error, W as WebhookPayload, d as Bakery3Client, e as RenderRecord, C as CostEstimate, f as BatchOpenRequest, b as Batch, A as AssetImportKind, g as AssetImportResponse, h as RetryPolicy, i as RenderSet, T as TestRunOptions } from '../testRun-BIPXaVPb.js';
4
+ export { j as BatchAddRequest, k as BatchAddResponse, l as BatchExportLine, m as BatchItemOutcome, n as BatchItemRecord, o as BatchItemRequest, p as BatchItemsPage, q as BatchPageRefused, a as BatchRecord, F as FlushOptions, r as RenderSetFailure, s as RenderSetImage, t as WaitOptions } from '../testRun-BIPXaVPb.js';
5
+ import { H as HdriPresetName } from '../catalog.gen-BM-aNf7n.js';
6
+ import { R as RoomPresetName, a as RoomPresetLighting } from '../rooms.gen-vhj6sFW5.js';
7
+ import '../animation-B1h0Ryvj.js';
8
+
9
+ /**
10
+ * Cameras without a scene graph: where to stand to photograph a box.
11
+ *
12
+ * In the browser a camera is an object the developer placed. From a server
13
+ * there is a model's bounds (`GlbFacts.bounds`, read at import) and nothing
14
+ * else, so a view is described the way a photographer would: from which
15
+ * side, how high, how tight. `framedCamera` turns that into the same
16
+ * `CameraSpec` a captured camera becomes.
17
+ *
18
+ * No three.js here: the server entry imports this file.
19
+ */
20
+
21
+ /** Where to look from: degrees around the model from its front (+Z, where glTF says a model faces) toward its right (+X), and degrees above the horizon. */
22
+ type Angle = {
23
+ azimuthDeg: number;
24
+ elevationDeg?: number;
25
+ };
26
+ /** The framings with names. */
27
+ declare const VIEW_WORDS: {
28
+ readonly front: {
29
+ readonly azimuthDeg: 0;
30
+ readonly elevationDeg: 6;
31
+ };
32
+ readonly 'three-quarter': {
33
+ readonly azimuthDeg: 35;
34
+ readonly elevationDeg: 14;
35
+ };
36
+ readonly side: {
37
+ readonly azimuthDeg: 90;
38
+ readonly elevationDeg: 6;
39
+ };
40
+ readonly back: {
41
+ readonly azimuthDeg: 180;
42
+ readonly elevationDeg: 6;
43
+ };
44
+ readonly 'three-quarter-back': {
45
+ readonly azimuthDeg: 145;
46
+ readonly elevationDeg: 14;
47
+ };
48
+ readonly top: {
49
+ readonly azimuthDeg: 0;
50
+ readonly elevationDeg: 88;
51
+ };
52
+ };
53
+ type ViewWord = keyof typeof VIEW_WORDS;
54
+
55
+ /**
56
+ * A catalog from a server: models by URL, shots per product, no browser.
57
+ *
58
+ * import { Bakery3Server } from '@oeave/bakery3/node';
59
+ * const api = new Bakery3Server(process.env.BAKERY3_SECRET_KEY!);
60
+ *
61
+ * const run = await api.renderBatch({
62
+ * key: 'fall-2026',
63
+ * products: [{ id: 'chair-aria', url: 'https://cdn.shop.com/models/chair-aria.glb' }],
64
+ * shots: [
65
+ * 'three-quarter',
66
+ * { kind: 'video', from: 'three-quarter', to: 'three-quarter-back', seconds: 4 },
67
+ * { id: 'low', azimuthDeg: -30, elevationDeg: 4 },
68
+ * ],
69
+ * room: { preset: 'loft_golden' },
70
+ * maxCost: 600,
71
+ * });
72
+ *
73
+ * A shot is a picture or a clip of a product from an angle: degrees around
74
+ * the model from its front and degrees above the horizon, or a word for one.
75
+ * The camera is framed from the model's own bounds, read when the model is
76
+ * imported, so a vase and a sofa both fill the frame from the same word and
77
+ * nothing is asked of the file itself. A clip is the camera carried from one
78
+ * angle to another over `seconds`, one framed camera per frame.
79
+ *
80
+ * Settings come in three layers: the run's, then a product's, then a shot's,
81
+ * the nearer winning. So one `room` at the top is every shot's room, a
82
+ * product that wants the studio instead says `room: null, environment: {…}`,
83
+ * and one shot in it can still be a close-up at 2048² in a jpeg.
84
+ *
85
+ * The model's bytes never pass through this process. Each URL is handed to
86
+ * `POST /v1/assets/import`; the service fetches it once, stores it content
87
+ * addressed like any upload, and answers with the hash and what the GLB says
88
+ * about itself. A model that did not change since the last run is not
89
+ * downloaded again. What this process sends is a few KB of manifest per shot.
90
+ *
91
+ * A manifest built here describes a model, cameras and light. A scene whose
92
+ * look lives in code exists only where that code runs, so it is recorded
93
+ * there, with `renderVariants()` in the page. Both kinds of item can share a
94
+ * batch.
95
+ *
96
+ * No three.js here.
97
+ */
98
+
99
+ /**
100
+ * Where to look from: a word, or degrees. `'room'` is the framing a preset
101
+ * room was published with, from level or above: never looking up at the
102
+ * product.
103
+ */
104
+ type View = ViewWord | 'room' | Angle;
105
+ type BatchEnvironment = {
106
+ /** A studio or a sky from the preset library. */
107
+ preset?: HdriPresetName;
108
+ /**
109
+ * Or an HDRI of your own: a public https URL of a .hdr or .exr, imported
110
+ * like the models.
111
+ */
112
+ url?: string;
113
+ /** How strongly it lights the product. Default 1 (0.6 for the default studio). */
114
+ intensity?: number;
115
+ /** Turn the environment about the vertical, in degrees. */
116
+ rotationDeg?: number;
117
+ };
118
+ type BatchRoom = {
119
+ /** A room from the preset library. */
120
+ preset: RoomPresetName;
121
+ /** Default: the mode the room was designed under. */
122
+ mode?: RoomPresetLighting;
123
+ /**
124
+ * What the product stands on. `'auto'` (the default) goes by its size: the
125
+ * room's own pedestal for a small thing, the low plinth for a mid-sized
126
+ * one, the floor for furniture and anything that would overhang. `'own'`
127
+ * and `'floor'` say so yourself.
128
+ */
129
+ stand?: 'auto' | 'own' | 'floor';
130
+ /**
131
+ * Where the room's own camera sees the product from, in degrees from the
132
+ * product's front. The room is turned about the product to make it so.
133
+ * Default 30: a three-quarter view from the side the room is open to.
134
+ */
135
+ viewFromDeg?: number;
136
+ };
137
+ /**
138
+ * What every shot can say about how it is taken. Set on the run for all of
139
+ * them, on a product for its own, on a shot for that one; the nearest wins.
140
+ */
141
+ type ShotSettings = {
142
+ /**
143
+ * A preset room to stand in. `null` says no room, where a wider layer had
144
+ * one.
145
+ */
146
+ room?: BatchRoom | null;
147
+ /**
148
+ * The light and backdrop when there is no room. Default: the
149
+ * `studio_small_03` preset at 0.6.
150
+ */
151
+ environment?: BatchEnvironment;
152
+ /**
153
+ * `'transparent'` (the default, with a traced shadow), a `#rrggbb`, or
154
+ * `'environment'`. A video has no alpha: `'transparent'` is `#f4f4f5` there
155
+ * (a `png-sequence` keeps it).
156
+ */
157
+ background?: 'transparent' | 'environment' | `#${string}`;
158
+ /** Default `'studio'`. */
159
+ quality?: QualityPreset;
160
+ /** Pixels. Default: the quality preset's long edge, square. */
161
+ width?: number;
162
+ height?: number;
163
+ /**
164
+ * `png` (the default), `jpeg` or `webp` for an image; `mp4` (the default),
165
+ * `webm` or `png-sequence` (every frame a PNG with alpha, in one tar) for a
166
+ * video.
167
+ */
168
+ format?: OutputSpec['format'];
169
+ /** JPEG/WebP only, 1 to 100. */
170
+ imageQuality?: number;
171
+ /**
172
+ * Vertical field of view in degrees. Default 30, or the room's own lens in
173
+ * a room.
174
+ */
175
+ fov?: number;
176
+ /**
177
+ * Room around the model, as a share of its size. Default 0.12, or 0.9 in
178
+ * a room.
179
+ */
180
+ margin?: number;
181
+ /** Frames a second, for a video. Default 30, at most 60. */
182
+ fps?: number;
183
+ /** Renderer settings beyond the quality preset. */
184
+ advanced?: RenderSpec['advanced'];
185
+ /**
186
+ * How the pictures are developed. Default: sRGB, ACES filmic, exposure 1
187
+ * (a room's own exposure in a room).
188
+ */
189
+ color?: Partial<ColorSpec>;
190
+ };
191
+ type ImageShot = ShotSettings & {
192
+ kind?: 'image';
193
+ /** Default: the view's word, or `az35-el14` for degrees. Ends the item's key. */
194
+ id?: string;
195
+ /** Where from. Default `'three-quarter'`, or `'room'` in a room. */
196
+ view?: View;
197
+ /**
198
+ * Instead of a view: a camera inside the model, by its node name, as the
199
+ * file has it.
200
+ */
201
+ camera?: string;
202
+ };
203
+ type VideoShot = ShotSettings & {
204
+ kind: 'video';
205
+ /** Default `orbit-<from>-<to>`. */
206
+ id?: string;
207
+ /** The camera is carried from here. */
208
+ from: View;
209
+ /**
210
+ * To here, azimuth as written: `{ azimuthDeg: 395 }` from 35 is a full
211
+ * turn.
212
+ */
213
+ to: View;
214
+ /** Default 4. At most 30. */
215
+ seconds?: number;
216
+ };
217
+ /** A word or an angle is an image from that view. */
218
+ type Shot = ViewWord | 'room' | Angle | ImageShot | VideoShot;
219
+ type BatchProduct = ShotSettings & {
220
+ /** Starts every shot's key. Stable across re-runs. */
221
+ id: string;
222
+ /** A public https URL of a binary glTF (.glb). */
223
+ url: string;
224
+ /** This product's shots, instead of the run's. */
225
+ shots?: Shot[];
226
+ /** Echoed on every shot of this product, its webhooks and the ledger. */
227
+ metadata?: Record<string, unknown>;
228
+ };
229
+ type RenderBatchProgress = BatchProgress & {
230
+ /** The product just recorded, or skipped. */
231
+ product: string;
232
+ /** Products recorded so far. */
233
+ products: number;
234
+ /** Shots recorded so far, clips included. */
235
+ images: number;
236
+ /** Products skipped so far. */
237
+ skipped: number;
238
+ };
239
+ /** A product left out of a run, with the reason. */
240
+ type ProductSkip = {
241
+ id: string;
242
+ url: string;
243
+ error: Bakery3Error;
244
+ };
245
+ type RenderBatchRecorded = Recorded & {
246
+ /**
247
+ * Products whose model could not be imported or read. The run went on
248
+ * without them.
249
+ */
250
+ skipped: number;
251
+ /** The first of those, with their reasons. */
252
+ skips: ProductSkip[];
253
+ };
254
+ type RenderBatchOptions = ShotSettings & {
255
+ /** Names the run. The same key re-opens it while it is unfinished. */
256
+ key: string;
257
+ /** A label for the console. Default: the key. */
258
+ name?: string;
259
+ /**
260
+ * An array, a generator, a database cursor. Pulled as it is needed, never
261
+ * collected.
262
+ */
263
+ products: Iterable<BatchProduct> | AsyncIterable<BatchProduct>;
264
+ /**
265
+ * Every product's shots, unless it has its own. Default: one image,
266
+ * `'three-quarter'` (`'room'` in a room).
267
+ */
268
+ shots?: Shot[];
269
+ /**
270
+ * Appended to every key as `@version`. Bump it to trace again what already
271
+ * rendered.
272
+ */
273
+ version?: string;
274
+ /** The ceiling for the whole run, in USD. */
275
+ maxCost: number;
276
+ /** A ceiling per shot, in USD. A shot quoted above it is refused. */
277
+ maxCostPerImage?: number;
278
+ /**
279
+ * `'batch'` (the default) never runs ahead of a person in a configurator;
280
+ * `'normal'` is the API's ordinary lane.
281
+ */
282
+ lane?: 'batch' | 'normal';
283
+ /** Fewer parallel shots than the plan allows, for a gentler budget. */
284
+ maxParallel?: number;
285
+ /** Imports in flight at once. Default 4, at most 16. */
286
+ concurrency?: number;
287
+ /** Called after each product is recorded or skipped. */
288
+ onProgress?: (progress: RenderBatchProgress) => void;
289
+ /**
290
+ * Stops between products. What was recorded stays admitted; call again to
291
+ * go on.
292
+ */
293
+ signal?: AbortSignal;
294
+ };
295
+ type RenderBatchRecording = RenderBatchRecorded & {
296
+ estimated: number;
297
+ maximum: number;
298
+ };
299
+
300
+ /**
301
+ * Webhook signature verification.
302
+ *
303
+ * A webhook endpoint is a public URL that causes writes in the developer's
304
+ * database. Verifying is not optional, so it is one function with no options
305
+ * and no way to get it subtly wrong.
306
+ *
307
+ * const event = await verifyWebhook({
308
+ * body: await request.text(),
309
+ * signature: request.headers.get('bakery3-signature'),
310
+ * secret: process.env.BAKERY3_WEBHOOK_SECRET!,
311
+ * });
312
+ *
313
+ * Pass the raw body, not a parsed and re-stringified object. Re-serializing
314
+ * JSON changes key order and whitespace and therefore the signature, and it
315
+ * is the single most common reason a correct implementation fails.
316
+ */
317
+
318
+ /**
319
+ * Verify a webhook request and return its payload. Throws a `Bakery3Error`
320
+ * (`Unauthorized`) when the header is missing, malformed, older than five
321
+ * minutes, or signed with another secret.
322
+ */
323
+ declare function verifyWebhook(input: {
324
+ /** The request body exactly as received, as text. */
325
+ body: string;
326
+ /**
327
+ * The `bakery3-signature` header. `headers.get()` gives `null` when the
328
+ * request has none, and that is refused like a bad signature.
329
+ */
330
+ signature: string | null | undefined;
331
+ /** The signing secret from your webhook settings. */
332
+ secret: string;
333
+ /** Test seam. Never pass this in production. */
334
+ now?: number;
335
+ }): Promise<WebhookPayload>;
336
+
337
+ /**
338
+ * @oeave/bakery3/node: the server half.
339
+ *
340
+ * The secret key lives here and only here. It never reaches a browser and is
341
+ * never an argument to anything in `@oeave/bakery3`. This module mints the
342
+ * short-lived, narrow token that goes in its place, verifies webhooks, and
343
+ * calls the API with the secret key for the things a browser may not do.
344
+ *
345
+ * import { Bakery3Server } from '@oeave/bakery3/node';
346
+ *
347
+ * const bakery3 = new Bakery3Server(process.env.BAKERY3_SECRET_KEY!);
348
+ *
349
+ * export async function POST() {
350
+ * return Response.json(
351
+ * await bakery3.tokens.create({
352
+ * expiresInSeconds: 300,
353
+ * maxRenders: 1,
354
+ * maxCost: 1,
355
+ * allowedOrigins: ['https://shop.example.com'],
356
+ * })
357
+ * );
358
+ * }
359
+ *
360
+ * That endpoint is the entire server-side integration.
361
+ */
362
+
363
+ type BrowserTokenOptions = {
364
+ /**
365
+ * How long the token lives, in seconds. Default 300. Keep it short: a
366
+ * browser token is a capability handed to a page, and the shorter its life
367
+ * the less a leaked one is worth. Five minutes is plenty, because the
368
+ * browser SDK re-mints on a 401 without the developer writing anything.
369
+ */
370
+ expiresInSeconds?: number;
371
+ /** Hard ceiling on renders this token can start. Default 5. */
372
+ maxRenders?: number;
373
+ /** Hard ceiling on spend across all of them, in USD. Default 2. */
374
+ maxCost?: number;
375
+ /**
376
+ * Origins allowed to spend it. Set this in production: it is what stops a
377
+ * token lifted out of one page's network tab from working anywhere else.
378
+ */
379
+ allowedOrigins?: string[];
380
+ /** Your own user/session id. Shows up in the usage ledger for attribution. */
381
+ subject?: string;
382
+ };
383
+ type BrowserToken = {
384
+ /** The token itself, a `bk_pt_` string. Hand it to the browser SDK. */
385
+ token: string;
386
+ /** When it stops working, as a Unix timestamp in milliseconds. */
387
+ expiresAt: number;
388
+ /** The project the token renders into. */
389
+ projectId: string;
390
+ };
391
+ type Bakery3ServerOptions = {
392
+ /** The API's base URL. Only for a self-hosted or test deployment. */
393
+ baseUrl?: string;
394
+ /** A `fetch` to use instead of the global one. */
395
+ fetch?: typeof fetch;
396
+ /** How a 429/503 is waited out. The browser SDK's defaults otherwise. */
397
+ retry?: RetryPolicy;
398
+ };
399
+ /** A run's pictures, plus which products were left out and why. */
400
+ type RenderBatchRun = RenderSet & {
401
+ readonly recorded: RenderBatchRecorded;
402
+ };
403
+ declare class Bakery3Server {
404
+ readonly tokens: TokensApi;
405
+ readonly renders: RendersApi;
406
+ readonly batches: BatchesApi;
407
+ readonly assets: AssetsApi;
408
+ private readonly secretKey;
409
+ private readonly options;
410
+ private transport;
411
+ /**
412
+ * @param secretKey Your `bk_sk_` key, as a string, from a server-only
413
+ * environment variable. Not the browser SDK's `{ apiKey }` object.
414
+ *
415
+ * The key is checked on the first request, not here. A route module that
416
+ * builds its client at the top level is evaluated by `next build`, often
417
+ * where the server's secrets are not set, and a constructor that threw
418
+ * there would fail the build over a key the build never uses.
419
+ */
420
+ constructor(secretKey: string, options?: Bakery3ServerOptions);
421
+ /**
422
+ * The same transport the browser SDK uses (one error shape, the same
423
+ * 429/503 backoff) with the secret key as the bearer. It has nothing to
424
+ * re-mint, so a 401 here is a 401. Made on first use, after the key is
425
+ * checked.
426
+ */
427
+ private client;
428
+ /**
429
+ * Render a catalog of models that already sit on a CDN: one call, no
430
+ * browser, and none of the models' bytes through this process. Needs a
431
+ * plan that includes imports (see https://bakery3.com/docs/catalog#plans).
432
+ *
433
+ * const run = await api.renderBatch({
434
+ * key: 'fall-2026',
435
+ * products: [{ id: 'chair-aria', url: 'https://cdn.shop.com/models/chair-aria.glb' }],
436
+ * shots: ['three-quarter', 'front', { kind: 'video', from: 'front', to: 'back', seconds: 4 }],
437
+ * room: { preset: 'loft_golden' },
438
+ * maxCost: 600,
439
+ * });
440
+ * await run.wait(); // or let a webhook tell you, and exit now
441
+ *
442
+ * Resolves once every product is recorded and the run is sealed. The
443
+ * shots render on from there; this process does not have to stay up.
444
+ * A product whose model cannot be imported is skipped and listed in
445
+ * `run.recorded.skips`, and the run goes on. Calling it again with the
446
+ * same `key` resumes a recording that stopped, re-downloads no model that
447
+ * did not change, and adopts every shot that already rendered.
448
+ *
449
+ * What a model by URL cannot carry (node materials, a shader-lit set) is
450
+ * recorded where that code runs; see `renderVariants()` in `@oeave/bakery3`.
451
+ */
452
+ renderBatch(options: RenderBatchOptions): Promise<RenderBatchRun>;
453
+ /**
454
+ * What `renderBatch()` would cost. The models are imported (that is how
455
+ * they are priced); nothing is rendered or charged.
456
+ */
457
+ quoteBatch(options: Omit<RenderBatchOptions, 'maxCost'> & {
458
+ maxCost?: number;
459
+ }): Promise<RenderBatchRecording>;
460
+ /**
461
+ * Look before paying: three products (the first, the middle and the last
462
+ * of an array; the first three of anything else), every shot, at preview
463
+ * quality and a small size, in the ordinary lane so they come back in
464
+ * about a minute. A clip is cut to two seconds at twelve frames. Same
465
+ * options as `renderBatch()`. The test run has keys of its own, so the
466
+ * real run never adopts these previews as finished shots.
467
+ *
468
+ * const test = await api.testBatch(options);
469
+ * for (const shot of await test.result()) console.log(shot.variant, shot.shot, shot.url);
470
+ */
471
+ testBatch(options: Omit<RenderBatchOptions, 'maxCost'> & {
472
+ maxCost?: number;
473
+ }, test?: TestRunOptions): Promise<RenderBatchRun>;
474
+ /**
475
+ * A run by id, from another process, a webhook handler, or tomorrow
476
+ * morning.
477
+ */
478
+ getRenderSet(id: string): Promise<RenderSet>;
479
+ private openRun;
480
+ }
481
+ declare class AssetsApi {
482
+ private readonly client;
483
+ constructor(client: () => Bakery3Client);
484
+ /**
485
+ * Have the service fetch a public https URL into the project's assets,
486
+ * and say what it found: the hash to put in a manifest, and for a model
487
+ * its triangles, bounds, cameras and anything the renderer cannot read.
488
+ *
489
+ * Secret key only, which is why it is here and not in the browser SDK.
490
+ * What is fetched is fenced on the service's side: https, public
491
+ * addresses only, no credentials, a size limit, and the file must be a
492
+ * model or an HDRI. An unchanged URL is answered without a download
493
+ * (`unchanged: true`).
494
+ */
495
+ import(url: string, options?: {
496
+ kind?: AssetImportKind;
497
+ }): Promise<AssetImportResponse>;
498
+ }
499
+ type Request = <T>(method: string, path: string, body?: unknown) => Promise<T>;
500
+ declare class TokensApi {
501
+ private readonly request;
502
+ constructor(request: Request);
503
+ /**
504
+ * Mint a browser token.
505
+ *
506
+ * Everything about it is a ceiling rather than a grant: it cannot outlive
507
+ * its expiry, cannot start more renders than `maxRenders`, cannot spend past
508
+ * `maxCost`, cannot be used from an origin outside `allowedOrigins`, and
509
+ * cannot perform a single billing or admin operation. That last one is not
510
+ * a policy: those routes require a `bk_sk_` key and this is not one.
511
+ */
512
+ create(options?: BrowserTokenOptions): Promise<BrowserToken>;
513
+ /** Revoke a token before its expiry. */
514
+ revoke(token: string): Promise<void>;
515
+ }
516
+ declare class RendersApi {
517
+ private readonly request;
518
+ private readonly client;
519
+ constructor(request: Request, client: () => Bakery3Client);
520
+ /**
521
+ * Render from a manifest you already have: a batch job, a re-render, a
522
+ * queued webhook. The browser flow is `@oeave/bakery3`; this is the API.
523
+ * Repeated on a 429/503 only when `spec.idempotencyKey` makes that safe.
524
+ */
525
+ create(spec: RenderSpec): Promise<RenderRecord>;
526
+ /** A render's record, with its state, cost and result URLs. */
527
+ get(id: string): Promise<RenderRecord>;
528
+ /** Cancel a render that has not finished. Already terminal is a no-op. */
529
+ cancel(id: string): Promise<RenderRecord>;
530
+ /**
531
+ * One more attempt for a render that failed with `attemptsExhausted`.
532
+ * Returns the new render, linked to the old one; a render that is not in
533
+ * that state answers 409.
534
+ */
535
+ retry(id: string): Promise<RenderRecord>;
536
+ /**
537
+ * Delete a render now rather than when retention would: the result, the
538
+ * preview, and every signed URL that pointed at them. The record survives
539
+ * as `expired`, so the receipt is still readable. A live render is
540
+ * cancelled first.
541
+ */
542
+ delete(id: string): Promise<RenderRecord>;
543
+ /** The project's renders, newest first, a page at a time. */
544
+ list(options?: {
545
+ limit?: number;
546
+ cursor?: string;
547
+ }): Promise<{
548
+ renders: RenderRecord[];
549
+ cursor: string | null;
550
+ }>;
551
+ /** The quote for a spec: a likely cost and a maximum, in USD. */
552
+ estimate(spec: Pick<RenderSpec, 'quality' | 'output' | 'advanced'>): Promise<CostEstimate>;
553
+ }
554
+ /**
555
+ * Batches from a server: manifests you already have, added by the hundred
556
+ * and rendered behind one cost ceiling. `renderBatch()` builds on this.
557
+ *
558
+ * const batch = await api.batches.open({ key: 'fall-2026-v3', lane: 'batch', maxCost: 600 });
559
+ * for await (const line of readLines('manifests.jsonl')) {
560
+ * const item = JSON.parse(line);
561
+ * batch.add({ key: item.key, spec: item.spec, metadata: item.metadata });
562
+ * if (batch.buffered >= 100) await batch.flush();
563
+ * }
564
+ * await batch.close();
565
+ * await batch.wait({ onProgress: (p) => console.log(`${p.completed}/${p.total}`) });
566
+ */
567
+ declare class BatchesApi {
568
+ private readonly client;
569
+ constructor(client: () => Bakery3Client);
570
+ /** Open, or re-open by key. */
571
+ open(request: BatchOpenRequest): Promise<Batch>;
572
+ /** A batch by id, with its latest counts. */
573
+ get(id: string): Promise<Batch>;
574
+ }
575
+
576
+ export { type Angle, Bakery3Error, Bakery3Server, type Bakery3ServerOptions, Batch, type BatchEnvironment, BatchOpenRequest, type BatchProduct, BatchProgress, type BatchRoom, type BrowserToken, type BrowserTokenOptions, CostEstimate, type ImageShot, type ProductSkip, Recorded, type RenderBatchOptions, type RenderBatchProgress, type RenderBatchRecorded, type RenderBatchRun, RenderRecord, RenderSet, RenderSpec, RetryPolicy, type Shot, type ShotSettings, TestRunOptions, type VideoShot, type View, type ViewWord, verifyWebhook };