@honua/app-platform 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 (233) hide show
  1. package/columnar/apache-arrow.d.ts +17 -0
  2. package/columnar/apache-arrow.d.ts.map +1 -0
  3. package/columnar/apache-arrow.js +1036 -0
  4. package/columnar/batch-cache-conformance.d.ts +66 -0
  5. package/columnar/batch-cache-conformance.d.ts.map +1 -0
  6. package/columnar/batch-cache-conformance.js +317 -0
  7. package/columnar/batch-cache-indexeddb.d.ts +31 -0
  8. package/columnar/batch-cache-indexeddb.d.ts.map +1 -0
  9. package/columnar/batch-cache-indexeddb.js +148 -0
  10. package/columnar/batch-cache.d.ts +298 -0
  11. package/columnar/batch-cache.d.ts.map +1 -0
  12. package/columnar/batch-cache.js +757 -0
  13. package/columnar/geoarrow-aggregate.d.ts +178 -0
  14. package/columnar/geoarrow-aggregate.d.ts.map +1 -0
  15. package/columnar/geoarrow-aggregate.js +976 -0
  16. package/columnar/geoarrow-patch.d.ts +382 -0
  17. package/columnar/geoarrow-patch.d.ts.map +1 -0
  18. package/columnar/geoarrow-patch.js +1656 -0
  19. package/columnar/geoarrow-reproject.d.ts +22 -0
  20. package/columnar/geoarrow-reproject.d.ts.map +1 -0
  21. package/columnar/geoarrow-reproject.js +119 -0
  22. package/columnar/geoarrow-serialization.d.ts +68 -0
  23. package/columnar/geoarrow-serialization.d.ts.map +1 -0
  24. package/columnar/geoarrow-serialization.js +331 -0
  25. package/columnar/geoarrow-types.d.ts +271 -0
  26. package/columnar/geoarrow-types.d.ts.map +1 -0
  27. package/columnar/geoarrow-types.js +15 -0
  28. package/columnar/geoarrow.d.ts +52 -0
  29. package/columnar/geoarrow.d.ts.map +1 -0
  30. package/columnar/geoarrow.js +1469 -0
  31. package/columnar/index.d.ts +43 -0
  32. package/columnar/index.d.ts.map +1 -0
  33. package/columnar/index.js +29 -0
  34. package/columnar/operators.d.ts +70 -0
  35. package/columnar/operators.d.ts.map +1 -0
  36. package/columnar/operators.js +307 -0
  37. package/columnar/result.d.ts +369 -0
  38. package/columnar/result.d.ts.map +1 -0
  39. package/columnar/result.js +815 -0
  40. package/columnar/telemetry.d.ts +152 -0
  41. package/columnar/telemetry.d.ts.map +1 -0
  42. package/columnar/telemetry.js +265 -0
  43. package/columnar/transfer.d.ts +28 -0
  44. package/columnar/transfer.d.ts.map +1 -0
  45. package/columnar/transfer.js +669 -0
  46. package/columnar/types.d.ts +175 -0
  47. package/columnar/types.d.ts.map +1 -0
  48. package/columnar/types.js +22 -0
  49. package/columnar/worker.d.ts +157 -0
  50. package/columnar/worker.d.ts.map +1 -0
  51. package/columnar/worker.js +1059 -0
  52. package/contract/pmtiles.d.ts +6 -5
  53. package/contract/pmtiles.d.ts.map +1 -1
  54. package/contract/pmtiles.js +30 -5
  55. package/controls/catalog.d.ts.map +1 -1
  56. package/controls/catalog.js +110 -0
  57. package/controls/index.d.ts +1 -1
  58. package/controls/index.js +1 -1
  59. package/controls/types.d.ts +1 -1
  60. package/controls/types.js +1 -1
  61. package/core/client.d.ts.map +1 -1
  62. package/core/client.js +87 -19
  63. package/core/deprecation-notice.d.ts +28 -0
  64. package/core/deprecation-notice.d.ts.map +1 -0
  65. package/core/deprecation-notice.js +22 -0
  66. package/core/esri-geojson.d.ts.map +1 -1
  67. package/core/esri-geojson.js +10 -6
  68. package/core/optional-grpc-peer.d.ts +15 -0
  69. package/core/optional-grpc-peer.d.ts.map +1 -0
  70. package/core/optional-grpc-peer.js +60 -0
  71. package/esri-compat/basemap-gallery.d.ts +2 -0
  72. package/esri-compat/basemap-gallery.d.ts.map +1 -1
  73. package/esri-compat/basemap-gallery.js +12 -0
  74. package/esri-compat/basemap.d.ts +4 -0
  75. package/esri-compat/basemap.d.ts.map +1 -1
  76. package/esri-compat/basemap.js +70 -0
  77. package/esri-compat/bookmarks.d.ts +2 -0
  78. package/esri-compat/bookmarks.d.ts.map +1 -1
  79. package/esri-compat/bookmarks.js +12 -0
  80. package/esri-compat/controls.d.ts +18 -0
  81. package/esri-compat/controls.d.ts.map +1 -1
  82. package/esri-compat/controls.js +120 -57
  83. package/esri-compat/directions.d.ts +4 -0
  84. package/esri-compat/directions.d.ts.map +1 -1
  85. package/esri-compat/directions.js +30 -20
  86. package/esri-compat/editor.d.ts +4 -0
  87. package/esri-compat/editor.d.ts.map +1 -1
  88. package/esri-compat/editor.js +16 -0
  89. package/esri-compat/esri-config.d.ts +2 -1
  90. package/esri-compat/esri-config.d.ts.map +1 -1
  91. package/esri-compat/feature-effect.d.ts +23 -0
  92. package/esri-compat/feature-effect.d.ts.map +1 -0
  93. package/esri-compat/feature-effect.js +12 -0
  94. package/esri-compat/feature-form.d.ts +6 -0
  95. package/esri-compat/feature-form.d.ts.map +1 -1
  96. package/esri-compat/feature-form.js +63 -0
  97. package/esri-compat/feature-layer.d.ts +21 -2
  98. package/esri-compat/feature-layer.d.ts.map +1 -1
  99. package/esri-compat/feature-layer.js +172 -1
  100. package/esri-compat/feature-table.d.ts +5 -0
  101. package/esri-compat/feature-table.d.ts.map +1 -1
  102. package/esri-compat/feature-table.js +23 -0
  103. package/esri-compat/identity-manager.d.ts +1 -1
  104. package/esri-compat/identity-manager.js +1 -1
  105. package/esri-compat/legend.d.ts +2 -0
  106. package/esri-compat/legend.d.ts.map +1 -1
  107. package/esri-compat/legend.js +98 -0
  108. package/esri-compat/locator.d.ts +1 -1
  109. package/esri-compat/locator.js +1 -1
  110. package/esri-compat/map-view-mount.d.ts +1 -0
  111. package/esri-compat/map-view-mount.d.ts.map +1 -1
  112. package/esri-compat/map-view-mount.js +46 -25
  113. package/esri-compat/map-view.d.ts +13 -0
  114. package/esri-compat/map-view.d.ts.map +1 -1
  115. package/esri-compat/map-view.js +94 -8
  116. package/esri-compat/map.d.ts +5 -1
  117. package/esri-compat/map.d.ts.map +1 -1
  118. package/esri-compat/map.js +10 -0
  119. package/esri-compat/measurement.d.ts +2 -0
  120. package/esri-compat/measurement.d.ts.map +1 -1
  121. package/esri-compat/measurement.js +14 -0
  122. package/esri-compat/point.d.ts +7 -0
  123. package/esri-compat/point.d.ts.map +1 -1
  124. package/esri-compat/point.js +33 -3
  125. package/esri-compat/popup.d.ts +2 -0
  126. package/esri-compat/popup.d.ts.map +1 -1
  127. package/esri-compat/popup.js +12 -0
  128. package/esri-compat/portal.d.ts +1 -1
  129. package/esri-compat/portal.js +1 -1
  130. package/esri-compat/print.d.ts +3 -0
  131. package/esri-compat/print.d.ts.map +1 -1
  132. package/esri-compat/print.js +15 -0
  133. package/esri-compat/search.d.ts +4 -0
  134. package/esri-compat/search.d.ts.map +1 -1
  135. package/esri-compat/search.js +25 -28
  136. package/esri-compat/sketch.d.ts +2 -0
  137. package/esri-compat/sketch.d.ts.map +1 -1
  138. package/esri-compat/sketch.js +12 -0
  139. package/esri-compat/widget-host.d.ts +46 -17
  140. package/esri-compat/widget-host.d.ts.map +1 -1
  141. package/esri-compat/widget-host.js +84 -20
  142. package/esri-compat/widget-shell-hosts.d.ts +61 -0
  143. package/esri-compat/widget-shell-hosts.d.ts.map +1 -0
  144. package/esri-compat/widget-shell-hosts.js +76 -0
  145. package/kernel/renderer.d.ts +82 -0
  146. package/kernel/renderer.js +1 -0
  147. package/offline/conflict-resolution.d.ts +183 -0
  148. package/offline/conflict-resolution.d.ts.map +1 -0
  149. package/offline/conflict-resolution.js +286 -0
  150. package/offline/credential-screen.d.ts +34 -0
  151. package/offline/credential-screen.d.ts.map +1 -0
  152. package/offline/credential-screen.js +78 -0
  153. package/offline/digest.d.ts +15 -0
  154. package/offline/digest.d.ts.map +1 -0
  155. package/offline/digest.js +58 -0
  156. package/offline/edit-queue.d.ts +403 -0
  157. package/offline/edit-queue.d.ts.map +1 -0
  158. package/offline/edit-queue.js +1807 -0
  159. package/offline/edit-replay.d.ts +116 -0
  160. package/offline/edit-replay.d.ts.map +1 -0
  161. package/offline/edit-replay.js +392 -0
  162. package/offline/fetch-handler.d.ts +12 -0
  163. package/offline/fetch-handler.d.ts.map +1 -0
  164. package/offline/fetch-handler.js +65 -0
  165. package/offline/index.d.ts +52 -0
  166. package/offline/index.d.ts.map +1 -0
  167. package/offline/index.js +34 -0
  168. package/offline/indexeddb.d.ts +135 -0
  169. package/offline/indexeddb.d.ts.map +1 -0
  170. package/offline/indexeddb.js +888 -0
  171. package/offline/memory-store.d.ts +17 -0
  172. package/offline/memory-store.d.ts.map +1 -0
  173. package/offline/memory-store.js +183 -0
  174. package/offline/quota.d.ts +161 -0
  175. package/offline/quota.d.ts.map +1 -0
  176. package/offline/quota.js +370 -0
  177. package/offline/read-gate.d.ts +175 -0
  178. package/offline/read-gate.d.ts.map +1 -0
  179. package/offline/read-gate.js +252 -0
  180. package/offline/read-resource.d.ts +99 -0
  181. package/offline/read-resource.d.ts.map +1 -0
  182. package/offline/read-resource.js +156 -0
  183. package/offline/read.d.ts +50 -0
  184. package/offline/read.d.ts.map +1 -0
  185. package/offline/read.js +65 -0
  186. package/offline/region.d.ts +29 -0
  187. package/offline/region.d.ts.map +1 -0
  188. package/offline/region.js +1007 -0
  189. package/offline/replay-conflict.d.ts +183 -0
  190. package/offline/replay-conflict.d.ts.map +1 -0
  191. package/offline/replay-conflict.js +482 -0
  192. package/offline/selection.d.ts +140 -0
  193. package/offline/selection.d.ts.map +1 -0
  194. package/offline/selection.js +391 -0
  195. package/offline/snapshot.d.ts +132 -0
  196. package/offline/snapshot.d.ts.map +1 -0
  197. package/offline/snapshot.js +312 -0
  198. package/offline/status.d.ts +136 -0
  199. package/offline/status.d.ts.map +1 -0
  200. package/offline/status.js +479 -0
  201. package/offline/store-conformance.d.ts +48 -0
  202. package/offline/store-conformance.d.ts.map +1 -0
  203. package/offline/store-conformance.js +233 -0
  204. package/offline/tile-protocol.d.ts +81 -0
  205. package/offline/tile-protocol.d.ts.map +1 -0
  206. package/offline/tile-protocol.js +119 -0
  207. package/offline/types.d.ts +349 -0
  208. package/offline/types.d.ts.map +1 -0
  209. package/offline/types.js +75 -0
  210. package/package.json +14 -2
  211. package/scene-workspace/index.d.ts +2 -2
  212. package/scene-workspace/index.d.ts.map +1 -1
  213. package/scene-workspace/index.js +1 -1
  214. package/scene-workspace/scene-discovery.d.ts +111 -91
  215. package/scene-workspace/scene-discovery.d.ts.map +1 -1
  216. package/scene-workspace/scene-discovery.js +340 -92
  217. package/scene-workspace/scene-view.d.ts +6 -6
  218. package/scene-workspace/scene-view.d.ts.map +1 -1
  219. package/scene-workspace/scene-view.js +32 -6
  220. package/web-components/elements.d.ts +34 -0
  221. package/web-components/elements.d.ts.map +1 -1
  222. package/web-components/elements.js +82 -8
  223. package/web-components/index.d.ts +2 -0
  224. package/web-components/index.d.ts.map +1 -1
  225. package/web-components/index.js +3 -1
  226. package/web-components/map-chrome.d.ts +103 -0
  227. package/web-components/map-chrome.d.ts.map +1 -0
  228. package/web-components/map-chrome.js +325 -0
  229. package/web-components/widget-shell.d.ts +68 -0
  230. package/web-components/widget-shell.d.ts.map +1 -0
  231. package/web-components/widget-shell.js +224 -0
  232. package/widget-capabilities.d.ts +67 -0
  233. package/widget-capabilities.js +152 -0
@@ -0,0 +1,370 @@
1
+ import { HonuaOfflineRegionError } from "./types.js";
2
+ /**
3
+ * Real browser storage-quota interrogation for offline-region admission.
4
+ *
5
+ * `logicalQuotaBytes` is an honest accounting of declared payload lengths, but
6
+ * it has no relationship to the space a browser will actually grant. This module
7
+ * closes that gap in the only way the platform supports: it reads
8
+ * `navigator.storage.estimate()` through an injectable interface, derives a
9
+ * conservative logical budget with an explicit reserve, and reports an explicit
10
+ * `unavailable` result rather than inventing a number when the platform cannot
11
+ * estimate.
12
+ *
13
+ * The estimate is deliberately imprecise and origin-scoped, and the accounting
14
+ * unit stays logical payload bytes: physical occupancy, deduplication, and index
15
+ * overhead are outside the contract and are exactly why a reserve exists. The
16
+ * derived budget is advisory, never a guarantee, so a device can still refuse a
17
+ * write that the budget admitted — which is why storage-pressure failures are
18
+ * classified here too.
19
+ *
20
+ * Nothing in this module performs network I/O, mutates the store, or reads or
21
+ * returns a credential, origin secret, or request URL. Persistence is never
22
+ * requested implicitly: `requestOfflinePersistentStorage()` is the only path to
23
+ * `navigator.storage.persist()` and it exists solely because the caller asked.
24
+ */
25
+ /** Stable discriminator and version for storage-budget probe results. */
26
+ export const HONUA_OFFLINE_STORAGE_BUDGET_KIND = "honua.offline-storage-budget";
27
+ export const HONUA_OFFLINE_STORAGE_BUDGET_VERSION = "1.0";
28
+ /** Stable discriminator and version for explicit persistence-request results. */
29
+ export const HONUA_OFFLINE_STORAGE_PERSISTENCE_KIND = "honua.offline-storage-persistence";
30
+ export const HONUA_OFFLINE_STORAGE_PERSISTENCE_VERSION = "1.0";
31
+ /** Fraction of the remaining estimate held back from the derived logical budget. */
32
+ export const DEFAULT_OFFLINE_STORAGE_HEADROOM_RATIO = 0.2;
33
+ /** Floor for the reserve, so a large-quota origin still keeps real headroom. */
34
+ export const DEFAULT_OFFLINE_STORAGE_MIN_RESERVE_BYTES = 16 * 1024 * 1024;
35
+ const PROBE_OPTION_KEYS = new Set(["storage", "headroomRatio", "minimumReserveBytes", "now"]);
36
+ const PERSIST_OPTION_KEYS = new Set(["storage"]);
37
+ const BUDGET_KEYS = new Set([
38
+ "kind",
39
+ "version",
40
+ "status",
41
+ "observedAt",
42
+ "quotaBytes",
43
+ "usageBytes",
44
+ "remainingBytes",
45
+ "reserveBytes",
46
+ "headroomRatio",
47
+ "logicalBudgetBytes",
48
+ "persistence",
49
+ "reason",
50
+ ]);
51
+ const PERSISTENCE_VALUES = new Set(["persisted", "best-effort", "unknown"]);
52
+ const UNAVAILABLE_REASONS = new Set([
53
+ "storage-manager-unavailable",
54
+ "estimate-unsupported",
55
+ "estimate-failed",
56
+ "estimate-incomplete",
57
+ ]);
58
+ /**
59
+ * Read the origin's real storage estimate and derive a conservative logical
60
+ * budget, or report explicitly that the platform cannot estimate.
61
+ *
62
+ * Performs no network I/O, writes nothing, and never calls
63
+ * `StorageManager.persist()`. `persisted()` is read when the platform offers it
64
+ * because persisted state changes the eviction risk of every cached region; a
65
+ * platform that does not offer it yields `persistence: "unknown"` rather than an
66
+ * assumption.
67
+ *
68
+ * Derivation is deterministic integer arithmetic over the reported values:
69
+ *
70
+ * ```text
71
+ * remaining = max(0, quota - usage)
72
+ * reserve = min(remaining, max(minimumReserveBytes, floor(remaining * headroomRatio)))
73
+ * budget = remaining - reserve
74
+ * ```
75
+ *
76
+ * so the budget can never exceed the platform-reported remaining quota, and a
77
+ * nearly full origin yields `0` instead of a number that cannot be honoured.
78
+ */
79
+ export async function probeOfflineStorageBudget(options = {}) {
80
+ const record = plainRecord(options, "options");
81
+ allowedKeys(record, PROBE_OPTION_KEYS, "options");
82
+ const headroomRatio = normalizeRatio(record.headroomRatio, "options.headroomRatio");
83
+ const minimumReserveBytes = record.minimumReserveBytes === undefined
84
+ ? DEFAULT_OFFLINE_STORAGE_MIN_RESERVE_BYTES
85
+ : nonNegativeInteger(record.minimumReserveBytes, "options.minimumReserveBytes");
86
+ const nowMs = record.now === undefined ? Date.now() : validDate(record.now, "options.now");
87
+ const observedAt = new Date(nowMs).toISOString();
88
+ const storage = resolveStorageManager(record.storage, "options.storage");
89
+ if (!storage)
90
+ return unavailable(observedAt, "storage-manager-unavailable", "unknown");
91
+ const persistence = await readPersistence(storage);
92
+ if (typeof storage.estimate !== "function")
93
+ return unavailable(observedAt, "estimate-unsupported", persistence);
94
+ let estimate;
95
+ try {
96
+ estimate = await storage.estimate();
97
+ }
98
+ catch {
99
+ // An estimate that failed is not a budget of zero; it is no budget at all.
100
+ return unavailable(observedAt, "estimate-failed", persistence);
101
+ }
102
+ if (typeof estimate !== "object" || estimate === null) {
103
+ return unavailable(observedAt, "estimate-incomplete", persistence);
104
+ }
105
+ const quotaBytes = readByteCount(estimate.quota);
106
+ const usageBytes = readByteCount(estimate.usage);
107
+ if (quotaBytes === undefined || usageBytes === undefined) {
108
+ return unavailable(observedAt, "estimate-incomplete", persistence);
109
+ }
110
+ const remainingBytes = Math.max(0, quotaBytes - usageBytes);
111
+ const reserveBytes = Math.min(remainingBytes, Math.max(minimumReserveBytes, Math.floor(remainingBytes * headroomRatio)));
112
+ return deepFreeze({
113
+ kind: HONUA_OFFLINE_STORAGE_BUDGET_KIND,
114
+ version: HONUA_OFFLINE_STORAGE_BUDGET_VERSION,
115
+ status: "available",
116
+ observedAt,
117
+ quotaBytes,
118
+ usageBytes,
119
+ remainingBytes,
120
+ reserveBytes,
121
+ headroomRatio,
122
+ logicalBudgetBytes: remainingBytes - reserveBytes,
123
+ persistence,
124
+ });
125
+ }
126
+ /**
127
+ * Ask the browser to make this origin's storage persistent.
128
+ *
129
+ * This is the only call site of `StorageManager.persist()` in the SDK and it
130
+ * runs only because an application called it. It is never invoked by
131
+ * `downloadOfflineRegion`, by the probe, or by any store adapter: the request
132
+ * can show a permission prompt, and a download is not consent to ask.
133
+ */
134
+ export async function requestOfflinePersistentStorage(options = {}) {
135
+ const record = plainRecord(options, "options");
136
+ allowedKeys(record, PERSIST_OPTION_KEYS, "options");
137
+ const storage = resolveStorageManager(record.storage, "options.storage");
138
+ if (!storage) {
139
+ return persistenceResult("unavailable", "unknown", "storage-manager-unavailable");
140
+ }
141
+ if (typeof storage.persist !== "function") {
142
+ return persistenceResult("unavailable", await readPersistence(storage), "persist-unsupported");
143
+ }
144
+ let granted;
145
+ try {
146
+ granted = (await storage.persist()) === true;
147
+ }
148
+ catch {
149
+ return persistenceResult("unavailable", await readPersistence(storage), "persist-failed");
150
+ }
151
+ return persistenceResult(granted ? "granted" : "denied", granted ? "persisted" : await readPersistence(storage));
152
+ }
153
+ const QUOTA_PRESSURE_NAMES = new Set(["QuotaExceededError", "NS_ERROR_DOM_QUOTA_REACHED", "QUOTA_EXCEEDED_ERR"]);
154
+ const QUOTA_PRESSURE_LEGACY_CODES = new Set([22, 1014]);
155
+ const MAX_CAUSE_NODES = 32;
156
+ /**
157
+ * Report whether a thrown value is, or wraps, a platform storage-pressure
158
+ * failure — the device refusing a write for lack of space.
159
+ *
160
+ * Recognizes the `QuotaExceededError` `DOMException` every engine raises, the
161
+ * Firefox `NS_ERROR_DOM_QUOTA_REACHED` name, and the legacy numeric
162
+ * `DOMException` codes, then walks a bounded `cause` and `AggregateError` graph
163
+ * so a store adapter that wrapped the failure is still classified correctly.
164
+ * Bounded and cycle-safe: it inspects at most 32 nodes and visits none twice.
165
+ *
166
+ * Exported because a host-supplied `OfflineRegionStore` must classify the same
167
+ * condition the same way; without a shared predicate an adapter's wrapper would
168
+ * silently downgrade a full device to an internal store failure.
169
+ */
170
+ export function isStorageQuotaPressureError(error) {
171
+ const pending = [error];
172
+ const seen = new Set();
173
+ let inspected = 0;
174
+ while (pending.length > 0 && inspected < MAX_CAUSE_NODES) {
175
+ const current = pending.shift();
176
+ if (typeof current !== "object" || current === null || seen.has(current))
177
+ continue;
178
+ seen.add(current);
179
+ inspected += 1;
180
+ if (isQuotaPressureShape(current))
181
+ return true;
182
+ const cause = readOwnOrInherited(current, "cause");
183
+ if (cause !== undefined)
184
+ pending.push(cause);
185
+ const errors = readOwnOrInherited(current, "errors");
186
+ if (Array.isArray(errors)) {
187
+ for (const nested of errors.slice(0, MAX_CAUSE_NODES))
188
+ pending.push(nested);
189
+ }
190
+ }
191
+ return false;
192
+ }
193
+ function isQuotaPressureShape(value) {
194
+ const name = readOwnOrInherited(value, "name");
195
+ if (typeof name === "string" && QUOTA_PRESSURE_NAMES.has(name))
196
+ return true;
197
+ const code = readOwnOrInherited(value, "code");
198
+ if (typeof code === "string" && QUOTA_PRESSURE_NAMES.has(code))
199
+ return true;
200
+ // Legacy numeric DOMException codes only count on a DOMException-shaped
201
+ // value; a bare `{ code: 22 }` from an unrelated API is not storage pressure.
202
+ return typeof code === "number" && typeof name === "string" && QUOTA_PRESSURE_LEGACY_CODES.has(code);
203
+ }
204
+ function readOwnOrInherited(value, key) {
205
+ try {
206
+ return value[key];
207
+ }
208
+ catch {
209
+ // A hostile getter must not be able to break failure classification.
210
+ return undefined;
211
+ }
212
+ }
213
+ /**
214
+ * Validate and take ownership of a caller-supplied budget envelope.
215
+ *
216
+ * Internal to the offline area: the diagnostic accepts a budget produced by
217
+ * `probeOfflineStorageBudget()`, and a public entry point cannot assume it was
218
+ * not hand-built.
219
+ */
220
+ export function captureOfflineStorageBudget(value, path) {
221
+ const record = plainRecord(value, path);
222
+ allowedKeys(record, BUDGET_KEYS, path);
223
+ if (record.kind !== HONUA_OFFLINE_STORAGE_BUDGET_KIND)
224
+ invalid(`${path}.kind is unsupported.`, `${path}.kind`);
225
+ if (record.version !== HONUA_OFFLINE_STORAGE_BUDGET_VERSION) {
226
+ invalid(`${path}.version is unsupported.`, `${path}.version`);
227
+ }
228
+ const observedAt = normalizedTimestamp(record.observedAt, `${path}.observedAt`);
229
+ const persistence = memberOf(record.persistence, PERSISTENCE_VALUES, `${path}.persistence`);
230
+ if (record.status === "unavailable") {
231
+ return unavailable(observedAt, memberOf(record.reason, UNAVAILABLE_REASONS, `${path}.reason`), persistence);
232
+ }
233
+ if (record.status !== "available")
234
+ invalid(`${path}.status is invalid.`, `${path}.status`);
235
+ if (record.reason !== undefined)
236
+ invalid(`${path}.reason is not supported.`, `${path}.reason`);
237
+ const quotaBytes = nonNegativeInteger(record.quotaBytes, `${path}.quotaBytes`);
238
+ const usageBytes = nonNegativeInteger(record.usageBytes, `${path}.usageBytes`);
239
+ const remainingBytes = nonNegativeInteger(record.remainingBytes, `${path}.remainingBytes`);
240
+ const reserveBytes = nonNegativeInteger(record.reserveBytes, `${path}.reserveBytes`);
241
+ const logicalBudgetBytes = nonNegativeInteger(record.logicalBudgetBytes, `${path}.logicalBudgetBytes`);
242
+ const headroomRatio = normalizeRatio(record.headroomRatio, `${path}.headroomRatio`);
243
+ // The arithmetic identities are part of the contract, so a forged envelope
244
+ // cannot claim a budget larger than the remaining space it reports.
245
+ if (remainingBytes !== Math.max(0, quotaBytes - usageBytes) || reserveBytes > remainingBytes) {
246
+ invalid(`${path}.remainingBytes does not agree with the reported estimate.`, `${path}.remainingBytes`);
247
+ }
248
+ if (logicalBudgetBytes !== remainingBytes - reserveBytes) {
249
+ invalid(`${path}.logicalBudgetBytes does not agree with the reported reserve.`, `${path}.logicalBudgetBytes`);
250
+ }
251
+ return deepFreeze({
252
+ kind: HONUA_OFFLINE_STORAGE_BUDGET_KIND,
253
+ version: HONUA_OFFLINE_STORAGE_BUDGET_VERSION,
254
+ status: "available",
255
+ observedAt,
256
+ quotaBytes,
257
+ usageBytes,
258
+ remainingBytes,
259
+ reserveBytes,
260
+ headroomRatio,
261
+ logicalBudgetBytes,
262
+ persistence,
263
+ });
264
+ }
265
+ function resolveStorageManager(value, path) {
266
+ if (value === undefined) {
267
+ const platform = globalThis.navigator?.storage;
268
+ return typeof platform === "object" && platform !== null ? platform : undefined;
269
+ }
270
+ if (typeof value !== "object" || value === null)
271
+ invalid(`${path} must be a StorageManager-like object.`, path);
272
+ return value;
273
+ }
274
+ async function readPersistence(storage) {
275
+ if (typeof storage.persisted !== "function")
276
+ return "unknown";
277
+ try {
278
+ return (await storage.persisted()) === true ? "persisted" : "best-effort";
279
+ }
280
+ catch {
281
+ return "unknown";
282
+ }
283
+ }
284
+ function readByteCount(value) {
285
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
286
+ return undefined;
287
+ const floored = Math.floor(value);
288
+ return Number.isSafeInteger(floored) ? floored : undefined;
289
+ }
290
+ function unavailable(observedAt, reason, persistence) {
291
+ return deepFreeze({
292
+ kind: HONUA_OFFLINE_STORAGE_BUDGET_KIND,
293
+ version: HONUA_OFFLINE_STORAGE_BUDGET_VERSION,
294
+ status: "unavailable",
295
+ observedAt,
296
+ reason,
297
+ persistence,
298
+ });
299
+ }
300
+ function persistenceResult(status, persistence, reason) {
301
+ return deepFreeze({
302
+ kind: HONUA_OFFLINE_STORAGE_PERSISTENCE_KIND,
303
+ version: HONUA_OFFLINE_STORAGE_PERSISTENCE_VERSION,
304
+ status,
305
+ persistence,
306
+ ...(reason === undefined ? {} : { reason }),
307
+ });
308
+ }
309
+ function plainRecord(value, path) {
310
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
311
+ invalid(`${path} must be a plain object.`, path);
312
+ }
313
+ const prototype = Object.getPrototypeOf(value);
314
+ if (prototype !== Object.prototype && prototype !== null)
315
+ invalid(`${path} must be a plain object.`, path);
316
+ if (Object.getOwnPropertySymbols(value).length > 0)
317
+ invalid(`${path} must not contain symbol properties.`, path);
318
+ return value;
319
+ }
320
+ function allowedKeys(record, allowed, path) {
321
+ for (const key in record) {
322
+ if (Object.hasOwn(record, key) && !allowed.has(key))
323
+ invalid(`${path}.${key} is not supported.`, `${path}.${key}`);
324
+ }
325
+ }
326
+ function memberOf(value, allowed, path) {
327
+ if (typeof value !== "string" || !allowed.has(value))
328
+ invalid(`${path} is invalid.`, path);
329
+ return value;
330
+ }
331
+ function normalizeRatio(value, path) {
332
+ if (value === undefined)
333
+ return DEFAULT_OFFLINE_STORAGE_HEADROOM_RATIO;
334
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
335
+ invalid(`${path} must be a finite ratio between 0 and 1.`, path);
336
+ }
337
+ return value;
338
+ }
339
+ function nonNegativeInteger(value, path) {
340
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
341
+ invalid(`${path} must be a non-negative safe integer.`, path);
342
+ }
343
+ return value;
344
+ }
345
+ function validDate(value, path) {
346
+ const time = value instanceof Date ? value.getTime() : Number.NaN;
347
+ if (!Number.isFinite(time))
348
+ invalid(`${path} must be a valid Date.`, path);
349
+ return time;
350
+ }
351
+ function normalizedTimestamp(value, path) {
352
+ if (typeof value !== "string" || value.length === 0)
353
+ invalid(`${path} must be an ISO-8601 timestamp.`, path);
354
+ const parsed = Date.parse(value);
355
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
356
+ invalid(`${path} must be a normalized ISO-8601 timestamp.`, path);
357
+ }
358
+ return value;
359
+ }
360
+ function invalid(message, path) {
361
+ throw new HonuaOfflineRegionError("invalid-manifest", message, { path });
362
+ }
363
+ function deepFreeze(value) {
364
+ if (typeof value !== "object" || value === null || Object.isFrozen(value))
365
+ return value;
366
+ Object.freeze(value);
367
+ for (const child of Object.values(value))
368
+ deepFreeze(child);
369
+ return value;
370
+ }
@@ -0,0 +1,175 @@
1
+ import type { Capability, DegradedReason, Query } from "../contract/types.js";
2
+ import type { QueryPlanCacheOptions } from "../query-planner/types.js";
3
+ import { type OfflineRegionResourceSelector, type OfflineRegionSelectionIdentityV1 } from "./selection.js";
4
+ import { HonuaOfflineRegionError, type OfflineRegionBounds, type OfflineRegionManifestV1, type OfflineRegionObservation, type OfflineRegionResourceKind, type OfflineRegionResourceV1, type OfflineRegionStore, type OfflineRegionValidator } from "./types.js";
5
+ /**
6
+ * The one gate every offline read passes through.
7
+ *
8
+ * Feature batches, tiles, assets, and metadata documents are different payloads
9
+ * answering different questions, but they are admitted by exactly the same
10
+ * discipline and in exactly the same order: region identity, then the caller's
11
+ * authorization scope, then version expectations, then extent, then expiry, then
12
+ * content-addressed lookup, then integrity. Keeping that order in one module is
13
+ * what makes "the narrowest true reason" a property of the contract rather than
14
+ * of whichever read path a caller happened to use.
15
+ *
16
+ * @experimental
17
+ */
18
+ export declare const HONUA_OFFLINE_REGION_READ_KIND: "honua.offline-region-read";
19
+ export declare const HONUA_OFFLINE_REGION_READ_VERSION: "1.0";
20
+ /** Default age at which a region's observation is reported stale. */
21
+ export declare const DEFAULT_OFFLINE_REGION_READ_STALE_AFTER_MS: number;
22
+ export type OfflineRegionReadFreshness = "fresh" | "stale";
23
+ /**
24
+ * What the persistent cache did, and why.
25
+ *
26
+ * `regionId` is the region manifest's own content digest, so anything served
27
+ * from storage is traceable to the exact snapshot identity that produced it.
28
+ */
29
+ export interface OfflineRegionReadCacheDecisionV1 {
30
+ readonly kind: typeof HONUA_OFFLINE_REGION_READ_KIND;
31
+ readonly version: typeof HONUA_OFFLINE_REGION_READ_VERSION;
32
+ readonly policy: "prefer-cache";
33
+ readonly action: "reuse";
34
+ readonly state: "offline";
35
+ readonly freshness: OfflineRegionReadFreshness;
36
+ readonly completeness: "complete" | "partial";
37
+ readonly reason: "offline-entry" | "stale-entry";
38
+ /** Region manifest identity. Binds this answer to one snapshot. */
39
+ readonly regionId: `sha256:${string}`;
40
+ readonly queryFingerprint: `sha256:${string}`;
41
+ readonly authorizationScopeDigest: `sha256:${string}`;
42
+ readonly observedAt: string;
43
+ readonly ageMs: number;
44
+ readonly staleAfterMs: number;
45
+ /** Exactly which stored resources answered this read. */
46
+ readonly resources: readonly {
47
+ readonly id: string;
48
+ readonly kind: OfflineRegionResourceKind;
49
+ readonly byteLength: number;
50
+ readonly integrity: `sha256:${string}`;
51
+ }[];
52
+ }
53
+ /** Credential-free provenance travelling with anything served from storage. */
54
+ export interface OfflineRegionReadProvenanceV1 {
55
+ readonly sourceId: string;
56
+ readonly endpoint: string;
57
+ readonly authorizationScopeDigest: `sha256:${string}`;
58
+ readonly sourceVersion: string;
59
+ readonly schemaVersion: string;
60
+ readonly planVersion: string;
61
+ readonly observation: OfflineRegionObservation;
62
+ readonly validator?: OfflineRegionValidator;
63
+ }
64
+ /**
65
+ * Inputs every offline read shares.
66
+ *
67
+ * A store is deliberately absent: admission is decided entirely from the
68
+ * manifest and the caller's own inputs, so the same options resolve a resource
69
+ * identity for a host's request matcher without any storage access at all.
70
+ */
71
+ export interface OfflineRegionReadGateOptions {
72
+ /**
73
+ * The caller's current authorization-scope partition input. Its digest must
74
+ * equal the region's, so a scope change can never serve another principal's
75
+ * cached bytes.
76
+ */
77
+ readonly authorizationScopeFingerprint: string;
78
+ /**
79
+ * Protocol-neutral query the snapshot was planned with. Every resource of a
80
+ * snapshot is identified against its selection's canonical query, so a tile or
81
+ * document planned under a query must be read back under the same one.
82
+ */
83
+ readonly query?: Query;
84
+ /** Extent the caller is asking about. Must equal the region's own extent. */
85
+ readonly bounds?: OfflineRegionBounds;
86
+ /** Versions the caller expects. A drift fails closed instead of serving stale schema. */
87
+ readonly expect?: {
88
+ readonly sourceVersion?: string;
89
+ readonly schemaVersion?: string;
90
+ readonly planVersion?: string;
91
+ };
92
+ readonly now?: () => Date;
93
+ readonly staleAfterMs?: number;
94
+ readonly signal?: AbortSignal;
95
+ }
96
+ /** Gate inputs plus the store a read will resolve its identity against. */
97
+ export interface OfflineRegionStoreReadOptions extends OfflineRegionReadGateOptions {
98
+ readonly store: OfflineRegionStore;
99
+ }
100
+ /** An admitted read: the region is verified, in scope, unexpired, and addressable. */
101
+ export interface OfflineRegionReadGateV1 {
102
+ readonly manifest: OfflineRegionManifestV1;
103
+ readonly sourceId: string;
104
+ readonly authorizationScopeDigest: `sha256:${string}`;
105
+ readonly selection: OfflineRegionSelectionIdentityV1;
106
+ readonly queryFingerprint: `sha256:${string}`;
107
+ readonly pagination: {
108
+ readonly offset: number;
109
+ readonly limit?: number;
110
+ };
111
+ readonly now: Date;
112
+ readonly ageMs: number;
113
+ readonly staleAfterMs: number;
114
+ readonly freshness: OfflineRegionReadFreshness;
115
+ }
116
+ /**
117
+ * Admit a read against a persisted region, or fail with the narrowest true reason.
118
+ *
119
+ * Nothing here touches stored bytes: every failure this raises is knowable from
120
+ * the manifest and the caller's own inputs, so an inadmissible read never causes
121
+ * a storage access at all.
122
+ */
123
+ export declare function openOfflineRegionRead(inputManifest: OfflineRegionManifestV1, options: OfflineRegionReadGateOptions): Promise<OfflineRegionReadGateV1>;
124
+ /** One verified stored payload plus the descriptor that vouched for it. */
125
+ export interface OfflineRegionGatedResourceV1 {
126
+ readonly resource: OfflineRegionResourceV1;
127
+ readonly bytes: Uint8Array;
128
+ }
129
+ /**
130
+ * Resolve a resource identity against the store and verify what comes back.
131
+ *
132
+ * The identity is content-addressed from the admitted selection, so a lookup can
133
+ * only ever find bytes stored for exactly this selection, kind, and selector; an
134
+ * absent entry is a miss rather than an empty success, and a stored payload that
135
+ * disagrees with its descriptor is an integrity failure rather than data.
136
+ */
137
+ export declare function readGatedOfflineRegionResource(gate: OfflineRegionReadGateV1, options: {
138
+ readonly store: OfflineRegionStore;
139
+ readonly kind: OfflineRegionResourceKind;
140
+ readonly selector?: OfflineRegionResourceSelector;
141
+ readonly signal?: AbortSignal;
142
+ }): Promise<OfflineRegionGatedResourceV1>;
143
+ /** Report the persistent-cache decision for one admitted read. */
144
+ export declare function createOfflineRegionReadCacheDecision(gate: OfflineRegionReadGateV1, resources: readonly OfflineRegionResourceV1[], completeness: "complete" | "partial"): OfflineRegionReadCacheDecisionV1;
145
+ export declare function createOfflineRegionReadProvenance(gate: OfflineRegionReadGateV1): OfflineRegionReadProvenanceV1;
146
+ /**
147
+ * Every offline answer is a snapshot, so every offline answer says so.
148
+ *
149
+ * `capability` names the canonical capability the cached bytes stand in for — a
150
+ * tile read stands in for `tiles`, a rendering asset for `render`, and a feature
151
+ * or metadata read for `query` — so a consumer that checks degradation before
152
+ * treating an answer as authoritative learns which live capability it replaced.
153
+ */
154
+ export declare function offlineRegionCachedSnapshotDegradation(gate: OfflineRegionReadGateV1, capability: Capability): DegradedReason;
155
+ /** Attribution for exactly the resources that answered a read. */
156
+ export declare function pickOfflineRegionAttribution(manifest: OfflineRegionManifestV1, resources: readonly OfflineRegionResourceV1[]): Readonly<Record<string, string>>;
157
+ /**
158
+ * Cache input binding a query plan to a persisted region.
159
+ *
160
+ * A fresh region carries its manifest identity as a `fingerprint` validator, so
161
+ * the plan's own fingerprint changes when the region does. A stale region carries
162
+ * freshness only: reporting a validator would claim a revalidation the offline
163
+ * read will never perform.
164
+ */
165
+ export declare function createOfflineRegionQueryPlanCache(manifest: OfflineRegionManifestV1, options?: {
166
+ readonly now?: Date;
167
+ readonly staleAfterMs?: number;
168
+ }): QueryPlanCacheOptions;
169
+ export declare function assertExpectedVersion(expected: string | undefined, actual: string, path: string): void;
170
+ export declare function normalizeStaleAfterMs(value: number | undefined): number;
171
+ export declare function throwIfAborted(signal: AbortSignal | undefined): void;
172
+ export declare function invalid(message: string, path: string): HonuaOfflineRegionError;
173
+ /** Freeze a returned read. Typed arrays are caller-owned copies and stay writable. */
174
+ export declare function deepFreezeRead<T>(value: T): T;
175
+ //# sourceMappingURL=read-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-gate.d.ts","sourceRoot":"","sources":["../../../src/offline/read-gate.ts"],"names":[],"mappings":"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"}