Command confirmation and resource lifecycle
Submitting a command, applying it, rendering a new frame, loading network resources, and becoming idle are separate milestones. Wait for the relevant confirmation rather than treating elapsed time as success.
Browser SDK development interface
These interfaces are implemented in the current MapView development source and have not yet been published in an updated npm package. They extend the existing SDK. Application scripts now connect map.sqs.flush() with a watermark scoped to their host request domain. The older npm SDK lacks backend confirmation, so Examples explicitly reports the capability as unavailable.
map.mapInstanceId: string
map.flush(options?: { render?: boolean; since?: number }): Promise<MapCommitResult>
flush() captures the watermark of map and application-extension commands submitted before the call. Later commands do not extend its wait. By default it confirms application. render: true also waits for an actual new rendered frame and updates the host camera snapshot. It does not wait for downloads, animation completion, or idle.
| Result field | Meaning |
|---|---|
| mapInstanceId | Non-reused identity; rebuilding a destroyed map creates a different identity |
| submittedSequence | Captured command watermark |
| appliedSequence | Highest successfully applied command sequence at that point |
| stage | applied or rendered |
| camera | Authoritative snapshot at application time or in the new frame |
| frameSequence | Actual frame sequence when stage is rendered |
Command sequences strictly increase within one map. The default since: 0 includes errors from all earlier commands. Failure rejects with code, commandSequence, and submittedSequence. After handling an error, pass its observed watermark as since to confirm subsequent corrective commands. Independent commands already applied are not atomically rolled back.
const before = await map.flush();
map.setBearing(30);
try {
const commit = await map.flush({ render: true, since: before.submittedSequence });
console.log(commit.mapInstanceId, commit.frameSequence, commit.camera.bearing);
} catch (error) {
console.error(error.code, error.commandSequence, error.message);
}
Budgets are 256 pending Worker requests, 32 pending frame confirmations, and 256 recorded command failures. Exceeding a budget rejects explicitly. Destruction or a Worker failure rejects pending requests and frame waits; further calls to a destroyed map also reject.
Script session ownership
Stopping a script terminates its Worker/interpreter, listeners, and pending requests. Applied map changes remain; stopping a script does not destroy the map. Explicitly host-owned scenes require a separate stop or removal.
Each run has a new script instance. A late response from a stopped run cannot remove a new run's cancellation handle. A detached old event subscription cannot dispatch into a new run. Forwarding accepts only the current map identity and increasing positive safe-integer sequences, discarding foreign or stale events.
User-run debug scripts keep their existing confirmation exemption. High-impact changes generated by AI still use host confirmation plans. Confirming command application and obtaining user authorization are separate steps.
Implementation and acceptance scope
This increment verifies the shared main-thread/Worker command protocol, actual runtime frame scheduling, failure and destruction, and isolation of late responses and events after script restart. These are deterministic protocol regressions. The real-browser connection failed in this run; substitute-renderer results are not reported as GPU acceptance.
The C ABI and standalone native-script command watermark is connected and verified with a real Linux GPU. Application shared-facade integration, source revisions and complete cross-platform resource identities remain for later increments. Windows/macOS require external-machine acceptance; mobile requires its real host and devices. See persistent script sessions and developer APIs.
Native library and standalone script receipts
First check the existing sqsm_map_runtime_flags() & SQSM_MAP_COMMAND_BARRIERS (8) before resolving or calling the new symbol; older ABI v1 libraries do not set this capability bit. ABI v1 adds sqsm_map_flush_json(map, since, flags, out): flags 0 confirms application, while flags 1 synchronously confirms a newly read-back GPU frame. Valid no-op mutations consume a command sequence too. Wrong-thread calls, invalid pointers and other admission failures are not submitted. Receipt fields match the table above; camera currently retains the native world-coordinate snapshot, rather than claiming the planned precise public camera. State exposes all three sequence watermarks.
Both one-shot and retained standalone scripts use await map.sqs.flush({ since, render }). The barrier enters that script’s ordered request queue at invocation; later mutations in the same queue cannot alter that receipt. Native queued calls are submitted to the map by host pump; independent C mutations before pump may execute first. This increment does not provide global enqueue ordering across mixed entries. C and scripts share one command journal. The default includes historical failures. A C failure clears the output and exposes JSON containing command watermarks through last_error, with message text bounded to 128 characters so the JSON remains valid. Native script errors preserve the same sequence fields. Returned receipts survive map destruction; C callers must free their separate allocations.
Complete C/C++ example · Shared native script · Matching declarations
import { map } from 'sqsmap';
await map.sqs.ready();
const before = await map.sqs.state();
map.setBearing(10);
const first = map.sqs.flush({ since: before.submittedSequence });
map.setBearing(20);
const applied = await first;
const rendered = await map.sqs.flush({ since: applied.submittedSequence, render: true });
export default { applied, rendered };
The identical script passed real QuickJS/GPU one-shot and retained entries in Linux debug and release-capi libraries: the first receipt captures bearing 10, and the second confirms bearing 20 and a new frame. An independent C host also verifies failure recovery, invalid parameters and output lifetime after destruction. This is not acceptance of the application/browser shared facade that remains to be connected.
Application shared-script confirmation
The desktop application and the web host with a matching development SDK provide this call. Map mutations still use the compatibility module; the complete MapLibre-style camera facade remains part of the camera stage.
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
import { map as compatibilityMap } from 'gis';
await map.sqs.ready();
await compatibilityMap.camera.setView({ bearing: 30 });
const commit = await map.sqs.flush({ render: true });
console.log(commit.mapInstanceId, commit.submittedSequence, commit.frameSequence);
Application hosts count map-mutation requests; SDK/C libraries count backend commands. One host request can contain several backend commands, so the watermarks are not interchangeable. Repeated runs against one host retain command history. Use only a watermark returned by that same entry point as since. Later host writes do not extend the captured wait. Desktop asynchronous requests retain their identities until completion, later writes are queued, and cancellation cannot produce a successful confirmation.
Desktop render: true confirms an actual offscreen draw and completed pixel snapshot of the visible map. It does not confirm operating-system presentation or complete tile readiness. The receipt camera currently supports identified EPSG:4326 / EPSG:3857 2D views; other views reject explicitly. Desktop application raw camera values use logical viewport pixels; browser SDK values use canvas pixels. Projection and pixel normalization belong to the camera stage. Source revisions and mobile-device acceptance remain later tasks.
The web host enables its confirmation queue only when the actual SDK provides both flush and instance identity. Every write is acknowledged by the engine before returning. Other existing operations retain their old path on the older SDK, while flush rejects explicitly. Development source and an older npm package are not a published matching release.
Acknowledging a failed confirmation does not erase history. After catching the failure, retain its submittedSequence as since for every later confirmation, advancing it only with receipts from the same entry. Omitting since checks historical failures again. Never substitute a watermark from another host or the underlying SDK.
Preserve the actual rejection reason
Shared reasons are INVALID_ARGUMENT, STALE_HANDLE, CONFLICT, RESOURCE_LIMIT, ENGINE_ERROR, NOT_FOUND, CAPABILITY_UNAVAILABLE and CANCELLED. Native mutation and subsequent flush preserve the same reason. Existing C values0–7 remain; cancellation adds8. On rejected acknowledgement last_error is a JSON envelope containing code/message/commandSequence/submittedSequence. Do not interpret every rejected barrier as a GPU error. Native QuickJS and the WASM SDK preserve these reasons without inferring them from prose.
Applied changes are not rolled back. Use only a previously returned submittedSequence from this entry point as since; it neither erases history nor crosses ordering domains. The complete script below verifies invalid data, duplicate resources, missing resources and a deleted source's old identity, explicitly recovering after each failure. Unknown engine failures remain ENGINE_ERROR and ordinary JavaScript syntax errors INVALID_ARGUMENT. Stopping follows session cancellation cleanup; throwing an exception labelled CANCELLED does not itself stop the map.
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import {map} from 'sqsmap';
const empty={type:'FeatureCollection',features:[]};
let since=0;const reasons=[];
async function rejected(invoke,code){
let direct;try{await invoke();}catch(error){direct=error;}
let failure;try{await map.sqs.flush({render:false,since});}catch(error){failure=error;}
if(!failure||failure.code!==code||direct&&direct.code!==code)throw Error('Command reason was lost: '+code+'/'+direct?.code+'/'+failure?.code);
if(!Number.isSafeInteger(failure.commandSequence)||!Number.isSafeInteger(failure.submittedSequence)||failure.commandSequence<=since||failure.submittedSequence<failure.commandSequence)throw Error('Failed command watermark missing');
since=failure.submittedSequence;await map.sqs.flush({render:false,since});reasons.push(code);
}
await rejected(()=>map.addSource('invalid',{type:'geojson',data:{type:'FeatureCollection',features:'invalid'}}),'INVALID_ARGUMENT');
map.addSource('reason',{type:'geojson',data:empty});await map.sqs.flush({render:false,since});
const stale=await map.getSource('reason');
await rejected(()=>map.addSource('reason',{type:'geojson',data:empty}),'CONFLICT');
await rejected(()=>map.removeSource('missing-reason'),'NOT_FOUND');
map.removeSource('reason');await map.sqs.flush({render:false,since});
await rejected(()=>stale.setData(empty),'STALE_HANDLE');
if(await map.getSource('reason')!==undefined||await map.getSource('invalid')!==undefined)throw Error('Failed command changed sources');
export default {reasons,recovered:true};
Shared error script · Complete C example. Rust development sources add the code field to SessionError::CommandFailed; external patterns should read it or use .. . C symbols and layouts remain unchanged.