Writer.applyPatch ​

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.

Method of Writer · inherited by Tx, Session, testing.FakeSession · stable · since 2.0.0 · called as session.applyPatch

ts
applyPatch(patch: readonly JsonPatchOp[], options?: RecordedOptions<C> & SignalOption): Promise<Report>;

Applies RFC 6902 operations to SessionState (recorded; merging; in the write queue). Preconditions: expectRevision plus 'test' operations; a failed 'test' refuses with reason 'conflict', and so does a path that resolved at the call and no longer resolves at the write's turn in the queue (a rollback or an earlier commit removed its node); nothing of the patch applies. The way to move a placed node and keep it: replace /model/node/{id}/position, its value a literal: a Point3 variable is an interface, which JsonValue refuses (TS2322), so write value: { ...p } (JsonPatchOp). Outcome: 'committed' at its commit point, after its loads; through a tx, 'staged' once the assets it names have loaded (a node it enables, a new glbUrl). Asset failures: problems, so throwing on 'asset-failed' rolls the transaction back. 'rolled-back' with reason 'internal-error' when applying it failed inside the runtime; through a tx, it then rejects AbortError and the transaction resolves 'rolled-back' (spec section 6.4). Errors: rejects HoloInputError on a bad path (a collection, stage-order or named-material path among them: the feed names those, another member writes them, and fix says which; see JsonPatchOp), or 'invalid-input' on a node scale with a 0 or non-finite component, or a non-finite number anywhere in a value (spec section 3.4); or 'unknown-field' on a node field path the stored format doesn't have and the node doesn't carry (didYouMean names close fields; a look-only field such as visible is find(q).set or node(id).set), or 'unknown-id' on a non-empty defaultNodeId that names no child of that node once the patch has run (didYouMean names its children). Nothing applies then. A field the node carries (an app's own, such as prices) still writes, a whole node keeps the fields it brings, null and '' still write as defaultNodeId, and a committed entry's inverse is never refused for these (spec sections 6.9, ID10, and 8.1). Not for: replacing the whole model from your store, which is setSceneModel; a collection's items or order, which are collection(name).set(); a stage layer, which is stage.set().

Options ​

Through Session:

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.
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…
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.

Through Tx:

Option Type Default What it does
signal AbortSignal undefined Cancels this call; the member's JSDoc says what an abort does.

Refused here (typed never, so passing one fails to compile): actor, checkpoint, dryRun, expectGroup, expectRevision, origin, supersede, undo.

Abort (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 tx the 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'.

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/05-Writer.applyPatch.ts):

ts
import type { Point3, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const nodeId: string;
declare const to: Point3;            // an interface: spread it into the value (`JsonPatchOp`)
const r = await session.applyPatch([
  // a precondition
  { op: 'test', path: `/model/node/${nodeId}/enabled`, value: true },
  { op: 'replace', path: `/model/node/${nodeId}/position`, value: { ...to } },
], { expectRevision: session.revision, undo: 'Move', actor: 'shopper', origin: 'nudge' });
// 'conflict': the test, the revision or a node gone at its turn; 'busy': a write is pending
if (r.outcome === 'refused') console.warn('not moved:', r.reason);

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: applyPatch names no product
2. A precedent not recorded: no ledger row (coverage.json) names this member or its owner
3. Two apps, or labs pass: 5 apps (fleet cross-check: app-04c2774145, app-4762caacdc, app-c1384707fc, app-f3598fad77, app-f9a32142f1)
4. The cheaper fix first not recorded per member (Appendix B decides it per need)