@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,346 @@
1
+ import { emptyRecorded, who } from './chunk-ZEBVAIJJ.js';
2
+ import { captureCamera } from './chunk-V3QINEEG.js';
3
+ import { Bakery3Error } from './chunk-3G5QL4F4.js';
4
+ import { PerspectiveCamera, Vector3, Matrix4, Quaternion } from 'three';
5
+
6
+ function variantHiddenIds(objects, finishes, finish) {
7
+ const shown = new Set(finish.show);
8
+ const hidden = /* @__PURE__ */ new Set();
9
+ for (const other of finishes) {
10
+ for (const name of other.show) {
11
+ if (shown.has(name)) continue;
12
+ const id = objects[name];
13
+ if (id === void 0) throw unknownObject(name, other.id, objects);
14
+ hidden.add(id);
15
+ }
16
+ }
17
+ for (const name of finish.show) {
18
+ if (objects[name] === void 0)
19
+ throw unknownObject(name, finish.id, objects);
20
+ }
21
+ return [...hidden];
22
+ }
23
+ function cameraSpec(camera, output) {
24
+ const position = new Vector3(...camera.position);
25
+ const target = new Vector3(...camera.target);
26
+ const distance = position.distanceTo(target);
27
+ if (!(distance > 0)) {
28
+ throw new Bakery3Error({
29
+ code: "InvalidRequest",
30
+ message: `Camera "${camera.id}" has its position on its target.`,
31
+ why: "A camera needs a direction to look in, and position === target gives it none.",
32
+ fix: "Move `position` or `target` so they differ.",
33
+ docs: "https://bakery3.com/docs/cameras"
34
+ });
35
+ }
36
+ const up = Math.abs(position.x - target.x) < 1e-9 && Math.abs(position.z - target.z) < 1e-9 ? (
37
+ // Straight down (or up): +Y up is degenerate, so face -Z as three does.
38
+ new Vector3(0, 0, -1)
39
+ ) : new Vector3(0, 1, 0);
40
+ const rotation = new Matrix4().lookAt(position, target, up);
41
+ const q = new Quaternion().setFromRotationMatrix(rotation);
42
+ const quaternion = [q.x, q.y, q.z, q.w];
43
+ return {
44
+ type: "perspective",
45
+ fov: camera.fov,
46
+ ...output ? { aspect: output.width / output.height } : {},
47
+ // Clip planes scaled to the shot: a watch a hand-span away and a sofa
48
+ // across a room both keep their subject well inside them.
49
+ near: Math.max(0.01, distance / 100),
50
+ far: Math.max(100, distance * 100),
51
+ position: [position.x, position.y, position.z],
52
+ quaternion,
53
+ name: camera.id
54
+ };
55
+ }
56
+ function variantManifest(manifest, camera, hiddenObjectIds) {
57
+ const runtime = { ...manifest.runtime };
58
+ if (hiddenObjectIds.length) runtime.hiddenObjectIds = hiddenObjectIds;
59
+ else delete runtime.hiddenObjectIds;
60
+ return {
61
+ ...manifest,
62
+ camera,
63
+ ...Object.keys(runtime).length ? { runtime } : {}
64
+ };
65
+ }
66
+ function disposeTree(root) {
67
+ const textures = /* @__PURE__ */ new Set();
68
+ const materials = /* @__PURE__ */ new Set();
69
+ root.traverse((object) => {
70
+ const mesh = object;
71
+ mesh.geometry?.dispose();
72
+ const list = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
73
+ for (const material of list) {
74
+ if (!material || typeof material !== "object") continue;
75
+ materials.add(material);
76
+ for (const value of Object.values(material)) {
77
+ if (value && typeof value === "object" && value.isTexture) {
78
+ textures.add(value);
79
+ }
80
+ }
81
+ }
82
+ });
83
+ for (const texture of textures) texture.dispose();
84
+ for (const material of materials) material.dispose();
85
+ }
86
+ function claimedIds(objects, finishes) {
87
+ const ids = /* @__PURE__ */ new Set();
88
+ for (const finish of finishes) {
89
+ for (const name of finish.show) {
90
+ const id = objects[name];
91
+ if (id !== void 0) ids.add(id);
92
+ }
93
+ }
94
+ return ids;
95
+ }
96
+ function unknownObject(name, finish, objects) {
97
+ const known = Object.keys(objects);
98
+ const sample = known.slice(0, 8).join(", ") + (known.length > 8 ? ", \u2026" : "");
99
+ return new Bakery3Error({
100
+ code: "InvalidRequest",
101
+ message: `Finish "${finish}" shows "${name}", but no object in the product has that name.`,
102
+ why: "The finish would render without the part it says it has, and the missing part would only be noticed in the catalog.",
103
+ fix: `Match \`show\` to the object names in the model${known.length ? ` (it has: ${sample})` : ""}.`,
104
+ path: name,
105
+ docs: "https://bakery3.com/docs/api"
106
+ });
107
+ }
108
+ var REFUSALS_KEPT = 50;
109
+ var KEY_LIMIT = 200;
110
+ var DOCS = "https://bakery3.com/docs/batches";
111
+ async function record(plan) {
112
+ validateCameras(plan.cameras);
113
+ const tally = {
114
+ ...emptyRecorded(),
115
+ estimated: 0,
116
+ maximum: 0
117
+ };
118
+ const sent = /* @__PURE__ */ new Set();
119
+ const seen = /* @__PURE__ */ new Set();
120
+ let index = 0;
121
+ for await (const variant of plan.variants) {
122
+ if (plan.signal?.aborted) throw plan.signal.reason ?? abortError();
123
+ const id = plan.idOf(variant, index++);
124
+ if (seen.has(id)) throw duplicateVariant(id);
125
+ seen.add(id);
126
+ const cleanup = await plan.apply?.(variant);
127
+ try {
128
+ const views = resolveCameras(plan.cameras, plan.scene, plan.output);
129
+ const capture = await plan.host.capture({
130
+ scene: plan.scene,
131
+ camera: views.find((view) => view.object)?.object ?? new PerspectiveCamera()
132
+ });
133
+ let upload;
134
+ if (!plan.dryRun && capture.assets.some((asset) => !sent.has(asset.hash))) {
135
+ upload = await plan.host.uploadAssets(capture);
136
+ for (const asset of capture.assets) sent.add(asset.hash);
137
+ }
138
+ const items = itemsOf(plan, variant, id, capture, views);
139
+ if (plan.keys) plan.keys.push(...items.map((item) => item.key));
140
+ let outcomes;
141
+ if (plan.dryRun) {
142
+ outcomes = await plan.batch.quote(items);
143
+ } else {
144
+ for (const item of items) plan.batch.add(item);
145
+ outcomes = await plan.batch.flush();
146
+ }
147
+ tally.variants++;
148
+ tally.images += items.length;
149
+ const metadata = new Map(items.map((item) => [item.key, item.metadata]));
150
+ for (const outcome of outcomes) {
151
+ if (outcome.error) {
152
+ tally.refused++;
153
+ if (tally.refusals.length < REFUSALS_KEPT) {
154
+ const { kind: _kind, ...ids } = who(metadata.get(outcome.key));
155
+ tally.refusals.push({
156
+ key: outcome.key,
157
+ ...ids,
158
+ error: outcome.error
159
+ });
160
+ }
161
+ } else if (outcome.adopted) {
162
+ tally.adopted++;
163
+ } else {
164
+ tally.estimated += outcome.estimatedCost;
165
+ tally.maximum += outcome.maximumCost;
166
+ }
167
+ }
168
+ plan.onVariant?.({
169
+ ...plan.batch.progress(),
170
+ variant: id,
171
+ variants: tally.variants,
172
+ images: tally.images,
173
+ ...upload ? { upload } : {}
174
+ });
175
+ } finally {
176
+ await cleanup?.();
177
+ }
178
+ }
179
+ tally.estimated = Math.round(tally.estimated * 1e4) / 1e4;
180
+ tally.maximum = Math.round(tally.maximum * 1e4) / 1e4;
181
+ return tally;
182
+ }
183
+ function itemsOf(plan, variant, id, capture, views) {
184
+ const finishes = plan.finishesOf?.(variant);
185
+ const items = [];
186
+ const push = (finish, hidden) => {
187
+ for (const view of views) {
188
+ const parts = [plan.prefix, id, finish?.id, view.id].filter(
189
+ (part) => Boolean(part)
190
+ );
191
+ const key = parts.join("/") + (plan.version ? `@${plan.version}` : "");
192
+ if (key.length > KEY_LIMIT) throw keyTooLong(key);
193
+ const manifest = hidden ? variantManifest(capture.manifest, view.spec, hidden) : { ...capture.manifest, camera: view.spec };
194
+ items.push({
195
+ key,
196
+ spec: plan.spec(manifest),
197
+ metadata: plan.metadataOf(variant, {
198
+ variant: id,
199
+ ...finish ? { finish: finish.id } : {},
200
+ camera: view.id
201
+ }),
202
+ ...plan.maxCostPerImage !== void 0 ? { maxCost: plan.maxCostPerImage } : {}
203
+ });
204
+ }
205
+ };
206
+ if (!finishes?.length) {
207
+ push(void 0, void 0);
208
+ return items;
209
+ }
210
+ const finishIds = /* @__PURE__ */ new Set();
211
+ const claimed = claimedIds(capture.objects, finishes);
212
+ const sceneHidden = (capture.manifest.runtime?.hiddenObjectIds ?? []).filter(
213
+ (hiddenId) => !claimed.has(hiddenId)
214
+ );
215
+ for (const finish of finishes) {
216
+ if (finishIds.has(finish.id)) throw duplicateFinish(id, finish.id);
217
+ finishIds.add(finish.id);
218
+ push(finish, [
219
+ ...sceneHidden,
220
+ ...variantHiddenIds(capture.objects, finishes, finish)
221
+ ]);
222
+ }
223
+ return items;
224
+ }
225
+ function idOfCamera(camera, index) {
226
+ if (typeof camera === "string") return camera;
227
+ if (isCameraObject(camera)) return camera.name || `camera-${index + 1}`;
228
+ return camera.id;
229
+ }
230
+ function isCameraObject(camera) {
231
+ return typeof camera === "object" && camera.isCamera === true;
232
+ }
233
+ function validateCameras(cameras) {
234
+ if (!cameras.length) {
235
+ throw new Bakery3Error({
236
+ code: "InvalidRequest",
237
+ message: "`cameras` is empty.",
238
+ why: "Every picture is taken from a camera, and the camera is part of its key.",
239
+ fix: "Name the cameras in your scene (`cameras: ['hero', 'front']`), pass Camera objects, or declare `{ id, position, target, fov }`. For the one live camera, leave `cameras` out.",
240
+ docs: "https://bakery3.com/docs/cameras"
241
+ });
242
+ }
243
+ const ids = /* @__PURE__ */ new Set();
244
+ cameras.forEach((camera, index) => {
245
+ const id = idOfCamera(camera, index);
246
+ if (ids.has(id)) {
247
+ throw new Bakery3Error({
248
+ code: "InvalidRequest",
249
+ message: `Two cameras are called "${id}".`,
250
+ why: "The camera id ends every picture\u2019s key, so two cameras under one id would be one picture.",
251
+ fix: "Give each camera its own `name` (or `id`).",
252
+ docs: "https://bakery3.com/docs/cameras"
253
+ });
254
+ }
255
+ ids.add(id);
256
+ });
257
+ }
258
+ function resolveCameras(cameras, scene, output) {
259
+ return cameras.map((camera, index) => {
260
+ const id = idOfCamera(camera, index);
261
+ if (typeof camera === "string") {
262
+ const object = findCamera(scene, camera);
263
+ return { id, spec: captureCamera(object), object };
264
+ }
265
+ if (isCameraObject(camera))
266
+ return {
267
+ id,
268
+ spec: { ...captureCamera(camera), name: id },
269
+ object: camera
270
+ };
271
+ return { id, spec: cameraSpec(camera, output) };
272
+ });
273
+ }
274
+ function findCamera(scene, name) {
275
+ let found;
276
+ const cameras = [];
277
+ scene.traverse((object) => {
278
+ if (!object.isCamera) return;
279
+ if (object.name) cameras.push(object.name);
280
+ if (!found && object.name === name) found = object;
281
+ });
282
+ if (found) return found;
283
+ throw new Bakery3Error({
284
+ code: "InvalidRequest",
285
+ message: `No camera named "${name}" in the scene.`,
286
+ why: "A camera given by name is looked up in the scene for every variant, so a rig the variant moves is read where it stands.",
287
+ fix: cameras.length ? `The scene's cameras are: ${cameras.slice(0, 8).join(", ")}${cameras.length > 8 ? ", \u2026" : ""}. Match one, or pass the Camera object itself.` : "The scene has no named cameras. Add the camera to the scene and set its `name`, pass the Camera object, or declare `{ id, position, target, fov }`.",
288
+ path: name,
289
+ docs: "https://bakery3.com/docs/cameras"
290
+ });
291
+ }
292
+ function defaultVariantId(variant, index) {
293
+ if (typeof variant === "string" && variant) return variant;
294
+ if (variant && typeof variant === "object") {
295
+ for (const field of ["id", "sku", "key"]) {
296
+ const value = variant[field];
297
+ if (typeof value === "string" && value) return value;
298
+ if (typeof value === "number") return String(value);
299
+ }
300
+ }
301
+ throw new Bakery3Error({
302
+ code: "InvalidRequest",
303
+ message: `Variant ${index + 1} has no id.`,
304
+ why: "The id starts every picture\u2019s key. It is what lets a re-run adopt the pictures that already rendered, and what a failure is reported under. A position in a list is not stable enough for either.",
305
+ fix: "Give each variant an `id` (or `sku`), or pass `id: (variant) => \u2026`.",
306
+ docs: DOCS
307
+ });
308
+ }
309
+ function duplicateVariant(id) {
310
+ return new Bakery3Error({
311
+ code: "InvalidRequest",
312
+ message: `Two variants are called "${id}".`,
313
+ why: "The second would be adopted as the first: same key, same picture, and the variant it was meant to show never rendered.",
314
+ fix: "Make the ids unique. When two variants share a SKU, put what differs in the id: `${sku}-${color}`.",
315
+ path: id,
316
+ docs: DOCS
317
+ });
318
+ }
319
+ function duplicateFinish(variant, finish) {
320
+ return new Bakery3Error({
321
+ code: "InvalidRequest",
322
+ message: `Finish "${finish}" of "${variant}" is declared twice.`,
323
+ why: "Finish ids are part of the item key.",
324
+ fix: "Make them unique per variant.",
325
+ path: `${variant}/${finish}`,
326
+ docs: DOCS
327
+ });
328
+ }
329
+ function keyTooLong(key) {
330
+ return new Bakery3Error({
331
+ code: "InvalidRequest",
332
+ message: `The key "${key.slice(0, 60)}\u2026" is ${key.length} characters; a key holds at most ${KEY_LIMIT}.`,
333
+ why: 'A picture\u2019s key is its variant, finish and camera ids joined with "/".',
334
+ fix: "Use shorter ids: a SKU rather than a product title.",
335
+ docs: DOCS
336
+ });
337
+ }
338
+ function abortError() {
339
+ const error = new Error("The recording was aborted.");
340
+ error.name = "AbortError";
341
+ return error;
342
+ }
343
+
344
+ export { defaultVariantId, disposeTree, record };
345
+ //# sourceMappingURL=chunk-WBIAFIHV.js.map
346
+ //# sourceMappingURL=chunk-WBIAFIHV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/variant.ts","../src/renderSet.ts"],"names":[],"mappings":"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","file":"chunk-WBIAFIHV.js","sourcesContent":["/**\n * One capture, many pictures: the pure pieces.\n *\n * A capture is one GLB with every finish inside it and a few KB of manifest.\n * A picture of it is that manifest with another camera and another hidden\n * list, and nothing else. `render({ cameras })`, `renderVariants()` and the\n * catalog recorder all build their items from these, and they are exported\n * from `@oeave/bakery3/catalog` so a recorder that writes `manifests.jsonl`\n * instead of submitting builds exactly the same ones.\n */\n\nimport { Matrix4, Quaternion, Vector3, type Object3D } from 'three';\nimport type {\n CameraSpec,\n OutputSpec,\n Quat,\n RuntimeState,\n SceneManifest,\n Vec3,\n} from './protocol/index.js';\nimport { Bakery3Error } from './errors.js';\n\nexport type CatalogCamera = {\n id: string;\n position: Vec3;\n target: Vec3;\n /** Vertical field of view in degrees, as `PerspectiveCamera.fov`. */\n fov: number;\n};\n\nexport type CatalogFinish = {\n id: string;\n /** Object names this finish shows. Anything another finish claims and this\n * one does not is hidden; anything no finish claims stays as it is. */\n show: string[];\n};\n\n/**\n * Which object ids this finish hides: everything any finish claims that this\n * finish does not show. What no finish claims is not touched: the frame a\n * chair's finishes share stays visible, and so does whatever the developer\n * had hidden themselves.\n */\nexport function variantHiddenIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n finish: CatalogFinish,\n): string[] {\n const shown = new Set(finish.show);\n const hidden = new Set<string>();\n for (const other of finishes) {\n for (const name of other.show) {\n if (shown.has(name)) continue;\n const id = objects[name];\n if (id === undefined) throw unknownObject(name, other.id, objects);\n hidden.add(id);\n }\n }\n // A shown name that names nothing is the same typo from the other side:\n // the finish would render silently without the part it says it has.\n for (const name of finish.show) {\n if (objects[name] === undefined)\n throw unknownObject(name, finish.id, objects);\n }\n return [...hidden];\n}\n\n/**\n * A `CameraSpec` for a catalog camera: perspective, at `position`, looking\n * at `target` with +Y up. `output` sets the aspect so the render frames what\n * the declaration meant, not what some viewport happened to be.\n */\nexport function cameraSpec(\n camera: CatalogCamera,\n output?: Pick<OutputSpec, 'width' | 'height'>,\n): CameraSpec {\n const position = new Vector3(...camera.position);\n const target = new Vector3(...camera.target);\n const distance = position.distanceTo(target);\n if (!(distance > 0)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Camera \"${camera.id}\" has its position on its target.`,\n why: 'A camera needs a direction to look in, and position === target gives it none.',\n fix: 'Move `position` or `target` so they differ.',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const up =\n Math.abs(position.x - target.x) < 1e-9 &&\n Math.abs(position.z - target.z) < 1e-9\n ? // Straight down (or up): +Y up is degenerate, so face -Z as three does.\n new Vector3(0, 0, -1)\n : new Vector3(0, 1, 0);\n // three's own convention: a camera looks down its local -Z.\n const rotation = new Matrix4().lookAt(position, target, up);\n const q = new Quaternion().setFromRotationMatrix(rotation);\n const quaternion: Quat = [q.x, q.y, q.z, q.w];\n\n return {\n type: 'perspective',\n fov: camera.fov,\n ...(output ? { aspect: output.width / output.height } : {}),\n // Clip planes scaled to the shot: a watch a hand-span away and a sofa\n // across a room both keep their subject well inside them.\n near: Math.max(0.01, distance / 100),\n far: Math.max(100, distance * 100),\n position: [position.x, position.y, position.z],\n quaternion,\n name: camera.id,\n };\n}\n\n/** One capture, one variant: a new camera and a new hidden list, nothing else. */\nexport function variantManifest(\n manifest: SceneManifest,\n camera: CameraSpec,\n hiddenObjectIds: string[],\n): SceneManifest {\n const runtime: RuntimeState = { ...manifest.runtime };\n if (hiddenObjectIds.length) runtime.hiddenObjectIds = hiddenObjectIds;\n else delete runtime.hiddenObjectIds;\n return {\n ...manifest,\n camera,\n ...(Object.keys(runtime).length ? { runtime } : {}),\n };\n}\n\n/**\n * Release everything a product holds on the GPU: geometries, materials and\n * every texture a material references. The tree is detached from the scene\n * by the caller; this is what lets the browser actually free it.\n */\nexport function disposeTree(root: Object3D): void {\n const textures = new Set<{ dispose(): void }>();\n const materials = new Set<{ dispose(): void }>();\n root.traverse((object) => {\n const mesh = object as Object3D & {\n geometry?: { dispose(): void };\n material?: unknown;\n };\n mesh.geometry?.dispose();\n const list = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n for (const material of list) {\n if (!material || typeof material !== 'object') continue;\n materials.add(material as { dispose(): void });\n for (const value of Object.values(material as Record<string, unknown>)) {\n if (\n value &&\n typeof value === 'object' &&\n (value as { isTexture?: boolean }).isTexture\n ) {\n textures.add(value as { dispose(): void });\n }\n }\n }\n });\n for (const texture of textures) texture.dispose();\n for (const material of materials) material.dispose();\n}\n\n/** Every id any finish of the product claims. */\nexport function claimedIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n): Set<string> {\n const ids = new Set<string>();\n for (const finish of finishes) {\n for (const name of finish.show) {\n const id = objects[name];\n if (id !== undefined) ids.add(id);\n }\n }\n return ids;\n}\n\nfunction unknownObject(\n name: string,\n finish: string,\n objects: Record<string, string>,\n): Bakery3Error {\n const known = Object.keys(objects);\n const sample = known.slice(0, 8).join(', ') + (known.length > 8 ? ', …' : '');\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" shows \"${name}\", but no object in the product has that name.`,\n why: 'The finish would render without the part it says it has, and the missing part would only be noticed in the catalog.',\n fix: `Match \\`show\\` to the object names in the model${known.length ? ` (it has: ${sample})` : ''}.`,\n path: name,\n docs: 'https://bakery3.com/docs/api',\n });\n}\n","/**\n * Many pictures from one call.\n *\n * // every camera of the scene you have\n * const shots = await bakery3.render({ cameras: ['hero', 'front', 'detail'] });\n * const images = await shots.result();\n *\n * // every variant, from every camera\n * const run = await bakery3.renderVariants({\n * key: 'fall-2026',\n * variants: products,\n * apply: (product) => configurator.show(product),\n * cameras: ['hero', 'front', 'detail'],\n * maxCost: 600,\n * });\n * await run.wait();\n * for await (const image of run.images()) save(image.variant, image.camera, image.url);\n *\n * Both record into a batch and hand back a `RenderSet`, so a set of three\n * and a catalog of six thousand are the same object with the same\n * guarantees: one ceiling for all of it, one poll for all of it, and a\n * re-run that adopts what already rendered.\n *\n * What this file is careful about is the customer's machine. A catalog is\n * recorded in a browser tab, and a tab that holds two hundred products, or\n * two hundred exported GLBs, or six thousand result records, is a tab that\n * dies at product 140 with nothing to show for it. So:\n *\n * - One variant resident. A variant is applied, captured, uploaded, turned\n * into items, flushed and let go of before the next is touched. The\n * exported GLB is the largest thing a run holds, and it holds one.\n * - The list need not exist. `variants` is any iterable or async iterable: a\n * generator, a database cursor, a paged fetch. It is pulled one at a time\n * and never collected.\n * - One capture, many pictures. Cameras and finishes multiply items, not\n * exports: each is the same few-KB manifest with another camera and\n * another hidden list (`variant.ts`).\n * - Nothing uploads twice. Assets are content addressed; a variant that\n * changed no geometry uploads nothing, and one whose hashes this run has\n * already sent does not even ask.\n * - Results are walked, not held. `images()` and `failures()` read the\n * ledger a page at a time by cursor. `result()` collects, and says so: it\n * is for a handful of cameras.\n */\n\nimport { PerspectiveCamera, type Camera, type Scene } from 'three';\nimport type {\n BatchItemOutcome,\n BatchItemRequest,\n CameraSpec,\n OutputSpec,\n RenderError,\n RenderResult,\n RenderSpec,\n SceneManifest,\n} from './protocol/index.js';\nimport type { Batch, BatchProgress, WaitOptions } from './batch.js';\nimport type { Capture } from './index.js';\nimport type { UploadProgress } from './upload.js';\nimport { captureCamera } from './capture/camera.js';\nimport { Bakery3Error } from './errors.js';\nimport { emptyRecorded, who, type Recorded } from './set.js';\nimport {\n cameraSpec,\n claimedIds,\n variantHiddenIds,\n variantManifest,\n type CatalogCamera,\n type CatalogFinish,\n} from './variant.js';\n\n// ---------------------------------------------------------------------------\n// What the developer types\n// ---------------------------------------------------------------------------\n\n/**\n * A camera to photograph from:\n *\n * - a **name**: a camera in the scene with that `name`. Looked up again for\n * every variant, after `apply`, so a camera that ships inside a product's\n * own GLB, or rides a rig the variant moves, is read where it then stands.\n * - a **Camera**: read as it stands at capture. Its `name` is its id.\n * - a **declaration**: `{ id, position, target, fov }`, for a shot no camera\n * object exists for. Its aspect is the output's.\n */\nexport type ViewCamera = string | Camera | CatalogCamera;\n\n/** What `apply` may hand back: how to undo it. Called before the next variant, and when a run stops. */\nexport type VariantCleanup = () => void | Promise<void>;\n\nexport type VariantProgress = BatchProgress & {\n /** The variant just recorded. */\n variant: string;\n /** Variants recorded so far, this one included. */\n variants: number;\n /** Pictures recorded so far: variants × finishes × cameras. */\n images: number;\n /** What this variant's capture uploaded. Usually nothing after the first. */\n upload?: UploadProgress;\n};\n\nexport { RenderSet, RenderSetRefused } from './set.js';\nexport type {\n Recorded,\n RenderSetFailure,\n RenderSetImage,\n RenderSetRefusal,\n VariantsQuote,\n} from './set.js';\n\nconst REFUSALS_KEPT = 50;\nconst KEY_LIMIT = 200;\nconst DOCS = 'https://bakery3.com/docs/batches';\n\n// ---------------------------------------------------------------------------\n// The recorder\n// ---------------------------------------------------------------------------\n\n/** The slice of the SDK a recording drives. `Bakery3` satisfies it; so does a fake. */\nexport type RecorderHost = {\n capture(options: { scene: Scene; camera: Camera }): Promise<Capture>;\n uploadAssets(\n capture: Pick<Capture, 'assets'>,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<UploadProgress>;\n};\n\nexport type Recording<V> = {\n host: RecorderHost;\n batch: Batch;\n scene: Scene;\n variants: Iterable<V> | AsyncIterable<V>;\n idOf: (variant: V, index: number) => string;\n apply?: (\n variant: V,\n ) => void | VariantCleanup | Promise<void | VariantCleanup>;\n finishesOf?: (variant: V) => CatalogFinish[] | undefined;\n cameras: ViewCamera[];\n output: OutputSpec;\n /** The manifest of one picture, wrapped in everything else its render needs. */\n spec: (manifest: SceneManifest) => RenderSpec;\n metadataOf: (\n variant: V,\n ids: { variant: string; finish?: string; camera: string },\n ) => Record<string, unknown>;\n /** Appended to every item key as `@version`. */\n version?: string;\n /** Prefixed to every item key, for a set whose variant ids are not unique in the project on their own. */\n prefix?: string;\n /** A ceiling for each picture, under the set's. */\n maxCostPerImage?: number;\n /** Price, admit nothing, upload nothing. */\n dryRun?: boolean;\n signal?: AbortSignal;\n onVariant?: (progress: VariantProgress) => void;\n /** Collect the item keys, in order. Only for a set small enough to want one. */\n keys?: string[];\n};\n\nexport type RecordingResult = Recorded & { estimated: number; maximum: number };\n\n/**\n * Walk the variants, one resident at a time.\n *\n * Every `await` in the loop is a point where the previous variant's capture\n * is already unreachable: it lives in a `const` inside the iteration, the\n * items built from it hold its manifest (a few KB) and never its blobs, and\n * the flush at the end of the iteration hands those to the server.\n */\nexport async function record<V>(plan: Recording<V>): Promise<RecordingResult> {\n validateCameras(plan.cameras);\n const tally: RecordingResult = {\n ...emptyRecorded(),\n estimated: 0,\n maximum: 0,\n };\n /** Asset hashes this run has already put in storage. Strings, not bytes. */\n const sent = new Set<string>();\n const seen = new Set<string>();\n let index = 0;\n\n for await (const variant of plan.variants) {\n if (plan.signal?.aborted) throw plan.signal.reason ?? abortError();\n\n const id = plan.idOf(variant, index++);\n if (seen.has(id)) throw duplicateVariant(id);\n seen.add(id);\n\n const cleanup = await plan.apply?.(variant);\n try {\n const views = resolveCameras(plan.cameras, plan.scene, plan.output);\n const capture = await plan.host.capture({\n scene: plan.scene,\n camera:\n views.find((view) => view.object)?.object ?? new PerspectiveCamera(),\n });\n\n let upload: UploadProgress | undefined;\n if (\n !plan.dryRun &&\n capture.assets.some((asset) => !sent.has(asset.hash))\n ) {\n upload = await plan.host.uploadAssets(capture);\n for (const asset of capture.assets) sent.add(asset.hash);\n }\n\n const items = itemsOf(plan, variant, id, capture, views);\n if (plan.keys) plan.keys.push(...items.map((item) => item.key));\n\n let outcomes: BatchItemOutcome[];\n if (plan.dryRun) {\n outcomes = await plan.batch.quote(items);\n } else {\n for (const item of items) plan.batch.add(item);\n // A refusal here is honest and touches nothing already admitted: the\n // page stays in the batch's buffer and the error says what to do.\n outcomes = await plan.batch.flush();\n }\n\n tally.variants++;\n tally.images += items.length;\n const metadata = new Map(items.map((item) => [item.key, item.metadata]));\n for (const outcome of outcomes) {\n if (outcome.error) {\n tally.refused++;\n if (tally.refusals.length < REFUSALS_KEPT) {\n // Named the way the set's images are: by variant, finish and\n // camera, read from the same metadata.\n const { kind: _kind, ...ids } = who(metadata.get(outcome.key));\n tally.refusals.push({\n key: outcome.key,\n ...ids,\n error: outcome.error,\n });\n }\n } else if (outcome.adopted) {\n tally.adopted++;\n } else {\n tally.estimated += outcome.estimatedCost;\n tally.maximum += outcome.maximumCost;\n }\n }\n\n plan.onVariant?.({\n ...plan.batch.progress(),\n variant: id,\n variants: tally.variants,\n images: tally.images,\n ...(upload ? { upload } : {}),\n });\n } finally {\n // Even when the capture or the flush threw: the caller's scene must not\n // be left holding a variant it did not ask to keep.\n await cleanup?.();\n }\n }\n\n // Sums of cents, kept as cents: 0.1 + 0.2 is not a number to show anybody.\n tally.estimated = Math.round(tally.estimated * 1e4) / 1e4;\n tally.maximum = Math.round(tally.maximum * 1e4) / 1e4;\n return tally;\n}\n\ntype ResolvedView = { id: string; spec: CameraSpec; object?: Camera };\n\nfunction itemsOf<V>(\n plan: Recording<V>,\n variant: V,\n id: string,\n capture: Capture,\n views: ResolvedView[],\n): BatchItemRequest[] {\n const finishes = plan.finishesOf?.(variant);\n const items: BatchItemRequest[] = [];\n\n const push = (\n finish: CatalogFinish | undefined,\n hidden: string[] | undefined,\n ) => {\n for (const view of views) {\n const parts = [plan.prefix, id, finish?.id, view.id].filter(\n (part): part is string => Boolean(part),\n );\n const key = parts.join('/') + (plan.version ? `@${plan.version}` : '');\n if (key.length > KEY_LIMIT) throw keyTooLong(key);\n const manifest = hidden\n ? variantManifest(capture.manifest, view.spec, hidden)\n : { ...capture.manifest, camera: view.spec };\n items.push({\n key,\n spec: plan.spec(manifest),\n metadata: plan.metadataOf(variant, {\n variant: id,\n ...(finish ? { finish: finish.id } : {}),\n camera: view.id,\n }),\n ...(plan.maxCostPerImage !== undefined\n ? { maxCost: plan.maxCostPerImage }\n : {}),\n });\n }\n };\n\n if (!finishes?.length) {\n push(undefined, undefined);\n return items;\n }\n\n const finishIds = new Set<string>();\n // What the scene itself hides stays hidden, unless a finish claims it:\n // a loader that starts on oak has not decided anything about walnut.\n const claimed = claimedIds(capture.objects, finishes);\n const sceneHidden = (capture.manifest.runtime?.hiddenObjectIds ?? []).filter(\n (hiddenId) => !claimed.has(hiddenId),\n );\n for (const finish of finishes) {\n if (finishIds.has(finish.id)) throw duplicateFinish(id, finish.id);\n finishIds.add(finish.id);\n push(finish, [\n ...sceneHidden,\n ...variantHiddenIds(capture.objects, finishes, finish),\n ]);\n }\n return items;\n}\n\n// ---------------------------------------------------------------------------\n// Cameras\n// ---------------------------------------------------------------------------\n\nfunction idOfCamera(camera: ViewCamera, index: number): string {\n if (typeof camera === 'string') return camera;\n if (isCameraObject(camera)) return camera.name || `camera-${index + 1}`;\n return camera.id;\n}\n\nfunction isCameraObject(camera: ViewCamera): camera is Camera {\n return typeof camera === 'object' && (camera as Camera).isCamera === true;\n}\n\nfunction validateCameras(cameras: ViewCamera[]): void {\n if (!cameras.length) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: '`cameras` is empty.',\n why: 'Every picture is taken from a camera, and the camera is part of its key.',\n fix: \"Name the cameras in your scene (`cameras: ['hero', 'front']`), pass Camera objects, or declare `{ id, position, target, fov }`. For the one live camera, leave `cameras` out.\",\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const ids = new Set<string>();\n cameras.forEach((camera, index) => {\n const id = idOfCamera(camera, index);\n if (ids.has(id)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two cameras are called \"${id}\".`,\n why: 'The camera id ends every picture’s key, so two cameras under one id would be one picture.',\n fix: 'Give each camera its own `name` (or `id`).',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n ids.add(id);\n });\n}\n\n/** Read every camera as it stands now: after `apply`, before the capture. */\nfunction resolveCameras(\n cameras: ViewCamera[],\n scene: Scene,\n output: OutputSpec,\n): ResolvedView[] {\n return cameras.map((camera, index) => {\n const id = idOfCamera(camera, index);\n if (typeof camera === 'string') {\n const object = findCamera(scene, camera);\n return { id, spec: captureCamera(object), object };\n }\n if (isCameraObject(camera))\n return {\n id,\n spec: { ...captureCamera(camera), name: id },\n object: camera,\n };\n return { id, spec: cameraSpec(camera, output) };\n });\n}\n\nfunction findCamera(scene: Scene, name: string): Camera {\n let found: Camera | undefined;\n const cameras: string[] = [];\n scene.traverse((object) => {\n if (!(object as Camera).isCamera) return;\n if (object.name) cameras.push(object.name);\n if (!found && object.name === name) found = object as Camera;\n });\n if (found) return found;\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `No camera named \"${name}\" in the scene.`,\n why: 'A camera given by name is looked up in the scene for every variant, so a rig the variant moves is read where it stands.',\n fix: cameras.length\n ? `The scene's cameras are: ${cameras.slice(0, 8).join(', ')}${cameras.length > 8 ? ', …' : ''}. Match one, or pass the Camera object itself.`\n : 'The scene has no named cameras. Add the camera to the scene and set its `name`, pass the Camera object, or declare `{ id, position, target, fov }`.',\n path: name,\n docs: 'https://bakery3.com/docs/cameras',\n });\n}\n\n// ---------------------------------------------------------------------------\n\n/** `variant.id`, `.sku` or `.key`, or the variant itself when it is a string. */\nexport function defaultVariantId(variant: unknown, index: number): string {\n if (typeof variant === 'string' && variant) return variant;\n if (variant && typeof variant === 'object') {\n for (const field of ['id', 'sku', 'key'] as const) {\n const value = (variant as Record<string, unknown>)[field];\n if (typeof value === 'string' && value) return value;\n if (typeof value === 'number') return String(value);\n }\n }\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Variant ${index + 1} has no id.`,\n why: 'The id starts every picture’s key. It is what lets a re-run adopt the pictures that already rendered, and what a failure is reported under. A position in a list is not stable enough for either.',\n fix: 'Give each variant an `id` (or `sku`), or pass `id: (variant) => …`.',\n docs: DOCS,\n });\n}\n\nfunction duplicateVariant(id: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two variants are called \"${id}\".`,\n why: 'The second would be adopted as the first: same key, same picture, and the variant it was meant to show never rendered.',\n fix: 'Make the ids unique. When two variants share a SKU, put what differs in the id: `${sku}-${color}`.',\n path: id,\n docs: DOCS,\n });\n}\n\nfunction duplicateFinish(variant: string, finish: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" of \"${variant}\" is declared twice.`,\n why: 'Finish ids are part of the item key.',\n fix: 'Make them unique per variant.',\n path: `${variant}/${finish}`,\n docs: DOCS,\n });\n}\n\nfunction keyTooLong(key: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `The key \"${key.slice(0, 60)}…\" is ${key.length} characters; a key holds at most ${KEY_LIMIT}.`,\n why: 'A picture’s key is its variant, finish and camera ids joined with \"/\".',\n fix: 'Use shorter ids: a SKU rather than a product title.',\n docs: DOCS,\n });\n}\n\nfunction abortError(): Error {\n const error = new Error('The recording was aborted.');\n error.name = 'AbortError';\n return error;\n}\n"]}
@@ -0,0 +1,156 @@
1
+ import { Bakery3Error } from './chunk-3G5QL4F4.js';
2
+
3
+ // src/set.ts
4
+ var RenderSet = class {
5
+ /** The batch underneath: pause, resume, retry, the export. */
6
+ batch;
7
+ id;
8
+ key;
9
+ /** What the call that made this set recorded. Empty for a set fetched by id. */
10
+ recorded;
11
+ /** Item key → position, for a set small enough to have asked for an order. */
12
+ order;
13
+ constructor(batch, recorded = emptyRecorded()) {
14
+ this.batch = batch;
15
+ this.id = batch.id;
16
+ this.key = batch.key;
17
+ this.recorded = recorded;
18
+ }
19
+ /** Counts and cost from the latest record. No request. */
20
+ progress() {
21
+ return this.batch.progress();
22
+ }
23
+ /**
24
+ * Until every picture is terminal. One long-poll on the set, however many
25
+ * pictures it has. Resolves with the final counts: pictures that failed
26
+ * are in `failures()`, they do not reject the wait.
27
+ */
28
+ async wait(options = {}) {
29
+ await this.batch.wait(options);
30
+ return this.batch.progress();
31
+ }
32
+ /**
33
+ * Every finished picture, a page at a time. Safe on a set of any size,
34
+ * and safe to call before the set is done: it walks what has completed
35
+ * so far.
36
+ */
37
+ async *images() {
38
+ for await (const item of this.batch.items({ state: "completed" })) {
39
+ if (!item.result) continue;
40
+ yield {
41
+ key: item.key,
42
+ id: item.id,
43
+ ...who(item.metadata),
44
+ url: item.result.url,
45
+ width: item.result.width,
46
+ height: item.result.height,
47
+ cost: item.result.cost,
48
+ ...item.metadata ? { metadata: item.metadata } : {},
49
+ result: item.result
50
+ };
51
+ }
52
+ }
53
+ /**
54
+ * Every picture that did not render, with the reason and the fix.
55
+ * `error.retryable` tells the two kinds apart: false is a refusal, the
56
+ * scene's fault and the same next time; true ran out of attempts on a
57
+ * fault of ours, and `set.batch.retry()` queues it again.
58
+ */
59
+ async *failures() {
60
+ for await (const item of this.batch.items({ state: "failed" })) {
61
+ yield {
62
+ key: item.key,
63
+ id: item.id,
64
+ ...who(item.metadata),
65
+ error: item.error ? Bakery3Error.from(item.error) : new Bakery3Error({
66
+ code: "WorkerFailure",
67
+ message: "Ended with no result and no reason.",
68
+ retryable: true
69
+ }),
70
+ ...item.metadata ? { metadata: item.metadata } : {}
71
+ };
72
+ }
73
+ }
74
+ /**
75
+ * Wait, then every picture in one array, in camera order for a
76
+ * `render({ cameras })`: one picture per camera, or a rejection. Rejects
77
+ * with `RenderSetRefused` when a picture was turned down at submission
78
+ * (it has no render, and the array would be one short with every later
79
+ * picture in the wrong place), and otherwise with the first failure, as
80
+ * `job.result()` does. The pictures that did render are still there:
81
+ * walk `images()`, each named by its `camera`.
82
+ *
83
+ * It collects, so it is for a handful of pictures. For a catalog, walk
84
+ * `images()`.
85
+ */
86
+ async result(options = {}) {
87
+ await this.wait(options);
88
+ if (this.recorded.refused > 0) throw new RenderSetRefused(this.recorded);
89
+ for await (const failure of this.failures()) throw failure.error;
90
+ const images = [];
91
+ for await (const image of this.images()) images.push(image);
92
+ const order = this.order;
93
+ if (order)
94
+ images.sort((a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0));
95
+ return images;
96
+ }
97
+ /** Cancel every picture that has not finished. What completed, completed. */
98
+ async cancel() {
99
+ await this.batch.cancel();
100
+ }
101
+ /** @internal */
102
+ ordered(keys) {
103
+ this.order = new Map(keys.map((key, index) => [key, index]));
104
+ return this;
105
+ }
106
+ };
107
+ var RenderSetRefused = class extends Bakery3Error {
108
+ /** Which pictures, and why: the first fifty when a run refused more. */
109
+ refusals;
110
+ /** How many were refused in all. */
111
+ refused;
112
+ /** How many pictures were asked for. */
113
+ requested;
114
+ constructor(recorded) {
115
+ const first = recorded.refusals[0];
116
+ const names = recorded.refusals.slice(0, 3).map(describeRefusal);
117
+ const more = recorded.refused - names.length;
118
+ super({
119
+ code: first?.error.code ?? "InvalidRequest",
120
+ message: `${recorded.refused} of ${recorded.images} pictures ${recorded.refused === 1 ? "was" : "were"} refused: ${names.join("; ")}${more > 0 ? `; and ${more} more` : ""}.`,
121
+ why: "A picture refused when it was submitted never renders. An array of the rest would be shorter than the list you asked for, with every later picture in the wrong place, so result() gives none.",
122
+ fix: "Read error.refusals for each refused picture and its reason. The pictures that were accepted still render: set.images() walks them, each named by its camera.",
123
+ docs: "https://bakery3.com/docs/batches#errors",
124
+ retryable: false
125
+ });
126
+ this.name = "RenderSetRefused";
127
+ this.refusals = recorded.refusals.map(({ error, ...who2 }) => ({
128
+ ...who2,
129
+ error: Bakery3Error.from(error)
130
+ }));
131
+ this.refused = recorded.refused;
132
+ this.requested = recorded.images;
133
+ }
134
+ };
135
+ function describeRefusal(refusal) {
136
+ const name = [refusal.variant, refusal.finish, refusal.camera, refusal.shot].filter(Boolean).join(" / ") || refusal.key;
137
+ return `"${name}" (${refusal.error.message.replace(/\.$/, "")})`;
138
+ }
139
+ function who(metadata) {
140
+ const out = {};
141
+ if (typeof metadata?.variant === "string") out.variant = metadata.variant;
142
+ else if (typeof metadata?.sku === "string") out.variant = metadata.sku;
143
+ if (typeof metadata?.finish === "string") out.finish = metadata.finish;
144
+ if (typeof metadata?.camera === "string") out.camera = metadata.camera;
145
+ if (typeof metadata?.shot === "string") out.shot = metadata.shot;
146
+ if (metadata?.kind === "video" || metadata?.kind === "image")
147
+ out.kind = metadata.kind;
148
+ return out;
149
+ }
150
+ function emptyRecorded() {
151
+ return { variants: 0, images: 0, adopted: 0, refused: 0, refusals: [] };
152
+ }
153
+
154
+ export { RenderSet, RenderSetRefused, emptyRecorded, who };
155
+ //# sourceMappingURL=chunk-ZEBVAIJJ.js.map
156
+ //# sourceMappingURL=chunk-ZEBVAIJJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/set.ts"],"names":["who"],"mappings":"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","file":"chunk-ZEBVAIJJ.js","sourcesContent":["/**\n * A set of pictures: what every many-pictures call hands back.\n *\n * `render({ cameras })` and `renderVariants()` in the browser, and\n * `renderBatch()` from Node, all record into a batch and return one of\n * these, so a set of three and a catalog of six thousand are the same object\n * with the same guarantees: one ceiling for all of it, one poll for all of\n * it, results walked a page at a time and never held.\n *\n * No three.js here: the server entry imports this file.\n */\n\nimport type { RenderError, RenderResult } from './protocol/index.js';\nimport type { Batch, BatchProgress, WaitOptions } from './batch.js';\nimport { Bakery3Error } from './errors.js';\n\n/** One finished picture of a set. */\nexport type RenderSetImage = {\n /** The item's key: `variant/finish/camera`, as recorded. */\n key: string;\n /** The render's id. */\n id: string;\n variant?: string;\n finish?: string;\n camera?: string;\n /** The shot this is, for a set recorded with shots. */\n shot?: string;\n /** `'video'` when the url is a clip. */\n kind?: 'image' | 'video';\n /** Signed and expiring. Copy the bytes; do not serve from it. */\n url: string;\n width: number;\n height: number;\n /** What this picture cost. Nothing, when it was adopted from an earlier run. */\n cost: number;\n metadata?: Record<string, unknown>;\n result: RenderResult;\n};\n\n/** One picture that did not render, and why. */\nexport type RenderSetFailure = {\n key: string;\n id: string;\n variant?: string;\n finish?: string;\n camera?: string;\n shot?: string;\n error: Bakery3Error;\n metadata?: Record<string, unknown>;\n};\n\n/** One picture turned down when it was submitted: it has no render and never will. */\nexport type RenderSetRefusal = {\n key: string;\n variant?: string;\n finish?: string;\n camera?: string;\n shot?: string;\n error: RenderError;\n};\n\n/** What a recording did, in numbers small enough to keep. */\nexport type Recorded = {\n variants: number;\n images: number;\n /** Already rendered under their keys in an earlier run. Cost nothing. */\n adopted: number;\n /** Turned down alone at admission (a spec that does not parse, an item over its own ceiling). */\n refused: number;\n /** The first of those, with their reasons. Capped, because a broken pipeline refuses every item. */\n refusals: RenderSetRefusal[];\n};\n\n/** What a whole run would cost, from `quoteVariants()`. Nothing was admitted. */\nexport type VariantsQuote = Recorded & {\n /** Σ estimate over the pictures that would render. */\n estimated: number;\n /** Σ maximum: what admission would reserve, and the number to set `maxCost` from. */\n maximum: number;\n};\n\nexport class RenderSet {\n /** The batch underneath: pause, resume, retry, the export. */\n readonly batch: Batch;\n readonly id: string;\n readonly key: string;\n /** What the call that made this set recorded. Empty for a set fetched by id. */\n readonly recorded: Recorded;\n /** Item key → position, for a set small enough to have asked for an order. */\n private order?: Map<string, number>;\n\n constructor(batch: Batch, recorded: Recorded = emptyRecorded()) {\n this.batch = batch;\n this.id = batch.id;\n this.key = batch.key;\n this.recorded = recorded;\n }\n\n /** Counts and cost from the latest record. No request. */\n progress(): BatchProgress {\n return this.batch.progress();\n }\n\n /**\n * Until every picture is terminal. One long-poll on the set, however many\n * pictures it has. Resolves with the final counts: pictures that failed\n * are in `failures()`, they do not reject the wait.\n */\n async wait(options: WaitOptions = {}): Promise<BatchProgress> {\n await this.batch.wait(options);\n return this.batch.progress();\n }\n\n /**\n * Every finished picture, a page at a time. Safe on a set of any size,\n * and safe to call before the set is done: it walks what has completed\n * so far.\n */\n async *images(): AsyncIterable<RenderSetImage> {\n for await (const item of this.batch.items({ state: 'completed' })) {\n if (!item.result) continue;\n yield {\n key: item.key,\n id: item.id,\n ...who(item.metadata),\n url: item.result.url,\n width: item.result.width,\n height: item.result.height,\n cost: item.result.cost,\n ...(item.metadata ? { metadata: item.metadata } : {}),\n result: item.result,\n };\n }\n }\n\n /**\n * Every picture that did not render, with the reason and the fix.\n * `error.retryable` tells the two kinds apart: false is a refusal, the\n * scene's fault and the same next time; true ran out of attempts on a\n * fault of ours, and `set.batch.retry()` queues it again.\n */\n async *failures(): AsyncIterable<RenderSetFailure> {\n for await (const item of this.batch.items({ state: 'failed' })) {\n yield {\n key: item.key,\n id: item.id,\n ...who(item.metadata),\n error: item.error\n ? Bakery3Error.from(item.error)\n : new Bakery3Error({\n code: 'WorkerFailure',\n message: 'Ended with no result and no reason.',\n retryable: true,\n }),\n ...(item.metadata ? { metadata: item.metadata } : {}),\n };\n }\n }\n\n /**\n * Wait, then every picture in one array, in camera order for a\n * `render({ cameras })`: one picture per camera, or a rejection. Rejects\n * with `RenderSetRefused` when a picture was turned down at submission\n * (it has no render, and the array would be one short with every later\n * picture in the wrong place), and otherwise with the first failure, as\n * `job.result()` does. The pictures that did render are still there:\n * walk `images()`, each named by its `camera`.\n *\n * It collects, so it is for a handful of pictures. For a catalog, walk\n * `images()`.\n */\n async result(options: WaitOptions = {}): Promise<RenderSetImage[]> {\n await this.wait(options);\n if (this.recorded.refused > 0) throw new RenderSetRefused(this.recorded);\n for await (const failure of this.failures()) throw failure.error;\n const images: RenderSetImage[] = [];\n for await (const image of this.images()) images.push(image);\n const order = this.order;\n if (order)\n images.sort((a, b) => (order.get(a.key) ?? 0) - (order.get(b.key) ?? 0));\n return images;\n }\n\n /** Cancel every picture that has not finished. What completed, completed. */\n async cancel(): Promise<void> {\n await this.batch.cancel();\n }\n\n /** @internal */\n ordered(keys: string[]): this {\n this.order = new Map(keys.map((key, index) => [key, index]));\n return this;\n }\n}\n\n/**\n * Pictures of a set were turned down when they were submitted, so the set\n * cannot give one picture per camera (or per variant) in order.\n *\n * `refusals` says which and why. Its `code` is the first refusal's. The\n * pictures that were accepted render as usual: `set.images()` walks them.\n */\nexport class RenderSetRefused extends Bakery3Error {\n /** Which pictures, and why: the first fifty when a run refused more. */\n readonly refusals: Array<\n Omit<RenderSetRefusal, 'error'> & { error: Bakery3Error }\n >;\n /** How many were refused in all. */\n readonly refused: number;\n /** How many pictures were asked for. */\n readonly requested: number;\n\n constructor(recorded: Recorded) {\n const first = recorded.refusals[0];\n const names = recorded.refusals.slice(0, 3).map(describeRefusal);\n const more = recorded.refused - names.length;\n super({\n code: first?.error.code ?? 'InvalidRequest',\n message: `${recorded.refused} of ${recorded.images} pictures ${recorded.refused === 1 ? 'was' : 'were'} refused: ${names.join('; ')}${more > 0 ? `; and ${more} more` : ''}.`,\n why: 'A picture refused when it was submitted never renders. An array of the rest would be shorter than the list you asked for, with every later picture in the wrong place, so result() gives none.',\n fix: 'Read error.refusals for each refused picture and its reason. The pictures that were accepted still render: set.images() walks them, each named by its camera.',\n docs: 'https://bakery3.com/docs/batches#errors',\n retryable: false,\n });\n this.name = 'RenderSetRefused';\n this.refusals = recorded.refusals.map(({ error, ...who }) => ({\n ...who,\n error: Bakery3Error.from(error),\n }));\n this.refused = recorded.refused;\n this.requested = recorded.images;\n }\n}\n\nfunction describeRefusal(refusal: RenderSetRefusal): string {\n const name =\n [refusal.variant, refusal.finish, refusal.camera, refusal.shot]\n .filter(Boolean)\n .join(' / ') || refusal.key;\n return `\"${name}\" (${refusal.error.message.replace(/\\.$/, '')})`;\n}\n\n/** The variant, finish, camera and shot an item was recorded under, from its metadata. */\nexport function who(\n metadata: Record<string, unknown> | undefined,\n): Pick<RenderSetImage, 'variant' | 'finish' | 'camera' | 'shot' | 'kind'> {\n const out: Pick<\n RenderSetImage,\n 'variant' | 'finish' | 'camera' | 'shot' | 'kind'\n > = {};\n if (typeof metadata?.variant === 'string') out.variant = metadata.variant;\n // The catalog recorder's own word for it.\n else if (typeof metadata?.sku === 'string') out.variant = metadata.sku;\n if (typeof metadata?.finish === 'string') out.finish = metadata.finish;\n if (typeof metadata?.camera === 'string') out.camera = metadata.camera;\n if (typeof metadata?.shot === 'string') out.shot = metadata.shot;\n if (metadata?.kind === 'video' || metadata?.kind === 'image')\n out.kind = metadata.kind;\n return out;\n}\n\nexport function emptyRecorded(): Recorded {\n return { variants: 0, images: 0, adopted: 0, refused: 0, refusals: [] };\n}\n"]}