Session.transaction ​Contract 2.0.0 · package sha256
a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e· generated fromconfigurator-runtime.d.tsbyservices/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.
Method of Session · inherited by testing.FakeSession · stable · since 2.0.0 · called as session.transaction
transaction<T>(work: (tx: Tx) => T | Promise<T>, options?: TransactionOptions): Promise<TransactionReport<T>>;
One all-or-nothing change that may await (loads, attach). Only writes through tx and handles
obtained from it belong to it; they take member options only. Outside writes made while it is
open never join: they are queued (default), supersede it (supersede: true), or are refused
'busy' (expectRevision). A second transaction() while one is open is an outside write:
queued transactions run in call order and never coalesce.
labs write), has passed its commit point or ended, so nothing commits under it except
through tx. Abort a pending preview's signal first if it must not wait for that
preview: a rule's textures (up to readyTimeoutMs from its call), or
a Collection.load's whole download (no bound). timeoutMs counts from the open.work, read through tx: it sees the transaction's staged writes and never a write
queued behind it, and the strict fake records nothing for it. A session read of a target
that has a write queued behind the transaction, in work or anywhere else on the page, is
unspecified (the old or the queued value) until that write applies, and the strict fake
records 'read-while-queued' for it (FakeSessionOptions.strict). Until it opens and
work stages its writes, a queued transaction adds nothing to intent reads: for newest-wins,
abort your older pending write first, then compare inside work (spec section 6.1, O2a).tx resolve 'deferred' and start when the commit is shown;
a durationMs or fadeMs tween (rules, materials, the stage) resolves 'staged' at once and
also starts then, so awaiting it inside work never waits for the tween;
animations.reset and stop are staged, so reads see frame 0 at once.changed: false); its deferred camera moves and clips still start, at the next drawn frame.
A staged write the state doesn't hold (animations.reset, a labs write) makes it not
equal: it commits look-only (spec section 6.1, O15).
Outcome: resolves at the commit point ('committed', with value), or with 'rolled-back',
'superseded', 'refused' or 'dry-run'; an internal failure applying a staged write rolls it back
with reason 'internal-error' (spec section 6.4). Rejects only with AbortError, work's own
error after rollback, or HoloInputError for invalid options (for example a checkpoint over
256 KB; nothing ran).
Timing: timeoutMs 10 s without progress (counted from the hold's start once held), then
readyTimeoutMs 4 s of readiness. A transaction requested during a drag opens after
'drag:end'. It seals, and readyTimeoutMs starts, at the first macrotask boundary where
work has settled and no operation started through tx, awaited or not, is pending (spec
section 6.1, O5): an unawaited tx write still loading keeps it open, so abort that write's
signal at your own deadline and catch its rejection (late.catch(() => {})): caught, it
stages nothing and the transaction commits without it; uncaught, its AbortError is an enlisted
rejection work never observed, and the whole transaction rolls back (O6). Write it again
after the commit.
Errors: throws HoloScopeError when called while work or beforeCommit is synchronously on
the stack.
Not for: one undo step over time (a drag, an assistant turn), which is history.group(): a
transaction holds the display.| Option | Type | Default | What it does |
|---|---|---|---|
actor |
Actor |
the expectGroup's actor when one is given, else 'app' |
Who made the write. |
checkpoint |
JsonValue |
unchanged | Page state stored atomically with this step; returned in state().checkpoint and the feed. |
dryRun |
boolean |
false | Runs and reports, then rolls back by design ('dry-run'). |
expectGroup |
HistoryGroup |
undefined | A guard, checked at the call: this write belongs to the undo group you are running. |
expectRevision |
number |
undefined (no check) | If-match on session.revision. |
origin |
string |
null | Your tag, echoed unchanged in Report.origin and cause.origin. |
readyTimeoutMs |
number |
4000 | After sealing (Session.transaction): decode and compile for at most this long, then show anyway with completed: false. |
signal |
AbortSignal |
undefined | Cancels this call; the member's JSDoc says what an abort does. |
supersede |
boolean |
false (the write is queued behind the transaction and applies after it closes) | While a transaction is open: roll it back (it resolves 'superseded'); the writes queued behind it apply in call order, then this one, which supersedes an… |
timeoutMs |
number |
10000 | Rolls back after this long without progress (a load receiving data, work making a write). |
undo |
'rebase' | (string & {}) |
a recorded-state change gets one step labelled 'Change'; a look-only write gets none; the session's first model (cause 'load') gets none, whatever the label: it is the floor of history | An undo label makes this write one undo step. |
signal) ​This call is a write (SignalOption.signal, the kinds of call).
A write: before the commit point it rolls back and rejects AbortError; after it, no effect, except that a running camera move stops where the last frame drew it (
CameraMoveOptions).Through a
txthe commit point is the transaction's: aborting a write still loading rejects AbortError and stages nothing (catch it: unobserved, it rolls the transaction back, spec section 6.1, O6); aborting one already staged unstages it, its paths keeping the latest other write to them as after a failed savepoint (O7), and its Promise stays resolved 'staged'.
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/05-Session.transaction.ts):
import { HoloAssetError } from '@holostep-configurators/runtime-client';
import type { Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
// All or nothing: if one load fails, nothing appears.
declare const propUrls: readonly string[];
try {
const r = await session.transaction(async (tx) => {
// a handle from tx: bound to it
const props = tx.collection('props', { role: 'scenery' });
await Promise.all(propUrls.map((url, i) => props.load(url, { key: `prop-${i}` })));
return propUrls.length;
}, { undo: 'Add props', origin: 'props-panel', signal });
if (r.outcome === 'committed') {
console.log(`${r.value} props shown in one frame`); // narrowed: no `!`
} else {
console.warn(r.outcome, r.reason); // 'rolled-back' (timeout), 'superseded', 'refused'
}
} catch (error) {
if (!(error instanceof HoloAssetError)) throw error; // work's own error, after the rollback
console.warn('nothing was added:', error.url);
}
| Rule | Result |
|---|---|
| 1. Neutral name | pass: transaction names no product |
| 2. A precedent | pass: ProseMirror history (addToHistory) (row K62); Unity Undo (row K62); a correctness rule (rows K36, X1, X4) |
| 3. Two apps, or labs | pass: 20 apps (ledger: app-04c2774145, app-4762caacdc, app-4fd23bc869, app-822bb9d6aa, app-8675b3225c, app-b362a81c85, app-bcb7ef21da, app-c1384707fc and 10 more; fleet cross-check: app-04c2774145, app-37356bf09f, app-4762caacdc, app-4fd23bc869, app-510d750508, app-822bb9d6aa, app-8675b3225c, app-bcb7ef21da and 9 more) |
| 4. The cheaper fix first | decided per need in Appendix B: rows K36, K62, X1, X4 at tier stable (a member only where no rule, data, asset or app code serves the need) |