@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,479 @@
1
+ import { canonicalStringify, sha256, toJsonValue } from "./canonical.js";
2
+ import { parseGeoParquetResourceHandle } from "./resource.js";
3
+ import { HonuaQueryPlanningError, QUERY_IR_KIND, QUERY_IR_V2_VERSION, QUERY_IR_VERSION, } from "./types.js";
4
+ const MAX_EFFECTIVE_SCHEMA_FIELDS = 512;
5
+ export function createQueryIr(options) {
6
+ try {
7
+ const ir = {
8
+ kind: QUERY_IR_KIND,
9
+ version: QUERY_IR_VERSION,
10
+ source: queryIrSourceIdentity(options.descriptor, options),
11
+ query: canonicalizeQuery(options.query),
12
+ };
13
+ return deepFreeze(ir);
14
+ }
15
+ catch (error) {
16
+ if (error instanceof HonuaQueryPlanningError)
17
+ throw error;
18
+ throw new HonuaQueryPlanningError("invalid-query", `Query cannot be represented by ${QUERY_IR_KIND}@${QUERY_IR_VERSION}: ${errorMessage(error)}`);
19
+ }
20
+ }
21
+ /** Build a GeoParquet v2 IR whose only resource addressing is an opaque handle. */
22
+ export function createGeoParquetQueryIr(options) {
23
+ try {
24
+ const ir = {
25
+ kind: QUERY_IR_KIND,
26
+ version: QUERY_IR_V2_VERSION,
27
+ source: queryIrSourceIdentityV2(options.descriptor, options.geoparquetResource, options),
28
+ query: canonicalizeQuery(options.query),
29
+ };
30
+ return deepFreeze(ir);
31
+ }
32
+ catch (error) {
33
+ if (error instanceof HonuaQueryPlanningError)
34
+ throw error;
35
+ throw new HonuaQueryPlanningError("invalid-query", `GeoParquet query cannot be represented safely by ${QUERY_IR_KIND}@${QUERY_IR_V2_VERSION}`);
36
+ }
37
+ }
38
+ export function hashQueryIr(ir) {
39
+ return sha256(canonicalStringify(toJsonValue(ir)));
40
+ }
41
+ export function canonicalizeQuery(query) {
42
+ if (!query)
43
+ return deepFreeze({});
44
+ if (query.where !== undefined)
45
+ assertSafeNativeExpression(query.where);
46
+ const canonical = {
47
+ ...(query.where !== undefined ? { where: { kind: "source-native", expression: query.where } } : {}),
48
+ // The typed filter and temporal member are identity-bearing: they change
49
+ // which rows a plan returns, so they belong in the canonical bytes that
50
+ // `hashQueryIr` / `hashQueryPlan` fingerprint.
51
+ ...(query.filter !== undefined ? { filter: query.filter } : {}),
52
+ ...(query.temporalFilter !== undefined ? { temporalFilter: query.temporalFilter } : {}),
53
+ ...(query.spatialFilter
54
+ ? {
55
+ spatialFilter: {
56
+ geometry: asJsonObject(query.spatialFilter.geometry, "$.query.spatialFilter.geometry"),
57
+ geometryType: query.spatialFilter.geometryType,
58
+ ...(query.spatialFilter.spatialRel ? { spatialRel: query.spatialFilter.spatialRel } : {}),
59
+ },
60
+ }
61
+ : {}),
62
+ ...(query.outFields ? { outFields: [...query.outFields] } : {}),
63
+ ...(query.orderBy
64
+ ? {
65
+ orderBy: query.orderBy.map((sort) => ({ field: sort.field, direction: sort.direction ?? "asc" })),
66
+ }
67
+ : {}),
68
+ ...(query.pagination
69
+ ? {
70
+ pagination: {
71
+ ...(query.pagination.offset !== undefined
72
+ ? { offset: checkedInteger(query.pagination.offset, "offset", 0) }
73
+ : {}),
74
+ ...(query.pagination.limit !== undefined
75
+ ? { limit: checkedInteger(query.pagination.limit, "limit", 0) }
76
+ : {}),
77
+ },
78
+ }
79
+ : {}),
80
+ ...(query.aggregation ? { aggregation: canonicalizeAggregation(query.aggregation) } : {}),
81
+ ...(query.returnGeometry !== undefined ? { returnGeometry: query.returnGeometry } : {}),
82
+ ...(query.outSr !== undefined ? { outSr: query.outSr } : {}),
83
+ };
84
+ return deepFreeze(canonical);
85
+ }
86
+ export function queryFromCanonical(query, signal) {
87
+ return {
88
+ ...(query.where ? { where: query.where.expression } : {}),
89
+ ...(query.filter !== undefined ? { filter: query.filter } : {}),
90
+ ...(query.temporalFilter !== undefined ? { temporalFilter: query.temporalFilter } : {}),
91
+ ...(query.spatialFilter
92
+ ? {
93
+ spatialFilter: {
94
+ geometry: query.spatialFilter.geometry,
95
+ geometryType: query.spatialFilter.geometryType,
96
+ ...(query.spatialFilter.spatialRel ? { spatialRel: query.spatialFilter.spatialRel } : {}),
97
+ },
98
+ }
99
+ : {}),
100
+ ...(query.outFields ? { outFields: query.outFields } : {}),
101
+ ...(query.orderBy ? { orderBy: query.orderBy } : {}),
102
+ ...(query.pagination ? { pagination: query.pagination } : {}),
103
+ ...(query.aggregation ? { aggregation: query.aggregation } : {}),
104
+ ...(query.returnGeometry !== undefined ? { returnGeometry: query.returnGeometry } : {}),
105
+ ...(query.outSr !== undefined ? { outSr: query.outSr } : {}),
106
+ ...(signal ? { signal } : {}),
107
+ };
108
+ }
109
+ export function queryIrSourceIdentity(descriptor, context = {}) {
110
+ const authorizationScope = [...new Set(context.authorizationScope ?? [])].sort();
111
+ if (authorizationScope.some((scope) => !isStableAuthorizationScope(scope))) {
112
+ throw new HonuaQueryPlanningError("invalid-query", "authorization scope identity is invalid");
113
+ }
114
+ // WFS names its geometry property per feature type; `locator.geometryName`
115
+ // carries either the caller's explicit pin or the value the adapter resolved
116
+ // from DescribeFeatureType, and outranks descriptor schema evidence.
117
+ const geometryProperty = (descriptor.protocol === "wfs" ? descriptor.locator.geometryName : undefined) ??
118
+ descriptor.schema?.fields?.find((field) => field.type === "esriFieldTypeGeometry")?.name;
119
+ const geoparquet = descriptor.protocol === "geoparquet" ? geoparquetIdentity(descriptor, geometryProperty) : undefined;
120
+ const schemaVersion = optionalPlanMetadata(context.schemaVersion, "schema version");
121
+ const sourceVersion = optionalPlanMetadata(context.sourceVersion, "source version");
122
+ const authorityFingerprint = descriptor.protocol === "wfs"
123
+ ? context.sourceAuthorityFingerprint === null
124
+ ? undefined
125
+ : (context.sourceAuthorityFingerprint ??
126
+ queryIrAuthorityFingerprint(descriptor.locator.url, authorizationScope))
127
+ : undefined;
128
+ if (context.sourceAuthorityFingerprint !== undefined &&
129
+ context.sourceAuthorityFingerprint !== null &&
130
+ !/^sha256:[0-9a-f]{64}$/.test(context.sourceAuthorityFingerprint)) {
131
+ throw new HonuaQueryPlanningError("invalid-query", "source authority fingerprint is invalid");
132
+ }
133
+ return deepFreeze({
134
+ id: descriptor.id,
135
+ protocol: descriptor.protocol,
136
+ endpoint: credentialFreeEndpoint(descriptor.locator.url),
137
+ ...(authorityFingerprint ? { authorityFingerprint } : {}),
138
+ ...(descriptor.locator.serviceId !== undefined ? { serviceId: descriptor.locator.serviceId } : {}),
139
+ ...(descriptor.locator.layerId !== undefined ? { layerId: descriptor.locator.layerId } : {}),
140
+ ...(descriptor.locator.collectionId !== undefined ? { collectionId: descriptor.locator.collectionId } : {}),
141
+ ...(descriptor.locator.typeName !== undefined ? { typeName: descriptor.locator.typeName } : {}),
142
+ ...(descriptor.locator.entitySet !== undefined ? { entitySet: descriptor.locator.entitySet } : {}),
143
+ ...(geometryProperty ? { geometryProperty } : {}),
144
+ ...(descriptor.locator.srsName !== undefined ? { srsName: String(descriptor.locator.srsName) } : {}),
145
+ ...(schemaVersion ? { schemaVersion } : {}),
146
+ ...(sourceVersion ? { sourceVersion } : {}),
147
+ ...(geoparquet ? { geoparquet } : {}),
148
+ authorizationScope,
149
+ capabilities: [...descriptor.capabilities].sort(),
150
+ });
151
+ }
152
+ /**
153
+ * Derive a stable WFS authority binding without retaining endpoint
154
+ * credentials, signed-query values, fragments, or authorization material.
155
+ */
156
+ export function queryIrAuthorityFingerprint(rawUrl, authorizationScope = []) {
157
+ const scopes = [...new Set(authorizationScope)].sort();
158
+ if (scopes.some((scope) => !isStableAuthorizationScope(scope))) {
159
+ throw new HonuaQueryPlanningError("invalid-query", "authorization scope identity is invalid");
160
+ }
161
+ const authority = canonicalAuthorityMaterial(rawUrl);
162
+ return sha256(canonicalStringify(toJsonValue({
163
+ domain: "honua:wfs-authority:v1",
164
+ authority,
165
+ authorizationScope: scopes,
166
+ })));
167
+ }
168
+ /** Rebuild the exact credential-free source identity used by a GeoParquet v2 plan. */
169
+ export function queryIrSourceIdentityV2(descriptor, resourceValue, context = {}) {
170
+ if (descriptor.protocol !== "geoparquet") {
171
+ throw new HonuaQueryPlanningError("invalid-query", "Opaque GeoParquet resources require protocol geoparquet");
172
+ }
173
+ const resource = parseGeoParquetResourceHandle(resourceValue);
174
+ const authorizationScope = [...new Set(context.authorizationScope ?? [])].sort();
175
+ if (authorizationScope.some((scope) => !isStableAuthorizationScope(scope))) {
176
+ throw new HonuaQueryPlanningError("invalid-query", "GeoParquet authorization scope identity is invalid");
177
+ }
178
+ const geometryProperty = descriptor.schema?.fields?.find((field) => field.type === "esriFieldTypeGeometry")?.name;
179
+ const primaryKey = optionalPlanMetadata(descriptor.schema?.primaryKey, "primary key");
180
+ const schemaVersion = optionalPlanMetadata(context.schemaVersion, "schema version");
181
+ const sourceVersion = optionalPlanMetadata(context.sourceVersion, "source version");
182
+ const geometryColumn = optionalPlanMetadata(descriptor.locator.geoparquet?.geometryColumn ?? geometryProperty, "geometry column");
183
+ const bboxColumn = optionalPlanMetadata(descriptor.locator.geoparquet?.bboxColumn, "bbox column");
184
+ const geometryEncoding = descriptor.locator.geoparquet?.geometryEncoding;
185
+ if (geometryEncoding !== undefined &&
186
+ geometryEncoding !== "wkb" &&
187
+ geometryEncoding !== "native" &&
188
+ geometryEncoding !== "geojson" &&
189
+ geometryEncoding !== "geoarrow-point" &&
190
+ geometryEncoding !== "geoarrow-linestring" &&
191
+ geometryEncoding !== "geoarrow-polygon" &&
192
+ geometryEncoding !== "geoarrow-multipoint" &&
193
+ geometryEncoding !== "geoarrow-multilinestring" &&
194
+ geometryEncoding !== "geoarrow-multipolygon") {
195
+ throw new HonuaQueryPlanningError("invalid-query", "GeoParquet geometry encoding identity is invalid");
196
+ }
197
+ const nativeDimensions = descriptor.locator.geoparquet?.nativeDimensions;
198
+ if (nativeDimensions !== undefined && nativeDimensions !== "xy" && nativeDimensions !== "xyz") {
199
+ throw new HonuaQueryPlanningError("invalid-query", "GeoParquet native geometry dimensions identity is invalid");
200
+ }
201
+ const effectiveSchema = geoparquetEffectiveSchema(context.effectiveSchema);
202
+ return deepFreeze({
203
+ id: resource.resource.id,
204
+ protocol: "geoparquet",
205
+ endpoint: "[opaque-resource]",
206
+ ...(primaryKey ? { primaryKey } : {}),
207
+ ...(schemaVersion ? { schemaVersion } : {}),
208
+ ...(sourceVersion ? { sourceVersion } : {}),
209
+ geoparquet: {
210
+ resource,
211
+ ...(geometryColumn ? { geometryColumn } : {}),
212
+ ...(geometryEncoding ? { geometryEncoding } : {}),
213
+ ...(bboxColumn ? { bboxColumn } : {}),
214
+ ...(nativeDimensions ? { nativeDimensions } : {}),
215
+ ...(effectiveSchema ? { effectiveSchema } : {}),
216
+ },
217
+ authorizationScope,
218
+ capabilities: [...descriptor.capabilities].sort(),
219
+ });
220
+ }
221
+ /**
222
+ * Validate and freeze caller-supplied effective GeoParquet schema evidence
223
+ * (#627). Reuses the same bounded string checks as other plan metadata so
224
+ * field name/type text can never smuggle credential material or control
225
+ * characters into a persisted plan.
226
+ */
227
+ function geoparquetEffectiveSchema(value) {
228
+ if (value === undefined)
229
+ return undefined;
230
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_EFFECTIVE_SCHEMA_FIELDS) {
231
+ throw new HonuaQueryPlanningError("invalid-query", "GeoParquet effective schema identity is invalid");
232
+ }
233
+ const names = new Set();
234
+ const fields = value.map((field, index) => {
235
+ if (field === null || typeof field !== "object" || Array.isArray(field)) {
236
+ throw new HonuaQueryPlanningError("invalid-query", `GeoParquet effective schema field ${index} is invalid`);
237
+ }
238
+ const keys = Object.keys(field);
239
+ if (keys.some((key) => key !== "name" && key !== "type" && key !== "nullable")) {
240
+ throw new HonuaQueryPlanningError("invalid-query", `GeoParquet effective schema field ${index} is invalid`);
241
+ }
242
+ const name = requiredPlanMetadata(field.name, `effective schema field ${index} name`);
243
+ const type = requiredPlanMetadata(field.type, `effective schema field ${index} type`);
244
+ if (names.has(name)) {
245
+ throw new HonuaQueryPlanningError("invalid-query", `GeoParquet effective schema field ${index} duplicates name "${name}"`);
246
+ }
247
+ names.add(name);
248
+ const nullable = field.nullable;
249
+ if (nullable !== undefined && typeof nullable !== "boolean") {
250
+ throw new HonuaQueryPlanningError("invalid-query", `GeoParquet effective schema field ${index} nullable is invalid`);
251
+ }
252
+ return deepFreeze({ name, type, ...(nullable !== undefined ? { nullable } : {}) });
253
+ });
254
+ return deepFreeze(fields);
255
+ }
256
+ function requiredPlanMetadata(value, label) {
257
+ const parsed = optionalPlanMetadata(value, label);
258
+ if (parsed === undefined) {
259
+ throw new HonuaQueryPlanningError("invalid-query", `Query plan ${label} identity is invalid`);
260
+ }
261
+ return parsed;
262
+ }
263
+ function optionalPlanMetadata(value, label) {
264
+ if (value === undefined)
265
+ return undefined;
266
+ if (typeof value !== "string" ||
267
+ value.length === 0 ||
268
+ new TextEncoder().encode(value).byteLength > 1_024 ||
269
+ containsControlCharacter(value) ||
270
+ containsCredentialMaterial(value)) {
271
+ throw new HonuaQueryPlanningError("invalid-query", `Query plan ${label} identity is invalid`);
272
+ }
273
+ return value;
274
+ }
275
+ const CREDENTIAL_MATERIAL = /(?:\bBearer\s+[A-Za-z0-9._~+/=-]{8,}|\bBasic\s+[A-Za-z0-9+/=]{8,}|\bAKIA[0-9A-Z]{16}\b|[?&;](?:access[-_]?token|id[-_]?token|refresh[-_]?token|x-amz-signature|x-goog-credential|signature|sig|token|api[-_]?key|password|secret)=[^\s&#;]*|\b(?:authorization|password|secret|token|api[-_]?key|account[-_]?key|shared[-_]?access[-_]?signature)\s*(?:=|:)\s*[^\s,;]+|[a-z][a-z0-9+.-]*:\/\/[^/\s"'<>]*@|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b)/i;
276
+ function containsCredentialMaterial(value) {
277
+ return CREDENTIAL_MATERIAL.test(value);
278
+ }
279
+ function containsControlCharacter(value) {
280
+ for (let index = 0; index < value.length; index += 1) {
281
+ const code = value.charCodeAt(index);
282
+ if (code <= 0x1f || code === 0x7f)
283
+ return true;
284
+ }
285
+ return false;
286
+ }
287
+ function isStableAuthorizationScope(value) {
288
+ return (typeof value === "string" &&
289
+ value.length > 0 &&
290
+ value.length <= 256 &&
291
+ /^[A-Za-z0-9][A-Za-z0-9._:/~-]*$/.test(value) &&
292
+ !value.endsWith("/") &&
293
+ !value.includes("//") &&
294
+ !value.split("/").some((segment) => segment === "." || segment === "..") &&
295
+ !/^eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}$/.test(value));
296
+ }
297
+ function assertSafeNativeExpression(value) {
298
+ if (typeof value !== "string" || value.length === 0 || value.length > 65_536 || containsControlCharacter(value)) {
299
+ throw new HonuaQueryPlanningError("invalid-query", "query.where is invalid");
300
+ }
301
+ const sensitive = /(?:\bBearer\s+[A-Za-z0-9._~+/=-]{8,}|\bBasic\s+[A-Za-z0-9+/=]{8,}|\bAKIA[0-9A-Z]{16}\b|\b(?:authorization|password|secret|token|api[-_]?key)\s*(?:=|eq)\s*['"][^'"]{4,}['"]|\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b)/i;
302
+ if (sensitive.test(value)) {
303
+ throw new HonuaQueryPlanningError("invalid-query", "query.where contains sensitive native expression material");
304
+ }
305
+ }
306
+ /**
307
+ * Derive deterministic DuckDB/GeoParquet addressing from the descriptor so the
308
+ * SQL compiler never needs a profiling round-trip. Mirrors the live GeoParquet
309
+ * `Source` addressing (`locator.url` + `locator.geoparquet.urls`).
310
+ */
311
+ function geoparquetIdentity(descriptor, geometryProperty) {
312
+ const { url, geoparquet } = descriptor.locator;
313
+ const sources = [];
314
+ if (typeof url === "string" && url.length > 0)
315
+ sources.push(url);
316
+ if (geoparquet?.urls)
317
+ sources.push(...geoparquet.urls);
318
+ const geometryColumn = geoparquet?.geometryColumn ?? geometryProperty;
319
+ return {
320
+ sources,
321
+ ...(geometryColumn ? { geometryColumn } : {}),
322
+ ...(geoparquet?.geometryEncoding ? { geometryEncoding: geoparquet.geometryEncoding } : {}),
323
+ ...(geoparquet?.bboxColumn ? { bboxColumn: geoparquet.bboxColumn } : {}),
324
+ ...(geoparquet?.nativeDimensions ? { nativeDimensions: geoparquet.nativeDimensions } : {}),
325
+ };
326
+ }
327
+ function canonicalizeAggregation(aggregation) {
328
+ if (aggregation.metrics.length === 0) {
329
+ throw new HonuaQueryPlanningError("invalid-query", "aggregation.metrics must contain at least one metric");
330
+ }
331
+ return {
332
+ ...(aggregation.groupBy ? { groupBy: [...aggregation.groupBy] } : {}),
333
+ metrics: aggregation.metrics.map((metric) => ({
334
+ fn: metric.fn,
335
+ field: metric.field,
336
+ ...(metric.alias !== undefined ? { alias: metric.alias } : {}),
337
+ })),
338
+ ...(aggregation.histogram
339
+ ? { histogram: toJsonValue(aggregation.histogram) }
340
+ : {}),
341
+ ...(aggregation.timeSeries
342
+ ? { timeSeries: toJsonValue(aggregation.timeSeries) }
343
+ : {}),
344
+ };
345
+ }
346
+ function checkedInteger(value, name, minimum) {
347
+ if (!Number.isSafeInteger(value) || value < minimum) {
348
+ throw new HonuaQueryPlanningError("invalid-query", `pagination.${name} must be a safe integer >= ${minimum}`);
349
+ }
350
+ return value;
351
+ }
352
+ function asJsonObject(value, path) {
353
+ const json = toJsonValue(value, path);
354
+ if (json === null || Array.isArray(json) || typeof json !== "object") {
355
+ throw new TypeError(`${path} must be an object`);
356
+ }
357
+ return json;
358
+ }
359
+ function canonicalAuthorityMaterial(rawUrl) {
360
+ if (typeof rawUrl !== "string" || rawUrl.length === 0) {
361
+ throw new HonuaQueryPlanningError("invalid-query", "source authority endpoint is invalid");
362
+ }
363
+ try {
364
+ const parsed = new URL(rawUrl);
365
+ parsed.hash = "";
366
+ const search = parsed.search;
367
+ parsed.search = "";
368
+ return `${parsed.toString().replace(/\/$/, "")}${search}`;
369
+ }
370
+ catch {
371
+ const withoutFragment = rawUrl.split("#", 1)[0] ?? rawUrl;
372
+ const queryIndex = withoutFragment.indexOf("?");
373
+ const path = queryIndex >= 0 ? withoutFragment.slice(0, queryIndex) : withoutFragment;
374
+ const search = queryIndex >= 0 ? withoutFragment.slice(queryIndex) : "";
375
+ const normalized = path === "/" ? "/" : path.replace(/\/$/, "");
376
+ if (normalized.length === 0 || hasSchemeAuthorityPrefix(rawUrl) || startsWithDoubleSlash(rawUrl)) {
377
+ throw new HonuaQueryPlanningError("invalid-query", "source authority endpoint is invalid");
378
+ }
379
+ return `${normalized}${search}`;
380
+ }
381
+ }
382
+ function credentialFreeEndpoint(rawUrl) {
383
+ const invalidEndpoint = "[invalid-endpoint]";
384
+ try {
385
+ const parsed = new URL(rawUrl);
386
+ if (!parsed.username && !parsed.password && hasOpaqueSchemeUserInfo(rawUrl))
387
+ return invalidEndpoint;
388
+ parsed.username = "";
389
+ parsed.password = "";
390
+ parsed.search = "";
391
+ parsed.hash = "";
392
+ return parsed.toString().replace(/\/$/, "");
393
+ }
394
+ catch {
395
+ const path = rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
396
+ const malformedAuthority = hasSchemeAuthorityPrefix(rawUrl) || startsWithDoubleSlash(rawUrl);
397
+ const bareUserInfo = hasBareUserInfo(rawUrl);
398
+ let unsafeCharacters = false;
399
+ for (const character of rawUrl) {
400
+ const codePoint = character.codePointAt(0);
401
+ if (codePoint <= 0x20 || codePoint === 0x7f || character.trim() === "") {
402
+ unsafeCharacters = true;
403
+ break;
404
+ }
405
+ }
406
+ return malformedAuthority || bareUserInfo || unsafeCharacters ? invalidEndpoint : path;
407
+ }
408
+ }
409
+ function hasSchemeAuthorityPrefix(value) {
410
+ const colon = value.indexOf(":");
411
+ if (colon < 1 || !asciiAlpha(value.charCodeAt(0)))
412
+ return false;
413
+ for (let index = 1; index < colon; index += 1) {
414
+ const code = value.charCodeAt(index);
415
+ if (!(asciiAlpha(code) || (code >= 48 && code <= 57) || code === 43 || code === 45 || code === 46)) {
416
+ return false;
417
+ }
418
+ }
419
+ return isSlash(value.charCodeAt(colon + 1)) && isSlash(value.charCodeAt(colon + 2));
420
+ }
421
+ function startsWithDoubleSlash(value) {
422
+ return isSlash(value.charCodeAt(0)) && isSlash(value.charCodeAt(1));
423
+ }
424
+ function hasOpaqueSchemeUserInfo(value) {
425
+ const colon = value.indexOf(":");
426
+ if (colon < 1 || !asciiAlpha(value.charCodeAt(0)))
427
+ return false;
428
+ for (let index = 1; index < colon; index += 1) {
429
+ const code = value.charCodeAt(index);
430
+ if (!(asciiAlpha(code) || (code >= 48 && code <= 57) || code === 43 || code === 45 || code === 46)) {
431
+ return false;
432
+ }
433
+ }
434
+ for (let index = colon + 1; index < value.length; index += 1) {
435
+ const code = value.charCodeAt(index);
436
+ if (code === 64)
437
+ return true;
438
+ if (endpointDelimiter(code, false))
439
+ return false;
440
+ }
441
+ return false;
442
+ }
443
+ function hasBareUserInfo(value) {
444
+ let colon = -1;
445
+ for (let index = 0; index < value.length; index += 1) {
446
+ const code = value.charCodeAt(index);
447
+ if (endpointDelimiter(code, true))
448
+ return false;
449
+ if (code === 58 && colon === -1) {
450
+ if (index === 0)
451
+ return false;
452
+ colon = index;
453
+ }
454
+ else if (code === 64) {
455
+ return colon !== -1;
456
+ }
457
+ }
458
+ return false;
459
+ }
460
+ function endpointDelimiter(code, backslash) {
461
+ return code <= 32 || code === 47 || code === 63 || code === 35 || (backslash && code === 92);
462
+ }
463
+ function asciiAlpha(code) {
464
+ return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
465
+ }
466
+ function isSlash(code) {
467
+ return code === 47 || code === 92;
468
+ }
469
+ export function deepFreeze(value) {
470
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
471
+ Object.freeze(value);
472
+ for (const child of Object.values(value))
473
+ deepFreeze(child);
474
+ }
475
+ return value;
476
+ }
477
+ function errorMessage(error) {
478
+ return error instanceof Error ? error.message : String(error);
479
+ }
@@ -0,0 +1,5 @@
1
+ import type { AggregationSpec } from "../contract/types.js";
2
+ import type { HonuaTypedFeature } from "../core/types.js";
3
+ import type { CanonicalQuery } from "./types.js";
4
+ export declare function aggregateLocally<T>(features: readonly HonuaTypedFeature<T>[], aggregation: AggregationSpec, output: Pick<CanonicalQuery, "orderBy" | "pagination">, signal?: AbortSignal): readonly Record<string, unknown>[];
5
+ //# sourceMappingURL=local-aggregate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-aggregate.d.ts","sourceRoot":"","sources":["../../../src/query-planner/local-aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoC,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,QAAQ,EAAE,SAAS,iBAAiB,CAAC,CAAC,CAAC,EAAE,EACzC,WAAW,EAAE,eAAe,EAC5B,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,YAAY,CAAC,EACtD,MAAM,CAAC,EAAE,WAAW,GACnB,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAcpC"}
@@ -0,0 +1,126 @@
1
+ import { HonuaAbortError } from "../core/errors.js";
2
+ export function aggregateLocally(features, aggregation, output, signal) {
3
+ throwIfLocalAggregationAborted(signal);
4
+ const groupKeys = aggregation.groupBy ?? [];
5
+ const rows = groupKeys.length === 0
6
+ ? [computeMetrics(features, aggregation.metrics, signal)]
7
+ : aggregateGroups(features, groupKeys, aggregation.metrics, signal);
8
+ throwIfLocalAggregationAborted(signal);
9
+ const ordered = output.orderBy && output.orderBy.length > 0 ? [...rows].sort(rowComparator(output.orderBy, signal)) : rows;
10
+ throwIfLocalAggregationAborted(signal);
11
+ const offset = output.pagination?.offset ?? 0;
12
+ const limit = output.pagination?.limit;
13
+ return limit === undefined ? ordered.slice(offset) : ordered.slice(offset, offset + limit);
14
+ }
15
+ function aggregateGroups(features, groupKeys, metrics, signal) {
16
+ const groups = new Map();
17
+ for (let index = 0; index < features.length; index += 1) {
18
+ if ((index & 255) === 0)
19
+ throwIfLocalAggregationAborted(signal);
20
+ const feature = features[index];
21
+ if (!feature)
22
+ continue;
23
+ const key = JSON.stringify(groupKeys.map((field) => readField(feature.attributes, field)));
24
+ const bucket = groups.get(key);
25
+ if (bucket)
26
+ bucket.push(feature);
27
+ else
28
+ groups.set(key, [feature]);
29
+ }
30
+ return Array.from(groups.values(), (bucket, index) => {
31
+ if ((index & 255) === 0)
32
+ throwIfLocalAggregationAborted(signal);
33
+ const row = {};
34
+ for (const field of groupKeys)
35
+ row[field] = readField(bucket[0]?.attributes, field);
36
+ return Object.assign(row, computeMetrics(bucket, metrics, signal));
37
+ });
38
+ }
39
+ function computeMetrics(features, metrics, signal) {
40
+ const output = {};
41
+ for (let metricIndex = 0; metricIndex < metrics.length; metricIndex += 1) {
42
+ throwIfLocalAggregationAborted(signal);
43
+ const metric = metrics[metricIndex];
44
+ if (!metric)
45
+ continue;
46
+ const alias = metric.alias ?? `${metric.fn}_${metric.field}`;
47
+ const values = [];
48
+ for (let featureIndex = 0; featureIndex < features.length; featureIndex += 1) {
49
+ if ((featureIndex & 255) === 0)
50
+ throwIfLocalAggregationAborted(signal);
51
+ const feature = features[featureIndex];
52
+ if (!feature)
53
+ continue;
54
+ const value = readField(feature.attributes, metric.field);
55
+ if (typeof value === "number" && Number.isFinite(value))
56
+ values.push(value);
57
+ }
58
+ output[alias] = applyAggregation(metric.fn, values, features.length);
59
+ }
60
+ return output;
61
+ }
62
+ function applyAggregation(fn, values, totalRows) {
63
+ if (fn === "count")
64
+ return totalRows;
65
+ if (values.length === 0)
66
+ return 0;
67
+ switch (fn) {
68
+ case "sum":
69
+ return values.reduce((sum, value) => sum + value, 0);
70
+ case "avg":
71
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
72
+ case "min":
73
+ return Math.min(...values);
74
+ case "max":
75
+ return Math.max(...values);
76
+ case "stddev":
77
+ case "var": {
78
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
79
+ const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length;
80
+ return fn === "var" ? variance : Math.sqrt(variance);
81
+ }
82
+ }
83
+ }
84
+ function rowComparator(orderBy, signal) {
85
+ let comparisons = 0;
86
+ return (left, right) => {
87
+ comparisons += 1;
88
+ if ((comparisons & 255) === 0)
89
+ throwIfLocalAggregationAborted(signal);
90
+ for (const sort of orderBy) {
91
+ const comparison = compareValues(left[sort.field], right[sort.field]);
92
+ if (comparison !== 0)
93
+ return sort.direction === "desc" ? -comparison : comparison;
94
+ }
95
+ return 0;
96
+ };
97
+ }
98
+ function throwIfLocalAggregationAborted(signal) {
99
+ if (!signal)
100
+ return;
101
+ try {
102
+ if (signal.aborted)
103
+ throw new HonuaAbortError();
104
+ }
105
+ catch (error) {
106
+ if (error instanceof HonuaAbortError)
107
+ throw error;
108
+ throw new HonuaAbortError();
109
+ }
110
+ }
111
+ function compareValues(left, right) {
112
+ if (left === right)
113
+ return 0;
114
+ if (left === null || left === undefined)
115
+ return -1;
116
+ if (right === null || right === undefined)
117
+ return 1;
118
+ if (typeof left === "number" && typeof right === "number")
119
+ return left - right;
120
+ return String(left).localeCompare(String(right));
121
+ }
122
+ function readField(attributes, field) {
123
+ if (attributes === null || typeof attributes !== "object")
124
+ return undefined;
125
+ return attributes[field];
126
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * First-party OData v4 protocol module.
3
+ *
4
+ * The module owns the same typed `HonuaOdataEntitySet` escape hatch exposed by
5
+ * `Source.protocol("odata")`, the deterministic planner compiler hook, and
6
+ * canonical query/queryAll wire execution. Runtime authority is the
7
+ * `HonuaClient` explicitly injected into {@link odataProtocolModule}; compiled
8
+ * artifacts carry no origin or credentials and cannot redirect a discovered
9
+ * handle to another entity set.
10
+ *
11
+ * @module
12
+ */
13
+ import type { ProtocolModuleQueryBinding, ProtocolModuleQueryOperation, QueryCapableProtocolModule } from "../contract/protocol-module.js";
14
+ import type { Result, SourceDescriptor } from "../contract/types.js";
15
+ import type { HonuaClient } from "../core/client.js";
16
+ import { HonuaOdataEntitySet, type HonuaOdataPage, type HonuaOdataQueryParams } from "../core/odata.js";
17
+ import type { HonuaFieldInfo } from "../core/types.js";
18
+ import type { CanonicalQuery, OdataProtocolCompiledQueryV1, QueryIrSourceIdentity } from "./types.js";
19
+ /** Runtime-only OData execution context. None of these values enter a plan fingerprint. */
20
+ export interface OdataProtocolExecutionQuery {
21
+ readonly count: boolean;
22
+ readonly fields: ReadonlyArray<HonuaFieldInfo>;
23
+ readonly returnGeometry?: boolean;
24
+ readonly geometryColumn?: string;
25
+ readonly limit?: number;
26
+ }
27
+ /** Exact compiler/executor types carried by the OData protocol module. */
28
+ export interface OdataProtocolQueryBinding extends ProtocolModuleQueryBinding {
29
+ readonly source: QueryIrSourceIdentity;
30
+ readonly compileQuery: CanonicalQuery;
31
+ readonly compiled: OdataProtocolCompiledQueryV1;
32
+ readonly executeQuery: OdataProtocolExecutionQuery;
33
+ }
34
+ export type OdataProtocolModule = QueryCapableProtocolModule<"odata", HonuaOdataEntitySet, OdataProtocolQueryBinding>;
35
+ /**
36
+ * Project already-negotiated OData query params onto the credential-free
37
+ * deterministic artifact consumed by the module executor.
38
+ *
39
+ * `count` and `signal` are intentionally omitted: count is runtime response
40
+ * metadata policy, and cancellation can never participate in request identity.
41
+ */
42
+ export declare function createOdataProtocolCompiledQuery(entitySet: string, operation: ProtocolModuleQueryOperation, params: HonuaOdataQueryParams): OdataProtocolCompiledQueryV1;
43
+ /**
44
+ * Construct the first-party query-capable OData protocol module.
45
+ *
46
+ * Discovery is synchronous and performs no I/O. `$metadata` remains lazy on
47
+ * the returned `HonuaOdataEntitySet`, exactly as it was before the migration.
48
+ * The client stays in this module instance's closure; there is no global
49
+ * protocol registry or ambient authority.
50
+ */
51
+ export declare function odataProtocolModule(client: HonuaClient): OdataProtocolModule;
52
+ /** Internal shared result projection used by query execution and OData streaming. */
53
+ export declare function odataProtocolResultFromPage<T>(descriptor: SourceDescriptor, page: HonuaOdataPage<Record<string, unknown>>, fields: ReadonlyArray<HonuaFieldInfo>, returnGeometry: boolean | undefined, geometryColumn: string | undefined): Result<T>;
54
+ //# sourceMappingURL=odata-protocol-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"odata-protocol-module.d.ts","sourceRoot":"","sources":["../../../src/query-planner/odata-protocol-module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAEV,0BAA0B,EAE1B,4BAA4B,EAC5B,0BAA0B,EAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAA0B,MAAM,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,kBAAkB,CAAC;AAE1E,OAAO,KAAK,EACV,cAAc,EAEd,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,2FAA2F;AAC3F,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,0EAA0E;AAC1E,MAAM,WAAW,yBAA0B,SAAQ,0BAA0B;IAC3E,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,4BAA4B,CAAC;IAChD,QAAQ,CAAC,YAAY,EAAE,2BAA2B,CAAC;CACpD;AAED,MAAM,MAAM,mBAAmB,GAAG,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,CAAC,CAAC;AAEtH;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,4BAA4B,EACvC,MAAM,EAAE,qBAAqB,GAC5B,4BAA4B,CAY9B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAwE5E;AA2FD,qFAAqF;AACrF,wBAAgB,2BAA2B,CAAC,CAAC,EAC3C,UAAU,EAAE,gBAAgB,EAC5B,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC7C,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,EACrC,cAAc,EAAE,OAAO,GAAG,SAAS,EACnC,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,MAAM,CAAC,CAAC,CAAC,CAUX"}