@honua/sdk-esri-compat 0.1.12 → 0.1.13

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 (248) hide show
  1. package/README.md +2 -0
  2. package/columnar/apache-arrow.d.ts +17 -0
  3. package/columnar/apache-arrow.d.ts.map +1 -0
  4. package/columnar/apache-arrow.js +1036 -0
  5. package/columnar/batch-cache-conformance.d.ts +66 -0
  6. package/columnar/batch-cache-conformance.d.ts.map +1 -0
  7. package/columnar/batch-cache-conformance.js +317 -0
  8. package/columnar/batch-cache-indexeddb.d.ts +31 -0
  9. package/columnar/batch-cache-indexeddb.d.ts.map +1 -0
  10. package/columnar/batch-cache-indexeddb.js +148 -0
  11. package/columnar/batch-cache.d.ts +298 -0
  12. package/columnar/batch-cache.d.ts.map +1 -0
  13. package/columnar/batch-cache.js +757 -0
  14. package/columnar/geoarrow-aggregate.d.ts +178 -0
  15. package/columnar/geoarrow-aggregate.d.ts.map +1 -0
  16. package/columnar/geoarrow-aggregate.js +976 -0
  17. package/columnar/geoarrow-patch.d.ts +382 -0
  18. package/columnar/geoarrow-patch.d.ts.map +1 -0
  19. package/columnar/geoarrow-patch.js +1656 -0
  20. package/columnar/geoarrow-reproject.d.ts +22 -0
  21. package/columnar/geoarrow-reproject.d.ts.map +1 -0
  22. package/columnar/geoarrow-reproject.js +119 -0
  23. package/columnar/geoarrow-serialization.d.ts +68 -0
  24. package/columnar/geoarrow-serialization.d.ts.map +1 -0
  25. package/columnar/geoarrow-serialization.js +331 -0
  26. package/columnar/geoarrow-types.d.ts +271 -0
  27. package/columnar/geoarrow-types.d.ts.map +1 -0
  28. package/columnar/geoarrow-types.js +15 -0
  29. package/columnar/geoarrow.d.ts +52 -0
  30. package/columnar/geoarrow.d.ts.map +1 -0
  31. package/columnar/geoarrow.js +1469 -0
  32. package/columnar/index.d.ts +43 -0
  33. package/columnar/index.d.ts.map +1 -0
  34. package/columnar/index.js +29 -0
  35. package/columnar/operators.d.ts +70 -0
  36. package/columnar/operators.d.ts.map +1 -0
  37. package/columnar/operators.js +307 -0
  38. package/columnar/result.d.ts +369 -0
  39. package/columnar/result.d.ts.map +1 -0
  40. package/columnar/result.js +815 -0
  41. package/columnar/telemetry.d.ts +152 -0
  42. package/columnar/telemetry.d.ts.map +1 -0
  43. package/columnar/telemetry.js +265 -0
  44. package/columnar/transfer.d.ts +28 -0
  45. package/columnar/transfer.d.ts.map +1 -0
  46. package/columnar/transfer.js +669 -0
  47. package/columnar/types.d.ts +175 -0
  48. package/columnar/types.d.ts.map +1 -0
  49. package/columnar/types.js +22 -0
  50. package/columnar/worker.d.ts +157 -0
  51. package/columnar/worker.d.ts.map +1 -0
  52. package/columnar/worker.js +1059 -0
  53. package/connect-url-safety.d.ts +53 -0
  54. package/connect-url-safety.js +189 -0
  55. package/contract/pmtiles.d.ts +6 -5
  56. package/contract/pmtiles.d.ts.map +1 -1
  57. package/contract/pmtiles.js +30 -5
  58. package/core/client.d.ts.map +1 -1
  59. package/core/client.js +87 -19
  60. package/core/deprecation-notice.d.ts +28 -0
  61. package/core/deprecation-notice.d.ts.map +1 -0
  62. package/core/deprecation-notice.js +22 -0
  63. package/core/esri-geojson.d.ts.map +1 -1
  64. package/core/esri-geojson.js +10 -6
  65. package/core/optional-grpc-peer.d.ts +15 -0
  66. package/core/optional-grpc-peer.d.ts.map +1 -0
  67. package/core/optional-grpc-peer.js +60 -0
  68. package/esri-compat/basemap-gallery.d.ts +2 -0
  69. package/esri-compat/basemap-gallery.d.ts.map +1 -1
  70. package/esri-compat/basemap-gallery.js +12 -0
  71. package/esri-compat/basemap.d.ts +4 -0
  72. package/esri-compat/basemap.d.ts.map +1 -1
  73. package/esri-compat/basemap.js +70 -0
  74. package/esri-compat/bookmarks.d.ts +2 -0
  75. package/esri-compat/bookmarks.d.ts.map +1 -1
  76. package/esri-compat/bookmarks.js +12 -0
  77. package/esri-compat/controls.d.ts +18 -0
  78. package/esri-compat/controls.d.ts.map +1 -1
  79. package/esri-compat/controls.js +120 -57
  80. package/esri-compat/directions.d.ts +4 -0
  81. package/esri-compat/directions.d.ts.map +1 -1
  82. package/esri-compat/directions.js +30 -20
  83. package/esri-compat/editor.d.ts +4 -0
  84. package/esri-compat/editor.d.ts.map +1 -1
  85. package/esri-compat/editor.js +16 -0
  86. package/esri-compat/esri-config.d.ts +2 -1
  87. package/esri-compat/esri-config.d.ts.map +1 -1
  88. package/esri-compat/feature-effect.d.ts +23 -0
  89. package/esri-compat/feature-effect.d.ts.map +1 -0
  90. package/esri-compat/feature-effect.js +12 -0
  91. package/esri-compat/feature-form.d.ts +6 -0
  92. package/esri-compat/feature-form.d.ts.map +1 -1
  93. package/esri-compat/feature-form.js +63 -0
  94. package/esri-compat/feature-layer.d.ts +21 -2
  95. package/esri-compat/feature-layer.d.ts.map +1 -1
  96. package/esri-compat/feature-layer.js +172 -1
  97. package/esri-compat/feature-table.d.ts +5 -0
  98. package/esri-compat/feature-table.d.ts.map +1 -1
  99. package/esri-compat/feature-table.js +23 -0
  100. package/esri-compat/identity-manager.d.ts +1 -1
  101. package/esri-compat/identity-manager.js +1 -1
  102. package/esri-compat/legend.d.ts +2 -0
  103. package/esri-compat/legend.d.ts.map +1 -1
  104. package/esri-compat/legend.js +98 -0
  105. package/esri-compat/locator.d.ts +1 -1
  106. package/esri-compat/locator.js +1 -1
  107. package/esri-compat/map-view-mount.d.ts +1 -0
  108. package/esri-compat/map-view-mount.d.ts.map +1 -1
  109. package/esri-compat/map-view-mount.js +46 -25
  110. package/esri-compat/map-view.d.ts +13 -0
  111. package/esri-compat/map-view.d.ts.map +1 -1
  112. package/esri-compat/map-view.js +94 -8
  113. package/esri-compat/map.d.ts +5 -1
  114. package/esri-compat/map.d.ts.map +1 -1
  115. package/esri-compat/map.js +10 -0
  116. package/esri-compat/measurement.d.ts +2 -0
  117. package/esri-compat/measurement.d.ts.map +1 -1
  118. package/esri-compat/measurement.js +14 -0
  119. package/esri-compat/point.d.ts +7 -0
  120. package/esri-compat/point.d.ts.map +1 -1
  121. package/esri-compat/point.js +33 -3
  122. package/esri-compat/popup.d.ts +2 -0
  123. package/esri-compat/popup.d.ts.map +1 -1
  124. package/esri-compat/popup.js +12 -0
  125. package/esri-compat/portal.d.ts +1 -1
  126. package/esri-compat/portal.js +1 -1
  127. package/esri-compat/print.d.ts +3 -0
  128. package/esri-compat/print.d.ts.map +1 -1
  129. package/esri-compat/print.js +15 -0
  130. package/esri-compat/search.d.ts +4 -0
  131. package/esri-compat/search.d.ts.map +1 -1
  132. package/esri-compat/search.js +25 -28
  133. package/esri-compat/sketch.d.ts +2 -0
  134. package/esri-compat/sketch.d.ts.map +1 -1
  135. package/esri-compat/sketch.js +12 -0
  136. package/esri-compat/widget-host.d.ts +46 -17
  137. package/esri-compat/widget-host.d.ts.map +1 -1
  138. package/esri-compat/widget-host.js +84 -20
  139. package/esri-compat/widget-shell-hosts.d.ts +61 -0
  140. package/esri-compat/widget-shell-hosts.d.ts.map +1 -0
  141. package/esri-compat/widget-shell-hosts.js +76 -0
  142. package/index.d.ts +3 -1
  143. package/index.js +1 -0
  144. package/offline/digest.d.ts +15 -0
  145. package/offline/digest.js +58 -0
  146. package/offline/quota.d.ts +161 -0
  147. package/offline/quota.js +370 -0
  148. package/offline/types.d.ts +349 -0
  149. package/offline/types.js +75 -0
  150. package/package.json +21 -2
  151. package/query-planner/canonical-filter.d.ts +30 -0
  152. package/query-planner/canonical-filter.d.ts.map +1 -0
  153. package/query-planner/canonical-filter.js +30 -0
  154. package/query-planner/canonical.d.ts +7 -0
  155. package/query-planner/canonical.d.ts.map +1 -0
  156. package/query-planner/canonical.js +145 -0
  157. package/query-planner/columnar-plan.d.ts +63 -0
  158. package/query-planner/columnar-plan.d.ts.map +1 -0
  159. package/query-planner/columnar-plan.js +94 -0
  160. package/query-planner/cql2-json.d.ts +17 -0
  161. package/query-planner/cql2-json.d.ts.map +1 -0
  162. package/query-planner/cql2-json.js +646 -0
  163. package/query-planner/diagnostics.d.ts +35 -0
  164. package/query-planner/diagnostics.d.ts.map +1 -0
  165. package/query-planner/diagnostics.js +386 -0
  166. package/query-planner/duckdb.d.ts +79 -0
  167. package/query-planner/duckdb.d.ts.map +1 -0
  168. package/query-planner/duckdb.js +772 -0
  169. package/query-planner/execution-context.d.ts +5 -0
  170. package/query-planner/execution-context.d.ts.map +1 -0
  171. package/query-planner/execution-context.js +52 -0
  172. package/query-planner/executor.d.ts +5 -0
  173. package/query-planner/executor.d.ts.map +1 -0
  174. package/query-planner/executor.js +229 -0
  175. package/query-planner/geoservices.d.ts +91 -0
  176. package/query-planner/geoservices.d.ts.map +1 -0
  177. package/query-planner/geoservices.js +688 -0
  178. package/query-planner/grpc.d.ts +100 -0
  179. package/query-planner/grpc.d.ts.map +1 -0
  180. package/query-planner/grpc.js +760 -0
  181. package/query-planner/index.d.ts +59 -0
  182. package/query-planner/index.d.ts.map +1 -0
  183. package/query-planner/index.js +43 -0
  184. package/query-planner/ir.d.ts +25 -0
  185. package/query-planner/ir.d.ts.map +1 -0
  186. package/query-planner/ir.js +479 -0
  187. package/query-planner/local-aggregate.d.ts +5 -0
  188. package/query-planner/local-aggregate.d.ts.map +1 -0
  189. package/query-planner/local-aggregate.js +126 -0
  190. package/query-planner/odata-protocol-module.d.ts +54 -0
  191. package/query-planner/odata-protocol-module.d.ts.map +1 -0
  192. package/query-planner/odata-protocol-module.js +199 -0
  193. package/query-planner/odata-v1.d.ts +23 -0
  194. package/query-planner/odata-v1.d.ts.map +1 -0
  195. package/query-planner/odata-v1.js +202 -0
  196. package/query-planner/odata.d.ts +45 -0
  197. package/query-planner/odata.d.ts.map +1 -0
  198. package/query-planner/odata.js +519 -0
  199. package/query-planner/ogc-compiler.d.ts +55 -0
  200. package/query-planner/ogc-compiler.d.ts.map +1 -0
  201. package/query-planner/ogc-compiler.js +480 -0
  202. package/query-planner/ogc-features.d.ts +53 -0
  203. package/query-planner/ogc-features.d.ts.map +1 -0
  204. package/query-planner/ogc-features.js +639 -0
  205. package/query-planner/planner.d.ts +17 -0
  206. package/query-planner/planner.d.ts.map +1 -0
  207. package/query-planner/planner.js +937 -0
  208. package/query-planner/representation.d.ts +59 -0
  209. package/query-planner/representation.d.ts.map +1 -0
  210. package/query-planner/representation.js +140 -0
  211. package/query-planner/resource.d.ts +94 -0
  212. package/query-planner/resource.d.ts.map +1 -0
  213. package/query-planner/resource.js +526 -0
  214. package/query-planner/semantic-canonical.d.ts +15 -0
  215. package/query-planner/semantic-canonical.d.ts.map +1 -0
  216. package/query-planner/semantic-canonical.js +62 -0
  217. package/query-planner/semantic-compat.d.ts +10 -0
  218. package/query-planner/semantic-compat.d.ts.map +1 -0
  219. package/query-planner/semantic-compat.js +35 -0
  220. package/query-planner/semantic-compiler.d.ts +64 -0
  221. package/query-planner/semantic-compiler.d.ts.map +1 -0
  222. package/query-planner/semantic-compiler.js +81 -0
  223. package/query-planner/semantic-literals.d.ts +30 -0
  224. package/query-planner/semantic-literals.d.ts.map +1 -0
  225. package/query-planner/semantic-literals.js +133 -0
  226. package/query-planner/semantic-types.d.ts +265 -0
  227. package/query-planner/semantic-types.d.ts.map +1 -0
  228. package/query-planner/semantic-types.js +1 -0
  229. package/query-planner/semantic.d.ts +54 -0
  230. package/query-planner/semantic.d.ts.map +1 -0
  231. package/query-planner/semantic.js +1421 -0
  232. package/query-planner/types.d.ts +712 -0
  233. package/query-planner/types.d.ts.map +1 -0
  234. package/query-planner/types.js +49 -0
  235. package/query-planner/wfs-protocol-module.d.ts +57 -0
  236. package/query-planner/wfs-protocol-module.d.ts.map +1 -0
  237. package/query-planner/wfs-protocol-module.js +731 -0
  238. package/query-planner/wfs-semantic.d.ts +58 -0
  239. package/query-planner/wfs-semantic.d.ts.map +1 -0
  240. package/query-planner/wfs-semantic.js +547 -0
  241. package/query-planner/wfs-v1.d.ts +41 -0
  242. package/query-planner/wfs-v1.d.ts.map +1 -0
  243. package/query-planner/wfs-v1.js +366 -0
  244. package/query-planner/wfs.d.ts +4 -0
  245. package/query-planner/wfs.d.ts.map +1 -0
  246. package/query-planner/wfs.js +2 -0
  247. package/widget-capabilities.d.ts +67 -0
  248. package/widget-capabilities.js +152 -0
@@ -0,0 +1,526 @@
1
+ import { HonuaAbortError } from "../core/errors.js";
2
+ import { canonicalStringify, sha256, toJsonValue } from "./canonical.js";
3
+ import { HonuaQueryPlanExecutionError } from "./types.js";
4
+ /** Discriminant for versioned, serializable query resource handles. */
5
+ export const QUERY_RESOURCE_HANDLE_KIND = "honua.query-resource";
6
+ /** First opaque query resource handle format. */
7
+ export const QUERY_RESOURCE_HANDLE_VERSION = "1.0";
8
+ const DEFAULT_MAX_ENTRIES = 256;
9
+ const HARD_MAX_ENTRIES = 4_096;
10
+ const MAX_SOURCE_COUNT = 64;
11
+ const MAX_SOURCE_BYTES = 16_384;
12
+ const MAX_TOTAL_SOURCE_BYTES = 131_072;
13
+ const MAX_RESOLVER_LENGTH = 255;
14
+ const MAX_IDENTITY_LENGTH = 256;
15
+ const EXTENSION_IDENTIFIER_PATTERN = /^[a-z0-9](?:[a-z0-9_-]*\.)+[a-z0-9][a-z0-9_-]*$/;
16
+ const RESOURCE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:~-]*$/;
17
+ const AUTHORIZATION_CONTEXT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/~-]*$/;
18
+ const JWT_SHAPE = /^eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}$/;
19
+ const INVALID_HANDLE_MESSAGE = "GeoParquet resource handle is invalid";
20
+ const RESOURCE_UNAVAILABLE_MESSAGE = "GeoParquet resource is unavailable";
21
+ const RESOURCE_EXPIRED_MESSAGE = "GeoParquet resource authorization has expired";
22
+ const RESOLUTION_FAILED_MESSAGE = "GeoParquet resource resolution failed";
23
+ const INVALID_REGISTRY_OPTIONS_MESSAGE = "GeoParquet resource registry options are invalid";
24
+ const INVALID_REGISTRATION_MESSAGE = "GeoParquet resource registration is invalid";
25
+ const EXPIRED_ENTRY = Object.freeze({ state: "expired" });
26
+ /** Create a credential-free handle without registering or resolving a locator. */
27
+ export function createGeoParquetResourceHandle(input) {
28
+ const parsed = inspectRecord(input, ["resolver", "id", "authorizationContextId"], ["resourceVersion"]);
29
+ if (!parsed)
30
+ throw invalidHandle();
31
+ return parseGeoParquetResourceHandle({
32
+ kind: QUERY_RESOURCE_HANDLE_KIND,
33
+ version: QUERY_RESOURCE_HANDLE_VERSION,
34
+ protocol: "geoparquet",
35
+ resource: {
36
+ kind: "resolver",
37
+ resolver: parsed.get("resolver"),
38
+ id: parsed.get("id"),
39
+ },
40
+ authorizationContextId: parsed.get("authorizationContextId"),
41
+ ...(parsed.has("resourceVersion") ? { resourceVersion: parsed.get("resourceVersion") } : {}),
42
+ });
43
+ }
44
+ /** Parse an exact v1 handle without invoking accessors or retaining rejected input. */
45
+ export function parseGeoParquetResourceHandle(value) {
46
+ const parsed = inspectRecord(value, ["kind", "version", "protocol", "resource", "authorizationContextId"], ["resourceVersion"]);
47
+ if (!parsed)
48
+ throw invalidHandle();
49
+ const resource = inspectRecord(parsed.get("resource"), ["kind", "resolver", "id"]);
50
+ const resolver = resource?.get("resolver");
51
+ const id = resource?.get("id");
52
+ const authorizationContextId = parsed.get("authorizationContextId");
53
+ const resourceVersion = parsed.get("resourceVersion");
54
+ const hasResourceVersion = parsed.has("resourceVersion");
55
+ if (parsed.get("kind") !== QUERY_RESOURCE_HANDLE_KIND ||
56
+ parsed.get("version") !== QUERY_RESOURCE_HANDLE_VERSION ||
57
+ parsed.get("protocol") !== "geoparquet" ||
58
+ resource?.get("kind") !== "resolver" ||
59
+ !isResolverIdentifier(resolver) ||
60
+ !isResourceIdentity(id) ||
61
+ !isAuthorizationContextId(authorizationContextId)) {
62
+ throw invalidHandle();
63
+ }
64
+ let normalizedResourceVersion;
65
+ if (hasResourceVersion) {
66
+ if (!isResourceIdentity(resourceVersion))
67
+ throw invalidHandle();
68
+ normalizedResourceVersion = resourceVersion;
69
+ }
70
+ return freezeHandle({
71
+ resource: { resolver, id },
72
+ authorizationContextId,
73
+ ...(normalizedResourceVersion !== undefined ? { resourceVersion: normalizedResourceVersion } : {}),
74
+ });
75
+ }
76
+ /** Stable credential-free fingerprint for plan/cache identity. */
77
+ export function hashGeoParquetResourceHandle(value) {
78
+ const handle = parseGeoParquetResourceHandle(value);
79
+ return sha256(canonicalStringify(toJsonValue(handle)));
80
+ }
81
+ /**
82
+ * Resolve a handle through an injected resolver after enforcing scope,
83
+ * cancellation, output bounds, and fixed-message error redaction.
84
+ */
85
+ export async function resolveGeoParquetResource(value, resolver, context) {
86
+ const handle = parseGeoParquetResourceHandle(value);
87
+ const safeContext = parseResolutionContext(context);
88
+ if (typeof resolver !== "function")
89
+ throw resolutionFailed();
90
+ if (isSignalAborted(safeContext.signal))
91
+ throw new HonuaAbortError();
92
+ if (safeContext.authorizationContextId !== handle.authorizationContextId)
93
+ throw resourceUnavailable();
94
+ let pending;
95
+ try {
96
+ pending = resolver(handle, safeContext);
97
+ }
98
+ catch (error) {
99
+ throw normalizeResolverFailure(error, safeContext.signal);
100
+ }
101
+ let resolved;
102
+ try {
103
+ resolved = await awaitAbortable(pending, safeContext.signal);
104
+ }
105
+ catch (error) {
106
+ throw normalizeResolverFailure(error, safeContext.signal);
107
+ }
108
+ const output = inspectRecord(resolved, ["sources"]);
109
+ const sources = output ? copyBoundedSources(output.get("sources")) : undefined;
110
+ if (!sources)
111
+ throw resolutionFailed();
112
+ return Object.freeze({ sources });
113
+ }
114
+ /** Create a bounded in-memory registry for private, rotation-friendly locators. */
115
+ export function createGeoParquetResourceRegistry(options) {
116
+ const parsedOptions = inspectRecord(options, ["resolver"], ["maxEntries", "now"]);
117
+ const resolverId = parsedOptions?.get("resolver");
118
+ const configuredMaxEntries = parsedOptions?.get("maxEntries");
119
+ const configuredNow = parsedOptions?.get("now");
120
+ if (!parsedOptions ||
121
+ !isResolverIdentifier(resolverId) ||
122
+ (configuredMaxEntries !== undefined && !isMaxEntries(configuredMaxEntries)) ||
123
+ (configuredNow !== undefined && !isClock(configuredNow))) {
124
+ throw new TypeError(INVALID_REGISTRY_OPTIONS_MESSAGE);
125
+ }
126
+ const maxEntries = configuredMaxEntries ?? DEFAULT_MAX_ENTRIES;
127
+ const now = configuredNow ?? Date.now;
128
+ const entries = new Map();
129
+ let disposed = false;
130
+ let readingClock = false;
131
+ const readCurrentTime = () => {
132
+ if (readingClock)
133
+ throw resolutionFailed();
134
+ readingClock = true;
135
+ try {
136
+ return readClock(now);
137
+ }
138
+ finally {
139
+ readingClock = false;
140
+ }
141
+ };
142
+ const resolver = (handleValue, contextValue) => {
143
+ const handle = parseGeoParquetResourceHandle(handleValue);
144
+ const context = parseResolutionContext(contextValue);
145
+ if (isSignalAborted(context.signal))
146
+ throw new HonuaAbortError();
147
+ if (context.authorizationContextId !== handle.authorizationContextId ||
148
+ handle.resource.resolver !== resolverId ||
149
+ disposed) {
150
+ throw resourceUnavailable();
151
+ }
152
+ const key = resourceKey(handle);
153
+ const entry = entries.get(key);
154
+ if (!entry)
155
+ throw resourceUnavailable();
156
+ if (entry.state === "expired")
157
+ throw resourceExpired();
158
+ if (entry.expiresAt !== undefined) {
159
+ const currentTime = readCurrentTime();
160
+ if (disposed || entries.get(key) !== entry)
161
+ throw resourceUnavailable();
162
+ if (currentTime >= entry.expiresAt) {
163
+ entries.set(key, EXPIRED_ENTRY);
164
+ throw resourceExpired();
165
+ }
166
+ }
167
+ return Object.freeze({ sources: entry.sources });
168
+ };
169
+ const registry = {
170
+ register(input) {
171
+ if (disposed)
172
+ throw resourceUnavailable();
173
+ const registration = parseRegistration(input);
174
+ const handle = freezeHandle({
175
+ resource: { resolver: resolverId, id: registration.id },
176
+ authorizationContextId: registration.authorizationContextId,
177
+ ...(registration.resourceVersion !== undefined ? { resourceVersion: registration.resourceVersion } : {}),
178
+ });
179
+ const key = resourceKey(handle);
180
+ if (!entries.has(key) && entries.size >= maxEntries)
181
+ reclaimExpiredEntries(entries, maxEntries);
182
+ if (!entries.has(key) && entries.size >= maxEntries && hasExpiringEntries(entries)) {
183
+ const snapshot = [...entries.entries()];
184
+ const currentTime = readCurrentTime();
185
+ if (disposed || !entriesMatchSnapshot(entries, snapshot))
186
+ throw resourceUnavailable();
187
+ reclaimExpiredEntries(entries, maxEntries, currentTime);
188
+ }
189
+ if (!entries.has(key) && entries.size >= maxEntries)
190
+ throw resourceUnavailable();
191
+ entries.set(key, Object.freeze({
192
+ state: "active",
193
+ sources: registration.sources,
194
+ ...(registration.expiresAt !== undefined ? { expiresAt: registration.expiresAt } : {}),
195
+ }));
196
+ return handle;
197
+ },
198
+ resolver,
199
+ revoke(handleValue) {
200
+ const handle = parseGeoParquetResourceHandle(handleValue);
201
+ if (handle.resource.resolver === resolverId)
202
+ entries.delete(resourceKey(handle));
203
+ },
204
+ dispose() {
205
+ if (disposed)
206
+ return;
207
+ disposed = true;
208
+ entries.clear();
209
+ },
210
+ };
211
+ return Object.freeze(registry);
212
+ }
213
+ function parseRegistration(input) {
214
+ const parsed = inspectRecord(input, ["id", "authorizationContextId", "sources"], ["resourceVersion", "expiresAt"]);
215
+ const id = parsed?.get("id");
216
+ const authorizationContextId = parsed?.get("authorizationContextId");
217
+ const resourceVersion = parsed?.get("resourceVersion");
218
+ const expiresAt = parsed?.get("expiresAt");
219
+ const sources = parsed ? copyBoundedSources(parsed.get("sources")) : undefined;
220
+ if (!parsed ||
221
+ !isResourceIdentity(id) ||
222
+ !isAuthorizationContextId(authorizationContextId) ||
223
+ (resourceVersion !== undefined && !isResourceIdentity(resourceVersion)) ||
224
+ (expiresAt !== undefined && !isEpochMilliseconds(expiresAt)) ||
225
+ !sources) {
226
+ throw new TypeError(INVALID_REGISTRATION_MESSAGE);
227
+ }
228
+ return {
229
+ id,
230
+ authorizationContextId,
231
+ ...(resourceVersion !== undefined ? { resourceVersion } : {}),
232
+ sources,
233
+ ...(expiresAt !== undefined ? { expiresAt } : {}),
234
+ };
235
+ }
236
+ function parseResolutionContext(value) {
237
+ const parsed = inspectRecord(value, ["authorizationContextId"], ["signal"]);
238
+ const authorizationContextId = parsed?.get("authorizationContextId");
239
+ const signal = parsed?.get("signal");
240
+ if (!parsed ||
241
+ !isAuthorizationContextId(authorizationContextId) ||
242
+ (signal !== undefined && !isAbortSignal(signal))) {
243
+ throw resourceUnavailable();
244
+ }
245
+ return Object.freeze({
246
+ authorizationContextId,
247
+ ...(signal !== undefined ? { signal } : {}),
248
+ });
249
+ }
250
+ function freezeHandle(input) {
251
+ const resource = Object.freeze({
252
+ kind: "resolver",
253
+ resolver: input.resource.resolver,
254
+ id: input.resource.id,
255
+ });
256
+ return Object.freeze({
257
+ kind: QUERY_RESOURCE_HANDLE_KIND,
258
+ version: QUERY_RESOURCE_HANDLE_VERSION,
259
+ protocol: "geoparquet",
260
+ resource,
261
+ authorizationContextId: input.authorizationContextId,
262
+ ...(input.resourceVersion !== undefined ? { resourceVersion: input.resourceVersion } : {}),
263
+ });
264
+ }
265
+ function resourceKey(handle) {
266
+ return canonicalStringify(toJsonValue([
267
+ handle.resource.resolver,
268
+ handle.resource.id,
269
+ handle.authorizationContextId,
270
+ handle.resourceVersion ?? null,
271
+ ]));
272
+ }
273
+ function reclaimExpiredEntries(entries, maxEntries, currentTime) {
274
+ for (const [key, entry] of entries) {
275
+ if (entry.state === "expired" ||
276
+ (currentTime !== undefined && entry.expiresAt !== undefined && currentTime >= entry.expiresAt)) {
277
+ entries.delete(key);
278
+ }
279
+ if (entries.size < maxEntries)
280
+ return;
281
+ }
282
+ }
283
+ function hasExpiringEntries(entries) {
284
+ for (const entry of entries.values()) {
285
+ if (entry.state === "active" && entry.expiresAt !== undefined)
286
+ return true;
287
+ }
288
+ return false;
289
+ }
290
+ function entriesMatchSnapshot(entries, snapshot) {
291
+ return entries.size === snapshot.length && snapshot.every(([key, entry]) => entries.get(key) === entry);
292
+ }
293
+ function inspectRecord(value, requiredKeys, optionalKeys = []) {
294
+ try {
295
+ if (value === null || typeof value !== "object" || Array.isArray(value))
296
+ return undefined;
297
+ const prototype = Object.getPrototypeOf(value);
298
+ if (prototype !== Object.prototype && prototype !== null)
299
+ return undefined;
300
+ const keys = Reflect.ownKeys(value);
301
+ if (keys.some((key) => typeof key !== "string"))
302
+ return undefined;
303
+ const allowed = new Set([...requiredKeys, ...optionalKeys]);
304
+ if (keys.length < requiredKeys.length || keys.length > allowed.size)
305
+ return undefined;
306
+ if (keys.some((key) => typeof key !== "string" || !allowed.has(key)))
307
+ return undefined;
308
+ const result = new Map();
309
+ for (const key of requiredKeys) {
310
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
311
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
312
+ return undefined;
313
+ result.set(key, descriptor.value);
314
+ }
315
+ for (const key of optionalKeys) {
316
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
317
+ if (!descriptor)
318
+ continue;
319
+ if (!descriptor.enumerable || !("value" in descriptor))
320
+ return undefined;
321
+ result.set(key, descriptor.value);
322
+ }
323
+ return result;
324
+ }
325
+ catch {
326
+ return undefined;
327
+ }
328
+ }
329
+ function copyBoundedSources(value) {
330
+ try {
331
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype)
332
+ return undefined;
333
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
334
+ const length = lengthDescriptor && "value" in lengthDescriptor ? lengthDescriptor.value : undefined;
335
+ if (typeof length !== "number" || !Number.isInteger(length) || length < 1 || length > MAX_SOURCE_COUNT) {
336
+ return undefined;
337
+ }
338
+ const keys = Reflect.ownKeys(value);
339
+ if (keys.length !== length + 1 || keys.some((key) => typeof key !== "string"))
340
+ return undefined;
341
+ if (keys.some((key) => typeof key === "string" && key !== "length" && !isArrayIndex(key, length))) {
342
+ return undefined;
343
+ }
344
+ let totalBytes = 0;
345
+ const copy = [];
346
+ for (let index = 0; index < length; index++) {
347
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
348
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
349
+ return undefined;
350
+ const source = descriptor.value;
351
+ if (typeof source !== "string" ||
352
+ source.length === 0 ||
353
+ source.length > MAX_SOURCE_BYTES ||
354
+ containsControlCharacter(source)) {
355
+ return undefined;
356
+ }
357
+ const bytes = new TextEncoder().encode(source).byteLength;
358
+ if (bytes > MAX_SOURCE_BYTES)
359
+ return undefined;
360
+ totalBytes += bytes;
361
+ if (totalBytes > MAX_TOTAL_SOURCE_BYTES)
362
+ return undefined;
363
+ copy.push(source);
364
+ }
365
+ return Object.freeze(copy);
366
+ }
367
+ catch {
368
+ return undefined;
369
+ }
370
+ }
371
+ function isArrayIndex(key, length) {
372
+ if (!/^(?:0|[1-9][0-9]*)$/.test(key))
373
+ return false;
374
+ const index = Number(key);
375
+ return Number.isSafeInteger(index) && index >= 0 && index < length;
376
+ }
377
+ function containsControlCharacter(value) {
378
+ for (let index = 0; index < value.length; index++) {
379
+ const code = value.charCodeAt(index);
380
+ if (code <= 0x1f || code === 0x7f)
381
+ return true;
382
+ }
383
+ return false;
384
+ }
385
+ function isResolverIdentifier(value) {
386
+ return typeof value === "string" && value.length <= MAX_RESOLVER_LENGTH && EXTENSION_IDENTIFIER_PATTERN.test(value);
387
+ }
388
+ function isResourceIdentity(value) {
389
+ return (typeof value === "string" &&
390
+ value.length <= MAX_IDENTITY_LENGTH &&
391
+ RESOURCE_ID_PATTERN.test(value) &&
392
+ !JWT_SHAPE.test(value));
393
+ }
394
+ function isAuthorizationContextId(value) {
395
+ return (typeof value === "string" &&
396
+ value.length <= MAX_IDENTITY_LENGTH &&
397
+ AUTHORIZATION_CONTEXT_PATTERN.test(value) &&
398
+ !value.endsWith("/") &&
399
+ !value.includes("//") &&
400
+ !value.split("/").some((segment) => segment === "." || segment === "..") &&
401
+ !JWT_SHAPE.test(value));
402
+ }
403
+ function isMaxEntries(value) {
404
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= HARD_MAX_ENTRIES;
405
+ }
406
+ function isClock(value) {
407
+ return typeof value === "function";
408
+ }
409
+ function isEpochMilliseconds(value) {
410
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
411
+ }
412
+ function isAbortSignal(value) {
413
+ try {
414
+ return (value !== null &&
415
+ typeof value === "object" &&
416
+ typeof value.aborted === "boolean" &&
417
+ typeof value.addEventListener === "function" &&
418
+ typeof value.removeEventListener === "function");
419
+ }
420
+ catch {
421
+ return false;
422
+ }
423
+ }
424
+ function isSignalAborted(signal) {
425
+ try {
426
+ return signal?.aborted === true;
427
+ }
428
+ catch {
429
+ throw resolutionFailed();
430
+ }
431
+ }
432
+ function readClock(now) {
433
+ try {
434
+ const value = now();
435
+ if (!isEpochMilliseconds(value))
436
+ throw resolutionFailed();
437
+ return value;
438
+ }
439
+ catch {
440
+ throw resolutionFailed();
441
+ }
442
+ }
443
+ function awaitAbortable(value, signal) {
444
+ const pending = Promise.resolve(value);
445
+ if (!signal)
446
+ return pending;
447
+ if (isSignalAborted(signal)) {
448
+ void pending.catch(() => undefined);
449
+ throw new HonuaAbortError();
450
+ }
451
+ return new Promise((resolve, reject) => {
452
+ let settled = false;
453
+ const finish = (callback) => {
454
+ if (settled)
455
+ return;
456
+ settled = true;
457
+ try {
458
+ signal.removeEventListener("abort", abort);
459
+ }
460
+ catch {
461
+ // The fixed failure below retains no foreign signal data.
462
+ }
463
+ callback();
464
+ };
465
+ const abort = () => finish(() => reject(new HonuaAbortError()));
466
+ try {
467
+ signal.addEventListener("abort", abort, { once: true });
468
+ }
469
+ catch {
470
+ void pending.catch(() => undefined);
471
+ finish(() => reject(resolutionFailed()));
472
+ return;
473
+ }
474
+ try {
475
+ if (isSignalAborted(signal)) {
476
+ abort();
477
+ void pending.catch(() => undefined);
478
+ return;
479
+ }
480
+ }
481
+ catch (error) {
482
+ void pending.catch(() => undefined);
483
+ finish(() => reject(error));
484
+ return;
485
+ }
486
+ pending.then((result) => finish(() => resolve(result)), (error) => finish(() => reject(error)));
487
+ });
488
+ }
489
+ function normalizeResolverFailure(error, signal) {
490
+ if (isSignalAborted(signal))
491
+ return new HonuaAbortError();
492
+ try {
493
+ if (error instanceof HonuaQueryPlanExecutionError) {
494
+ const code = ownDataProperty(error, "code");
495
+ if (code === "resource-unavailable")
496
+ return resourceUnavailable();
497
+ if (code === "resource-expired")
498
+ return resourceExpired();
499
+ }
500
+ }
501
+ catch {
502
+ // Proxy and cross-realm failures are projected to the same fixed error.
503
+ }
504
+ return resolutionFailed();
505
+ }
506
+ function ownDataProperty(value, key) {
507
+ try {
508
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
509
+ return descriptor && "value" in descriptor ? descriptor.value : undefined;
510
+ }
511
+ catch {
512
+ return undefined;
513
+ }
514
+ }
515
+ function invalidHandle() {
516
+ return new HonuaQueryPlanExecutionError("invalid-resource-handle", INVALID_HANDLE_MESSAGE);
517
+ }
518
+ function resourceUnavailable() {
519
+ return new HonuaQueryPlanExecutionError("resource-unavailable", RESOURCE_UNAVAILABLE_MESSAGE);
520
+ }
521
+ function resourceExpired() {
522
+ return new HonuaQueryPlanExecutionError("resource-expired", RESOURCE_EXPIRED_MESSAGE);
523
+ }
524
+ function resolutionFailed() {
525
+ return new HonuaQueryPlanExecutionError("resource-resolution-failed", RESOLUTION_FAILED_MESSAGE);
526
+ }
@@ -0,0 +1,15 @@
1
+ import type { CanonicalSemanticQueryOptions } from "./semantic-types.js";
2
+ export declare const SEMANTIC_QUERY_CANONICAL_KIND: "honua.semantic-query-canonical";
3
+ export declare const SEMANTIC_QUERY_CANONICAL_VERSION: 1;
4
+ export declare const SEMANTIC_QUERY_HASH_DOMAIN: "honua.semantic-query.hash.v1";
5
+ /**
6
+ * Serialize a validated semantic query plus its schema/CRS/policy identity.
7
+ * Object keys are UTF-8 deterministically ordered; array order stays
8
+ * significant. Volatile execution state is not accepted by the semantic AST.
9
+ */
10
+ export declare function serializeCanonicalSemanticQuery(value: unknown, options?: CanonicalSemanticQueryOptions): string;
11
+ /** Return a fresh UTF-8 byte copy of the canonical semantic-query envelope. */
12
+ export declare function canonicalSemanticQueryBytes(value: unknown, options?: CanonicalSemanticQueryOptions): Uint8Array;
13
+ /** Domain-separated SHA-256 identity for a canonical semantic query. */
14
+ export declare function hashSemanticQuery(value: unknown, options?: CanonicalSemanticQueryOptions): `sha256:${string}`;
15
+ //# sourceMappingURL=semantic-canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-canonical.d.ts","sourceRoot":"","sources":["../../../src/query-planner/semantic-canonical.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAIzE,eAAO,MAAM,6BAA6B,EAAG,gCAAyC,CAAC;AACvF,eAAO,MAAM,gCAAgC,EAAG,CAAU,CAAC;AAC3D,eAAO,MAAM,0BAA0B,EAAG,8BAAuC,CAAC;AAKlF;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,MAAM,CAoBnH;AAED,+EAA+E;AAC/E,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,UAAU,CAEnH;AAED,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,6BAAkC,GAAG,UAAU,MAAM,EAAE,CAEjH"}
@@ -0,0 +1,62 @@
1
+ import { parseSourceSchemaV2 } from "../contract/schema.js";
2
+ import { canonicalStringify, sha256, toJsonValue } from "./canonical.js";
3
+ import { parseSemanticQuery } from "./semantic.js";
4
+ import { HonuaQueryPlanningError } from "./types.js";
5
+ export const SEMANTIC_QUERY_CANONICAL_KIND = "honua.semantic-query-canonical";
6
+ export const SEMANTIC_QUERY_CANONICAL_VERSION = 1;
7
+ export const SEMANTIC_QUERY_HASH_DOMAIN = "honua.semantic-query.hash.v1";
8
+ const MAX_IDENTITY_VERSION_BYTES = 4096;
9
+ const TEXT_ENCODER = new TextEncoder();
10
+ /**
11
+ * Serialize a validated semantic query plus its schema/CRS/policy identity.
12
+ * Object keys are UTF-8 deterministically ordered; array order stays
13
+ * significant. Volatile execution state is not accepted by the semantic AST.
14
+ */
15
+ export function serializeCanonicalSemanticQuery(value, options = {}) {
16
+ const schema = verifiedSchema(options.schema);
17
+ const query = parseSemanticQuery(value, {
18
+ ...(schema ? { schema } : {}),
19
+ ...(options.protocol !== undefined ? { protocol: options.protocol } : {}),
20
+ });
21
+ const identity = {
22
+ schemaFingerprint: schema?.fingerprint ?? null,
23
+ crsVersion: identityVersion(options.crsVersion, "crsVersion"),
24
+ policyVersion: identityVersion(options.policyVersion, "policyVersion"),
25
+ protocol: options.protocol ?? null,
26
+ };
27
+ return canonicalStringify(toJsonValue({
28
+ kind: SEMANTIC_QUERY_CANONICAL_KIND,
29
+ version: SEMANTIC_QUERY_CANONICAL_VERSION,
30
+ identity,
31
+ query,
32
+ }));
33
+ }
34
+ /** Return a fresh UTF-8 byte copy of the canonical semantic-query envelope. */
35
+ export function canonicalSemanticQueryBytes(value, options = {}) {
36
+ return TEXT_ENCODER.encode(serializeCanonicalSemanticQuery(value, options));
37
+ }
38
+ /** Domain-separated SHA-256 identity for a canonical semantic query. */
39
+ export function hashSemanticQuery(value, options = {}) {
40
+ return sha256(`${SEMANTIC_QUERY_HASH_DOMAIN}\n${serializeCanonicalSemanticQuery(value, options)}`);
41
+ }
42
+ function verifiedSchema(value) {
43
+ if (value === undefined)
44
+ return undefined;
45
+ try {
46
+ return parseSourceSchemaV2(value);
47
+ }
48
+ catch {
49
+ throw new HonuaQueryPlanningError("invalid-query", "Canonical semantic-query identity requires a valid schema");
50
+ }
51
+ }
52
+ function identityVersion(value, name) {
53
+ if (value === undefined)
54
+ return null;
55
+ if (typeof value !== "string" || value.length === 0) {
56
+ throw new HonuaQueryPlanningError("invalid-query", `${name} must be a non-empty string when supplied`);
57
+ }
58
+ if (TEXT_ENCODER.encode(value).byteLength > MAX_IDENTITY_VERSION_BYTES) {
59
+ throw new HonuaQueryPlanningError("invalid-query", `${name} exceeds the ${MAX_IDENTITY_VERSION_BYTES}-byte bound`);
60
+ }
61
+ return value;
62
+ }
@@ -0,0 +1,10 @@
1
+ import type { LegacyWhereDialectFor, LegacyWhereProtocol, NativeFilter } from "./semantic-types.js";
2
+ /**
3
+ * Convert deprecated v1 `Query.where` text into an explicitly tagged native
4
+ * filter. This is a migration bridge, not a protocol-neutral parser.
5
+ *
6
+ * @deprecated Build a typed semantic filter instead. Keep this only while a
7
+ * v1 caller still owns protocol-native `where` text.
8
+ */
9
+ export declare function legacyWhereToNativeFilter<TProtocol extends LegacyWhereProtocol>(protocol: TProtocol, where: string): NativeFilter<LegacyWhereDialectFor<TProtocol>>;
10
+ //# sourceMappingURL=semantic-compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-compat.d.ts","sourceRoot":"","sources":["../../../src/query-planner/semantic-compat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAepG;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,SAAS,mBAAmB,EAC7E,QAAQ,EAAE,SAAS,EACnB,KAAK,EAAE,MAAM,GACZ,YAAY,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAsBhD"}
@@ -0,0 +1,35 @@
1
+ import { parseSemanticQuery } from "./semantic.js";
2
+ import { HonuaQueryPlanningError } from "./types.js";
3
+ const LEGACY_WHERE_DIALECT = {
4
+ "geoservices-feature-service": "geoservices-sql92",
5
+ "geoservices-map-service": "geoservices-sql92",
6
+ "geoservices-image-service": "geoservices-sql92",
7
+ "ogc-features": "cql2-text",
8
+ "ogc-records": "cql2-text",
9
+ stac: "cql2-text",
10
+ odata: "odata-4.0",
11
+ geoparquet: "duckdb-sql",
12
+ };
13
+ /**
14
+ * Convert deprecated v1 `Query.where` text into an explicitly tagged native
15
+ * filter. This is a migration bridge, not a protocol-neutral parser.
16
+ *
17
+ * @deprecated Build a typed semantic filter instead. Keep this only while a
18
+ * v1 caller still owns protocol-native `where` text.
19
+ */
20
+ export function legacyWhereToNativeFilter(protocol, where) {
21
+ const dialect = typeof protocol === "string" && Object.prototype.hasOwnProperty.call(LEGACY_WHERE_DIALECT, protocol)
22
+ ? LEGACY_WHERE_DIALECT[protocol]
23
+ : undefined;
24
+ if (!dialect) {
25
+ throw new HonuaQueryPlanningError("unsupported-query", "The supplied protocol has no lossless raw-where compatibility dialect");
26
+ }
27
+ if (typeof where !== "string" || where.trim().length === 0) {
28
+ throw new HonuaQueryPlanningError("invalid-query", "Legacy raw where must be a non-empty string");
29
+ }
30
+ const query = parseSemanticQuery({
31
+ kind: "features",
32
+ filter: { kind: "native", dialect, payload: { format: "text", text: where } },
33
+ }, { protocol });
34
+ return query.filter;
35
+ }