@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,58 @@
1
+ import type { SourceSchemaV2 } from "../contract/schema.js";
2
+ import { type SemanticCompilationResult } from "./semantic-compiler.js";
3
+ import type { SemanticQuery, SourceSpatiality } from "./semantic-types.js";
4
+ /** Explicit WFS/FES metadata discovered for the concrete feature type. */
5
+ export interface Wfs20FilterCapabilitiesEvidence {
6
+ /** Advertised WFS/FES version. Only the WFS 2.0 family is accepted. */
7
+ readonly version: string;
8
+ /** `ImplementsAdHocQuery` from `fes:Conformance`. */
9
+ readonly implementsAdHocQuery: boolean;
10
+ /** `ImplementsSorting` from `fes:Conformance`. */
11
+ readonly implementsSorting: boolean;
12
+ /** Presence of `fes:LogicalOperators` in scalar capabilities. */
13
+ readonly logicalOperators: boolean;
14
+ readonly comparisonOperators: readonly string[];
15
+ /** Geometry operands safe for every compiled spatial operator (the conservative intersection of operator-local evidence). */
16
+ readonly geometryOperands: readonly string[];
17
+ readonly spatialOperators: readonly string[];
18
+ /** Temporal operands safe for every compiled temporal operator (the conservative intersection of operator-local evidence). */
19
+ readonly temporalOperands: readonly string[];
20
+ readonly temporalOperators: readonly string[];
21
+ /** CRS identifiers explicitly discovered as usable by spatial filter operands. */
22
+ readonly supportedFilterCrs: readonly string[];
23
+ /** CRS identifiers explicitly discovered as valid WFS result `srsName` values. */
24
+ readonly supportedOutputCrs: readonly string[];
25
+ }
26
+ /** Stable WFS feature-type identity and the namespaces used by its QNames. */
27
+ export interface SemanticWfsSourceIdentity {
28
+ readonly typeName: string;
29
+ readonly namespaces?: Readonly<Record<string, string>>;
30
+ }
31
+ export interface SemanticWfsCompileOptions<TRecord = Record<string, unknown>, TSpatiality extends SourceSpatiality = SourceSpatiality> {
32
+ readonly query: SemanticQuery<TRecord, "wfs", TSpatiality>;
33
+ readonly schema: SourceSchemaV2;
34
+ readonly source: SemanticWfsSourceIdentity;
35
+ readonly capabilities: Wfs20FilterCapabilitiesEvidence;
36
+ }
37
+ /** Canonical, wire-ready WFS 2.0 GetFeature request semantics. */
38
+ export interface SemanticWfsCompiledQueryV1 {
39
+ readonly compiler: "wfs-2.0-semantic-query-v1";
40
+ readonly version: "2.0.0" | "2.0.2";
41
+ readonly dialect?: "fes-2.0";
42
+ readonly schemaFingerprint: SourceSchemaV2["fingerprint"];
43
+ readonly queryFingerprint: `sha256:${string}`;
44
+ readonly capabilityFingerprint: `sha256:${string}`;
45
+ readonly requestFingerprint: `sha256:${string}`;
46
+ readonly typeName: string;
47
+ readonly namespaces: Readonly<Record<string, string>>;
48
+ readonly filter?: string;
49
+ readonly propertyName?: readonly string[];
50
+ readonly sortBy?: string;
51
+ readonly startIndex?: number;
52
+ readonly count?: number;
53
+ readonly srsName?: string;
54
+ readonly usesNativeFilter: boolean;
55
+ }
56
+ /** Compile a typed semantic query using only explicit WFS 2.0/FES capability evidence. */
57
+ export declare function compileSemanticWfsQuery<TRecord, TSpatiality extends SourceSpatiality>(options: SemanticWfsCompileOptions<TRecord, TSpatiality>): SemanticCompilationResult<SemanticWfsCompiledQueryV1>;
58
+ //# sourceMappingURL=wfs-semantic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wfs-semantic.d.ts","sourceRoot":"","sources":["../../../src/query-planner/wfs-semantic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,547 @@
1
+ import { toJsonValue } from "./canonical.js";
2
+ import { bboxInDefinitionOrder, compiledRequestFingerprint, compilerEvidenceFingerprint, compilerField, fesValueReference, geometryInDefinitionOrder, normalizeIdentifierUris, normalizeNamespaceBindings, ogcAxisPlan, ogcCrsUri, ogcNumber, validateFesTypeName, xmlAttribute, xmlText, } from "./ogc-compiler.js";
3
+ import { hashSemanticQuery } from "./semantic-canonical.js";
4
+ import { prepareSemanticCompilerQuery, runSemanticCompiler, semanticUnsupported, } from "./semantic-compiler.js";
5
+ import { HonuaQueryPlanningError } from "./types.js";
6
+ const WFS_CAPABILITY_FINGERPRINT_DOMAIN = "honua.wfs-2.0.filter-capabilities.v1";
7
+ const WFS_REQUEST_FINGERPRINT_DOMAIN = "honua.wfs-2.0.semantic-request.v1";
8
+ const MAX_CAPABILITY_NAMES = 256;
9
+ const MAX_CAPABILITY_NAME_BYTES = 1_024;
10
+ const TEXT_ENCODER = new TextEncoder();
11
+ const CAPABILITY_NAME = /^[A-Za-z_][A-Za-z0-9_.:-]*$/;
12
+ const EXACT_INTEGER = /^-?(?:0|[1-9]\d*)$/;
13
+ const EXACT_DECIMAL = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
14
+ const XML_DATE = /^\d{4}-\d{2}-\d{2}(?:Z|[+-]\d{2}:\d{2})?$/;
15
+ const XML_DATE_TIME = /^-?\d{4,}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/;
16
+ const FES_COMPARISON = {
17
+ eq: "PropertyIsEqualTo",
18
+ ne: "PropertyIsNotEqualTo",
19
+ lt: "PropertyIsLessThan",
20
+ lte: "PropertyIsLessThanOrEqualTo",
21
+ gt: "PropertyIsGreaterThan",
22
+ gte: "PropertyIsGreaterThanOrEqualTo",
23
+ };
24
+ const FES_SPATIAL = {
25
+ equals: "Equals",
26
+ intersects: "Intersects",
27
+ within: "Within",
28
+ contains: "Contains",
29
+ disjoint: "Disjoint",
30
+ touches: "Touches",
31
+ overlaps: "Overlaps",
32
+ crosses: "Crosses",
33
+ "within-distance": "DWithin",
34
+ "beyond-distance": "Beyond",
35
+ };
36
+ const FES_TEMPORAL = {
37
+ before: "Before",
38
+ after: "After",
39
+ during: "During",
40
+ "time-intersects": "AnyInteracts",
41
+ };
42
+ const DISTANCE_UOM = {
43
+ metre: "m",
44
+ kilometre: "km",
45
+ foot: "[ft_i]",
46
+ "us-survey-foot": "[ft_us]",
47
+ mile: "[mi_i]",
48
+ "nautical-mile": "[nmi_i]",
49
+ degree: "deg",
50
+ radian: "rad",
51
+ };
52
+ /** Compile a typed semantic query using only explicit WFS 2.0/FES capability evidence. */
53
+ export function compileSemanticWfsQuery(options) {
54
+ return runSemanticCompiler(() => {
55
+ const { query, schema } = prepareSemanticCompilerQuery(options.query, options.schema, "wfs");
56
+ const namespaces = normalizeNamespaceBindings(options.source?.namespaces, "options.source.namespaces");
57
+ const typeName = validateFesTypeName(options.source?.typeName, namespaces, "options.source.typeName");
58
+ const capabilities = normalizeWfsCapabilities(options.capabilities);
59
+ if (!capabilities.implementsAdHocQuery) {
60
+ semanticUnsupported("unsupported-source", "options.capabilities.implementsAdHocQuery", "WFS semantic compilation requires explicitly advertised ad hoc query support");
61
+ }
62
+ const state = {
63
+ schema,
64
+ namespaces,
65
+ capabilities,
66
+ geometryId: 0,
67
+ timeId: 0,
68
+ usesNativeFilter: false,
69
+ };
70
+ const filter = query.filter ? compileFesFilter(query.filter, state, "$.filter") : undefined;
71
+ const propertyName = wfsProjection(query, state);
72
+ const sortBy = wfsSort(query, state);
73
+ const srsName = query.outputCrs ? wfsOutputCrs(query.outputCrs, capabilities, "$.outputCrs") : undefined;
74
+ const queryFingerprint = hashSemanticQuery(query, { schema, protocol: "wfs" });
75
+ const preimage = {
76
+ compiler: "wfs-2.0-semantic-query-v1",
77
+ version: capabilities.version,
78
+ ...(filter ? { dialect: "fes-2.0" } : {}),
79
+ schemaFingerprint: schema.fingerprint,
80
+ queryFingerprint,
81
+ capabilityFingerprint: capabilities.fingerprint,
82
+ typeName,
83
+ namespaces: namespaces.bindings,
84
+ ...(filter ? { filter } : {}),
85
+ ...(propertyName ? { propertyName } : {}),
86
+ ...(sortBy ? { sortBy } : {}),
87
+ ...(query.page?.kind === "offset" ? { startIndex: query.page.offset } : {}),
88
+ ...(query.page?.limit !== undefined ? { count: query.page.limit } : {}),
89
+ ...(srsName ? { srsName } : {}),
90
+ usesNativeFilter: state.usesNativeFilter,
91
+ };
92
+ return {
93
+ artifact: {
94
+ ...preimage,
95
+ requestFingerprint: compiledRequestFingerprint(WFS_REQUEST_FINGERPRINT_DOMAIN, toJsonValue(preimage)),
96
+ },
97
+ };
98
+ });
99
+ }
100
+ function normalizeWfsCapabilities(value) {
101
+ if (!value || typeof value !== "object") {
102
+ throw new HonuaQueryPlanningError("invalid-query", "options.capabilities is invalid");
103
+ }
104
+ if (value.version !== "2.0.0" && value.version !== "2.0.2") {
105
+ semanticUnsupported("unsupported-source", "options.capabilities.version", "FES compilation requires WFS 2.0");
106
+ }
107
+ const version = value.version;
108
+ for (const key of ["implementsAdHocQuery", "implementsSorting", "logicalOperators"]) {
109
+ if (typeof value[key] !== "boolean") {
110
+ throw new HonuaQueryPlanningError("invalid-query", `options.capabilities.${key} must be boolean`);
111
+ }
112
+ }
113
+ const normalized = {
114
+ version,
115
+ implementsAdHocQuery: value.implementsAdHocQuery,
116
+ implementsSorting: value.implementsSorting,
117
+ logicalOperators: value.logicalOperators,
118
+ comparisonOperators: normalizeCapabilityNames(value.comparisonOperators, "options.capabilities.comparisonOperators"),
119
+ geometryOperands: normalizeCapabilityNames(value.geometryOperands, "options.capabilities.geometryOperands"),
120
+ spatialOperators: normalizeCapabilityNames(value.spatialOperators, "options.capabilities.spatialOperators"),
121
+ temporalOperands: normalizeCapabilityNames(value.temporalOperands, "options.capabilities.temporalOperands"),
122
+ temporalOperators: normalizeCapabilityNames(value.temporalOperators, "options.capabilities.temporalOperators"),
123
+ supportedFilterCrs: normalizeIdentifierUris(value.supportedFilterCrs, "options.capabilities.supportedFilterCrs"),
124
+ supportedOutputCrs: normalizeIdentifierUris(value.supportedOutputCrs, "options.capabilities.supportedOutputCrs"),
125
+ };
126
+ return {
127
+ ...normalized,
128
+ fingerprint: compilerEvidenceFingerprint(WFS_CAPABILITY_FINGERPRINT_DOMAIN, toJsonValue(normalized)),
129
+ };
130
+ }
131
+ function normalizeCapabilityNames(value, path) {
132
+ if (!Array.isArray(value))
133
+ throw new HonuaQueryPlanningError("invalid-query", `${path} must be an array`);
134
+ if (value.length > MAX_CAPABILITY_NAMES) {
135
+ throw new HonuaQueryPlanningError("invalid-query", `${path} exceeds the ${MAX_CAPABILITY_NAMES}-entry capability bound`);
136
+ }
137
+ const normalized = value.map((entry, index) => {
138
+ if (typeof entry !== "string" ||
139
+ !CAPABILITY_NAME.test(entry) ||
140
+ TEXT_ENCODER.encode(entry).byteLength > MAX_CAPABILITY_NAME_BYTES) {
141
+ throw new HonuaQueryPlanningError("invalid-query", `${path}[${index}] is not a safe capability name`);
142
+ }
143
+ return entry;
144
+ });
145
+ normalized.sort(compareUtf8);
146
+ for (let index = 1; index < normalized.length; index += 1) {
147
+ if (normalized[index] === normalized[index - 1]) {
148
+ throw new HonuaQueryPlanningError("invalid-query", `${path} contains duplicate capability name`);
149
+ }
150
+ }
151
+ return Object.freeze(normalized);
152
+ }
153
+ function compileFesFilter(filter, state, path) {
154
+ if (filter.kind === "native") {
155
+ if (filter.dialect !== "fes-2.0" || filter.payload.format !== "xml") {
156
+ semanticUnsupported("unsupported-native-filter", `${path}.dialect`, "WFS accepts only a matching fes-2.0 XML native filter");
157
+ }
158
+ if (filter.payload.text.trim().length === 0) {
159
+ semanticUnsupported("unsupported-native-filter", `${path}.payload.text`, "native FES XML cannot be empty");
160
+ }
161
+ xmlText(filter.payload.text, `${path}.payload.text`);
162
+ state.usesNativeFilter = true;
163
+ return filter.payload.text;
164
+ }
165
+ const declarations = [
166
+ 'xmlns:fes="http://www.opengis.net/fes/2.0"',
167
+ 'xmlns:gml="http://www.opengis.net/gml/3.2"',
168
+ 'xmlns:xs="http://www.w3.org/2001/XMLSchema"',
169
+ ...state.namespaces.ordered.map(([prefix, uri]) => `xmlns:${prefix}="${xmlAttribute(uri, `options.source.namespaces.${prefix}`)}"`),
170
+ ];
171
+ return `<fes:Filter ${declarations.join(" ")}>${compileFesNode(filter, state, path)}</fes:Filter>`;
172
+ }
173
+ function compileFesNode(filter, state, path) {
174
+ switch (filter.kind) {
175
+ case "comparison": {
176
+ const operator = FES_COMPARISON[filter.operator];
177
+ requireComparison(state, operator, path);
178
+ const field = compilerField(state.schema, filter.left.name, `${path}.left.name`);
179
+ return `<fes:${operator} matchCase="true">${valueReference(state, filter.left.name, `${path}.left.name`)}${fesLiteral(filter.right.value, field, `${path}.right.value`)}</fes:${operator}>`;
180
+ }
181
+ case "boolean": {
182
+ if (filter.args.length === 1)
183
+ return compileFesNode(filter.args[0], state, `${path}.args[0]`);
184
+ requireLogical(state, path);
185
+ const operator = filter.operator === "and" ? "And" : "Or";
186
+ return `<fes:${operator}>${filter.args
187
+ .map((entry, index) => compileFesNode(entry, state, `${path}.args[${index}]`))
188
+ .join("")}</fes:${operator}>`;
189
+ }
190
+ case "not":
191
+ requireLogical(state, path);
192
+ return `<fes:Not>${compileFesNode(filter.arg, state, `${path}.arg`)}</fes:Not>`;
193
+ case "null": {
194
+ requireComparison(state, "PropertyIsNull", path);
195
+ const node = `<fes:PropertyIsNull>${valueReference(state, filter.operand.name, `${path}.operand.name`)}</fes:PropertyIsNull>`;
196
+ if (filter.operator === "is-null")
197
+ return node;
198
+ requireLogical(state, path);
199
+ return `<fes:Not>${node}</fes:Not>`;
200
+ }
201
+ case "list": {
202
+ requireComparison(state, "PropertyIsEqualTo", path);
203
+ const field = compilerField(state.schema, filter.operand.name, `${path}.operand.name`);
204
+ const nodes = filter.values.map((entry, index) => `<fes:PropertyIsEqualTo matchCase="true">${valueReference(state, filter.operand.name, `${path}.operand.name`)}${fesLiteral(entry.value, field, `${path}.values[${index}].value`)}</fes:PropertyIsEqualTo>`);
205
+ if (nodes.length === 1)
206
+ return nodes[0];
207
+ requireLogical(state, path);
208
+ return `<fes:Or>${nodes.join("")}</fes:Or>`;
209
+ }
210
+ case "range": {
211
+ requireComparison(state, "PropertyIsBetween", path);
212
+ const field = compilerField(state.schema, filter.operand.name, `${path}.operand.name`);
213
+ return `<fes:PropertyIsBetween>${valueReference(state, filter.operand.name, `${path}.operand.name`)}<fes:LowerBoundary>${fesLiteral(filter.lower.value, field, `${path}.lower.value`)}</fes:LowerBoundary><fes:UpperBoundary>${fesLiteral(filter.upper.value, field, `${path}.upper.value`)}</fes:UpperBoundary></fes:PropertyIsBetween>`;
214
+ }
215
+ case "pattern": {
216
+ requireComparison(state, "PropertyIsLike", path);
217
+ const matchCase = filter.caseSensitive === false ? "false" : "true";
218
+ return `<fes:PropertyIsLike wildCard="%" singleChar="_" escapeChar="\\" matchCase="${matchCase}">${valueReference(state, filter.operand.name, `${path}.operand.name`)}<fes:Literal type="xs:string">${xmlText(filter.pattern, `${path}.pattern`)}</fes:Literal></fes:PropertyIsLike>`;
219
+ }
220
+ case "spatial":
221
+ return compileFesSpatial(filter, state, path);
222
+ case "temporal": {
223
+ if (filter.operator === "time-intersects") {
224
+ semanticUnsupported("unsupported-node", `${path}.operator`, "FES AnyInteracts requires a period-valued property, but semantic temporal fields are instant-valued");
225
+ }
226
+ const operator = FES_TEMPORAL[filter.operator];
227
+ requireTemporal(state, operator, path);
228
+ const operand = filter.value.valueType === "interval" ? "gml:TimePeriod" : "gml:TimeInstant";
229
+ requireTemporalOperand(state, operand, `${path}.value`);
230
+ return `<fes:${operator}>${valueReference(state, filter.operand.name, `${path}.operand.name`)}${fesTemporalLiteral(filter.value, state, `${path}.value`)}</fes:${operator}>`;
231
+ }
232
+ }
233
+ }
234
+ function compileFesSpatial(filter, state, path) {
235
+ const property = spatialProperty(filter, state.schema, path);
236
+ if (filter.operator === "bbox-intersects") {
237
+ requireSpatial(state, "BBOX", path);
238
+ requireGeometryOperand(state, "gml:Envelope", `${path}.bbox`);
239
+ const bounds = bboxInDefinitionOrder(filter.bbox, `${path}.bbox`);
240
+ const dimensions = filter.bbox.box.layout === "xy" ? 2 : 3;
241
+ const srsName = requiredFilterCrs(filter.bbox.crs, dimensions, state.capabilities, `${path}.bbox`);
242
+ const lower = bounds
243
+ .slice(0, dimensions)
244
+ .map((value, index) => ogcNumber(value, `${path}.bbox.box.bounds[${index}]`));
245
+ const upper = bounds
246
+ .slice(dimensions)
247
+ .map((value, index) => ogcNumber(value, `${path}.bbox.box.bounds[${dimensions + index}]`));
248
+ return `<fes:BBOX>${valueReference(state, property, `${path}.property.name`)}<gml:Envelope srsName="${xmlAttribute(srsName, `${path}.bbox.crs`)}" srsDimension="${dimensions}"><gml:lowerCorner>${lower.join(" ")}</gml:lowerCorner><gml:upperCorner>${upper.join(" ")}</gml:upperCorner></gml:Envelope></fes:BBOX>`;
249
+ }
250
+ const operator = FES_SPATIAL[filter.operator];
251
+ requireSpatial(state, operator, path);
252
+ const distanceOperand = filter.operator === "within-distance" || filter.operator === "beyond-distance" ? filter.distance : undefined;
253
+ if (distanceOperand?.mode === "geodesic") {
254
+ semanticUnsupported("unsupported-distance", `${path}.distance.mode`, "FES 2.0 distance operators do not encode geodesic-versus-planar execution mode");
255
+ }
256
+ const geometry = geometryInDefinitionOrder(filter.geometry, `${path}.geometry`);
257
+ const dimensions = filter.geometry.layout === "xy" ? 2 : 3;
258
+ const srsName = requiredFilterCrs(filter.geometry.crs, dimensions, state.capabilities, `${path}.geometry`);
259
+ requireGeometryOperand(state, gmlOperand(geometry), `${path}.geometry.geometry`);
260
+ const gml = geometryToGml(geometry, dimensions, srsName, state, `${path}.geometry.geometry`);
261
+ const distance = distanceOperand
262
+ ? `<fes:Distance uom="${xmlAttribute(DISTANCE_UOM[distanceOperand.unit], `${path}.distance.unit`)}">${ogcNumber(distanceOperand.value, `${path}.distance.value`)}</fes:Distance>`
263
+ : "";
264
+ return `<fes:${operator}>${valueReference(state, property, `${path}.property.name`)}${gml}${distance}</fes:${operator}>`;
265
+ }
266
+ function fesLiteral(value, field, path) {
267
+ if (value === null || Array.isArray(value) || typeof value === "object") {
268
+ semanticUnsupported("unsupported-field-type", path, "FES scalar literals must be string, number, or boolean");
269
+ }
270
+ let lexical;
271
+ let type;
272
+ switch (field.type.kind) {
273
+ case "boolean":
274
+ if (typeof value !== "boolean")
275
+ return incompatibleFesLiteral(field, path);
276
+ lexical = value ? "true" : "false";
277
+ type = "xs:boolean";
278
+ break;
279
+ case "integer":
280
+ lexical = exactInteger(value, path);
281
+ type = "xs:integer";
282
+ break;
283
+ case "float":
284
+ if (typeof value !== "number")
285
+ return incompatibleFesLiteral(field, path);
286
+ lexical = ogcNumber(value, path);
287
+ type = "xs:double";
288
+ break;
289
+ case "decimal":
290
+ lexical = exactDecimal(value, path);
291
+ type = "xs:decimal";
292
+ break;
293
+ case "date":
294
+ if (typeof value !== "string" || !XML_DATE.test(value))
295
+ return incompatibleFesLiteral(field, path);
296
+ lexical = value;
297
+ type = "xs:date";
298
+ break;
299
+ case "time":
300
+ if (typeof value !== "string")
301
+ return incompatibleFesLiteral(field, path);
302
+ lexical = value;
303
+ type = "xs:time";
304
+ break;
305
+ case "timestamp":
306
+ if (typeof value !== "string" || !XML_DATE_TIME.test(value))
307
+ return incompatibleFesLiteral(field, path);
308
+ lexical = value;
309
+ type = "xs:dateTime";
310
+ break;
311
+ case "duration":
312
+ if (typeof value !== "string")
313
+ return incompatibleFesLiteral(field, path);
314
+ lexical = value;
315
+ type = "xs:duration";
316
+ break;
317
+ case "binary":
318
+ if (field.type.encoding !== "base64" || typeof value !== "string") {
319
+ semanticUnsupported("unsupported-field-type", path, `FES cannot type ${field.type.encoding} binary literals exactly`);
320
+ }
321
+ lexical = value;
322
+ type = "xs:base64Binary";
323
+ break;
324
+ case "string":
325
+ case "uuid":
326
+ if (typeof value !== "string")
327
+ return incompatibleFesLiteral(field, path);
328
+ lexical = value;
329
+ type = "xs:string";
330
+ break;
331
+ default:
332
+ semanticUnsupported("unsupported-field-type", path, `FES cannot encode ${field.type.kind} literals as exact scalar XML values`);
333
+ }
334
+ return `<fes:Literal type="${type}">${xmlText(lexical, path)}</fes:Literal>`;
335
+ }
336
+ function incompatibleFesLiteral(field, path) {
337
+ semanticUnsupported("unsupported-field-type", path, `FES literal is incompatible with ${field.name}:${field.type.kind}`);
338
+ }
339
+ function exactInteger(value, path) {
340
+ if (typeof value === "number" && Number.isSafeInteger(value))
341
+ return String(value);
342
+ if (typeof value === "string" && EXACT_INTEGER.test(value))
343
+ return value;
344
+ semanticUnsupported("unsupported-field-type", path, "FES integer literal is not an exact integer token");
345
+ }
346
+ function exactDecimal(value, path) {
347
+ const lexical = typeof value === "number" ? ogcNumber(value, path) : value;
348
+ if (typeof lexical === "string" && EXACT_DECIMAL.test(lexical))
349
+ return lexical;
350
+ semanticUnsupported("unsupported-field-type", path, "FES decimal literal is not an exact decimal token");
351
+ }
352
+ function fesTemporalLiteral(value, state, path) {
353
+ if (value.valueType !== "interval") {
354
+ const id = nextTimeId(state);
355
+ return `<gml:TimeInstant gml:id="${id}"><gml:timePosition>${xmlText(value.value, `${path}.value`)}</gml:timePosition></gml:TimeInstant>`;
356
+ }
357
+ const periodId = nextTimeId(state);
358
+ const beginId = nextTimeId(state);
359
+ const endId = nextTimeId(state);
360
+ return `<gml:TimePeriod gml:id="${periodId}"><gml:begin><gml:TimeInstant gml:id="${beginId}"><gml:timePosition>${xmlText(value.value[0], `${path}.value[0]`)}</gml:timePosition></gml:TimeInstant></gml:begin><gml:end><gml:TimeInstant gml:id="${endId}"><gml:timePosition>${xmlText(value.value[1], `${path}.value[1]`)}</gml:timePosition></gml:TimeInstant></gml:end></gml:TimePeriod>`;
361
+ }
362
+ function geometryToGml(geometry, dimensions, srsName, state, path) {
363
+ const id = nextGeometryId(state);
364
+ const attributes = `gml:id="${id}" srsName="${xmlAttribute(srsName, `${path}.crs`)}" srsDimension="${dimensions}"`;
365
+ const position = (value, valuePath) => value.map((ordinate, index) => ogcNumber(ordinate, `${valuePath}[${index}]`)).join(" ");
366
+ const posList = (values, valuePath) => values.map((value, index) => position(value, `${valuePath}[${index}]`)).join(" ");
367
+ const ring = (values, valuePath, exterior) => {
368
+ const boundary = exterior ? "exterior" : "interior";
369
+ return `<gml:${boundary}><gml:LinearRing><gml:posList srsDimension="${dimensions}">${posList(values, valuePath)}</gml:posList></gml:LinearRing></gml:${boundary}>`;
370
+ };
371
+ const polygonBody = (rings, valuePath) => rings.map((value, index) => ring(value, `${valuePath}[${index}]`, index === 0)).join("");
372
+ switch (geometry.type) {
373
+ case "Point":
374
+ return `<gml:Point ${attributes}><gml:pos>${position(geometry.coordinates, `${path}.coordinates`)}</gml:pos></gml:Point>`;
375
+ case "LineString":
376
+ return `<gml:LineString ${attributes}><gml:posList srsDimension="${dimensions}">${posList(geometry.coordinates, `${path}.coordinates`)}</gml:posList></gml:LineString>`;
377
+ case "Polygon":
378
+ return `<gml:Polygon ${attributes}>${polygonBody(geometry.coordinates, `${path}.coordinates`)}</gml:Polygon>`;
379
+ case "MultiPoint":
380
+ return `<gml:MultiPoint ${attributes}>${geometry.coordinates
381
+ .map((value, index) => `<gml:pointMember>${geometryToGml({ type: "Point", coordinates: value }, dimensions, srsName, state, `${path}.coordinates[${index}]`)}</gml:pointMember>`)
382
+ .join("")}</gml:MultiPoint>`;
383
+ case "MultiLineString":
384
+ return `<gml:MultiCurve ${attributes}>${geometry.coordinates
385
+ .map((value, index) => `<gml:curveMember>${geometryToGml({ type: "LineString", coordinates: value }, dimensions, srsName, state, `${path}.coordinates[${index}]`)}</gml:curveMember>`)
386
+ .join("")}</gml:MultiCurve>`;
387
+ case "MultiPolygon":
388
+ return `<gml:MultiSurface ${attributes}>${geometry.coordinates
389
+ .map((value, index) => `<gml:surfaceMember>${geometryToGml({ type: "Polygon", coordinates: value }, dimensions, srsName, state, `${path}.coordinates[${index}]`)}</gml:surfaceMember>`)
390
+ .join("")}</gml:MultiSurface>`;
391
+ case "GeometryCollection":
392
+ return `<gml:MultiGeometry ${attributes}>${geometry.geometries
393
+ .map((value, index) => `<gml:geometryMember>${geometryToGml(value, dimensions, srsName, state, `${path}.geometries[${index}]`)}</gml:geometryMember>`)
394
+ .join("")}</gml:MultiGeometry>`;
395
+ }
396
+ }
397
+ function wfsProjection(query, state) {
398
+ if (query.kind === "aggregate") {
399
+ semanticUnsupported("unsupported-node", "$.kind", "WFS 2.0 has no portable remote aggregation");
400
+ }
401
+ const geometryField = requestedGeometryField(query, state.schema);
402
+ if (!query.select) {
403
+ if (query.geometry !== "omit" && onlyRequestedGeometryIsReturned(geometryField, state.schema))
404
+ return undefined;
405
+ if (state.schema.openContent !== "closed") {
406
+ semanticUnsupported("unsupported-projection", "$.geometry", "WFS property projection requires a closed schema to exclude geometry without dropping unknown attributes");
407
+ }
408
+ }
409
+ const selected = query.select
410
+ ? [...query.select]
411
+ : state.schema.fields.filter((field) => field.type.kind !== "geometry").map((field) => field.name);
412
+ if (query.geometry === "omit") {
413
+ selected.forEach((name, index) => {
414
+ if (compilerField(state.schema, name, `$.select[${index}]`).type.kind === "geometry") {
415
+ semanticUnsupported("unsupported-projection", `$.select[${index}]`, "WFS cannot select a geometry property while geometry projection is omitted");
416
+ }
417
+ });
418
+ }
419
+ if (geometryField && !selected.includes(geometryField))
420
+ selected.push(geometryField);
421
+ const propertyName = selected.map((name, index) => fesValueReference(state.schema, name, state.namespaces, `$.select[${index}]`));
422
+ if (propertyName.length === 0) {
423
+ semanticUnsupported("unsupported-projection", "$.select", "WFS cannot represent an empty property projection");
424
+ }
425
+ return Object.freeze(propertyName);
426
+ }
427
+ function requestedGeometryField(query, schema) {
428
+ if (query.kind === "aggregate" || query.geometry === "omit")
429
+ return undefined;
430
+ if (query.geometry && typeof query.geometry === "object")
431
+ return query.geometry.field;
432
+ if (schema.geometry.state === "none")
433
+ return undefined;
434
+ if (schema.geometry.state === "known" && schema.geometry.primaryField.state === "known") {
435
+ return schema.geometry.primaryField.field;
436
+ }
437
+ semanticUnsupported("unsupported-projection", "$.geometry", "WFS geometry inclusion requires a known primary geometry field or an explicit geometry field");
438
+ }
439
+ function onlyRequestedGeometryIsReturned(geometryField, schema) {
440
+ if (!geometryField || schema.geometry.state !== "known")
441
+ return geometryField === undefined;
442
+ return schema.geometry.fields.length === 1 && schema.geometry.fields[0]?.field === geometryField;
443
+ }
444
+ function wfsSort(query, state) {
445
+ if (!query.sort || query.sort.length === 0)
446
+ return undefined;
447
+ if (!state.capabilities.implementsSorting) {
448
+ semanticUnsupported("unsupported-sort", "$.sort", "WFS sorting was not present in explicit discovered conformance");
449
+ }
450
+ return query.sort
451
+ .map((sort, index) => {
452
+ if (sort.nulls !== undefined && sort.nulls !== "native") {
453
+ semanticUnsupported("unsupported-sort", `$.sort[${index}].nulls`, "WFS SortBy cannot request explicit null placement");
454
+ }
455
+ const property = fesValueReference(state.schema, sort.field, state.namespaces, `$.sort[${index}].field`);
456
+ return `${property} ${sort.direction === "desc" ? "DESC" : "ASC"}`;
457
+ })
458
+ .join(",");
459
+ }
460
+ function wfsOutputCrs(definition, capabilities, path) {
461
+ const uri = ogcCrsUri(definition, path);
462
+ if (!capabilities.supportedOutputCrs.includes(uri)) {
463
+ semanticUnsupported("unsupported-crs", path, `output CRS ${uri} was not present in discovered source metadata`);
464
+ }
465
+ return uri;
466
+ }
467
+ function requiredFilterCrs(binding, dimensions, capabilities, path) {
468
+ const uri = ogcAxisPlan(binding, dimensions, `${path}.crs`).srsName;
469
+ if (!capabilities.supportedFilterCrs.includes(uri)) {
470
+ semanticUnsupported("unsupported-crs", `${path}.crs`, `filter CRS ${uri} was not present in discovered source metadata`);
471
+ }
472
+ return uri;
473
+ }
474
+ function spatialProperty(filter, schema, path) {
475
+ if (filter.property)
476
+ return filter.property.name;
477
+ if (schema.geometry.state === "known" && schema.geometry.primaryField.state === "known") {
478
+ return schema.geometry.primaryField.field;
479
+ }
480
+ semanticUnsupported("unsupported-source", `${path}.property`, "implicit spatial property requires a known primary geometry field");
481
+ }
482
+ function valueReference(state, name, path) {
483
+ return `<fes:ValueReference>${xmlText(fesValueReference(state.schema, name, state.namespaces, path), path)}</fes:ValueReference>`;
484
+ }
485
+ function requireComparison(state, operator, path) {
486
+ if (!state.capabilities.comparisonOperators.includes(operator)) {
487
+ semanticUnsupported("unsupported-node", path, `${operator} was not present in discovered FES capabilities`);
488
+ }
489
+ }
490
+ function requireLogical(state, path) {
491
+ if (!state.capabilities.logicalOperators) {
492
+ semanticUnsupported("unsupported-node", path, "logical operators were not present in discovered FES capabilities");
493
+ }
494
+ }
495
+ function requireSpatial(state, operator, path) {
496
+ if (!state.capabilities.spatialOperators.includes(operator)) {
497
+ semanticUnsupported("unsupported-node", path, `${operator} was not present in discovered FES capabilities`);
498
+ }
499
+ }
500
+ function requireGeometryOperand(state, operand, path) {
501
+ if (!state.capabilities.geometryOperands.includes(operand)) {
502
+ semanticUnsupported("unsupported-geometry", path, `${operand} was not present in discovered FES capabilities`);
503
+ }
504
+ }
505
+ function requireTemporal(state, operator, path) {
506
+ if (!state.capabilities.temporalOperators.includes(operator)) {
507
+ semanticUnsupported("unsupported-node", path, `${operator} was not present in discovered FES capabilities`);
508
+ }
509
+ }
510
+ function requireTemporalOperand(state, operand, path) {
511
+ if (!state.capabilities.temporalOperands.includes(operand)) {
512
+ semanticUnsupported("unsupported-node", path, `${operand} was not present in discovered FES capabilities`);
513
+ }
514
+ }
515
+ function gmlOperand(geometry) {
516
+ switch (geometry.type) {
517
+ case "MultiLineString":
518
+ return "gml:MultiCurve";
519
+ case "MultiPolygon":
520
+ return "gml:MultiSurface";
521
+ case "GeometryCollection":
522
+ return "gml:MultiGeometry";
523
+ default:
524
+ return `gml:${geometry.type}`;
525
+ }
526
+ }
527
+ function nextGeometryId(state) {
528
+ const id = `honua-g${state.geometryId}`;
529
+ state.geometryId += 1;
530
+ return id;
531
+ }
532
+ function nextTimeId(state) {
533
+ const id = `honua-t${state.timeId}`;
534
+ state.timeId += 1;
535
+ return id;
536
+ }
537
+ function compareUtf8(left, right) {
538
+ const leftBytes = TEXT_ENCODER.encode(left);
539
+ const rightBytes = TEXT_ENCODER.encode(right);
540
+ const length = Math.min(leftBytes.length, rightBytes.length);
541
+ for (let index = 0; index < length; index += 1) {
542
+ const difference = leftBytes[index] - rightBytes[index];
543
+ if (difference !== 0)
544
+ return difference;
545
+ }
546
+ return leftBytes.length - rightBytes.length;
547
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lightweight deterministic WFS 2.0 compiler.
3
+ *
4
+ * Kept separate from the semantic WFS compiler so importing the public
5
+ * `wfsProtocolModule()` does not pull the larger semantic compiler graph into
6
+ * contract-only bundles.
7
+ */
8
+ import type { ProtocolModuleQueryCompileInput, ProtocolModuleQueryOperation } from "../contract/protocol-module.js";
9
+ import type { CanonicalQuery, QueryIrSourceIdentity, WfsCompiledQueryV1, WfsProtocolCompiledQueryV1 } from "./types.js";
10
+ /** Compile canonical query IR to the legacy deterministic WFS 2.0 GetFeature request. */
11
+ export declare function compileWfsQuery(source: QueryIrSourceIdentity, query: CanonicalQuery): WfsCompiledQueryV1;
12
+ /**
13
+ * The exact deterministic compiler hook installed on the first-party WFS
14
+ * protocol module and used by planner dispatch.
15
+ */
16
+ export declare const wfsProtocolQueryCompiler: Readonly<{
17
+ kind: "wfs";
18
+ compile(input: ProtocolModuleQueryCompileInput<QueryIrSourceIdentity, CanonicalQuery>): WfsProtocolCompiledQueryV1;
19
+ }>;
20
+ type WfsProtocolRequest = Omit<WfsProtocolCompiledQueryV1, "compiler" | "operation">;
21
+ /** Bind a WFS wire request to the exact operation authorized to execute it. */
22
+ export declare function bindWfsProtocolQueryOperation(request: WfsProtocolRequest, operation: ProtocolModuleQueryOperation): WfsProtocolCompiledQueryV1;
23
+ /** Credential-free endpoint identity used to bind an executable artifact to a runtime handle. */
24
+ export declare function wfsRuntimeEndpointIdentity(value: string): string;
25
+ /** Derive the only transport method authorized by the compiled filter budget. */
26
+ export declare function wfsProtocolMethodForFilter(filter: string | undefined): "GET" | "POST";
27
+ /**
28
+ * @internal True when the compiled request must name the geometry property in
29
+ * an FES spatial predicate. The `bbox=` KVP shortcut addresses the feature
30
+ * type's default geometry positionally, so it needs no name; every other
31
+ * spatial predicate emits `<fes:ValueReference>` and does. The live adapter
32
+ * uses this to resolve the property (and fail closed) only when the wire
33
+ * actually depends on it.
34
+ */
35
+ export declare function wfsSpatialFilterNeedsGeometryProperty(query: CanonicalQuery): boolean;
36
+ /** @internal Shared with legacy non-query WFS drains to preserve CRS parity. */
37
+ export declare function wfsSrsNameFromOutSr(outSr: string | number | undefined): string | undefined;
38
+ /** @internal Resolve only the input geometry CRS; response `outSr` is separate. */
39
+ export declare function wfsFilterSrsNameFromGeometry(geometry: Readonly<Record<string, unknown>>, fallback: string | number | undefined): string | undefined;
40
+ export {};
41
+ //# sourceMappingURL=wfs-v1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wfs-v1.d.ts","sourceRoot":"","sources":["../../../src/query-planner/wfs-v1.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,+BAA+B,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAkBpH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAaxH,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,cAAc,GAAG,kBAAkB,CA4BxG;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB;;mBAEpB,+BAA+B,CAAC,qBAAqB,EAAE,cAAc,CAAC,GAAG,0BAA0B;EAuDlH,CAAC;AAEH,KAAK,kBAAkB,GAAG,IAAI,CAAC,0BAA0B,EAAE,UAAU,GAAG,WAAW,CAAC,CAAC;AAErF,+EAA+E;AAC/E,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,4BAA4B,GACtC,0BAA0B,CA+B5B;AAED,iGAAiG;AACjG,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,iFAAiF;AACjF,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAErF;AAuFD;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAGpF;AAeD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAa1F;AAQD,mFAAmF;AACnF,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC3C,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GACpC,MAAM,GAAG,SAAS,CAiCpB"}