# `ExportOptions.prepare`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Function property of [`ExportOptions`](ExportOptions.md) · stable · since 2.0.0

```ts
prepare?: (copy: Collection) => void | Promise<void>;
```

Edit a detached copy first (rest pose, simple glass, export opacities). The copy holds every
part [`query`](ExportOptions.query.md) matches, hidden ones included, with its committed look written in as values: the
live session's rules are not carried, so a `null` reset on the copy releases nothing; write the
value you want. Copied parts keep `node`, `collection`, `key`, `name`, `tags` and `meta`, so
`copy.root.find(q)` with the query that matched them live finds them. `copy.set()` reconciles
only what `set()` and `load()` added to the copy: it adds content and never removes a copied
part. Reach the copy's parts through `copy.root.find(q)` or `copy.part(key)`, never
[`session.find()`](Writer.find.md), which changes the live scene. Every write through the copy's handles belongs
to the copy, whatever `owner` says: it never reaches the live scene, [`state()`](Writer.state.md), the commit feed
or history, and it ends with the export. `copy.set(spec)` and `copy.load(url, placement)` add
content to the copy (a brand placard, a box standing in for a fabric): applied at the call,
under the copy's root (`placement.parent` is not available on it), found by `copy.part(key)`
and `copy.root.find(q)`, and written to the file. `copy.load` resolves at the call with the
item's part, which has no bounds on the copy: its GLB loads when the file is written, and one
that fails is an 'asset-failed' in [`ExportResult.problems`](ExportResult.problems.md). Whatever you leave in
it, the file stays well formed for the AR viewers ([`Session.export`](Session.export.md)).

**Default:** undefined (export the committed look)

## Abort (`signal`)

This call is neither a write, a registration, a wait nor a creator (`SignalOption.signal`, [the kinds of call](../read-this-first.md#conventions)).

> Cancels this call; the member's JSDoc says what an abort does.

## Example

Imports: values (`has`, `validate`, the `Holo*Error` classes, the pure helpers) from `@holostep-configurators/runtime-client`, types from `@holostep-configurators/runtime-client/contract`, `/math` and `/testing` names from those entries; `boot` from the runtime module the root's `loadRuntimeAtFloor` resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (`examples/declaration/16-ExportOptions.prepare.ts`):

```ts
import type { Query, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const doors: Query;                  // the view shows them open through a pose rule
declare const cutAway: Query;                // and see-through through a material rule
declare function offerDownload(file: Blob, name: string): void;
const { blob } = await session.export({
  format: 'glb',
  // The copy holds the look those rules gave, not the rules: write the canonical values.
  prepare: async (copy) => {
    for (const door of copy.root.find(doors).list()) {
      const { position, quaternion, scale } = door.restPose;
      await door.set({ pose: { position, quaternion, scale } });
    }
    await copy.root.find(cutAway).set({ material: { alpha: 1, alphaMode: 'opaque' } });
    // a placard only the file carries, above the copy's parts
    const top = copy.root.bounds()?.max.y ?? 0;
    await copy.set({ items: [{
      key: 'placard', name: 'placard', kind: 'mesh',
      shape: { kind: 'plane', size: { x: 0.4, y: 0.2 } },
      at: { position: { x: 0, y: top + 0.15, z: 0 }, rotationDeg: { x: 90, y: 0, z: 0 } },
      material: { baseColor: '#ffffff', lighting: 'unlit', side: 'double' },
    }] });
  },
  signal,
});
offerDownload(blob, 'design.glb');
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `prepare` names no product |
| 2. A precedent | pass: [Babylon.js GLTF2Export](https://github.com/BabylonJS/Babylon.js/blob/master/packages/dev/serializers/src/glTF/2.0/glTFSerializer.ts) (row `K39`); [USDZ file format](https://openusd.org/release/spec_usdz.html) (row `K39`) |
| 3. Two apps, or labs | pass: 11 apps (ledger: app-4f16d2f6da, app-510d750508, app-bcb7ef21da, app-ceb4eccc72, app-f07e9033d9, app-fb16dd1985, app-fc07850558, app-fc306ec3dc and 1 more; fleet cross-check: app-4f16d2f6da, app-510d750508, app-59c5b5751e, app-ceb4eccc72, app-e61a0b42ad, app-f07e9033d9, app-fb16dd1985, app-fc07850558) |
| 4. The cheaper fix first | decided per need in Appendix B: rows `K39` at tier stable (a member only where no rule, data, asset or app code serves the need) |
