Live source batches
SQSAPI-11.4b connects shared live point sources. Register an empty GeoJSON using MapLibre-style addSource with the SqsMap sqsLive extension, then await map.getSource(id). The caller supplies batches. HTTP polling receivers are connected by 11.4c1; 11.4c2 connects WebSocket and reconnection; shared receiver scene ownership is connected by15.1; static scene resources/playback/tracks and mobile hosting remain pending. See the protocol and source API.
import {map} from 'sqsmap';
map.addSource('positions',{type:'geojson',data:{type:'FeatureCollection',features:[]},sqsLive:{fields:{speed:'number'},allowDelta:true}});
await map.sqs.flush();
const source=await map.getSource('positions');
const result=await source.sqs.applyLiveUpdate({kind:'snapshot',epoch:'trip',sequence:'9007199254740993',points:[{id:'a',coordinate:[116.4,39.9],properties:{speed:12}}]});
const state=await source.sqs.getLiveState();
const frozen=await source.sqs.freezeLiveSnapshot();
fields is required, with at most 32 string/number/boolean declarations. Every point supplies exactly those fields; null values are allowed. allowDelta defaults to false. Creation requires an empty FeatureCollection, with no URL, promoteId or generateId. Unconfigured sources report CAPABILITY_UNAVAILABLE; a deleted/recreated source invalidates its old handle with STALE_HANDLE. Browser capability: liveSourceUpdates. Native flag: SQSM_MAP_LIVE_UPDATES (134217728). ABI v1 source JSON uses the three same method names with source identity as the first argument; ABI/export count is unchanged.
applyLiveUpdate(frame) returns {applied,state,dataRevision}. State contains initialized, epoch, sequence, protocolRevision, pointCount, allowDelta and fields. Sequences are decimal u128 strings; never convert them to JavaScript number. Before initialization epoch/sequence are null. protocolRevision is a decimal revision string separate from dataRevision. An exact replay of the previous batch returns applied:false without rebuilding the index, advancing revisions or emitting source notifications. Cluster configuration may advance the data revision while preserving protocol revision.
A snapshot has kind, epoch, sequence and points. A delta has kind, epoch, baseSequence, sequence, upserts and deletes. Deltas require explicit opt-in, the same epoch, an exact baseSequence and a greater sequence. A new epoch starts with a complete snapshot. Continuity errors report CONFLICT, invalid schema/coordinates report INVALID_ARGUMENT and budget failures report RESOURCE_LIMIT. setData/updateData conflict on live sources to preserve the protocol baseline.
Limits: 4096 points and 8MiB JSON per batch; point IDs up to 128 UTF-8 bytes, string properties up to 1024 bytes; WGS84 lng,lat, longitude ±180 and latitude ±85.0511287798066. JSON depth is at most 32 and node count at most 200000. Protocol/raw/decoded/cluster structures share the map's estimated 128MiB resident budget; raw source data has a separate 64MiB total. Old and prepared state may coexist temporarily. These are not process memory ceilings.
Protocol, data, cluster membership, styled display and events prepare before committing. Failures preserve the last good state. Stable original-point feature state survives; rebuilt synthetic cluster state expires. The optional sqsLive in sourcedata is a historical commit snapshot. A later query cannot replace its history; ordinary source events have no new field. freezeLiveSnapshot returns an independent {source,state,data} copy, without starting a transport or task. Editing this copy does not alter the map. Complete output is bounded to 8MiB; script hosts have an additional 64KiB response budget. Use paged queries for large script results; independent SDK/C output does not have that script-only limit.
Independent SDK code uses the same source.sqs calls, map.flush() and map.capabilities.liveSourceUpdates. Portable scripts use map.sqs.flush. The user owns layer/tool UI. The full example contains every button handler, CSS, events, credits and destruction. Download complete HTML and portable script, served beside the matched SDK dist/wasm directories.
The same 52 checks run through Linux native once/retained QuickJS, actual desktop scripting and browser main/strict Worker. Browser display and complete button code have separate acceptance. Windows/macOS require the designated external machine; mobile hosts and network receivers remain pending. This phase does not establish formal release or remote deployment acceptance.
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SqsMap live batches</title><style>html,body{height:100%;margin:0;font-family:system-ui}#map{height:100%}.tools{position:absolute;top:12px;left:12px;background:#fffe;padding:12px;border-radius:8px;max-width:360px}button{padding:6px;margin:2px}pre{max-height:200px;overflow:auto;white-space:pre-wrap}#credits{position:absolute;bottom:0;right:0;background:#fffd;padding:4px;font-size:12px}</style></head><body><div id="map"></div><section class="tools"><strong>Live point batches</strong><p>Manual producer: apply a snapshot, a delta, a replay or freeze an independent copy.</p><button id="snapshot" disabled>Snapshot</button><button id="delta" disabled>Delta</button><button id="replay" disabled>Replay last batch</button><button id="freeze" disabled>Freeze snapshot</button><pre id="log" role="status">Loading</pre></section><div id="credits"></div><script type="module">
// Serve next to the matched standalone SDK's dist/ and wasm/ directories.
import {MapView} from './dist/index.js';
const log=document.querySelector('#log'),buttons=[...document.querySelectorAll('button')],subscriptions=[];let map,source,lastFrame,sequence=0n;
const point=(id,lng,n)=>({id,coordinate:[lng,0],properties:{n}});
async function run(action){buttons.forEach(b=>b.disabled=true);try{await action();}catch(error){log.textContent=String(error);}finally{if(map)buttons.forEach(b=>b.disabled=false);}}
async function apply(frame){const result=await source.sqs.applyLiveUpdate(frame);lastFrame=structuredClone(frame);sequence=BigInt(result.state.sequence);log.textContent=JSON.stringify(result,null,2);}
try{
map=await MapView.create(document.querySelector('#map'),{worker:false});if(!map.capabilities.liveSourceUpdates)throw Error('Matched live-source SDK/WASM required');
subscriptions.push(map.on('sqs.attributionschange',event=>document.querySelector('#credits').textContent=event.attributions.text));
subscriptions.push(map.on('sourcedata',event=>{if(event.sourceId==='points'&&event.sqsLive)console.log('Committed live state',event.sqsLive);}));
map.jumpTo({center:[.0005,0],zoom:12});map.addSource('points',{type:'geojson',data:{type:'FeatureCollection',features:[]},attribution:'Example data © SqsMap',sqsLive:{fields:{n:'number'},allowDelta:true}});
map.addLayer({id:'points',type:'circle',source:'points',paint:{'circle-radius':['get','n'],'circle-color':'#ff0000'}});await map.flush();source=await map.getSource('points');document.querySelector('#credits').textContent=(await map.getAttributions()).text;
document.querySelector('#snapshot').onclick=()=>run(()=>apply({kind:'snapshot',epoch:'example',sequence:String(sequence+1n),points:[point('a',0,10),point('b',.001,8)]}));
document.querySelector('#delta').onclick=()=>run(async()=>{const state=await source.sqs.getLiveState();if(!state.initialized)throw Error('Apply a snapshot first');await apply({kind:'delta',epoch:state.epoch,baseSequence:state.sequence,sequence:String(BigInt(state.sequence)+1n),upserts:[point('a',0,16),point('c',.002,6)],deletes:['b']});});
document.querySelector('#replay').onclick=()=>run(async()=>{if(!lastFrame)throw Error('Apply a batch first');await apply(structuredClone(lastFrame));});
document.querySelector('#freeze').onclick=()=>run(async()=>{const frozen=await source.sqs.freezeLiveSnapshot();log.textContent=JSON.stringify(frozen,null,2);});
buttons.forEach(b=>b.disabled=false);log.textContent='Ready';
}catch(error){log.textContent=String(error);}
window.addEventListener('pagehide',()=>{subscriptions.forEach(off=>off());map?.destroy();},{once:true});
</script></body></html>
See shared HTTP polling and cancellation.
Shared scenes and receiver ownership provides draft/start/pause/resume/stop and historical events; static GeoJSON ownership is available in static scenes; playback, tracks and mobile acceptance continue separately.
Static scene resources and layer visibility covers same-source layers, draft hiding, controls, queries/credits and complete HTML.