Optional native JavaScript module
Development native sessions now support common nonzero planar pitch. See native pitched maps for the complete script, capability scope and actual pixel acceptance. Browser and application adapters remain in progress.
The map core does not depend on a JavaScript interpreter. The separate sqs-map-script module uses rquickjs 0.12.2 / QuickJS-NG; native libraries include it only when the script feature is enabled. The default map library excludes QuickJS. Browsers continue to use their own JavaScript engine. The desktop application shares VM initialization while retaining its own GIS host and persistent sessions.
This describes development source for one-shot and retained-session script ABI v1, not a newly released DLL/SO package. An independent C host has been verified on Linux with NVIDIA GeForce RTX 4060 / Vulkan. Windows/macOS will be checked on another machine; standalone persistent sessions have passed the Linux C host; mobile devices remain unverified.
Build and capability detection
Run from the root of the sqs-map core repository:
cargo build --locked -p sqs-map-capi --no-default-features --features script --profile release-capi
cc -std=c11 -Wall -Wextra -Werror \
crates/sqs-map-capi/examples/map_script.c \
-I crates/sqs-map-capi/include -L target/release-capi \
-lsqs_map -pthread -o map_script
LD_LIBRARY_PATH=target/release-capi SQS_REQUIRE_GPU=1 ./map_script
Both builds export the same C symbols when using the same base feature preset. Default features preserve all existing exports; --no-default-features excludes three existing optional MBTiles exports without affecting the map/script entry points documented here. Check sqsm_map_runtime_flags() & SQSM_MAP_SCRIPT and sqsm_map_script_api_version(). A disabled module reports version 0 and returns CAPABILITY_UNAVAILABLE; an enabled module reports version 1. Use release-capi: the ordinary aborting release profile cannot create high-level map sessions.
Download the C header, complete C acceptance example, standalone module declarations, and source revision and SHA-256 manifest. These declarations describe this standalone subset and do not replace application or browser script declarations.
The added shared planar camera methods (center, zoom, padding, bounds fitting, and coordinate conversion) are described in Camera API. They use the same native module and confirmation journal. Globe API connects spherical pitch and common projection switching. Globe animation and anchors are connected; native hosts must keep pumping rendering or script sessions.
Current script support
Scripts are UTF-8 ES modules and can import only the built-in sqsmap module. Execution is one-shot by default; sessionMode='once' is also accepted. A default export is JSON-serialized into { "value": ... }, with null when no result is provided.
import { map } from 'sqsmap';
await map.sqs.ready();
map.setBearing(30).setBearing(42);
await map.sqs.flush();
export default {
bearing: await map.getBearing(),
state: await map.sqs.state()
};
Create a map using sqsm_map_create, then call sqsm_map_script_run_json(map, source, len, &out) on the creation thread. On success, read out.len bytes from out.data; JSON is not null-terminated. Free the allocation exactly once using sqsm_map_bytes_free(out.allocation), even after map destruction. Do not call free(out.data). See native C API for ownership, threading, and status codes.
| Method | Current behavior |
|---|---|
map.getBearing() | Promise of the shared bearing normalized to [-180, 180) |
map.setBearing(bearing) | Returns map immediately for chaining; ordered requests are applied before flush confirmation |
map.sqs.flush() | Promise of a captured command receipt; optional render confirms a new GPU frame; historical failures reject |
map.sqs.ready() | Promise confirming the map connection, without asserting resource readiness or idle |
map.sqs.state() | Promise of a native map snapshot |
map.sqs.addGeoJSON(data) | Promise loading convenience WGS84 GeoJSON and returning a string layer identity |
map.sqs.removeNativeLayer(id) | Promise removing that convenience layer |
map.sqs.events() | Empty for the legacy entry; sqs.viewchange / sqs.layerschange for retained sessions |
Snapshot mapInstanceId, sequence, and layer id use decimal strings to preserve 64-bit precision in JavaScript. nativeView.scale is not MapLibre zoom. Convenience GeoJSON is not the full addSource/addLayer style system.
Retained sessions and events
Check SQSM_MAP_SCRIPT_SESSIONS and sqsm_map_script_session_api_version() separately: version 1 when enabled, 0 in pure libraries. The old script version remains 1 and script_run_json remains one-shot, rejecting listeners and persistent mode. Download the complete C session host and shared event script. The same script is also verified unchanged by the application runner.
import { map } from 'sqsmap';
export const sessionMode = 'persistent';
await map.sqs.ready();
const subscription = map.on('sqs.viewchange', async event => {
// Update your controls. event.target === map; state is the event-time snapshot.
const bearing = event.state.nativeView.bearing;
});
map.once('sqs.layerschange', event => { /* Update your layer list. */ });
export default { connected: true };
// subscription.unsubscribe() or map.off(type, fn) removes a listener.
| C entry (sqsm_map_ prefix omitted) | Behavior |
|---|---|
script_session_start(map, source, len, out_session) | Evaluate synchronous entry on the map owner thread and return a handle; pump completes async initialization |
script_session_pump_json(session, max_events, out) | Advance promises, map requests, and 1–32 event envelopes on that thread; return status JSON |
script_session_state_json(session, out) | Inspect cached metadata without executing JavaScript |
script_session_stop(session) | Release VM and subscriptions synchronously; idempotent before release |
script_session_release(session) | Stop and release the identity; further calls report STALE_HANDLE |
Call pump in your own frame/event loop and continue polling C map events. There is no background interpreter thread. States are initializing / running / succeeded / cancelled / failed, with initialized, value, callbacks, error, sessionId and mapInstanceId. Initialization is separate from session closure; omitting persistent mode finishes with succeeded. value is the initialization JSON snapshot, not a live export. callbacks reports cumulative invocations and current listeners/pending callbacks. Top-level await map.once works because pump can dispatch events before initialization completes.
cc -std=c11 -Wall -Wextra -Werror \
crates/sqs-map-capi/examples/map_script_session.c \
-I crates/sqs-map-capi/include -L target/release-capi \
-lsqs_map -pthread -o map_script_session
LD_LIBRARY_PATH=target/release-capi SQS_REQUIRE_GPU=1 ./map_script_session \
crates/sqs-map-capi/examples/portable_native_events.js
Connected events are sqs.viewchange for native bearing/resize sqs.layerschange for convenience layer addition/removal, and sqs.resourceschange for style settings. Complete camera/source/render/idle semantics remain subsequent tasks. on returns Subscription; off and callback once return map; Promise once resolves with the event. Equal type/function/listener-mode subscriptions are deduplicated. Events contain type, target, string mapInstanceId, safe-integer sequence and event-time state. Sequence increases but can have gaps from coalescing. Envelopes are shallow-frozen and snapshots do not reference current map state.
Each session queue is separate from C poll, bounded to 128 events/256 KiB. Consecutive view notices may coalesce; discrete overflow fails that session on its next pump without blocking other consumers. Pump does not replace C poll; the original full C queue still rejects map mutations. Limits are four retained handles/map and sixteen/process, including terminal handles: release them. Stop, success and failure release the VM/subscription. Map destruction first stops bound sessions, retaining terminal handles and returned buffers for inspection/freeing. Owner-thread exit releases its VMs/maps and makes old handles stale.
Synchronous/async listener errors, unhandled rejections and budgets end the session. A failing pump clears output; state still exposes error, and later terminal pumps only return cached metadata. Stop executes no user cleanup and prevents future callbacks. map.sqs.disposeSession() closes from JavaScript. Output allocation failures cannot roll back executed callbacks or map mutations; do not repeat a business operation merely because its output allocation failed.
Style downloads are connected through C host resources. General file/network script injection, GIS, and remaining map methods are still unavailable. Unsupported events and layer-filtered subscriptions report CAPABILITY_UNAVAILABLE. Application API inventories do not establish standalone support.
Budgets and failures
Each one-shot run or retained start/pump allows at most 128 KiB source, 32 MiB VM memory, 512 KiB stack, 2 seconds of interpreter execution, 32 queued requests, 256 requests total, and 4096 microtasks. Arguments per call, individual host responses, and final JSON are each limited to 64 KiB. Native/GPU calls are synchronous; interpreter interruption cannot preempt an active native driver call.
Invalid arguments/modules/results and unhandled Promise rejections fail the run. Time and resource limits return RESOURCE_LIMIT. Failures clear the output; retrieve error text using sqsm_map_last_error(). Functions, symbols, BigInt, non-finite numbers, and non-JSON-serializable results are unsupported. The VM and callbacks are released when one-shot execution returns or a retained session becomes terminal; JavaScript values and map pointers do not escape.
Applied map changes survive script errors; a script is not an atomic transaction. Session-mode declarations are checked after execution and cannot serve as rollback. Idle retained sessions have no total lifetime timeout; the 2-second and 4096-microtask budgets reset each turn. Initial queued requests count toward the first 256-request turn. Each session permits 32 listeners and 32 pending async callbacks; request identities are never reused across turns.
Third-party licenses
Script-enabled distributions must retain the rquickjs MIT notice and QuickJS-NG MIT notice. These notices cover interpreter dependencies; the map engine remains independently implemented.
Command receipts, failure recovery and complete examples
Native styles and resource hosts: native libraries now provide set_style_json, request polling, and response submission. Optional native scripts support chainable map.setStyle(url, {diff:false}) and sqs.resourceschange. Styles enable Mercator display; clearing restores geographic display. The shared style facade is documented in style scripts; common setProjection({type}) is described in Globe API.
Shared constant lighting, solar extensions and historical events use the same script shape; query capabilities for actual per-platform 3D layer support.