@honua/app-platform 0.0.20-alpha.0 → 0.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contract/discovery.d.ts +231 -0
- package/contract/discovery.d.ts.map +1 -0
- package/contract/discovery.js +554 -0
- package/contract/edit-sketch.d.ts +8 -0
- package/contract/edit-sketch.d.ts.map +1 -1
- package/contract/edit-sketch.js +15 -0
- package/contract/edit-snapping.d.ts +160 -0
- package/contract/edit-snapping.d.ts.map +1 -0
- package/contract/edit-snapping.js +675 -0
- package/contract/generated/projjson-v0.7-crs-validator.d.ts +35 -0
- package/contract/generated/projjson-v0.7-crs-validator.d.ts.map +1 -0
- package/contract/generated/projjson-v0.7-crs-validator.js +3941 -0
- package/contract/index.d.ts +11 -2
- package/contract/index.d.ts.map +1 -1
- package/contract/index.js +4 -1
- package/contract/pmtiles.d.ts +31 -0
- package/contract/pmtiles.d.ts.map +1 -1
- package/contract/pmtiles.js +60 -20
- package/contract/protocol-module.d.ts +70 -0
- package/contract/protocol-module.d.ts.map +1 -0
- package/contract/protocol-module.js +1 -0
- package/contract/schema-envelope.d.ts +26 -0
- package/contract/schema-envelope.d.ts.map +1 -0
- package/contract/schema-envelope.js +25 -0
- package/contract/schema.d.ts +582 -0
- package/contract/schema.d.ts.map +1 -0
- package/contract/schema.js +2483 -0
- package/contract/source-capability-support.d.ts +6 -0
- package/contract/source-capability-support.d.ts.map +1 -0
- package/contract/source-capability-support.js +154 -0
- package/contract/source.d.ts +35 -17
- package/contract/source.d.ts.map +1 -1
- package/contract/source.js +267 -71
- package/contract/types.d.ts +140 -4
- package/contract/types.d.ts.map +1 -1
- package/contract/types.js +6 -14
- package/core/bounded-json.d.ts +4 -0
- package/core/bounded-json.d.ts.map +1 -0
- package/core/bounded-json.js +38 -0
- package/core/cache-state.d.ts +9 -0
- package/core/cache-state.d.ts.map +1 -1
- package/core/cache-state.js +10 -0
- package/core/capabilities-xml.d.ts +33 -0
- package/core/capabilities-xml.d.ts.map +1 -0
- package/core/capabilities-xml.js +331 -0
- package/core/client.d.ts +4 -1
- package/core/client.d.ts.map +1 -1
- package/core/client.js +115 -29
- package/core/error-classifications.d.ts +153 -0
- package/core/error-classifications.d.ts.map +1 -0
- package/core/error-classifications.js +140 -0
- package/core/error-code-registry.d.ts +158 -0
- package/core/error-code-registry.d.ts.map +1 -0
- package/core/error-code-registry.js +143 -0
- package/core/error-envelope.d.ts +68 -0
- package/core/error-envelope.d.ts.map +1 -0
- package/core/error-envelope.js +357 -0
- package/core/errors.d.ts +45 -29
- package/core/errors.d.ts.map +1 -1
- package/core/errors.js +97 -50
- package/core/geoparquet-sql.d.ts +30 -2
- package/core/geoparquet-sql.d.ts.map +1 -1
- package/core/geoparquet-sql.js +38 -3
- package/core/odata-key-path.d.ts +12 -0
- package/core/odata-key-path.d.ts.map +1 -0
- package/core/odata-key-path.js +28 -0
- package/core/odata-write-codec.d.ts +66 -0
- package/core/odata-write-codec.d.ts.map +1 -0
- package/core/odata-write-codec.js +1083 -0
- package/core/odata.d.ts +59 -7
- package/core/odata.d.ts.map +1 -1
- package/core/odata.js +431 -99
- package/core/ogc-features.d.ts.map +1 -1
- package/core/ogc-features.js +3 -0
- package/core/ogc-maps.d.ts +7 -0
- package/core/ogc-maps.d.ts.map +1 -1
- package/core/ogc-maps.js +46 -7
- package/core/ogc-processes.d.ts +2 -1
- package/core/ogc-processes.d.ts.map +1 -1
- package/core/ogc-processes.js +33 -5
- package/core/ogc-records.d.ts +9 -0
- package/core/ogc-records.d.ts.map +1 -1
- package/core/ogc-records.js +78 -29
- package/core/ogc-tiles.d.ts +10 -1
- package/core/ogc-tiles.d.ts.map +1 -1
- package/core/ogc-tiles.js +56 -17
- package/core/path-utils.d.ts +9 -0
- package/core/path-utils.d.ts.map +1 -1
- package/core/path-utils.js +19 -0
- package/core/protocol-transport.d.ts +1 -0
- package/core/protocol-transport.d.ts.map +1 -1
- package/core/request-pipeline.d.ts.map +1 -1
- package/core/request-pipeline.js +1 -1
- package/core/spatial-filter.d.ts +8 -4
- package/core/spatial-filter.d.ts.map +1 -1
- package/core/spatial-filter.js +206 -16
- package/core/stac-static.d.ts +6 -2
- package/core/stac-static.d.ts.map +1 -1
- package/core/stac-static.js +260 -69
- package/core/surfaces.d.ts.map +1 -1
- package/core/surfaces.js +69 -5
- package/core/types.d.ts +125 -7
- package/core/types.d.ts.map +1 -1
- package/core/wfs-capabilities.d.ts +2 -0
- package/core/wfs-capabilities.d.ts.map +1 -1
- package/core/wfs-capabilities.js +8 -1
- package/core/wfs.d.ts.map +1 -1
- package/core/wfs.js +10 -0
- package/core/wms-capabilities.d.ts +56 -84
- package/core/wms-capabilities.d.ts.map +1 -1
- package/core/wms-capabilities.js +212 -509
- package/core/wmts-capabilities.d.ts +62 -62
- package/core/wmts-capabilities.d.ts.map +1 -1
- package/core/wmts-capabilities.js +224 -371
- package/esri-compat/editor.d.ts +8 -0
- package/esri-compat/editor.d.ts.map +1 -1
- package/esri-compat/editor.js +13 -0
- package/esri-compat/feature-layer.d.ts +2 -0
- package/esri-compat/feature-layer.d.ts.map +1 -1
- package/esri-compat/feature-layer.js +1 -0
- package/esri-compat/layer-list.d.ts +11 -0
- package/esri-compat/layer-list.d.ts.map +1 -1
- package/esri-compat/layer-list.js +49 -0
- package/esri-compat/legend.d.ts +10 -0
- package/esri-compat/legend.d.ts.map +1 -1
- package/esri-compat/legend.js +27 -0
- package/esri-compat/portal.d.ts +197 -0
- package/esri-compat/portal.d.ts.map +1 -0
- package/esri-compat/portal.js +251 -0
- package/esri-compat/renderer-objects.d.ts +40 -0
- package/esri-compat/renderer-objects.d.ts.map +1 -0
- package/esri-compat/renderer-objects.js +62 -0
- package/esri-compat/sketch.d.ts +43 -0
- package/esri-compat/sketch.d.ts.map +1 -1
- package/esri-compat/sketch.js +46 -0
- package/esri-compat/snapping.d.ts +43 -0
- package/esri-compat/snapping.d.ts.map +1 -0
- package/esri-compat/snapping.js +45 -0
- package/esri-compat/url.d.ts.map +1 -1
- package/esri-compat/url.js +1 -1
- package/esri-compat/widget-host.d.ts +84 -0
- package/esri-compat/widget-host.d.ts.map +1 -0
- package/esri-compat/widget-host.js +159 -0
- package/generated-app/errors.d.ts +18 -2
- package/generated-app/errors.d.ts.map +1 -1
- package/generated-app/errors.js +44 -11
- package/geocoding/index.d.ts +7 -0
- package/geocoding/index.d.ts.map +1 -1
- package/geocoding/index.js +5 -0
- package/geocoding/provider-http.d.ts +37 -0
- package/geocoding/provider-http.d.ts.map +1 -0
- package/geocoding/provider-http.js +91 -0
- package/geocoding/provider.d.ts +145 -0
- package/geocoding/provider.d.ts.map +1 -0
- package/geocoding/provider.js +41 -0
- package/geocoding/providers/honua.d.ts +28 -0
- package/geocoding/providers/honua.d.ts.map +1 -0
- package/geocoding/providers/honua.js +68 -0
- package/geocoding/providers/nominatim.d.ts +47 -0
- package/geocoding/providers/nominatim.d.ts.map +1 -0
- package/geocoding/providers/nominatim.js +105 -0
- package/geocoding/providers/pelias.d.ts +40 -0
- package/geocoding/providers/pelias.d.ts.map +1 -0
- package/geocoding/providers/pelias.js +115 -0
- package/geocoding/providers/photon.d.ts +39 -0
- package/geocoding/providers/photon.d.ts.map +1 -0
- package/geocoding/providers/photon.js +107 -0
- package/geometry/convert.d.ts +13 -0
- package/geometry/convert.d.ts.map +1 -0
- package/geometry/convert.js +12 -0
- package/geometry/index.d.ts +17 -0
- package/geometry/index.d.ts.map +1 -0
- package/geometry/index.js +15 -0
- package/geometry/internal.d.ts +25 -0
- package/geometry/internal.d.ts.map +1 -0
- package/geometry/internal.js +40 -0
- package/geometry/ops.d.ts +71 -0
- package/geometry/ops.d.ts.map +1 -0
- package/geometry/ops.js +147 -0
- package/geometry/project.d.ts +40 -0
- package/geometry/project.d.ts.map +1 -0
- package/geometry/project.js +88 -0
- package/geometry/types.d.ts +36 -0
- package/geometry/types.d.ts.map +1 -0
- package/geometry/types.js +12 -0
- package/map/automatic-mount-integration.d.ts +134 -0
- package/map/automatic-mount-integration.d.ts.map +1 -0
- package/map/automatic-mount-integration.js +305 -0
- package/map/automatic-source-strategy.d.ts +125 -0
- package/map/automatic-source-strategy.d.ts.map +1 -0
- package/map/automatic-source-strategy.js +494 -0
- package/map/data-to-map-bridge.d.ts +293 -0
- package/map/data-to-map-bridge.d.ts.map +1 -0
- package/map/data-to-map-bridge.js +817 -0
- package/map/geojson-projection.d.ts +33 -0
- package/map/geojson-projection.d.ts.map +1 -0
- package/map/geojson-projection.js +118 -0
- package/map/index.d.ts +14 -0
- package/map/index.d.ts.map +1 -1
- package/map/index.js +8 -0
- package/map/raster-source-spec.d.ts +30 -0
- package/map/raster-source-spec.d.ts.map +1 -0
- package/map/raster-source-spec.js +190 -0
- package/map/raster-source-strategy.d.ts +65 -0
- package/map/raster-source-strategy.d.ts.map +1 -0
- package/map/raster-source-strategy.js +232 -0
- package/map/source-to-maplibre.d.ts +91 -0
- package/map/source-to-maplibre.d.ts.map +1 -0
- package/map/source-to-maplibre.js +454 -0
- package/map/temporal-playback.d.ts +139 -0
- package/map/temporal-playback.d.ts.map +1 -0
- package/map/temporal-playback.js +247 -0
- package/package.json +33 -2
- package/query-planner/canonical.d.ts +7 -0
- package/query-planner/canonical.d.ts.map +1 -0
- package/query-planner/canonical.js +145 -0
- package/query-planner/cql2-json.d.ts +17 -0
- package/query-planner/cql2-json.d.ts.map +1 -0
- package/query-planner/cql2-json.js +646 -0
- package/query-planner/diagnostics.d.ts +35 -0
- package/query-planner/diagnostics.d.ts.map +1 -0
- package/query-planner/diagnostics.js +373 -0
- package/query-planner/duckdb.d.ts +79 -0
- package/query-planner/duckdb.d.ts.map +1 -0
- package/query-planner/duckdb.js +758 -0
- package/query-planner/execution-context.d.ts +5 -0
- package/query-planner/execution-context.d.ts.map +1 -0
- package/query-planner/execution-context.js +50 -0
- package/query-planner/executor.d.ts +5 -0
- package/query-planner/executor.d.ts.map +1 -0
- package/query-planner/executor.js +223 -0
- package/query-planner/geoservices.d.ts +91 -0
- package/query-planner/geoservices.d.ts.map +1 -0
- package/query-planner/geoservices.js +667 -0
- package/query-planner/grpc.d.ts +100 -0
- package/query-planner/grpc.d.ts.map +1 -0
- package/query-planner/grpc.js +736 -0
- package/query-planner/index.d.ts +44 -0
- package/query-planner/index.d.ts.map +1 -0
- package/query-planner/index.js +30 -0
- package/query-planner/ir.d.ts +20 -0
- package/query-planner/ir.d.ts.map +1 -0
- package/query-planner/ir.js +417 -0
- package/query-planner/local-aggregate.d.ts +5 -0
- package/query-planner/local-aggregate.d.ts.map +1 -0
- package/query-planner/local-aggregate.js +126 -0
- package/query-planner/odata.d.ts +48 -0
- package/query-planner/odata.d.ts.map +1 -0
- package/query-planner/odata.js +662 -0
- package/query-planner/ogc-compiler.d.ts +55 -0
- package/query-planner/ogc-compiler.d.ts.map +1 -0
- package/query-planner/ogc-compiler.js +480 -0
- package/query-planner/ogc-features.d.ts +53 -0
- package/query-planner/ogc-features.d.ts.map +1 -0
- package/query-planner/ogc-features.js +617 -0
- package/query-planner/planner.d.ts +17 -0
- package/query-planner/planner.d.ts.map +1 -0
- package/query-planner/planner.js +818 -0
- package/query-planner/resource.d.ts +94 -0
- package/query-planner/resource.d.ts.map +1 -0
- package/query-planner/resource.js +526 -0
- package/query-planner/semantic-canonical.d.ts +15 -0
- package/query-planner/semantic-canonical.d.ts.map +1 -0
- package/query-planner/semantic-canonical.js +62 -0
- package/query-planner/semantic-compat.d.ts +10 -0
- package/query-planner/semantic-compat.d.ts.map +1 -0
- package/query-planner/semantic-compat.js +35 -0
- package/query-planner/semantic-compiler.d.ts +64 -0
- package/query-planner/semantic-compiler.d.ts.map +1 -0
- package/query-planner/semantic-compiler.js +81 -0
- package/query-planner/semantic-literals.d.ts +30 -0
- package/query-planner/semantic-literals.d.ts.map +1 -0
- package/query-planner/semantic-literals.js +133 -0
- package/query-planner/semantic-types.d.ts +265 -0
- package/query-planner/semantic-types.d.ts.map +1 -0
- package/query-planner/semantic-types.js +1 -0
- package/query-planner/semantic.d.ts +54 -0
- package/query-planner/semantic.d.ts.map +1 -0
- package/query-planner/semantic.js +1421 -0
- package/query-planner/types.d.ts +560 -0
- package/query-planner/types.d.ts.map +1 -0
- package/query-planner/types.js +49 -0
- package/query-planner/wfs-semantic.d.ts +58 -0
- package/query-planner/wfs-semantic.d.ts.map +1 -0
- package/query-planner/wfs-semantic.js +547 -0
- package/query-planner/wfs.d.ts +6 -0
- package/query-planner/wfs.d.ts.map +1 -0
- package/query-planner/wfs.js +71 -0
- package/realtime/contract.d.ts +142 -0
- package/realtime/contract.d.ts.map +1 -0
- package/realtime/contract.js +154 -0
- package/realtime/honua-server.d.ts.map +1 -1
- package/realtime/honua-server.js +13 -8
- package/realtime/index.d.ts +19 -4
- package/realtime/index.d.ts.map +1 -1
- package/realtime/index.js +14 -4
- package/realtime/odata-delta.d.ts +114 -0
- package/realtime/odata-delta.d.ts.map +1 -0
- package/realtime/odata-delta.js +483 -0
- package/realtime/resumable-transport.d.ts +111 -0
- package/realtime/resumable-transport.d.ts.map +1 -0
- package/realtime/resumable-transport.js +406 -0
- package/realtime/resumable.d.ts +104 -0
- package/realtime/resumable.d.ts.map +1 -0
- package/realtime/resumable.js +632 -0
- package/realtime/sse.d.ts.map +1 -1
- package/realtime/sse.js +27 -23
- package/realtime/websocket.d.ts +60 -0
- package/realtime/websocket.d.ts.map +1 -0
- package/realtime/websocket.js +157 -0
- package/realtime/wire.d.ts +27 -0
- package/realtime/wire.d.ts.map +1 -0
- package/realtime/wire.js +57 -0
- package/replica-sync/errors.d.ts +2 -1
- package/replica-sync/errors.d.ts.map +1 -1
- package/replica-sync/errors.js +33 -2
- package/runtime/edit-snapping.d.ts +107 -0
- package/runtime/edit-snapping.d.ts.map +1 -0
- package/runtime/edit-snapping.js +190 -0
- package/runtime/errors.d.ts +3 -2
- package/runtime/errors.d.ts.map +1 -1
- package/runtime/errors.js +19 -2
- package/runtime/index.d.ts +6 -0
- package/runtime/index.d.ts.map +1 -1
- package/runtime/index.js +3 -0
- package/runtime/maplibre-renderer.d.ts +41 -0
- package/runtime/maplibre-renderer.d.ts.map +1 -0
- package/runtime/maplibre-renderer.js +322 -0
- package/runtime/query-tiles.d.ts +2 -1
- package/runtime/query-tiles.d.ts.map +1 -1
- package/runtime/query-tiles.js +6 -2
- package/runtime/source-bridge.d.ts +4 -4
- package/runtime/source-bridge.d.ts.map +1 -1
- package/runtime/source-bridge.js +9 -56
- package/runtime/style-interactions.d.ts +3 -2
- package/runtime/style-interactions.d.ts.map +1 -1
- package/runtime/style-interactions.js +11 -3
- package/runtime/terra-draw-sketch.d.ts +275 -0
- package/runtime/terra-draw-sketch.d.ts.map +1 -0
- package/runtime/terra-draw-sketch.js +404 -0
- package/scene-workspace/index.d.ts +4 -0
- package/scene-workspace/index.d.ts.map +1 -1
- package/scene-workspace/index.js +2 -0
- package/scene-workspace/source-to-cesium.d.ts +116 -0
- package/scene-workspace/source-to-cesium.d.ts.map +1 -0
- package/scene-workspace/source-to-cesium.js +791 -0
- package/scene-workspace/state-sync.d.ts +147 -0
- package/scene-workspace/state-sync.d.ts.map +1 -0
- package/scene-workspace/state-sync.js +1123 -0
- package/source-capability-registry.d.ts +1 -0
- package/source-capability-registry.js +1 -0
- package/source-capability-types.d.ts +186 -0
- package/source-capability-types.js +7 -0
- package/style/index.d.ts +2 -0
- package/style/index.d.ts.map +1 -1
- package/style/index.js +2 -0
- package/style/renderers.d.ts +326 -0
- package/style/renderers.d.ts.map +1 -0
- package/style/renderers.js +517 -0
- package/web-components/controller.d.ts +2 -0
- package/web-components/controller.d.ts.map +1 -1
- package/web-components/controller.js +49 -8
- package/web-components/elements.d.ts +95 -1
- package/web-components/elements.d.ts.map +1 -1
- package/web-components/elements.js +458 -19
- package/web-components/index.d.ts +8 -1
- package/web-components/index.d.ts.map +1 -1
- package/web-components/index.js +7 -0
- package/web-components/maplibre-renderer.d.ts.map +1 -1
- package/web-components/maplibre-renderer.js +87 -0
- package/web-components/measurement.d.ts +69 -0
- package/web-components/measurement.d.ts.map +1 -0
- package/web-components/measurement.js +523 -0
- package/web-components/types.d.ts +109 -0
- package/web-components/types.d.ts.map +1 -1
- package/webmap/convert-renderer.d.ts +24 -3
- package/webmap/convert-renderer.d.ts.map +1 -1
- package/webmap/convert-renderer.js +93 -112
- package/webmap/index.d.ts +1 -1
- package/webmap/index.d.ts.map +1 -1
- package/webmap/index.js +1 -1
package/realtime/sse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeDefaultRealtimeEnvelope, normalizeRealtimeErrorEvent, realtimeFailure } from "./wire.js";
|
|
1
2
|
const DEFAULT_EVENT_TYPES = ["snapshot", "upsert", "delete", "delta", "heartbeat", "status", "error"];
|
|
2
3
|
export function createRealtimeServerSentEventsTransport(options) {
|
|
3
4
|
return {
|
|
@@ -18,20 +19,35 @@ export function createRealtimeServerSentEventsTransport(options) {
|
|
|
18
19
|
observer.complete();
|
|
19
20
|
return { close: () => { } };
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
let source;
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(options.url, "http://honua.local");
|
|
25
|
+
encodeDefaultRealtimeRequest(url, request);
|
|
26
|
+
options.encodeRequest?.(url, request);
|
|
27
|
+
source = createEventSource(resolveEventSourceUrl(options.url, url), options);
|
|
28
|
+
}
|
|
29
|
+
catch (cause) {
|
|
30
|
+
throw realtimeFailure("delivery-failed", "Unable to initialize the realtime Server-Sent Events transport.", cause);
|
|
31
|
+
}
|
|
25
32
|
let closed = false;
|
|
26
33
|
const listeners = [];
|
|
27
34
|
const emitPayload = (payload) => {
|
|
28
35
|
if (closed || payload.trim() === "")
|
|
29
36
|
return;
|
|
37
|
+
let event;
|
|
38
|
+
try {
|
|
39
|
+
const decoded = (options.decodeEvent ?? (decodeRealtimeServerSentEvent))(JSON.parse(payload));
|
|
40
|
+
event = normalizeRealtimeErrorEvent(decoded);
|
|
41
|
+
}
|
|
42
|
+
catch (cause) {
|
|
43
|
+
observer.error(realtimeFailure("invalid-event", "Realtime Server-Sent Events payload decoding failed.", cause));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
30
46
|
try {
|
|
31
|
-
observer.next(
|
|
47
|
+
observer.next(event);
|
|
32
48
|
}
|
|
33
|
-
catch (
|
|
34
|
-
observer.error(
|
|
49
|
+
catch (cause) {
|
|
50
|
+
observer.error(realtimeFailure("consumer-failed", "Realtime event observer rejected delivery.", cause));
|
|
35
51
|
}
|
|
36
52
|
};
|
|
37
53
|
const messageListener = (event) => emitPayload(event.data);
|
|
@@ -43,8 +59,9 @@ export function createRealtimeServerSentEventsTransport(options) {
|
|
|
43
59
|
source.onerror = (event) => {
|
|
44
60
|
if (!closed)
|
|
45
61
|
observer.next({ type: "status", status: "reconnecting", reason: "sse-error", receivedAt: Date.now() });
|
|
46
|
-
if (isEventSourceClosed(source))
|
|
47
|
-
observer.error(event);
|
|
62
|
+
if (isEventSourceClosed(source)) {
|
|
63
|
+
observer.error(realtimeFailure("transport-gap", "Realtime Server-Sent Events transport closed unexpectedly.", event));
|
|
64
|
+
}
|
|
48
65
|
};
|
|
49
66
|
for (const type of options.eventTypes ?? DEFAULT_EVENT_TYPES) {
|
|
50
67
|
if (!source.addEventListener)
|
|
@@ -106,17 +123,7 @@ export function encodeDefaultRealtimeRequest(url, request) {
|
|
|
106
123
|
return url;
|
|
107
124
|
}
|
|
108
125
|
export function decodeRealtimeServerSentEvent(payload) {
|
|
109
|
-
|
|
110
|
-
throw new Error("Realtime SSE payload must be a JSON object.");
|
|
111
|
-
if (typeof payload.type === "string")
|
|
112
|
-
return payload;
|
|
113
|
-
if (isRecord(payload.event) && typeof payload.event.type === "string") {
|
|
114
|
-
return payload.event;
|
|
115
|
-
}
|
|
116
|
-
if (typeof payload.kind === "string") {
|
|
117
|
-
return { ...payload, type: payload.kind };
|
|
118
|
-
}
|
|
119
|
-
throw new Error("Realtime SSE payload is missing an event type.");
|
|
126
|
+
return decodeDefaultRealtimeEnvelope(payload);
|
|
120
127
|
}
|
|
121
128
|
function createEventSource(url, options) {
|
|
122
129
|
if (options.eventSourceFactory) {
|
|
@@ -138,6 +145,3 @@ function resolveEventSourceUrl(input, url) {
|
|
|
138
145
|
function isEventSourceClosed(source) {
|
|
139
146
|
return source.readyState === 2;
|
|
140
147
|
}
|
|
141
|
-
function isRecord(value) {
|
|
142
|
-
return typeof value === "object" && value !== null;
|
|
143
|
-
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw WebSocket wire transport for `@honua/sdk-js/realtime`. Mirrors
|
|
3
|
+
* `sse.ts`'s adapter shape (subscribe params encoded onto the connection URL,
|
|
4
|
+
* default JSON event decoding, `HonuaRealtimeResumeError` failure tagging)
|
|
5
|
+
* so the two transports are interchangeable everywhere a
|
|
6
|
+
* `RealtimeFeatureTransport` is expected, including
|
|
7
|
+
* {@link "./resumable-transport.js".createResumableRealtimeTransport}.
|
|
8
|
+
*
|
|
9
|
+
* This module opens exactly one socket per `subscribe()` call and never
|
|
10
|
+
* reconnects on its own — reconnect/backoff, bounded queueing, and resume
|
|
11
|
+
* ownership live in `resumable-transport.ts` (#557), matching the resumable
|
|
12
|
+
* delivery gate's explicit non-goal ("the gate never reconnects a
|
|
13
|
+
* transport itself").
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { RealtimeFeatureEvent, RealtimeFeatureTransport, RealtimeSubscriptionRequest } from "./types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Minimal WebSocket surface the transport depends on. The global `WebSocket`
|
|
20
|
+
* constructor (browsers, and Node's built-in client since Node 22) already
|
|
21
|
+
* satisfies this; pass `webSocketFactory` to supply a polyfill or a mock in
|
|
22
|
+
* tests without adding a runtime dependency.
|
|
23
|
+
*/
|
|
24
|
+
export interface RealtimeWebSocket {
|
|
25
|
+
readonly readyState?: number;
|
|
26
|
+
onopen: ((event: Event) => void) | null;
|
|
27
|
+
onmessage: ((event: MessageEvent<string>) => void) | null;
|
|
28
|
+
onerror: ((event: Event) => void) | null;
|
|
29
|
+
onclose: ((event: RealtimeWebSocketCloseEvent) => void) | null;
|
|
30
|
+
close(code?: number, reason?: string): void;
|
|
31
|
+
send(data: string): void;
|
|
32
|
+
}
|
|
33
|
+
/** Subset of `CloseEvent` the transport reads; avoids depending on the DOM lib for `wasClean`. */
|
|
34
|
+
export interface RealtimeWebSocketCloseEvent {
|
|
35
|
+
readonly code?: number;
|
|
36
|
+
readonly reason?: string;
|
|
37
|
+
readonly wasClean?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export type RealtimeWebSocketFactory = (url: string, protocols: string | ReadonlyArray<string> | undefined) => RealtimeWebSocket;
|
|
40
|
+
export interface RealtimeWebSocketTransportOptions<TFeature = unknown> {
|
|
41
|
+
readonly url: string;
|
|
42
|
+
readonly protocols?: string | ReadonlyArray<string>;
|
|
43
|
+
readonly webSocketFactory?: RealtimeWebSocketFactory;
|
|
44
|
+
readonly encodeRequest?: (url: URL, request: RealtimeSubscriptionRequest) => void;
|
|
45
|
+
readonly decodeEvent?: (payload: unknown) => RealtimeFeatureEvent<TFeature>;
|
|
46
|
+
/**
|
|
47
|
+
* Sent as a single JSON text frame immediately after the socket opens, for
|
|
48
|
+
* servers that expect an explicit subscribe message instead of (or in
|
|
49
|
+
* addition to) URL query parameters. The default transport never sends a
|
|
50
|
+
* subscribe frame on its own.
|
|
51
|
+
*/
|
|
52
|
+
readonly encodeSubscribeMessage?: (request: RealtimeSubscriptionRequest) => unknown;
|
|
53
|
+
/**
|
|
54
|
+
* WebSocket close codes treated as a normal, non-reconnectable end of the
|
|
55
|
+
* stream rather than a `transport-gap` failure. @default [1000]
|
|
56
|
+
*/
|
|
57
|
+
readonly cleanCloseCodes?: ReadonlyArray<number>;
|
|
58
|
+
}
|
|
59
|
+
export declare function createRealtimeWebSocketTransport<TFeature = unknown>(options: RealtimeWebSocketTransportOptions<TFeature>): RealtimeFeatureTransport<TFeature>;
|
|
60
|
+
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../src/realtime/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EACV,oBAAoB,EAEpB,wBAAwB,EAExB,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAGpB;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,2BAA2B,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,kGAAkG;AAClG,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,KAClD,iBAAiB,CAAC;AAEvB,MAAM,WAAW,iCAAiC,CAAC,QAAQ,GAAG,OAAO;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IACrD,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;IAClF,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC5E;;;;;OAKG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,OAAO,CAAC;IACpF;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAClD;AAID,wBAAgB,gCAAgC,CAAC,QAAQ,GAAG,OAAO,EACjE,OAAO,EAAE,iCAAiC,CAAC,QAAQ,CAAC,GACnD,wBAAwB,CAAC,QAAQ,CAAC,CAwGpC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw WebSocket wire transport for `@honua/sdk-js/realtime`. Mirrors
|
|
3
|
+
* `sse.ts`'s adapter shape (subscribe params encoded onto the connection URL,
|
|
4
|
+
* default JSON event decoding, `HonuaRealtimeResumeError` failure tagging)
|
|
5
|
+
* so the two transports are interchangeable everywhere a
|
|
6
|
+
* `RealtimeFeatureTransport` is expected, including
|
|
7
|
+
* {@link "./resumable-transport.js".createResumableRealtimeTransport}.
|
|
8
|
+
*
|
|
9
|
+
* This module opens exactly one socket per `subscribe()` call and never
|
|
10
|
+
* reconnects on its own — reconnect/backoff, bounded queueing, and resume
|
|
11
|
+
* ownership live in `resumable-transport.ts` (#557), matching the resumable
|
|
12
|
+
* delivery gate's explicit non-goal ("the gate never reconnects a
|
|
13
|
+
* transport itself").
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { encodeDefaultRealtimeRequest } from "./sse.js";
|
|
18
|
+
import { decodeDefaultRealtimeEnvelope, normalizeRealtimeErrorEvent, realtimeFailure } from "./wire.js";
|
|
19
|
+
const DEFAULT_CLEAN_CLOSE_CODES = [1000];
|
|
20
|
+
export function createRealtimeWebSocketTransport(options) {
|
|
21
|
+
return {
|
|
22
|
+
capabilities: {
|
|
23
|
+
kind: "websocket",
|
|
24
|
+
resumeModes: ["cursor", "watermark", "timestamp", "sequence", "delta-token"],
|
|
25
|
+
emitsHeartbeats: true,
|
|
26
|
+
emitsWatermarks: true,
|
|
27
|
+
},
|
|
28
|
+
subscribe(request, observer) {
|
|
29
|
+
// Mirrors sse.ts: a signal already aborted at subscribe time will never
|
|
30
|
+
// fire its "abort" listener, so bail out before opening a socket that
|
|
31
|
+
// would otherwise leak.
|
|
32
|
+
if (request.signal?.aborted) {
|
|
33
|
+
observer.complete();
|
|
34
|
+
return { close: () => { } };
|
|
35
|
+
}
|
|
36
|
+
let socket;
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(options.url, "http://honua.local");
|
|
39
|
+
encodeDefaultRealtimeRequest(url, request);
|
|
40
|
+
options.encodeRequest?.(url, request);
|
|
41
|
+
socket = createSocket(resolveWebSocketUrl(options.url, url), options);
|
|
42
|
+
}
|
|
43
|
+
catch (cause) {
|
|
44
|
+
throw realtimeFailure("delivery-failed", "Unable to initialize the realtime WebSocket transport.", cause);
|
|
45
|
+
}
|
|
46
|
+
let closed = false;
|
|
47
|
+
const emitPayload = (payload) => {
|
|
48
|
+
if (closed || payload.trim() === "")
|
|
49
|
+
return;
|
|
50
|
+
let event;
|
|
51
|
+
try {
|
|
52
|
+
const decoded = (options.decodeEvent ?? (decodeDefaultRealtimeEnvelope))(JSON.parse(payload));
|
|
53
|
+
event = normalizeRealtimeErrorEvent(decoded);
|
|
54
|
+
}
|
|
55
|
+
catch (cause) {
|
|
56
|
+
observer.error(realtimeFailure("invalid-event", "Realtime WebSocket payload decoding failed.", cause));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
observer.next(event);
|
|
61
|
+
}
|
|
62
|
+
catch (cause) {
|
|
63
|
+
observer.error(realtimeFailure("consumer-failed", "Realtime event observer rejected delivery.", cause));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
socket.onopen = () => {
|
|
67
|
+
if (closed)
|
|
68
|
+
return;
|
|
69
|
+
observer.next({ type: "status", status: "live", receivedAt: Date.now() });
|
|
70
|
+
if (options.encodeSubscribeMessage) {
|
|
71
|
+
try {
|
|
72
|
+
socket.send(JSON.stringify(options.encodeSubscribeMessage(request)));
|
|
73
|
+
}
|
|
74
|
+
catch (cause) {
|
|
75
|
+
observer.error(realtimeFailure("delivery-failed", "Unable to send the realtime WebSocket subscribe message.", cause));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
socket.onmessage = (event) => emitPayload(event.data);
|
|
80
|
+
socket.onerror = () => {
|
|
81
|
+
if (!closed)
|
|
82
|
+
observer.next({ type: "status", status: "reconnecting", reason: "websocket-error", receivedAt: Date.now() });
|
|
83
|
+
};
|
|
84
|
+
socket.onclose = (event) => {
|
|
85
|
+
if (closed)
|
|
86
|
+
return;
|
|
87
|
+
const cleanCodes = options.cleanCloseCodes ?? DEFAULT_CLEAN_CLOSE_CODES;
|
|
88
|
+
const clean = event.wasClean !== false && event.code !== undefined && cleanCodes.includes(event.code);
|
|
89
|
+
closed = true;
|
|
90
|
+
request.signal?.removeEventListener("abort", abortListener);
|
|
91
|
+
detachSocket(socket);
|
|
92
|
+
if (clean) {
|
|
93
|
+
observer.complete();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
observer.error(realtimeFailure("transport-gap", `Realtime WebSocket transport closed unexpectedly (code ${event.code ?? "unknown"}).`, event));
|
|
97
|
+
};
|
|
98
|
+
const abortListener = () => closeSocket();
|
|
99
|
+
request.signal?.addEventListener("abort", abortListener, { once: true });
|
|
100
|
+
function closeSocket() {
|
|
101
|
+
if (closed)
|
|
102
|
+
return;
|
|
103
|
+
closed = true;
|
|
104
|
+
request.signal?.removeEventListener("abort", abortListener);
|
|
105
|
+
detachSocket(socket);
|
|
106
|
+
try {
|
|
107
|
+
socket.close(1000, "Subscription closed by caller.");
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Closing an already-broken socket is not itself a delivery
|
|
111
|
+
// failure; the caller asked to stop, and stop is what happens.
|
|
112
|
+
}
|
|
113
|
+
observer.complete();
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
close: closeSocket,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function detachSocket(socket) {
|
|
122
|
+
socket.onopen = null;
|
|
123
|
+
socket.onmessage = null;
|
|
124
|
+
socket.onerror = null;
|
|
125
|
+
socket.onclose = null;
|
|
126
|
+
}
|
|
127
|
+
function createSocket(url, options) {
|
|
128
|
+
if (options.webSocketFactory) {
|
|
129
|
+
return options.webSocketFactory(url, options.protocols);
|
|
130
|
+
}
|
|
131
|
+
// The global `WebSocket` is a constructor; guard rather than assume it is
|
|
132
|
+
// present so Node runtimes without a global WebSocket (or older Node) get
|
|
133
|
+
// an actionable error instead of a silent `undefined` crash, mirroring the
|
|
134
|
+
// `EventSource` guard in sse.ts. No runtime dependency is added for this:
|
|
135
|
+
// `WebSocket` is a web-standard global, optionally polyfilled by callers.
|
|
136
|
+
const WebSocketCtor = globalThis.WebSocket;
|
|
137
|
+
if (!WebSocketCtor) {
|
|
138
|
+
throw new Error("WebSocket is not available; provide webSocketFactory for this runtime.");
|
|
139
|
+
}
|
|
140
|
+
return new WebSocketCtor(url, options.protocols);
|
|
141
|
+
}
|
|
142
|
+
function resolveWebSocketUrl(input, url) {
|
|
143
|
+
if (input.startsWith("ws://") ||
|
|
144
|
+
input.startsWith("wss://") ||
|
|
145
|
+
input.startsWith("http://") ||
|
|
146
|
+
input.startsWith("https://")) {
|
|
147
|
+
// A WebSocket URL must use the ws(s) scheme; rewrite an http(s) origin
|
|
148
|
+
// the same way callers commonly configure REST base URLs.
|
|
149
|
+
const rewritten = new URL(url.toString());
|
|
150
|
+
if (rewritten.protocol === "http:")
|
|
151
|
+
rewritten.protocol = "ws:";
|
|
152
|
+
else if (rewritten.protocol === "https:")
|
|
153
|
+
rewritten.protocol = "wss:";
|
|
154
|
+
return rewritten.toString();
|
|
155
|
+
}
|
|
156
|
+
return `${url.pathname}${url.search}`;
|
|
157
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-decoding and failure-tagging helpers shared by every raw realtime
|
|
3
|
+
* transport adapter (`sse.ts`, `websocket.ts`). Extracted so the two
|
|
4
|
+
* transports decode the same default JSON event vocabulary and classify
|
|
5
|
+
* failures through the exact same `HonuaRealtimeResumeError` taxonomy
|
|
6
|
+
* instead of maintaining two copies of the same parsing logic.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { HonuaRealtimeResumeError } from "./resumable.js";
|
|
11
|
+
import type { RealtimeFeatureEvent } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Default JSON envelope decoder shared by the SSE and WebSocket transports.
|
|
14
|
+
* Accepts a bare `{ type: ... }` event, a `{ event: { type: ... } } }`
|
|
15
|
+
* wrapper, or a `{ kind: ... }` alias for `type`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeDefaultRealtimeEnvelope<TFeature = unknown>(payload: unknown): RealtimeFeatureEvent<TFeature>;
|
|
18
|
+
/**
|
|
19
|
+
* Never trust a structural tag received over the wire. A server could spoof
|
|
20
|
+
* the common envelope while retaining unsanitized context and no `toJSON`.
|
|
21
|
+
* Only a locally constructed realtime error is already inside the boundary.
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeRealtimeErrorEvent<TFeature>(event: RealtimeFeatureEvent<TFeature>): RealtimeFeatureEvent<TFeature>;
|
|
24
|
+
export type RealtimeTransportFailureCode = "consumer-failed" | "delivery-failed" | "invalid-event" | "transport-gap";
|
|
25
|
+
export declare function realtimeFailure(code: RealtimeTransportFailureCode, message: string, cause: unknown): HonuaRealtimeResumeError;
|
|
26
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
27
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../src/realtime/wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAclH;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAClD,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACpC,oBAAoB,CAAC,QAAQ,CAAC,CAehC;AAED,MAAM,MAAM,4BAA4B,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,eAAe,GAAG,eAAe,CAAC;AAErH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,4BAA4B,EAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,GACb,wBAAwB,CAI1B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
|
package/realtime/wire.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-decoding and failure-tagging helpers shared by every raw realtime
|
|
3
|
+
* transport adapter (`sse.ts`, `websocket.ts`). Extracted so the two
|
|
4
|
+
* transports decode the same default JSON event vocabulary and classify
|
|
5
|
+
* failures through the exact same `HonuaRealtimeResumeError` taxonomy
|
|
6
|
+
* instead of maintaining two copies of the same parsing logic.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { isHonuaSdkError } from "../core/error-envelope.js";
|
|
11
|
+
import { HonuaRealtimeResumeError } from "./resumable.js";
|
|
12
|
+
/**
|
|
13
|
+
* Default JSON envelope decoder shared by the SSE and WebSocket transports.
|
|
14
|
+
* Accepts a bare `{ type: ... }` event, a `{ event: { type: ... } } }`
|
|
15
|
+
* wrapper, or a `{ kind: ... }` alias for `type`.
|
|
16
|
+
*/
|
|
17
|
+
export function decodeDefaultRealtimeEnvelope(payload) {
|
|
18
|
+
if (!isRecord(payload)) {
|
|
19
|
+
throw new HonuaRealtimeResumeError("invalid-event", "Realtime payload must be a JSON object.");
|
|
20
|
+
}
|
|
21
|
+
if (typeof payload.type === "string") {
|
|
22
|
+
return normalizeRealtimeErrorEvent(payload);
|
|
23
|
+
}
|
|
24
|
+
if (isRecord(payload.event) && typeof payload.event.type === "string") {
|
|
25
|
+
return normalizeRealtimeErrorEvent(payload.event);
|
|
26
|
+
}
|
|
27
|
+
if (typeof payload.kind === "string") {
|
|
28
|
+
return normalizeRealtimeErrorEvent({ ...payload, type: payload.kind });
|
|
29
|
+
}
|
|
30
|
+
throw new HonuaRealtimeResumeError("invalid-event", "Realtime payload is missing an event type.");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Never trust a structural tag received over the wire. A server could spoof
|
|
34
|
+
* the common envelope while retaining unsanitized context and no `toJSON`.
|
|
35
|
+
* Only a locally constructed realtime error is already inside the boundary.
|
|
36
|
+
*/
|
|
37
|
+
export function normalizeRealtimeErrorEvent(event) {
|
|
38
|
+
if (event.type !== "error")
|
|
39
|
+
return event;
|
|
40
|
+
const code = event.terminal ? "invalid-event" : "transport-gap";
|
|
41
|
+
if (event.error instanceof HonuaRealtimeResumeError && isHonuaSdkError(event.error) && event.error.code === code)
|
|
42
|
+
return event;
|
|
43
|
+
return {
|
|
44
|
+
...event,
|
|
45
|
+
error: realtimeFailure(code, event.terminal
|
|
46
|
+
? "Realtime stream reported a terminal protocol failure."
|
|
47
|
+
: "Realtime stream reported a reconnectable transport failure.", event.error),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function realtimeFailure(code, message, cause) {
|
|
51
|
+
return cause instanceof HonuaRealtimeResumeError && isHonuaSdkError(cause) && cause.code === code
|
|
52
|
+
? cause
|
|
53
|
+
: new HonuaRealtimeResumeError(code, message, { cause });
|
|
54
|
+
}
|
|
55
|
+
export function isRecord(value) {
|
|
56
|
+
return typeof value === "object" && value !== null;
|
|
57
|
+
}
|
package/replica-sync/errors.d.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
+
import { HonuaSdkError } from "../core/error-envelope.js";
|
|
10
11
|
export type ReplicaSyncErrorCode = "unsupported-sync" | "unsupported-conflict-review" | "unsupported-conflict-resolution" | "replica-not-found" | "conflict-not-found" | "replica-expired" | "conflict-already-resolved" | "merge-required" | "permission-denied" | "transport-failure";
|
|
11
|
-
export declare class HonuaReplicaSyncError extends
|
|
12
|
+
export declare class HonuaReplicaSyncError extends HonuaSdkError {
|
|
12
13
|
readonly code: ReplicaSyncErrorCode;
|
|
13
14
|
readonly details: unknown;
|
|
14
15
|
constructor(code: ReplicaSyncErrorCode, message: string, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/replica-sync/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB,6BAA6B,GAC7B,iCAAiC,GACjC,mBAAmB,GACnB,oBAAoB,GACpB,iBAAiB,GACjB,2BAA2B,GAC3B,gBAAgB,GAChB,mBAAmB,GACnB,mBAAmB,CAAC;AAExB,qBAAa,qBAAsB,SAAQ,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/replica-sync/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAuB,aAAa,EAAyC,MAAM,2BAA2B,CAAC;AAEtH,MAAM,MAAM,oBAAoB,GAC5B,kBAAkB,GAClB,6BAA6B,GAC7B,iCAAiC,GACjC,mBAAmB,GACnB,oBAAoB,GACpB,iBAAiB,GACjB,2BAA2B,GAC3B,gBAAgB,GAChB,mBAAmB,GACnB,mBAAmB,CAAC;AAExB,qBAAa,qBAAsB,SAAQ,aAAa;IACtD,SAAgB,IAAI,EAAE,oBAAoB,CAAC;IAC3C,SAAgB,OAAO,EAAE,OAAO,CAAC;gBAG/B,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAWzE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAEtF;AAED,oDAAoD;AACpD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAO5F"}
|
package/replica-sync/errors.js
CHANGED
|
@@ -7,11 +7,16 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
import { HonuaSdkError, isRetryableNetworkOrTimeoutHonuaError } from "../core/error-envelope.js";
|
|
11
|
+
export class HonuaReplicaSyncError extends HonuaSdkError {
|
|
11
12
|
code;
|
|
12
13
|
details;
|
|
13
14
|
constructor(code, message, options = {}) {
|
|
14
|
-
|
|
15
|
+
const cause = options.cause;
|
|
16
|
+
super(replicaSyncSdkCode(code, cause), message, {
|
|
17
|
+
...(cause === undefined ? {} : { cause }),
|
|
18
|
+
context: { reasonCode: replicaSyncContextReason(code) },
|
|
19
|
+
});
|
|
15
20
|
this.name = "HonuaReplicaSyncError";
|
|
16
21
|
this.code = code;
|
|
17
22
|
this.details = options.details;
|
|
@@ -27,3 +32,29 @@ export function isUnsupportedReplicaSyncError(error) {
|
|
|
27
32
|
error.code === "unsupported-conflict-review" ||
|
|
28
33
|
error.code === "unsupported-conflict-resolution"));
|
|
29
34
|
}
|
|
35
|
+
const REPLICA_SYNC_ERROR_CODES = {
|
|
36
|
+
"unsupported-sync": "offline.replica-sync.capability",
|
|
37
|
+
"unsupported-conflict-review": "offline.replica-sync.capability",
|
|
38
|
+
"unsupported-conflict-resolution": "offline.replica-sync.capability",
|
|
39
|
+
"replica-not-found": "offline.replica-sync.validation",
|
|
40
|
+
"conflict-not-found": "offline.replica-sync.validation",
|
|
41
|
+
"replica-expired": "offline.replica-sync.validation",
|
|
42
|
+
"conflict-already-resolved": "offline.replica-sync.validation",
|
|
43
|
+
"merge-required": "offline.replica-sync.validation",
|
|
44
|
+
"permission-denied": "offline.replica-sync.permission-denied",
|
|
45
|
+
"transport-failure": "offline.transport.failure",
|
|
46
|
+
};
|
|
47
|
+
function replicaSyncSdkCode(code, cause) {
|
|
48
|
+
if (!isReplicaSyncErrorCode(code))
|
|
49
|
+
return "offline.replica-sync.validation";
|
|
50
|
+
if (code === "transport-failure" && isRetryableNetworkOrTimeoutHonuaError(cause)) {
|
|
51
|
+
return "offline.transport.transient";
|
|
52
|
+
}
|
|
53
|
+
return REPLICA_SYNC_ERROR_CODES[code];
|
|
54
|
+
}
|
|
55
|
+
function replicaSyncContextReason(code) {
|
|
56
|
+
return isReplicaSyncErrorCode(code) ? code : "invalid-error-code";
|
|
57
|
+
}
|
|
58
|
+
function isReplicaSyncErrorCode(code) {
|
|
59
|
+
return typeof code === "string" && Object.hasOwn(REPLICA_SYNC_ERROR_CODES, code);
|
|
60
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MapLibre runtime binding for edit-sketch snapping.
|
|
3
|
+
*
|
|
4
|
+
* Wires pointer-move on a MapLibre-shaped map to the renderer-neutral
|
|
5
|
+
* snapping engine in `contract/edit-snapping.ts`, applies snapped
|
|
6
|
+
* coordinates to an `EditSketchWorkflowModel`, surfaces snapped / unsnapped
|
|
7
|
+
* events, and maintains a default visual indicator via a lightweight GeoJSON
|
|
8
|
+
* circle layer.
|
|
9
|
+
*
|
|
10
|
+
* The map interfaces are duck-typed (same pattern as
|
|
11
|
+
* `interactions/feature-state.ts`) so the SDK carries no hard runtime
|
|
12
|
+
* dependency on `maplibre-gl`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import type { EditSketchTool, EditSketchWorkflowModel } from "../contract/edit-sketch.js";
|
|
17
|
+
import { type SnapCandidate, type SnapIndex, type SnapPosition, type SnapResolution, type SnapScreenPoint, type SnappingConfig } from "../contract/edit-snapping.js";
|
|
18
|
+
/** Minimal subset of a MapLibre `Map` needed for snapping. */
|
|
19
|
+
export interface SnappingMap {
|
|
20
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
21
|
+
off(event: string, handler: (...args: unknown[]) => void): void;
|
|
22
|
+
project(position: readonly [number, number]): SnapScreenPoint | {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
};
|
|
26
|
+
addSource?(id: string, source: Record<string, unknown>): void;
|
|
27
|
+
removeSource?(id: string): void;
|
|
28
|
+
addLayer?(layer: Record<string, unknown>, beforeId?: string): void;
|
|
29
|
+
removeLayer?(id: string): void;
|
|
30
|
+
getSource?(id: string): unknown;
|
|
31
|
+
getLayer?(id: string): unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface EditSketchSnapIndicatorOptions {
|
|
34
|
+
/** Render the default indicator layer. @default true */
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
/** Indicator source and layer id. @default "honua-snap-indicator" */
|
|
37
|
+
id?: string;
|
|
38
|
+
/** Paint overrides merged over the default circle paint. */
|
|
39
|
+
paint?: Record<string, unknown>;
|
|
40
|
+
/** Insert the indicator layer before this layer id. */
|
|
41
|
+
beforeId?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface EditSketchSnappingEvents {
|
|
44
|
+
/** Fired when the pointer acquires or changes a snap target. */
|
|
45
|
+
onSnap?(resolution: SnapResolution): void;
|
|
46
|
+
/** Fired when the pointer leaves a snap target; receives the lost candidate. */
|
|
47
|
+
onUnsnap?(previous: SnapCandidate): void;
|
|
48
|
+
}
|
|
49
|
+
export interface BindEditSketchSnappingOptions<T = Record<string, unknown>> extends EditSketchSnappingEvents {
|
|
50
|
+
/** Snap index over the loaded snap-source features. */
|
|
51
|
+
index: SnapIndex;
|
|
52
|
+
/**
|
|
53
|
+
* Sketch workflow whose snapping config drives resolution and that
|
|
54
|
+
* receives snapped geometry via {@link EditSketchSnappingHandle.applySketchGeometry}.
|
|
55
|
+
* When omitted, pass `config` and consume snaps through events / `current`.
|
|
56
|
+
*/
|
|
57
|
+
model?: EditSketchWorkflowModel<T>;
|
|
58
|
+
/** Snapping configuration when no model is bound (or overrides for one-off use). */
|
|
59
|
+
config?: Partial<SnappingConfig>;
|
|
60
|
+
/** Pointer event name to listen for. @default "mousemove" */
|
|
61
|
+
pointerEvent?: string;
|
|
62
|
+
/** Default snap indicator; `false` disables it. */
|
|
63
|
+
indicator?: boolean | EditSketchSnapIndicatorOptions;
|
|
64
|
+
}
|
|
65
|
+
export interface EditSketchSnappingHandle {
|
|
66
|
+
/** The currently snapped candidate, if any. */
|
|
67
|
+
readonly current: SnapCandidate | undefined;
|
|
68
|
+
/** Resolve a pointer position without going through a map event. */
|
|
69
|
+
resolve(input: {
|
|
70
|
+
point: SnapScreenPoint;
|
|
71
|
+
position: SnapPosition;
|
|
72
|
+
}): SnapResolution;
|
|
73
|
+
/** Return the snapped position for `position`, or `position` unchanged. */
|
|
74
|
+
snapPosition(position: SnapPosition): SnapPosition;
|
|
75
|
+
/** Effective snapping configuration. */
|
|
76
|
+
config(): SnappingConfig;
|
|
77
|
+
/** Merge configuration changes (writes through to the bound model). */
|
|
78
|
+
setConfig(config: Partial<SnappingConfig>): void;
|
|
79
|
+
/**
|
|
80
|
+
* Apply `geometry` to the bound sketch model with its active vertex
|
|
81
|
+
* replaced by the current snap position (no-op replacement when the
|
|
82
|
+
* pointer is not snapped). Returns `true` when the model accepted the
|
|
83
|
+
* geometry.
|
|
84
|
+
*/
|
|
85
|
+
applySketchGeometry(tool: EditSketchTool, geometry: Record<string, unknown> | null): boolean;
|
|
86
|
+
/** Detach event handlers and remove the indicator layer. */
|
|
87
|
+
remove(): void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Bind edit-sketch snapping to a MapLibre-shaped map.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const index = createSnapIndex();
|
|
95
|
+
* index.setSourceFeatures("parcels", parcels);
|
|
96
|
+
* const snapping = bindEditSketchSnapping(map, {
|
|
97
|
+
* index,
|
|
98
|
+
* model: workflow,
|
|
99
|
+
* onSnap: ({ candidate }) => showGuide(candidate),
|
|
100
|
+
* });
|
|
101
|
+
* map.on("click", (e) => {
|
|
102
|
+
* snapping.applySketchGeometry("point", { type: "Point", coordinates: [e.lngLat.lng, e.lngLat.lat] });
|
|
103
|
+
* });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare function bindEditSketchSnapping<T = Record<string, unknown>>(map: SnappingMap, options: BindEditSketchSnappingOptions<T>): EditSketchSnappingHandle;
|
|
107
|
+
//# sourceMappingURL=edit-snapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-snapping.d.ts","sourceRoot":"","sources":["../../../src/runtime/edit-snapping.ts"],"names":[],"mappings":"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"}
|