@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,251 @@
1
+ import { HonuaClient } from "../core/client.js";
2
+ import { esriConfig } from "./esri-config.js";
3
+ import { FeatureLayerCompat } from "./feature-layer.js";
4
+ import { identityManager } from "./identity-manager.js";
5
+ import { createArcGisTokenInterceptor } from "./request.js";
6
+ import { parseFeatureLayerUrl, parseImageServiceUrl, parseMapServiceUrl } from "./url.js";
7
+ /** Error thrown when the Portal facade returns an Esri error envelope. */
8
+ export class PortalError extends Error {
9
+ code;
10
+ details;
11
+ constructor(message, code, details) {
12
+ super(message);
13
+ this.name = "PortalError";
14
+ this.code = code;
15
+ this.details = details;
16
+ }
17
+ }
18
+ /**
19
+ * Client for a Honua `/sharing/rest` Portal facade. Lets a repointed ArcGIS app
20
+ * authenticate, search for items, and open an item that resolves to a
21
+ * `/rest/services` URL — carrying the portal token through to the opened layer
22
+ * so the whole flow authenticates end to end.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { PortalCompat } from "@honua/sdk-js/esri-compat";
27
+ *
28
+ * const portal = new PortalCompat({ portalUrl: "https://honua.example" });
29
+ * await portal.generateToken({ username: "u", password: "p" });
30
+ * const { results } = await portal.search({ q: "roads" });
31
+ * const opened = await portal.openFeatureLayer(results[0]);
32
+ * if (opened.type === "feature-service") {
33
+ * await opened.layer.queryFeatures({ where: "1=1" });
34
+ * }
35
+ * ```
36
+ */
37
+ export class PortalCompat {
38
+ /** Normalized `.../sharing/rest` base for this portal. */
39
+ sharingRestBase;
40
+ token;
41
+ tokenExpiresAtMs;
42
+ apiKey;
43
+ fetchFn;
44
+ constructor(options = {}) {
45
+ this.sharingRestBase = normalizeSharingRestBase(options.portalUrl ?? esriConfig.portalUrl);
46
+ this.token = options.token;
47
+ this.tokenExpiresAtMs = undefined;
48
+ this.apiKey = options.apiKey ?? esriConfig.apiKey;
49
+ // Bind so passing a bare `window.fetch`/`globalThis.fetch` cannot throw an
50
+ // "Illegal invocation" when called as `this.fetchFn(...)`.
51
+ this.fetchFn = (options.fetchFn ?? fetch).bind(globalThis);
52
+ }
53
+ /** The currently held portal token, if any. */
54
+ getToken() {
55
+ return this.token;
56
+ }
57
+ /**
58
+ * Exchange username/password for a portal token via
59
+ * `POST /sharing/rest/generateToken`. Stores the token on this instance and
60
+ * returns the credential (`expires` ms → `expiresAtMs`).
61
+ */
62
+ async generateToken(options) {
63
+ const referer = options.referer ?? defaultReferer(this.sharingRestBase);
64
+ const body = new URLSearchParams();
65
+ body.set("username", options.username);
66
+ body.set("password", options.password);
67
+ body.set("client", options.client ?? "referer");
68
+ if (referer) {
69
+ body.set("referer", referer);
70
+ }
71
+ if (typeof options.expirationMinutes === "number" && Number.isFinite(options.expirationMinutes)) {
72
+ body.set("expiration", String(options.expirationMinutes));
73
+ }
74
+ body.set("f", "json");
75
+ const response = await this.fetchFn(`${this.sharingRestBase}/generateToken`, {
76
+ method: "POST",
77
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
78
+ body: body.toString(),
79
+ });
80
+ const json = (await response.json());
81
+ throwIfEsriError(json, "Portal generateToken failed.");
82
+ if (typeof json.token !== "string" || json.token.length === 0) {
83
+ throw new PortalError("Portal generateToken response did not include a token.");
84
+ }
85
+ const expiresAtMs = typeof json.expires === "number" && Number.isFinite(json.expires) ? json.expires : 0;
86
+ this.token = json.token;
87
+ this.tokenExpiresAtMs = expiresAtMs || undefined;
88
+ return { token: json.token, expiresAtMs, ssl: json.ssl ?? true };
89
+ }
90
+ /** `GET /sharing/rest/info`. */
91
+ async getInfo() {
92
+ return this.getJson("/info", {}, undefined);
93
+ }
94
+ /** `GET /sharing/rest/portals/self` (attaches the token when present). */
95
+ async getPortalSelf() {
96
+ return this.getJson("/portals/self", {}, this.token);
97
+ }
98
+ /** `GET /sharing/rest/search`. */
99
+ async search(options = {}) {
100
+ const params = {
101
+ q: options.q,
102
+ start: options.start,
103
+ num: options.num,
104
+ };
105
+ const raw = await this.getJson("/search", params, options.token ?? this.token);
106
+ return {
107
+ query: raw.query ?? options.q ?? "",
108
+ total: raw.total ?? 0,
109
+ start: raw.start ?? options.start ?? 1,
110
+ num: raw.num ?? options.num ?? 0,
111
+ nextStart: raw.nextStart ?? -1,
112
+ results: Array.isArray(raw.results) ? raw.results : [],
113
+ };
114
+ }
115
+ /** `GET /sharing/rest/content/items/{id}`. */
116
+ async getItem(itemId, options = {}) {
117
+ return this.getJson(`/content/items/${encodeURIComponent(itemId)}`, {}, options.token ?? this.token);
118
+ }
119
+ /**
120
+ * Resolve an item (fetching it when given an id), parse its service `url`, and
121
+ * return an authenticated handle. Feature Service items yield a
122
+ * {@link FeatureLayerCompat} bound to a client carrying the portal token;
123
+ * Map/Image Service items yield the resolved `{ baseUrl, serviceId, client }`.
124
+ */
125
+ async openFeatureLayer(itemOrId, options = {}) {
126
+ const item = typeof itemOrId === "string" ? await this.getItem(itemOrId) : itemOrId;
127
+ if (!item.url) {
128
+ throw new PortalError(`Portal item ${item.id} has no service URL to open.`);
129
+ }
130
+ const serviceUrl = trimTrailingSlashes(item.url);
131
+ if (isFeatureService(item.type, serviceUrl)) {
132
+ const hasLayerId = /\/FeatureServer\/\d+$/i.test(serviceUrl);
133
+ const service = parseFeatureLayerUrl(hasLayerId ? serviceUrl : `${serviceUrl}/0`);
134
+ const client = this.buildAuthenticatedClient(service.baseUrl);
135
+ let layerId = service.layerId;
136
+ if (!hasLayerId) {
137
+ if (options.layerId !== undefined) {
138
+ layerId = options.layerId;
139
+ }
140
+ else {
141
+ const metadata = await client.getFeatureServiceMetadata(service.serviceId);
142
+ const firstLayerId = metadata.layers?.[0]?.id;
143
+ if (typeof firstLayerId !== "number" || !Number.isSafeInteger(firstLayerId)) {
144
+ throw new PortalError(`Portal Feature Service item ${item.id} does not advertise a layer to open.`);
145
+ }
146
+ layerId = firstLayerId;
147
+ }
148
+ }
149
+ const layerUrl = hasLayerId ? serviceUrl : `${serviceUrl}/${layerId}`;
150
+ const parsed = parseFeatureLayerUrl(layerUrl);
151
+ const layer = new FeatureLayerCompat({ url: layerUrl, client, title: item.title, id: item.id });
152
+ return {
153
+ type: "feature-service",
154
+ item,
155
+ baseUrl: parsed.baseUrl,
156
+ serviceId: parsed.serviceId,
157
+ layerId: parsed.layerId,
158
+ client,
159
+ layer,
160
+ };
161
+ }
162
+ if (isImageService(item.type, serviceUrl)) {
163
+ const parsed = parseImageServiceUrl(serviceUrl);
164
+ const client = this.buildAuthenticatedClient(parsed.baseUrl);
165
+ return { type: "image-service", item, baseUrl: parsed.baseUrl, serviceId: parsed.serviceId, client };
166
+ }
167
+ const parsed = parseMapServiceUrl(serviceUrl);
168
+ const client = this.buildAuthenticatedClient(parsed.baseUrl);
169
+ return { type: "map-service", item, baseUrl: parsed.baseUrl, serviceId: parsed.serviceId, client };
170
+ }
171
+ /**
172
+ * Register the currently held token with the shared {@link identityManager}
173
+ * under this portal's `/sharing/rest` server, so subsequent esri-compat calls
174
+ * resolve the same credential.
175
+ */
176
+ registerWithIdentityManager() {
177
+ if (!this.token) {
178
+ throw new PortalError("PortalCompat has no token to register; call generateToken first.");
179
+ }
180
+ identityManager.registerToken({
181
+ server: this.sharingRestBase,
182
+ token: this.token,
183
+ ...(this.tokenExpiresAtMs !== undefined ? { expires: this.tokenExpiresAtMs } : {}),
184
+ });
185
+ }
186
+ buildAuthenticatedClient(baseUrl) {
187
+ const interceptors = [];
188
+ if (this.token) {
189
+ interceptors.push(createArcGisTokenInterceptor({ getToken: () => this.token }));
190
+ }
191
+ return new HonuaClient({
192
+ baseUrl,
193
+ fetchFn: this.fetchFn,
194
+ ...(this.apiKey ? { apiKey: this.apiKey } : {}),
195
+ ...(interceptors.length > 0 ? { interceptors } : {}),
196
+ });
197
+ }
198
+ async getJson(path, params, token) {
199
+ const query = new URLSearchParams();
200
+ query.set("f", "json");
201
+ for (const [key, value] of Object.entries(params)) {
202
+ if (value !== undefined && value !== null && value !== "") {
203
+ query.set(key, String(value));
204
+ }
205
+ }
206
+ if (token) {
207
+ query.set("token", token);
208
+ }
209
+ const response = await this.fetchFn(`${this.sharingRestBase}${path}?${query.toString()}`);
210
+ const json = (await response.json());
211
+ throwIfEsriError(json, `Portal request to ${path} failed.`);
212
+ return json;
213
+ }
214
+ }
215
+ function normalizeSharingRestBase(portalUrl) {
216
+ const trimmed = trimTrailingSlashes(portalUrl);
217
+ if (/\/sharing\/rest$/i.test(trimmed)) {
218
+ return trimmed;
219
+ }
220
+ return `${trimmed}/sharing/rest`;
221
+ }
222
+ function trimTrailingSlashes(value) {
223
+ let end = value.length;
224
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
225
+ end--;
226
+ }
227
+ return value.slice(0, end);
228
+ }
229
+ function defaultReferer(sharingRestBase) {
230
+ if (globalThis.location?.origin) {
231
+ return globalThis.location.origin;
232
+ }
233
+ try {
234
+ return new URL(sharingRestBase).origin;
235
+ }
236
+ catch {
237
+ return undefined;
238
+ }
239
+ }
240
+ function isFeatureService(type, serviceUrl) {
241
+ return /feature\s*service/i.test(type) || /\/FeatureServer(\/\d+)?$/i.test(serviceUrl);
242
+ }
243
+ function isImageService(type, serviceUrl) {
244
+ return /image\s*service/i.test(type) || /\/ImageServer(\/\d+)?$/i.test(serviceUrl);
245
+ }
246
+ function throwIfEsriError(json, fallbackMessage) {
247
+ if (json && typeof json === "object" && json.error) {
248
+ const { code, message, details } = json.error;
249
+ throw new PortalError(message ?? fallbackMessage, code, details);
250
+ }
251
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Bridges the esri-compat renderer shims to the first-class renderer objects
3
+ * in `@honua/sdk-js/style` (issue #497 REQ-002). The compat classes stay
4
+ * pure state holders; these helpers project their state through the same
5
+ * WebMap symbol/renderer conversion path the WebMap converter uses, so the
6
+ * SDK has exactly one class-breaks/unique-value implementation.
7
+ *
8
+ * Kept out of the compat classes themselves so consumers who never emit
9
+ * renderer objects do not pay for the conversion pipeline.
10
+ *
11
+ * @module
12
+ */
13
+ import type { ClassBreaksRenderer, UniqueValueRenderer } from "../style/renderers.js";
14
+ import type { WarningCollector } from "../webmap/warnings.js";
15
+ import type { ClassBreaksRendererCompat } from "./class-breaks-renderer.js";
16
+ import type { UniqueValueRendererCompat } from "./unique-value-renderer.js";
17
+ /** Result of projecting a compat renderer to a renderer object. @experimental */
18
+ export interface CompatRendererProjection<R> {
19
+ /** The emitted renderer object, or `undefined` when nothing was convertible. */
20
+ readonly renderer: R | undefined;
21
+ /** Symbol/renderer conversion warnings collected during projection. */
22
+ readonly warnings: WarningCollector["warnings"];
23
+ }
24
+ /**
25
+ * Emit a first-class {@link ClassBreaksRenderer} from a
26
+ * `ClassBreaksRendererCompat` shim. Compat `minValue`/`maxValue` map to the
27
+ * WebMap `classMinValue`/`classMaxValue` fields and symbols convert through
28
+ * the shared WebMap symbol pipeline.
29
+ *
30
+ * @experimental
31
+ */
32
+ export declare function rendererObjectFromClassBreaksCompat(compat: ClassBreaksRendererCompat): CompatRendererProjection<ClassBreaksRenderer>;
33
+ /**
34
+ * Emit a first-class {@link UniqueValueRenderer} from a
35
+ * `UniqueValueRendererCompat` shim.
36
+ *
37
+ * @experimental
38
+ */
39
+ export declare function rendererObjectFromUniqueValueCompat(compat: UniqueValueRendererCompat): CompatRendererProjection<UniqueValueRenderer>;
40
+ //# sourceMappingURL=renderer-objects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer-objects.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/renderer-objects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAE5E,iFAAiF;AACjF,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,SAAS,CAAC;IACjC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;CACjD;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yBAAyB,GAChC,wBAAwB,CAAC,mBAAmB,CAAC,CAmB/C;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yBAAyB,GAChC,wBAAwB,CAAC,mBAAmB,CAAC,CAmB/C"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Bridges the esri-compat renderer shims to the first-class renderer objects
3
+ * in `@honua/sdk-js/style` (issue #497 REQ-002). The compat classes stay
4
+ * pure state holders; these helpers project their state through the same
5
+ * WebMap symbol/renderer conversion path the WebMap converter uses, so the
6
+ * SDK has exactly one class-breaks/unique-value implementation.
7
+ *
8
+ * Kept out of the compat classes themselves so consumers who never emit
9
+ * renderer objects do not pay for the conversion pipeline.
10
+ *
11
+ * @module
12
+ */
13
+ import { classBreaksRendererFromWebMap, uniqueValueRendererFromWebMap } from "../webmap/convert-renderer.js";
14
+ import { createWarningCollector } from "../webmap/warnings.js";
15
+ /**
16
+ * Emit a first-class {@link ClassBreaksRenderer} from a
17
+ * `ClassBreaksRendererCompat` shim. Compat `minValue`/`maxValue` map to the
18
+ * WebMap `classMinValue`/`classMaxValue` fields and symbols convert through
19
+ * the shared WebMap symbol pipeline.
20
+ *
21
+ * @experimental
22
+ */
23
+ export function rendererObjectFromClassBreaksCompat(compat) {
24
+ const warn = createWarningCollector();
25
+ const renderer = classBreaksRendererFromWebMap({
26
+ type: "classBreaks",
27
+ field: compat.field,
28
+ minValue: compat.minValue,
29
+ defaultSymbol: compat.defaultSymbol,
30
+ defaultLabel: compat.defaultLabel,
31
+ classBreakInfos: compat.classBreakInfos.map((info) => ({
32
+ classMinValue: info.minValue,
33
+ classMaxValue: info.maxValue,
34
+ label: info.label,
35
+ symbol: info.symbol,
36
+ })),
37
+ }, warn);
38
+ return { renderer, warnings: warn.warnings };
39
+ }
40
+ /**
41
+ * Emit a first-class {@link UniqueValueRenderer} from a
42
+ * `UniqueValueRendererCompat` shim.
43
+ *
44
+ * @experimental
45
+ */
46
+ export function rendererObjectFromUniqueValueCompat(compat) {
47
+ const warn = createWarningCollector();
48
+ const renderer = uniqueValueRendererFromWebMap({
49
+ type: "uniqueValue",
50
+ field1: compat.field,
51
+ field2: compat.field2,
52
+ field3: compat.field3,
53
+ defaultSymbol: compat.defaultSymbol,
54
+ defaultLabel: compat.defaultLabel,
55
+ uniqueValueInfos: compat.uniqueValueInfos.map((info) => ({
56
+ value: info.value,
57
+ label: info.label,
58
+ symbol: info.symbol,
59
+ })),
60
+ }, warn);
61
+ return { renderer, warnings: warn.warnings };
62
+ }
@@ -1,6 +1,32 @@
1
+ import type { EditSketchTool } from "../contract/edit-sketch.js";
2
+ import type { SnappingConfig } from "../contract/edit-snapping.js";
1
3
  import { CompatEventBus } from "./event-bus.js";
4
+ import { type SnappingOptionsCompat } from "./snapping.js";
2
5
  export type SketchCreationModeCompat = "single" | "update" | "continuous";
3
6
  export type SketchToolCompat = "point" | "polyline" | "polygon" | "rectangle" | "circle";
7
+ /**
8
+ * Duck-typed subset of the runtime terra-draw sketch handle
9
+ * (`bindTerraDrawSketch` / `createTerraDrawSketch` from
10
+ * `@honua/sdk-js/runtime`) that the Sketch shim can delegate tool modes to.
11
+ * Structural on purpose: the compat layer never imports the optional
12
+ * terra-draw peer, it only feature-detects a binding the app wired up.
13
+ *
14
+ * @experimental
15
+ */
16
+ export interface SketchToolBindingCompat {
17
+ setTool(tool: EditSketchTool): unknown;
18
+ select?(): void;
19
+ cancel?(): void;
20
+ }
21
+ /**
22
+ * Feature-detect a terra-draw sketch binding: any object exposing a callable
23
+ * `setTool` qualifies; anything else (including `undefined` when terra-draw
24
+ * is not installed) resolves to `undefined` and the shim keeps its current
25
+ * headless behavior.
26
+ *
27
+ * @experimental
28
+ */
29
+ export declare function resolveSketchToolBindingCompat(value: unknown): SketchToolBindingCompat | undefined;
4
30
  export interface SketchCreateOptionsCompat {
5
31
  mode?: "click" | "freehand" | "hybrid";
6
32
  }
@@ -20,6 +46,15 @@ export interface SketchCompatOptions {
20
46
  creationMode?: SketchCreationModeCompat;
21
47
  defaultCreateOptions?: Partial<SketchCreateOptionsCompat>;
22
48
  defaultUpdateOptions?: Partial<SketchUpdateOptionsCompat>;
49
+ snappingOptions?: SnappingOptionsCompat;
50
+ /**
51
+ * Optional terra-draw sketch binding (see {@link SketchToolBindingCompat}).
52
+ * When present, `create`/`update`/`cancel`/`reset` delegate tool-mode
53
+ * changes to the binding; otherwise the shim keeps its headless behavior.
54
+ *
55
+ * @experimental
56
+ */
57
+ sketchBinding?: unknown;
23
58
  }
24
59
  export interface SketchCreateResultCompat {
25
60
  state: "complete" | "cancel";
@@ -46,8 +81,16 @@ export declare class SketchCompat {
46
81
  activeCreateOptions: Partial<SketchCreateOptionsCompat> | undefined;
47
82
  activeUpdateGraphics: Record<string, unknown>[];
48
83
  activeUpdateOptions: Partial<SketchUpdateOptionsCompat> | undefined;
84
+ snappingOptions: SnappingOptionsCompat;
49
85
  private readonly watchListeners;
86
+ private readonly sketchBinding;
50
87
  constructor(options?: SketchCompatOptions);
88
+ /** Whether tool modes delegate to a terra-draw sketch binding. @experimental */
89
+ delegatesToSketchBinding(): boolean;
90
+ /** Replace the ArcGIS-shaped snapping options and notify watchers. */
91
+ setSnappingOptions(options: SnappingOptionsCompat): void;
92
+ /** The snapping options mapped onto the contract `SnappingConfig`. */
93
+ snappingConfig(): SnappingConfig;
51
94
  load(): Promise<SketchCompat>;
52
95
  when(callback?: (widget: SketchCompat) => void): Promise<SketchCompat>;
53
96
  watch(propertyName: string, listener: (value: unknown) => void): SketchHandleCompat;
@@ -1 +1 @@
1
- {"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/sketch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/sketch.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,26 @@
1
1
  import { CompatEventBus, resolveCompatEventBus, safeInvokeCompatListener } from "./event-bus.js";
2
+ import { snappingOptionsToSnappingConfig } from "./snapping.js";
3
+ const SKETCH_COMPAT_TOOL_TO_EDIT_TOOL = Object.freeze({
4
+ point: "point",
5
+ polyline: "line",
6
+ polygon: "polygon",
7
+ rectangle: "rectangle",
8
+ circle: "circle",
9
+ });
10
+ /**
11
+ * Feature-detect a terra-draw sketch binding: any object exposing a callable
12
+ * `setTool` qualifies; anything else (including `undefined` when terra-draw
13
+ * is not installed) resolves to `undefined` and the shim keeps its current
14
+ * headless behavior.
15
+ *
16
+ * @experimental
17
+ */
18
+ export function resolveSketchToolBindingCompat(value) {
19
+ if (value !== null && typeof value === "object" && typeof value.setTool === "function") {
20
+ return value;
21
+ }
22
+ return undefined;
23
+ }
2
24
  export class SketchCompat {
3
25
  view;
4
26
  layer;
@@ -15,7 +37,9 @@ export class SketchCompat {
15
37
  activeCreateOptions;
16
38
  activeUpdateGraphics;
17
39
  activeUpdateOptions;
40
+ snappingOptions;
18
41
  watchListeners;
42
+ sketchBinding;
19
43
  constructor(options = {}) {
20
44
  this.view = options.view;
21
45
  this.layer = options.layer;
@@ -32,7 +56,23 @@ export class SketchCompat {
32
56
  this.activeCreateOptions = undefined;
33
57
  this.activeUpdateGraphics = [];
34
58
  this.activeUpdateOptions = undefined;
59
+ this.snappingOptions = { ...(options.snappingOptions ?? {}) };
35
60
  this.watchListeners = new Map();
61
+ this.sketchBinding = resolveSketchToolBindingCompat(options.sketchBinding);
62
+ }
63
+ /** Whether tool modes delegate to a terra-draw sketch binding. @experimental */
64
+ delegatesToSketchBinding() {
65
+ return this.sketchBinding !== undefined;
66
+ }
67
+ /** Replace the ArcGIS-shaped snapping options and notify watchers. */
68
+ setSnappingOptions(options) {
69
+ this.snappingOptions = { ...options };
70
+ this.notifyWatchers("snappingOptions", this.snappingOptions);
71
+ this.eventBus.emit("sketch.snapping-options-changed", { options: this.snappingOptions }, this);
72
+ }
73
+ /** The snapping options mapped onto the contract `SnappingConfig`. */
74
+ snappingConfig() {
75
+ return snappingOptionsToSnappingConfig(this.snappingOptions);
36
76
  }
37
77
  async load() {
38
78
  if (this.loaded) {
@@ -83,6 +123,7 @@ export class SketchCompat {
83
123
  creationMode: this.creationMode,
84
124
  options: this.activeCreateOptions,
85
125
  }, this);
126
+ this.sketchBinding?.setTool(SKETCH_COMPAT_TOOL_TO_EDIT_TOOL[tool]);
86
127
  }
87
128
  complete(graphic) {
88
129
  if (this.state !== "active" || !this.activeTool) {
@@ -97,6 +138,8 @@ export class SketchCompat {
97
138
  graphic: createdGraphic,
98
139
  layerGraphicCount: getLayerGraphics(this.layer).length,
99
140
  }, this);
141
+ if (this.creationMode !== "continuous")
142
+ this.sketchBinding?.cancel?.();
100
143
  return {
101
144
  state: "complete",
102
145
  tool,
@@ -110,6 +153,7 @@ export class SketchCompat {
110
153
  const tool = this.activeTool;
111
154
  this.clearActiveState();
112
155
  this.eventBus.emit("sketch.create-cancelled", { tool }, this);
156
+ this.sketchBinding?.cancel?.();
113
157
  return {
114
158
  state: "cancel",
115
159
  tool,
@@ -128,6 +172,7 @@ export class SketchCompat {
128
172
  count: normalizedGraphics.length,
129
173
  options: this.activeUpdateOptions,
130
174
  }, this);
175
+ this.sketchBinding?.select?.();
131
176
  return this.activeUpdateGraphics;
132
177
  }
133
178
  delete(graphics) {
@@ -157,6 +202,7 @@ export class SketchCompat {
157
202
  this.activeUpdateOptions = undefined;
158
203
  this.notifyWatchers("activeUpdateOptions", this.activeUpdateOptions);
159
204
  this.eventBus.emit("sketch.reset", undefined, this);
205
+ this.sketchBinding?.cancel?.();
160
206
  }
161
207
  clearActiveState() {
162
208
  this.state = "ready";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * ArcGIS `SnappingOptions`-shaped compat surface, mapped onto the
3
+ * renderer-neutral `SnappingConfig` from `contract/edit-snapping.ts`.
4
+ *
5
+ * @module
6
+ */
7
+ import { type SnappingConfig } from "../contract/edit-snapping.js";
8
+ /** ArcGIS `FeatureSnappingLayerSource`-shaped entry. */
9
+ export interface SnappingFeatureSourceCompat {
10
+ /** Layer (or layer-like object) providing snap features. */
11
+ layer?: unknown;
12
+ /** Explicit Honua source id when no layer id is derivable. */
13
+ id?: string;
14
+ /** @default true */
15
+ enabled?: boolean;
16
+ }
17
+ /** ArcGIS `SnappingOptions`-shaped configuration accepted by the shims. */
18
+ export interface SnappingOptionsCompat {
19
+ /** Master switch. ArcGIS defaults snapping off. @default false */
20
+ enabled?: boolean;
21
+ /** Snap distance in pixels; maps to `SnappingConfig.tolerance`. */
22
+ distance?: number;
23
+ /** Snap to features from `featureSources`. @default true */
24
+ featureEnabled?: boolean;
25
+ /**
26
+ * Snap to the geometry being drawn. Stored for parity; self snapping is a
27
+ * runtime concern (index the active sketch source to approximate it).
28
+ * @default true
29
+ */
30
+ selfEnabled?: boolean;
31
+ featureSources?: readonly SnappingFeatureSourceCompat[];
32
+ }
33
+ /**
34
+ * Map ArcGIS-shaped snapping options to the contract `SnappingConfig`.
35
+ *
36
+ * `distance` maps to pixel tolerance; `featureSources` map to per-source
37
+ * enablement keyed by `source.id` (falling back to a duck-typed `layer.id` /
38
+ * `layer.sourceId`); `featureEnabled: false` disables every listed source.
39
+ * Vertex and edge candidates are enabled — matching ArcGIS 2D snapping,
40
+ * which always resolves both.
41
+ */
42
+ export declare function snappingOptionsToSnappingConfig(options?: SnappingOptionsCompat): SnappingConfig;
43
+ //# sourceMappingURL=snapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapping.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/snapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,8BAA8B,CAAC;AAEtC,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC1C,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,SAAS,2BAA2B,EAAE,CAAC;CACzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAcnG"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * ArcGIS `SnappingOptions`-shaped compat surface, mapped onto the
3
+ * renderer-neutral `SnappingConfig` from `contract/edit-snapping.ts`.
4
+ *
5
+ * @module
6
+ */
7
+ import { DEFAULT_SNAPPING_CONFIG, resolveSnappingConfig, } from "../contract/edit-snapping.js";
8
+ /**
9
+ * Map ArcGIS-shaped snapping options to the contract `SnappingConfig`.
10
+ *
11
+ * `distance` maps to pixel tolerance; `featureSources` map to per-source
12
+ * enablement keyed by `source.id` (falling back to a duck-typed `layer.id` /
13
+ * `layer.sourceId`); `featureEnabled: false` disables every listed source.
14
+ * Vertex and edge candidates are enabled — matching ArcGIS 2D snapping,
15
+ * which always resolves both.
16
+ */
17
+ export function snappingOptionsToSnappingConfig(options = {}) {
18
+ const kinds = ["vertex", "edge"];
19
+ const sources = {};
20
+ for (const featureSource of options.featureSources ?? []) {
21
+ const sourceId = featureSourceId(featureSource);
22
+ if (sourceId === undefined)
23
+ continue;
24
+ sources[sourceId] = options.featureEnabled === false ? false : (featureSource.enabled ?? true);
25
+ }
26
+ return resolveSnappingConfig({
27
+ enabled: options.enabled ?? false,
28
+ tolerance: options.distance ?? DEFAULT_SNAPPING_CONFIG.tolerance,
29
+ kinds,
30
+ sources,
31
+ });
32
+ }
33
+ function featureSourceId(featureSource) {
34
+ if (featureSource.id !== undefined)
35
+ return featureSource.id;
36
+ const layer = featureSource.layer;
37
+ if (layer && typeof layer === "object") {
38
+ const candidate = layer;
39
+ if (typeof candidate.id === "string")
40
+ return candidate.id;
41
+ if (typeof candidate.sourceId === "string")
42
+ return candidate.sourceId;
43
+ }
44
+ return undefined;
45
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/url.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAkBvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAanE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAarE"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../src/esri-compat/url.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAkBvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAanE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAarE"}
@@ -1,7 +1,7 @@
1
1
  const ABSOLUTE_URL_RE = /^[A-Za-z][A-Za-z\d+.-]*:\/\//;
2
2
  const PROTOCOL_RELATIVE_URL_RE = /^\/\//;
3
3
  const RELATIVE_URL_BASE = "https://honua.invalid";
4
- const FEATURE_LAYER_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/FeatureServer\/(?<layerId>\d+)\/?$/;
4
+ const FEATURE_LAYER_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+(?:\/[^/]+)*)\/FeatureServer\/(?<layerId>\d+)\/?$/;
5
5
  const MAP_SERVICE_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/MapServer(?:\/\d+)?\/?$/;
6
6
  const IMAGE_SERVICE_PATH_RE = /^(?<prefix>.*)\/rest\/services\/(?<serviceId>[^/]+)\/ImageServer(?:\/\d+)?\/?$/;
7
7
  export function parseFeatureLayerUrl(url) {