@honua/react 0.1.0-beta.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.
Files changed (315) hide show
  1. package/contract/discovery.d.ts +231 -0
  2. package/contract/discovery.d.ts.map +1 -0
  3. package/contract/discovery.js +554 -0
  4. package/contract/edit-sketch.d.ts +8 -0
  5. package/contract/edit-sketch.d.ts.map +1 -1
  6. package/contract/edit-sketch.js +15 -0
  7. package/contract/edit-snapping.d.ts +160 -0
  8. package/contract/edit-snapping.d.ts.map +1 -0
  9. package/contract/edit-snapping.js +675 -0
  10. package/contract/generated/projjson-v0.7-crs-validator.d.ts +35 -0
  11. package/contract/generated/projjson-v0.7-crs-validator.d.ts.map +1 -0
  12. package/contract/generated/projjson-v0.7-crs-validator.js +3941 -0
  13. package/contract/index.d.ts +11 -2
  14. package/contract/index.d.ts.map +1 -1
  15. package/contract/index.js +4 -1
  16. package/contract/pmtiles.d.ts +31 -0
  17. package/contract/pmtiles.d.ts.map +1 -1
  18. package/contract/pmtiles.js +60 -20
  19. package/contract/protocol-module.d.ts +70 -0
  20. package/contract/protocol-module.d.ts.map +1 -0
  21. package/contract/protocol-module.js +1 -0
  22. package/contract/schema-envelope.d.ts +26 -0
  23. package/contract/schema-envelope.d.ts.map +1 -0
  24. package/contract/schema-envelope.js +25 -0
  25. package/contract/schema.d.ts +582 -0
  26. package/contract/schema.d.ts.map +1 -0
  27. package/contract/schema.js +2483 -0
  28. package/contract/source-capability-support.d.ts +6 -0
  29. package/contract/source-capability-support.d.ts.map +1 -0
  30. package/contract/source-capability-support.js +154 -0
  31. package/contract/source.d.ts +35 -17
  32. package/contract/source.d.ts.map +1 -1
  33. package/contract/source.js +267 -71
  34. package/contract/types.d.ts +140 -4
  35. package/contract/types.d.ts.map +1 -1
  36. package/contract/types.js +6 -14
  37. package/core/bounded-json.d.ts +4 -0
  38. package/core/bounded-json.d.ts.map +1 -0
  39. package/core/bounded-json.js +38 -0
  40. package/core/cache-state.d.ts +9 -0
  41. package/core/cache-state.d.ts.map +1 -1
  42. package/core/cache-state.js +10 -0
  43. package/core/capabilities-xml.d.ts +33 -0
  44. package/core/capabilities-xml.d.ts.map +1 -0
  45. package/core/capabilities-xml.js +331 -0
  46. package/core/client.d.ts +4 -1
  47. package/core/client.d.ts.map +1 -1
  48. package/core/client.js +115 -29
  49. package/core/error-classifications.d.ts +153 -0
  50. package/core/error-classifications.d.ts.map +1 -0
  51. package/core/error-classifications.js +140 -0
  52. package/core/error-code-registry.d.ts +158 -0
  53. package/core/error-code-registry.d.ts.map +1 -0
  54. package/core/error-code-registry.js +143 -0
  55. package/core/error-envelope.d.ts +68 -0
  56. package/core/error-envelope.d.ts.map +1 -0
  57. package/core/error-envelope.js +357 -0
  58. package/core/errors.d.ts +45 -29
  59. package/core/errors.d.ts.map +1 -1
  60. package/core/errors.js +97 -50
  61. package/core/geoparquet-sql.d.ts +30 -2
  62. package/core/geoparquet-sql.d.ts.map +1 -1
  63. package/core/geoparquet-sql.js +38 -3
  64. package/core/odata-key-path.d.ts +12 -0
  65. package/core/odata-key-path.d.ts.map +1 -0
  66. package/core/odata-key-path.js +28 -0
  67. package/core/odata-write-codec.d.ts +66 -0
  68. package/core/odata-write-codec.d.ts.map +1 -0
  69. package/core/odata-write-codec.js +1083 -0
  70. package/core/odata.d.ts +59 -7
  71. package/core/odata.d.ts.map +1 -1
  72. package/core/odata.js +431 -99
  73. package/core/ogc-features.d.ts.map +1 -1
  74. package/core/ogc-features.js +3 -0
  75. package/core/ogc-maps.d.ts +7 -0
  76. package/core/ogc-maps.d.ts.map +1 -1
  77. package/core/ogc-maps.js +46 -7
  78. package/core/ogc-processes.d.ts +2 -1
  79. package/core/ogc-processes.d.ts.map +1 -1
  80. package/core/ogc-processes.js +33 -5
  81. package/core/ogc-records.d.ts +9 -0
  82. package/core/ogc-records.d.ts.map +1 -1
  83. package/core/ogc-records.js +78 -29
  84. package/core/ogc-tiles.d.ts +10 -1
  85. package/core/ogc-tiles.d.ts.map +1 -1
  86. package/core/ogc-tiles.js +56 -17
  87. package/core/path-utils.d.ts +9 -0
  88. package/core/path-utils.d.ts.map +1 -1
  89. package/core/path-utils.js +19 -0
  90. package/core/protocol-transport.d.ts +1 -0
  91. package/core/protocol-transport.d.ts.map +1 -1
  92. package/core/request-pipeline.d.ts.map +1 -1
  93. package/core/request-pipeline.js +1 -1
  94. package/core/spatial-filter.d.ts +8 -4
  95. package/core/spatial-filter.d.ts.map +1 -1
  96. package/core/spatial-filter.js +206 -16
  97. package/core/stac-static.d.ts +6 -2
  98. package/core/stac-static.d.ts.map +1 -1
  99. package/core/stac-static.js +260 -69
  100. package/core/surfaces.d.ts.map +1 -1
  101. package/core/surfaces.js +69 -5
  102. package/core/types.d.ts +125 -7
  103. package/core/types.d.ts.map +1 -1
  104. package/core/wfs-capabilities.d.ts +2 -0
  105. package/core/wfs-capabilities.d.ts.map +1 -1
  106. package/core/wfs-capabilities.js +8 -1
  107. package/core/wfs.d.ts.map +1 -1
  108. package/core/wfs.js +10 -0
  109. package/core/wms-capabilities.d.ts +56 -84
  110. package/core/wms-capabilities.d.ts.map +1 -1
  111. package/core/wms-capabilities.js +212 -509
  112. package/core/wmts-capabilities.d.ts +62 -62
  113. package/core/wmts-capabilities.d.ts.map +1 -1
  114. package/core/wmts-capabilities.js +224 -371
  115. package/esri-compat/editor.d.ts +8 -0
  116. package/esri-compat/editor.d.ts.map +1 -1
  117. package/esri-compat/editor.js +13 -0
  118. package/esri-compat/feature-layer.d.ts +2 -0
  119. package/esri-compat/feature-layer.d.ts.map +1 -1
  120. package/esri-compat/feature-layer.js +1 -0
  121. package/esri-compat/layer-list.d.ts +11 -0
  122. package/esri-compat/layer-list.d.ts.map +1 -1
  123. package/esri-compat/layer-list.js +49 -0
  124. package/esri-compat/legend.d.ts +10 -0
  125. package/esri-compat/legend.d.ts.map +1 -1
  126. package/esri-compat/legend.js +27 -0
  127. package/esri-compat/portal.d.ts +197 -0
  128. package/esri-compat/portal.d.ts.map +1 -0
  129. package/esri-compat/portal.js +251 -0
  130. package/esri-compat/renderer-objects.d.ts +40 -0
  131. package/esri-compat/renderer-objects.d.ts.map +1 -0
  132. package/esri-compat/renderer-objects.js +62 -0
  133. package/esri-compat/sketch.d.ts +43 -0
  134. package/esri-compat/sketch.d.ts.map +1 -1
  135. package/esri-compat/sketch.js +46 -0
  136. package/esri-compat/snapping.d.ts +43 -0
  137. package/esri-compat/snapping.d.ts.map +1 -0
  138. package/esri-compat/snapping.js +45 -0
  139. package/esri-compat/url.d.ts.map +1 -1
  140. package/esri-compat/url.js +1 -1
  141. package/esri-compat/widget-host.d.ts +84 -0
  142. package/esri-compat/widget-host.d.ts.map +1 -0
  143. package/esri-compat/widget-host.js +159 -0
  144. package/filter-registry/index.d.ts +145 -0
  145. package/filter-registry/index.d.ts.map +1 -0
  146. package/filter-registry/index.js +531 -0
  147. package/generated-app/errors.d.ts +18 -2
  148. package/generated-app/errors.d.ts.map +1 -1
  149. package/generated-app/errors.js +44 -11
  150. package/map/automatic-mount-integration.d.ts +134 -0
  151. package/map/automatic-mount-integration.d.ts.map +1 -0
  152. package/map/automatic-mount-integration.js +305 -0
  153. package/map/automatic-source-strategy.d.ts +125 -0
  154. package/map/automatic-source-strategy.d.ts.map +1 -0
  155. package/map/automatic-source-strategy.js +494 -0
  156. package/map/data-to-map-bridge.d.ts +293 -0
  157. package/map/data-to-map-bridge.d.ts.map +1 -0
  158. package/map/data-to-map-bridge.js +817 -0
  159. package/map/geojson-projection.d.ts +33 -0
  160. package/map/geojson-projection.d.ts.map +1 -0
  161. package/map/geojson-projection.js +118 -0
  162. package/map/index.d.ts +14 -0
  163. package/map/index.d.ts.map +1 -1
  164. package/map/index.js +8 -0
  165. package/map/raster-source-spec.d.ts +30 -0
  166. package/map/raster-source-spec.d.ts.map +1 -0
  167. package/map/raster-source-spec.js +190 -0
  168. package/map/raster-source-strategy.d.ts +65 -0
  169. package/map/raster-source-strategy.d.ts.map +1 -0
  170. package/map/raster-source-strategy.js +232 -0
  171. package/map/source-to-maplibre.d.ts +91 -0
  172. package/map/source-to-maplibre.d.ts.map +1 -0
  173. package/map/source-to-maplibre.js +454 -0
  174. package/map/temporal-playback.d.ts +139 -0
  175. package/map/temporal-playback.d.ts.map +1 -0
  176. package/map/temporal-playback.js +247 -0
  177. package/package.json +14 -1
  178. package/query-planner/canonical.d.ts +7 -0
  179. package/query-planner/canonical.d.ts.map +1 -0
  180. package/query-planner/canonical.js +145 -0
  181. package/query-planner/cql2-json.d.ts +17 -0
  182. package/query-planner/cql2-json.d.ts.map +1 -0
  183. package/query-planner/cql2-json.js +646 -0
  184. package/query-planner/diagnostics.d.ts +35 -0
  185. package/query-planner/diagnostics.d.ts.map +1 -0
  186. package/query-planner/diagnostics.js +373 -0
  187. package/query-planner/duckdb.d.ts +79 -0
  188. package/query-planner/duckdb.d.ts.map +1 -0
  189. package/query-planner/duckdb.js +758 -0
  190. package/query-planner/execution-context.d.ts +5 -0
  191. package/query-planner/execution-context.d.ts.map +1 -0
  192. package/query-planner/execution-context.js +50 -0
  193. package/query-planner/executor.d.ts +5 -0
  194. package/query-planner/executor.d.ts.map +1 -0
  195. package/query-planner/executor.js +223 -0
  196. package/query-planner/geoservices.d.ts +91 -0
  197. package/query-planner/geoservices.d.ts.map +1 -0
  198. package/query-planner/geoservices.js +667 -0
  199. package/query-planner/grpc.d.ts +100 -0
  200. package/query-planner/grpc.d.ts.map +1 -0
  201. package/query-planner/grpc.js +736 -0
  202. package/query-planner/index.d.ts +44 -0
  203. package/query-planner/index.d.ts.map +1 -0
  204. package/query-planner/index.js +30 -0
  205. package/query-planner/ir.d.ts +20 -0
  206. package/query-planner/ir.d.ts.map +1 -0
  207. package/query-planner/ir.js +417 -0
  208. package/query-planner/local-aggregate.d.ts +5 -0
  209. package/query-planner/local-aggregate.d.ts.map +1 -0
  210. package/query-planner/local-aggregate.js +126 -0
  211. package/query-planner/odata.d.ts +48 -0
  212. package/query-planner/odata.d.ts.map +1 -0
  213. package/query-planner/odata.js +662 -0
  214. package/query-planner/ogc-compiler.d.ts +55 -0
  215. package/query-planner/ogc-compiler.d.ts.map +1 -0
  216. package/query-planner/ogc-compiler.js +480 -0
  217. package/query-planner/ogc-features.d.ts +53 -0
  218. package/query-planner/ogc-features.d.ts.map +1 -0
  219. package/query-planner/ogc-features.js +617 -0
  220. package/query-planner/planner.d.ts +17 -0
  221. package/query-planner/planner.d.ts.map +1 -0
  222. package/query-planner/planner.js +818 -0
  223. package/query-planner/resource.d.ts +94 -0
  224. package/query-planner/resource.d.ts.map +1 -0
  225. package/query-planner/resource.js +526 -0
  226. package/query-planner/semantic-canonical.d.ts +15 -0
  227. package/query-planner/semantic-canonical.d.ts.map +1 -0
  228. package/query-planner/semantic-canonical.js +62 -0
  229. package/query-planner/semantic-compat.d.ts +10 -0
  230. package/query-planner/semantic-compat.d.ts.map +1 -0
  231. package/query-planner/semantic-compat.js +35 -0
  232. package/query-planner/semantic-compiler.d.ts +64 -0
  233. package/query-planner/semantic-compiler.d.ts.map +1 -0
  234. package/query-planner/semantic-compiler.js +81 -0
  235. package/query-planner/semantic-literals.d.ts +30 -0
  236. package/query-planner/semantic-literals.d.ts.map +1 -0
  237. package/query-planner/semantic-literals.js +133 -0
  238. package/query-planner/semantic-types.d.ts +265 -0
  239. package/query-planner/semantic-types.d.ts.map +1 -0
  240. package/query-planner/semantic-types.js +1 -0
  241. package/query-planner/semantic.d.ts +54 -0
  242. package/query-planner/semantic.d.ts.map +1 -0
  243. package/query-planner/semantic.js +1421 -0
  244. package/query-planner/types.d.ts +560 -0
  245. package/query-planner/types.d.ts.map +1 -0
  246. package/query-planner/types.js +49 -0
  247. package/query-planner/wfs-semantic.d.ts +58 -0
  248. package/query-planner/wfs-semantic.d.ts.map +1 -0
  249. package/query-planner/wfs-semantic.js +547 -0
  250. package/query-planner/wfs.d.ts +6 -0
  251. package/query-planner/wfs.d.ts.map +1 -0
  252. package/query-planner/wfs.js +71 -0
  253. package/react/external-map.d.ts +67 -0
  254. package/react/external-map.d.ts.map +1 -0
  255. package/react/external-map.js +63 -0
  256. package/react/honua-map.d.ts.map +1 -1
  257. package/react/honua-map.js +3 -2
  258. package/react/honua-source-layer.d.ts +110 -0
  259. package/react/honua-source-layer.d.ts.map +1 -0
  260. package/react/honua-source-layer.js +101 -0
  261. package/react/hooks.d.ts +28 -0
  262. package/react/hooks.d.ts.map +1 -1
  263. package/react/hooks.js +26 -4
  264. package/react/index.d.ts +8 -0
  265. package/react/index.d.ts.map +1 -1
  266. package/react/index.js +5 -0
  267. package/react/query-cache.d.ts +26 -4
  268. package/react/query-cache.d.ts.map +1 -1
  269. package/react/query-cache.js +51 -8
  270. package/react/selection.d.ts +177 -0
  271. package/react/selection.d.ts.map +1 -0
  272. package/react/selection.js +0 -0
  273. package/react/use-mounted-source.d.ts +69 -0
  274. package/react/use-mounted-source.d.ts.map +1 -0
  275. package/react/use-mounted-source.js +346 -0
  276. package/runtime/edit-snapping.d.ts +107 -0
  277. package/runtime/edit-snapping.d.ts.map +1 -0
  278. package/runtime/edit-snapping.js +190 -0
  279. package/runtime/errors.d.ts +3 -2
  280. package/runtime/errors.d.ts.map +1 -1
  281. package/runtime/errors.js +19 -2
  282. package/runtime/index.d.ts +6 -0
  283. package/runtime/index.d.ts.map +1 -1
  284. package/runtime/index.js +3 -0
  285. package/runtime/maplibre-renderer.d.ts +41 -0
  286. package/runtime/maplibre-renderer.d.ts.map +1 -0
  287. package/runtime/maplibre-renderer.js +322 -0
  288. package/runtime/query-tiles.d.ts +2 -1
  289. package/runtime/query-tiles.d.ts.map +1 -1
  290. package/runtime/query-tiles.js +6 -2
  291. package/runtime/source-bridge.d.ts +4 -4
  292. package/runtime/source-bridge.d.ts.map +1 -1
  293. package/runtime/source-bridge.js +9 -56
  294. package/runtime/style-interactions.d.ts +3 -2
  295. package/runtime/style-interactions.d.ts.map +1 -1
  296. package/runtime/style-interactions.js +11 -3
  297. package/runtime/terra-draw-sketch.d.ts +275 -0
  298. package/runtime/terra-draw-sketch.d.ts.map +1 -0
  299. package/runtime/terra-draw-sketch.js +404 -0
  300. package/source-capability-registry.d.ts +1 -0
  301. package/source-capability-registry.js +1 -0
  302. package/source-capability-types.d.ts +186 -0
  303. package/source-capability-types.js +7 -0
  304. package/style/index.d.ts +2 -0
  305. package/style/index.d.ts.map +1 -1
  306. package/style/index.js +2 -0
  307. package/style/renderers.d.ts +326 -0
  308. package/style/renderers.d.ts.map +1 -0
  309. package/style/renderers.js +517 -0
  310. package/webmap/convert-renderer.d.ts +24 -3
  311. package/webmap/convert-renderer.d.ts.map +1 -1
  312. package/webmap/convert-renderer.js +93 -112
  313. package/webmap/index.d.ts +1 -1
  314. package/webmap/index.d.ts.map +1 -1
  315. package/webmap/index.js +1 -1
@@ -0,0 +1,554 @@
1
+ /**
2
+ * Protocol-neutral discovery truth and cache identity contracts.
3
+ *
4
+ * Protocol defaults describe what an adapter can implement; they are not
5
+ * evidence that a particular endpoint enables those operations. These helpers
6
+ * keep that distinction explicit while endpoint-specific discovery lands.
7
+ */
8
+ import { HonuaDiscoveryError } from "../core/errors.js";
9
+ import { sourceSchemaV2EnvelopeFingerprint } from "./schema-envelope.js";
10
+ /** Default adapter implementation version included in every discovery cache identity. */
11
+ export const HONUA_DISCOVERY_ADAPTER_VERSION = "honua-discovery-adapter@1";
12
+ /** Default normalized inspection projection version included in every discovery cache identity. */
13
+ export const HONUA_DISCOVERY_PROJECTION_VERSION = "honua-source-inspection@1";
14
+ import { CAPABILITIES, PROTOCOLS, PROTOCOL_DEFAULT_CAPABILITIES, } from "./types.js";
15
+ const DISCOVERY_EVIDENCE_KINDS = ["metadata", "declared", "inferred", "unavailable"];
16
+ /**
17
+ * Intersect adapter support, endpoint evidence, and caller policy.
18
+ *
19
+ * `unavailable` and unaccepted `inferred` evidence intentionally produce no
20
+ * effective capabilities. Callers with trusted configuration should use
21
+ * `declared` evidence rather than presenting protocol defaults as discovery.
22
+ */
23
+ export function resolveDiscoveryCapabilities(protocol, evidence, policy = {}) {
24
+ const adapter = DISCOVERY_ADAPTER_MAXIMA.get(protocol);
25
+ if (!adapter) {
26
+ throw new HonuaDiscoveryError("unsupported-protocol", `Unknown discovery protocol "${String(protocol)}".`, {
27
+ protocol,
28
+ });
29
+ }
30
+ const evidenceRecords = normalizeEvidence(evidence, adapter);
31
+ const allow = policy.allow ? capabilitySet(policy.allow, "policy.allow") : undefined;
32
+ const deny = capabilitySet(policy.deny ?? [], "policy.deny");
33
+ const effective = [];
34
+ const decisions = CAPABILITIES.map((capability) => {
35
+ const adapterSupported = adapter.has(capability);
36
+ const relevant = evidenceRecords.filter((record) => record.scope.has(capability));
37
+ const positive = relevant.filter((record) => record.capabilities.has(capability));
38
+ const authoritativeNegative = relevant.some((record) => (record.kind === "metadata" || record.kind === "declared") && !record.capabilities.has(capability));
39
+ const metadataPositive = positive.some((record) => record.kind === "metadata");
40
+ const declaredPositive = positive.some((record) => record.kind === "declared");
41
+ const inferredPositive = positive.some((record) => record.kind === "inferred");
42
+ const unavailable = relevant.some((record) => record.kind === "unavailable");
43
+ const positiveEvidence = metadataPositive || declaredPositive || inferredPositive;
44
+ const policyAllowed = (allow === undefined || allow.has(capability)) && !deny.has(capability);
45
+ const code = !adapterSupported
46
+ ? "adapter-unsupported"
47
+ : authoritativeNegative
48
+ ? "not-advertised"
49
+ : metadataPositive
50
+ ? policyAllowed
51
+ ? "enabled"
52
+ : "policy-denied"
53
+ : declaredPositive
54
+ ? policyAllowed
55
+ ? "enabled"
56
+ : "policy-denied"
57
+ : inferredPositive
58
+ ? policy.acceptInferred === true
59
+ ? policyAllowed
60
+ ? "enabled"
61
+ : "policy-denied"
62
+ : "inferred-not-accepted"
63
+ : unavailable
64
+ ? "discovery-unavailable"
65
+ : "not-advertised";
66
+ const isEffective = code === "enabled";
67
+ if (isEffective)
68
+ effective.push(capability);
69
+ return Object.freeze({
70
+ capability,
71
+ effective: isEffective,
72
+ code,
73
+ evidence: Object.freeze(relevant.map((record) => evidenceSummary(record, capability))),
74
+ adapterSupported,
75
+ positiveEvidence,
76
+ policyAllowed,
77
+ reason: capabilityDecisionReason(code, capability),
78
+ });
79
+ });
80
+ const observed = new Set(evidenceRecords.flatMap((record) => [...record.capabilities]));
81
+ const outsideAdapter = [...observed].filter((capability) => !adapter.has(capability));
82
+ const policyRestricted = decisions
83
+ .filter((decision) => decision.code === "policy-denied")
84
+ .map((decision) => decision.capability);
85
+ const diagnostics = [];
86
+ const unavailableRecords = evidenceRecords.filter((record) => record.kind === "unavailable");
87
+ for (const record of unavailableRecords) {
88
+ diagnostics.push({
89
+ code: "discovery-unavailable",
90
+ severity: "warning",
91
+ message: record.reason ?? "Discovery evidence is unavailable.",
92
+ capabilities: orderedCapabilities(record.scope),
93
+ });
94
+ }
95
+ if (unavailableRecords.length > 0 && evidenceRecords.some((record) => record.kind !== "unavailable")) {
96
+ diagnostics.push({
97
+ code: "partial-discovery",
98
+ severity: "warning",
99
+ message: "Some capability evidence is available while one or more discovery sources failed.",
100
+ });
101
+ }
102
+ const inferredRejected = decisions
103
+ .filter((decision) => decision.code === "inferred-not-accepted")
104
+ .map((decision) => decision.capability);
105
+ if (inferredRejected.length > 0) {
106
+ diagnostics.push({
107
+ code: "inferred-capabilities-rejected",
108
+ severity: "warning",
109
+ message: "Inferred capabilities were recorded but not enabled without explicit caller acceptance.",
110
+ capabilities: Object.freeze(inferredRejected),
111
+ });
112
+ }
113
+ const conflicts = decisions
114
+ .filter((decision) => decision.code === "not-advertised" &&
115
+ decision.evidence.some((entry) => (entry.kind === "metadata" || entry.kind === "declared") && entry.supported) &&
116
+ decision.evidence.some((entry) => (entry.kind === "metadata" || entry.kind === "declared") && !entry.supported))
117
+ .map((decision) => decision.capability);
118
+ if (conflicts.length > 0) {
119
+ diagnostics.push({
120
+ code: "conflicting-evidence",
121
+ severity: "warning",
122
+ message: "Conflicting authoritative evidence was resolved conservatively by disabling the affected capabilities.",
123
+ capabilities: Object.freeze(conflicts),
124
+ });
125
+ }
126
+ if (outsideAdapter.length > 0) {
127
+ diagnostics.push({
128
+ code: "evidence-exceeds-adapter",
129
+ severity: "warning",
130
+ message: "Discovery evidence asserts operations that this SDK adapter cannot implement.",
131
+ capabilities: orderedCapabilities(new Set(outsideAdapter)),
132
+ });
133
+ }
134
+ if (policyRestricted.length > 0) {
135
+ diagnostics.push({
136
+ code: "capability-policy-restricted",
137
+ severity: "info",
138
+ message: "Caller policy removed endpoint-supported capabilities.",
139
+ capabilities: orderedCapabilities(new Set(policyRestricted)),
140
+ });
141
+ }
142
+ return Object.freeze({
143
+ protocol,
144
+ discovery: discoveryState(evidenceRecords),
145
+ evidence: Object.freeze(evidenceRecords.map((record) => record.original)),
146
+ provenance: Object.freeze(uniqueProvenance(evidenceRecords.flatMap((record) => record.provenance))),
147
+ capabilities: immutableCapabilities(effective),
148
+ decisions: Object.freeze(decisions),
149
+ diagnostics: Object.freeze(diagnostics.map(freezeDiagnostic)),
150
+ });
151
+ }
152
+ /** Apply one reviewed discovery resolution to a source descriptor. */
153
+ export function inspectDiscoveredSource(descriptor, resolution) {
154
+ if (descriptor.protocol !== resolution.protocol) {
155
+ throw new HonuaDiscoveryError("protocol-mismatch", `Source protocol "${descriptor.protocol}" does not match discovery protocol "${resolution.protocol}".`, { sourceId: descriptor.id, descriptorProtocol: descriptor.protocol, discoveryProtocol: resolution.protocol });
156
+ }
157
+ assertResolutionIntegrity(resolution);
158
+ return Object.freeze({
159
+ descriptor: immutableDescriptor(descriptor, resolution.capabilities),
160
+ discovery: resolution.discovery,
161
+ provenance: resolution.provenance,
162
+ capabilityDecisions: resolution.decisions,
163
+ diagnostics: resolution.diagnostics,
164
+ });
165
+ }
166
+ function assertResolutionIntegrity(resolution) {
167
+ const effectiveDecisions = resolution.decisions
168
+ .filter((decision) => decision.effective && decision.code === "enabled")
169
+ .map((decision) => decision.capability);
170
+ const resolved = orderedCapabilities(resolution.capabilities);
171
+ if (effectiveDecisions.length !== resolved.length ||
172
+ effectiveDecisions.some((capability, index) => capability !== resolved[index])) {
173
+ throw new HonuaDiscoveryError("invalid-capability", "Discovery capability resolution was mutated or does not match its per-capability decisions.", { protocol: resolution.protocol });
174
+ }
175
+ }
176
+ const CREDENTIAL_ENDPOINT_PARAMETERS = new Set([
177
+ "access_token",
178
+ "api_key",
179
+ "apikey",
180
+ "authorization",
181
+ "bearer",
182
+ "client_secret",
183
+ "code",
184
+ "id_token",
185
+ "jwt",
186
+ "password",
187
+ "passwd",
188
+ "refresh_token",
189
+ "secret",
190
+ "session",
191
+ "session_id",
192
+ "sessionid",
193
+ "sig",
194
+ "signature",
195
+ "token",
196
+ ]);
197
+ const AMBIGUOUS_CREDENTIAL_ENDPOINT_PARAMETERS = new Set([
198
+ "api-key",
199
+ "api_key",
200
+ "apikey",
201
+ "auth",
202
+ "code",
203
+ "credential",
204
+ "key",
205
+ "ocp-apim-subscription-key",
206
+ "secret",
207
+ "session",
208
+ "session_id",
209
+ "sessionid",
210
+ "subscription-key",
211
+ "subscription_key",
212
+ ]);
213
+ /** Normalize an endpoint for discovery identity without retaining credentials. */
214
+ export function normalizeDiscoveryEndpoint(endpoint, options = {}) {
215
+ const callerTransient = new Set((options.transientQueryParameters ?? []).map((value) => value.toLowerCase()));
216
+ return normalizeEndpoint(endpoint, (key) => {
217
+ const normalized = key.toLowerCase();
218
+ return isCredentialEndpointParameter(normalized) || callerTransient.has(normalized);
219
+ });
220
+ }
221
+ /**
222
+ * Build the deterministic logical identity used by discovery metadata caches.
223
+ * The key contains no URL credentials, bearer tokens, signed-URL parameters,
224
+ * or raw authorization material.
225
+ */
226
+ export async function createDiscoveryCacheIdentity(options) {
227
+ const authorizationScopeFingerprint = requiredIdentity(options.authorizationScopeFingerprint, "authorizationScopeFingerprint");
228
+ const adapterVersion = options.adapterVersion === undefined
229
+ ? HONUA_DISCOVERY_ADAPTER_VERSION
230
+ : requiredIdentity(options.adapterVersion, "adapterVersion");
231
+ const projectionVersion = options.projectionVersion === undefined
232
+ ? HONUA_DISCOVERY_PROJECTION_VERSION
233
+ : requiredIdentity(options.projectionVersion, "projectionVersion");
234
+ if (options.protocol !== "auto" && !PROTOCOLS.includes(options.protocol)) {
235
+ throw new HonuaDiscoveryError("unsupported-protocol", `Unknown discovery protocol "${String(options.protocol)}".`, {
236
+ protocol: options.protocol,
237
+ });
238
+ }
239
+ const endpoint = normalizeDiscoveryEndpoint(options.endpoint, options);
240
+ const callerTransient = new Set((options.transientQueryParameters ?? []).map((value) => value.toLowerCase()));
241
+ const identityEndpoint = normalizeEndpoint(options.endpoint, (key) => {
242
+ const normalized = key.toLowerCase();
243
+ return ((isCredentialEndpointParameter(normalized) && !AMBIGUOUS_CREDENTIAL_ENDPOINT_PARAMETERS.has(normalized)) ||
244
+ callerTransient.has(normalized));
245
+ });
246
+ const [endpointDigest, authorizationScopeDigest] = await Promise.all([
247
+ sha256(identityEndpoint),
248
+ sha256(`honua-discovery-scope:v1:${authorizationScopeFingerprint}`),
249
+ ]);
250
+ const dimensions = {
251
+ endpointDigest,
252
+ protocol: options.protocol,
253
+ authorizationScopeDigest,
254
+ adapterVersion,
255
+ projectionVersion,
256
+ };
257
+ for (const key of DISCOVERY_RESOURCE_KEYS) {
258
+ const value = options[key];
259
+ if (value !== undefined && String(value).length > 0)
260
+ dimensions[key] = String(value);
261
+ }
262
+ const key = `discovery:v1:${Object.keys(dimensions)
263
+ .sort()
264
+ .map((name) => `${encodeURIComponent(name)}=${encodeURIComponent(dimensions[name])}`)
265
+ .join("&")}`;
266
+ return Object.freeze({
267
+ version: 1,
268
+ endpoint,
269
+ protocol: options.protocol,
270
+ authorizationScopeDigest,
271
+ key,
272
+ });
273
+ }
274
+ const DISCOVERY_RESOURCE_KEYS = [
275
+ "tenantId",
276
+ "projectId",
277
+ "sourceId",
278
+ "serviceId",
279
+ "layerId",
280
+ "collectionId",
281
+ "typeName",
282
+ "tileMatrixSetId",
283
+ "styleId",
284
+ "entitySet",
285
+ "processId",
286
+ "crs",
287
+ "format",
288
+ "locale",
289
+ "profile",
290
+ "assetVariant",
291
+ ];
292
+ function capabilitySet(values, path) {
293
+ const known = new Set(CAPABILITIES);
294
+ const out = new Set();
295
+ for (const value of values) {
296
+ if (!known.has(value)) {
297
+ throw new HonuaDiscoveryError("invalid-capability", `${path} contains unknown capability "${String(value)}".`, {
298
+ path,
299
+ capability: value,
300
+ });
301
+ }
302
+ out.add(value);
303
+ }
304
+ return out;
305
+ }
306
+ function normalizeEvidence(input, adapter) {
307
+ const values = Array.isArray(input)
308
+ ? input
309
+ : [input];
310
+ if (values.length === 0) {
311
+ throw new HonuaDiscoveryError("invalid-capability", "Discovery evidence must contain at least one record.");
312
+ }
313
+ return Object.freeze(values.map((value, index) => {
314
+ const path = `evidence[${index}]`;
315
+ const kind = discoveryEvidenceKind(value, path);
316
+ const observed = kind === "unavailable"
317
+ ? new Set()
318
+ : capabilitySet(value.capabilities, path);
319
+ const scope = value.scope
320
+ ? capabilitySet(value.scope, `${path}.scope`)
321
+ : new Set([...adapter, ...observed]);
322
+ for (const capability of observed) {
323
+ if (!scope.has(capability)) {
324
+ throw new HonuaDiscoveryError("invalid-capability", `${path}.capabilities contains "${capability}" outside its evaluated scope.`, { path, capability });
325
+ }
326
+ }
327
+ const provenance = Object.freeze((value.provenance ?? []).map((entry, provenanceIndex) => normalizeProvenance(entry, `${path}.provenance[${provenanceIndex}]`)));
328
+ const original = Object.freeze({
329
+ ...value,
330
+ ...(kind !== "unavailable" ? { capabilities: Object.freeze([...observed]) } : {}),
331
+ ...(value.scope ? { scope: Object.freeze([...scope]) } : {}),
332
+ ...(provenance.length > 0 ? { provenance } : {}),
333
+ });
334
+ return Object.freeze({
335
+ kind,
336
+ capabilities: observed,
337
+ scope,
338
+ ...(kind === "inferred" || kind === "unavailable"
339
+ ? { reason: value.reason }
340
+ : {}),
341
+ provenance,
342
+ original,
343
+ });
344
+ }));
345
+ }
346
+ function discoveryEvidenceKind(value, path) {
347
+ const kind = value !== null && typeof value === "object" ? value.kind : undefined;
348
+ if (!DISCOVERY_EVIDENCE_KINDS.includes(kind)) {
349
+ throw new HonuaDiscoveryError("invalid-capability", `${path}.kind must be one of ${DISCOVERY_EVIDENCE_KINDS.join(", ")}; received "${String(kind)}".`, { path: `${path}.kind`, kind });
350
+ }
351
+ return kind;
352
+ }
353
+ function evidenceSummary(record, capability) {
354
+ return Object.freeze({
355
+ kind: record.kind,
356
+ supported: record.capabilities.has(capability),
357
+ ...(record.reason ? { reason: record.reason } : {}),
358
+ provenance: record.provenance,
359
+ });
360
+ }
361
+ function discoveryState(records) {
362
+ const kinds = new Set(records.map((record) => record.kind));
363
+ return kinds.size === 1 ? (records[0]?.kind ?? "unavailable") : "mixed";
364
+ }
365
+ function normalizeProvenance(value, path) {
366
+ const source = value.source.trim();
367
+ if (source.length === 0) {
368
+ throw new HonuaDiscoveryError("invalid-capability", `${path}.source must be non-empty.`, { path });
369
+ }
370
+ return Object.freeze({
371
+ source,
372
+ ...(value.retrievedAt ? { retrievedAt: value.retrievedAt } : {}),
373
+ ...(value.validator ? { validator: value.validator } : {}),
374
+ });
375
+ }
376
+ function uniqueProvenance(values) {
377
+ const seen = new Set();
378
+ return values.filter((value) => {
379
+ const key = `${value.source}\u0000${value.retrievedAt ?? ""}\u0000${value.validator ?? ""}`;
380
+ if (seen.has(key))
381
+ return false;
382
+ seen.add(key);
383
+ return true;
384
+ });
385
+ }
386
+ function immutableCapabilities(values) {
387
+ return Object.freeze(new ImmutableCapabilitySet(values));
388
+ }
389
+ /** @internal Preserve discovery's mutation-resistant legacy set representation. */
390
+ export function immutableDiscoveredCapabilities(values) {
391
+ return immutableCapabilities([...values]);
392
+ }
393
+ class ImmutableCapabilitySet {
394
+ #values;
395
+ constructor(values) {
396
+ this.#values = new Set(values);
397
+ }
398
+ get size() {
399
+ return this.#values.size;
400
+ }
401
+ has(value) {
402
+ return this.#values.has(value);
403
+ }
404
+ entries() {
405
+ return this.#values.entries();
406
+ }
407
+ keys() {
408
+ return this.#values.keys();
409
+ }
410
+ values() {
411
+ return this.#values.values();
412
+ }
413
+ forEach(callbackfn, thisArg) {
414
+ for (const value of this.#values)
415
+ callbackfn.call(thisArg, value, value, this);
416
+ }
417
+ [Symbol.iterator]() {
418
+ return this.values();
419
+ }
420
+ [Symbol.toStringTag] = "Set";
421
+ }
422
+ const DISCOVERY_ADAPTER_MAXIMA = new Map(PROTOCOLS.map((protocol) => [protocol, immutableCapabilities([...PROTOCOL_DEFAULT_CAPABILITIES[protocol]])]));
423
+ function freezeDiagnostic(diagnostic) {
424
+ return Object.freeze({
425
+ ...diagnostic,
426
+ ...(diagnostic.capabilities ? { capabilities: Object.freeze([...diagnostic.capabilities]) } : {}),
427
+ });
428
+ }
429
+ function immutableDescriptor(descriptor, effectiveCapabilities) {
430
+ return Object.freeze({
431
+ ...descriptor,
432
+ locator: cloneAndDeepFreeze(descriptor.locator),
433
+ capabilities: immutableCapabilities([...effectiveCapabilities]),
434
+ ...(descriptor.schema ? { schema: cloneAndDeepFreeze(descriptor.schema) } : {}),
435
+ ...(descriptor.schemaV2 !== undefined ? { schemaV2: immutableSourceSchemaV2(descriptor.schemaV2) } : {}),
436
+ ...(descriptor.analytics ? { analytics: cloneAndDeepFreeze(descriptor.analytics) } : {}),
437
+ });
438
+ }
439
+ function immutableSourceSchemaV2(value) {
440
+ const cloned = cloneAndDeepFreeze(value);
441
+ try {
442
+ sourceSchemaV2EnvelopeFingerprint(cloned);
443
+ }
444
+ catch (cause) {
445
+ throw new HonuaDiscoveryError("invalid-capability", "Source discovery schemaV2 has an invalid discriminator or fingerprint envelope.", undefined, { cause });
446
+ }
447
+ return cloned;
448
+ }
449
+ function cloneAndDeepFreeze(value) {
450
+ try {
451
+ return deepFreeze(structuredClone(value));
452
+ }
453
+ catch (cause) {
454
+ throw new HonuaDiscoveryError("invalid-capability", "Source discovery descriptors must contain structured-clone-compatible metadata.", undefined, { cause });
455
+ }
456
+ }
457
+ function deepFreeze(value, seen = new WeakSet()) {
458
+ if (value === null || typeof value !== "object" || Object.isFrozen(value))
459
+ return value;
460
+ if (seen.has(value))
461
+ return value;
462
+ seen.add(value);
463
+ const prototype = Object.getPrototypeOf(value);
464
+ if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {
465
+ throw new TypeError("Discovery descriptor metadata must contain only arrays and plain objects.");
466
+ }
467
+ for (const child of Object.values(value))
468
+ deepFreeze(child, seen);
469
+ Object.freeze(value);
470
+ return value;
471
+ }
472
+ function capabilityDecisionReason(code, capability) {
473
+ switch (code) {
474
+ case "enabled":
475
+ return `${capability} is supported by the adapter, endpoint evidence, and caller policy.`;
476
+ case "adapter-unsupported":
477
+ return `${capability} is not implemented by this protocol adapter.`;
478
+ case "not-advertised":
479
+ return `${capability} was not advertised by endpoint evidence.`;
480
+ case "policy-denied":
481
+ return `${capability} was removed by caller policy.`;
482
+ case "inferred-not-accepted":
483
+ return `${capability} was inferred but the caller did not accept inferred support.`;
484
+ case "discovery-unavailable":
485
+ return `${capability} cannot be enabled because discovery evidence is unavailable.`;
486
+ }
487
+ }
488
+ function orderedCapabilities(values) {
489
+ return CAPABILITIES.filter((capability) => values.has(capability));
490
+ }
491
+ function requiredIdentity(value, name) {
492
+ const normalized = value.trim();
493
+ if (normalized.length === 0) {
494
+ throw new HonuaDiscoveryError("invalid-cache-identity", `${name} must be a non-empty opaque fingerprint.`, {
495
+ name,
496
+ });
497
+ }
498
+ return normalized;
499
+ }
500
+ function compareText(left, right) {
501
+ return left < right ? -1 : left > right ? 1 : 0;
502
+ }
503
+ function isCredentialEndpointParameter(normalized) {
504
+ return (CREDENTIAL_ENDPOINT_PARAMETERS.has(normalized) ||
505
+ AMBIGUOUS_CREDENTIAL_ENDPOINT_PARAMETERS.has(normalized) ||
506
+ normalized.startsWith("x-amz-") ||
507
+ normalized.startsWith("x-goog-"));
508
+ }
509
+ const AZURE_SAS_PARAMETER = /^(?:rscc|rscd|rsce|rscl|rsct|saoid|scid|sdd|se|ses|sig|sip|si|ske|skoid|sks|skt|sktid|skv|sp|spr|sr|srt|ss|st|suoid|sv)$/;
510
+ const CLOUDFRONT_SIGNED_URL_PARAMETER = /^(?:expires|key-pair-id|policy|signature)$/;
511
+ const AWS_V2_SIGNED_URL_PARAMETER = /^(?:awsaccesskeyid|expires|securitytoken|signature)$/;
512
+ const GCS_V2_SIGNED_URL_PARAMETER = /^(?:expires|googleaccessid|signature)$/;
513
+ function signedUrlTransientParameter(parameters) {
514
+ const names = new Set([...parameters.keys()].map((name) => name.toLowerCase()));
515
+ const azure = names.has("sig") && [...names].some((name) => name !== "sig" && AZURE_SAS_PARAMETER.test(name));
516
+ const signedV2 = names.has("signature");
517
+ const cloudFront = signedV2 && names.has("key-pair-id");
518
+ const aws = signedV2 && names.has("awsaccesskeyid");
519
+ const gcs = signedV2 && names.has("googleaccessid");
520
+ return (name) => (azure && AZURE_SAS_PARAMETER.test(name)) ||
521
+ (cloudFront && CLOUDFRONT_SIGNED_URL_PARAMETER.test(name)) ||
522
+ (aws && AWS_V2_SIGNED_URL_PARAMETER.test(name)) ||
523
+ (gcs && GCS_V2_SIGNED_URL_PARAMETER.test(name));
524
+ }
525
+ function normalizeEndpoint(endpoint, omit) {
526
+ let parsed;
527
+ try {
528
+ parsed = new URL(endpoint.toString());
529
+ }
530
+ catch {
531
+ throw new HonuaDiscoveryError("invalid-endpoint", "Discovery endpoints must be absolute URLs.");
532
+ }
533
+ parsed.username = "";
534
+ parsed.password = "";
535
+ parsed.hash = "";
536
+ const isSignedUrlTransient = signedUrlTransientParameter(parsed.searchParams);
537
+ const retained = [...parsed.searchParams.entries()]
538
+ .filter(([key]) => !omit(key) && !isSignedUrlTransient(key.toLowerCase()))
539
+ .sort(([leftKey, leftValue], [rightKey, rightValue]) => leftKey === rightKey ? compareText(leftValue, rightValue) : compareText(leftKey, rightKey));
540
+ parsed.search = "";
541
+ for (const [key, value] of retained)
542
+ parsed.searchParams.append(key, value);
543
+ while (parsed.pathname.length > 1 && parsed.pathname.endsWith("/"))
544
+ parsed.pathname = parsed.pathname.slice(0, -1);
545
+ const normalized = parsed.toString();
546
+ return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
547
+ }
548
+ async function sha256(value) {
549
+ if (!globalThis.crypto?.subtle) {
550
+ throw new HonuaDiscoveryError("invalid-cache-identity", "Discovery cache identity requires Web Crypto SHA-256.");
551
+ }
552
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
553
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
554
+ }
@@ -1,4 +1,5 @@
1
1
  import { type CreateEditSessionOptions, type EditAttachmentMutation, type EditWorkflowKind, type EditWorkflowMetadata, type EditWorkflowSnapshot, type EditWorkflowSubmitResult, type EditWorkflowValidationResult } from "./edit-session.js";
2
+ import { type SnappingConfig } from "./edit-snapping.js";
2
3
  import type { CanonicalFeature, FeatureId, Protocol, Source, SourceId } from "./types.js";
3
4
  export type EditSketchTool = "point" | "line" | "polygon" | "rectangle" | "circle" | "buffer";
4
5
  export type EditSketchCapabilityState = "supported" | "unsupported";
@@ -17,6 +18,8 @@ export interface EditSketchWorkflowOptions<T = Record<string, unknown>> extends
17
18
  sketchTools?: Partial<Record<EditSketchTool, EditSketchToolCapabilityInput>>;
18
19
  optimistic?: CreateEditSessionOptions<T>["optimistic"];
19
20
  annotationPersistence?: EditAnnotationPersistenceHook<T>;
21
+ /** Initial snapping configuration for the sketch workflow. */
22
+ snapping?: Partial<SnappingConfig>;
20
23
  }
21
24
  export interface EditAnnotationPersistenceHook<T = Record<string, unknown>> {
22
25
  persist(snapshot: EditWorkflowSnapshot<T>, result: EditWorkflowSubmitResult<T>): void | Promise<void>;
@@ -44,6 +47,7 @@ export interface EditSketchWorkflowSnapshot<T = Record<string, unknown>> {
44
47
  tool?: EditSketchTool;
45
48
  tools: readonly EditSketchToolCapability[];
46
49
  geometry: Record<string, unknown> | null | undefined;
50
+ snapping: SnappingConfig;
47
51
  };
48
52
  dirty: boolean;
49
53
  validation: EditWorkflowValidationResult;
@@ -55,6 +59,10 @@ export declare class EditSketchWorkflowModel<T = Record<string, unknown>> {
55
59
  readonly source: Source<T>;
56
60
  readonly kind: EditWorkflowKind;
57
61
  constructor(options: EditSketchWorkflowOptions<T>);
62
+ /** Current snapping configuration (disabled unless configured). */
63
+ snappingConfig(): SnappingConfig;
64
+ /** Merge snapping configuration changes into the workflow. */
65
+ setSnapping(config: Partial<SnappingConfig>): this;
58
66
  snapshot(): EditSketchWorkflowSnapshot<T>;
59
67
  tools(): readonly EditSketchToolCapability[];
60
68
  toolCapability(tool: EditSketchTool): EditSketchToolCapability;
@@ -1 +1 @@
1
- {"version":3,"file":"edit-sketch.d.ts","sourceRoot":"","sources":["../../../src/contract/edit-sketch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"edit-sketch.d.ts","sourceRoot":"","sources":["../../../src/contract/edit-sketch.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { createEditSession, } from "./edit-session.js";
2
+ import { resolveSnappingConfig } from "./edit-snapping.js";
2
3
  const EDIT_SKETCH_TOOLS = ["point", "line", "polygon", "rectangle", "circle", "buffer"];
3
4
  const DEFAULT_SKETCH_TOOL_CAPABILITIES = {
4
5
  point: { tool: "point", state: "supported" },
@@ -19,6 +20,7 @@ export class EditSketchWorkflowModel {
19
20
  #activeTool;
20
21
  #lastTool;
21
22
  #annotationState;
23
+ #snapping;
22
24
  constructor(options) {
23
25
  this.source = options.source;
24
26
  this.kind = options.kind ?? (options.feature?.id === undefined ? "create" : "update");
@@ -26,6 +28,18 @@ export class EditSketchWorkflowModel {
26
28
  this.#session = createEditSession(options);
27
29
  this.#base = this.#session.snapshot();
28
30
  this.#annotationState = options.annotationPersistence ? { state: "supported" } : { state: "not-configured" };
31
+ this.#snapping = options.snapping
32
+ ? resolveSnappingConfig(options.snapping)
33
+ : resolveSnappingConfig({ enabled: false });
34
+ }
35
+ /** Current snapping configuration (disabled unless configured). */
36
+ snappingConfig() {
37
+ return resolveSnappingConfig(this.#snapping);
38
+ }
39
+ /** Merge snapping configuration changes into the workflow. */
40
+ setSnapping(config) {
41
+ this.#snapping = resolveSnappingConfig({ ...this.#snapping, ...config });
42
+ return this;
29
43
  }
30
44
  snapshot() {
31
45
  const snapshot = this.#session.snapshot();
@@ -43,6 +57,7 @@ export class EditSketchWorkflowModel {
43
57
  ...(this.#lastTool ? { tool: this.#lastTool } : {}),
44
58
  tools: this.tools(),
45
59
  geometry: snapshot.feature.geometry,
60
+ snapping: this.snappingConfig(),
46
61
  },
47
62
  dirty: this.dirty(),
48
63
  validation,