@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,17 @@
1
+ import { type OfflineRegionCacheAdmin, type OfflineRegionCacheInventory, type OfflineRegionResourceRead, type OfflineRegionStore, type OfflineRegionWriteTransaction } from "./types.js";
2
+ export interface MemoryOfflineRegionStoreOptions {
3
+ /** Injectable clock so tests can observe deterministic access timestamps. */
4
+ readonly now?: () => Date;
5
+ }
6
+ export declare class MemoryOfflineRegionStore implements OfflineRegionStore, OfflineRegionCacheAdmin {
7
+ #private;
8
+ constructor(options?: MemoryOfflineRegionStoreOptions);
9
+ inventory(): Promise<OfflineRegionCacheInventory>;
10
+ readResource(regionId: string, resourceId: string): Promise<OfflineRegionResourceRead | undefined>;
11
+ removeRegion(regionId: string): Promise<boolean>;
12
+ setRegionPinned(regionId: string, pinned: boolean): Promise<boolean>;
13
+ pruneExpired(now?: Date): Promise<readonly string[]>;
14
+ beginWrite(regionId: string): Promise<OfflineRegionWriteTransaction>;
15
+ }
16
+ export declare function createMemoryOfflineRegionStore(options?: MemoryOfflineRegionStoreOptions): MemoryOfflineRegionStore;
17
+ //# sourceMappingURL=memory-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/offline/memory-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAGhC,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EAEvB,KAAK,6BAA6B,EACnC,MAAM,YAAY,CAAC;AA0BpB,MAAM,WAAW,+BAA+B;IAC9C,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,qBAAa,wBAAyB,YAAW,kBAAkB,EAAE,uBAAuB;;gBAOvE,OAAO,GAAE,+BAAoC;IAInD,SAAS,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAejD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC;IAqBlG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOhD,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAUpE,YAAY,CAAC,GAAG,GAAE,IAAkB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC;IAajE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,CAAC;CAiGlF;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,wBAAwB,CAE1B"}
@@ -0,0 +1,183 @@
1
+ import { assertCredentialFreeManifest, credentialScreenMessage, screenPersistedString } from "./credential-screen.js";
2
+ import { compareCodeUnits } from "./digest.js";
3
+ import { HonuaOfflineRegionError, } from "./types.js";
4
+ export class MemoryOfflineRegionStore {
5
+ #regions = new Map();
6
+ #staging = new Map();
7
+ #now;
8
+ #revision = "0";
9
+ #sequence = 0;
10
+ constructor(options = {}) {
11
+ this.#now = options.now ?? (() => new Date());
12
+ }
13
+ async inventory() {
14
+ const regions = [];
15
+ for (const [id, record] of this.#regions) {
16
+ regions.push({
17
+ id,
18
+ logicalByteLength: record.logicalByteLength,
19
+ lastAccessedAt: record.lastAccessedAt,
20
+ ...(record.expiresAt ? { expiresAt: record.expiresAt } : {}),
21
+ ...(record.pinned ? { pinned: true } : {}),
22
+ });
23
+ }
24
+ regions.sort((left, right) => compareCodeUnits(left.id, right.id));
25
+ return { revision: this.#revision, regions };
26
+ }
27
+ async readResource(regionId, resourceId) {
28
+ requireId(regionId, "regionId");
29
+ requireId(resourceId, "resourceId");
30
+ const record = this.#regions.get(regionId);
31
+ if (!record)
32
+ return undefined;
33
+ const resource = record.manifest.resources.find((candidate) => candidate.id === resourceId);
34
+ if (!resource)
35
+ return undefined;
36
+ const bytes = record.resources.get(resourceId);
37
+ if (!bytes)
38
+ return undefined;
39
+ if (bytes.byteLength !== resource.byteLength) {
40
+ throw new HonuaOfflineRegionError("integrity-mismatch", `Offline resource ${resourceId} failed its stored byte-length check.`, { resourceId });
41
+ }
42
+ record.lastAccessedAt = this.#now().toISOString();
43
+ this.#advance();
44
+ return { regionId, manifest: record.manifest, resource, bytes: Uint8Array.from(bytes) };
45
+ }
46
+ async removeRegion(regionId) {
47
+ requireId(regionId, "regionId");
48
+ if (!this.#regions.delete(regionId))
49
+ return false;
50
+ this.#advance();
51
+ return true;
52
+ }
53
+ async setRegionPinned(regionId, pinned) {
54
+ requireId(regionId, "regionId");
55
+ if (typeof pinned !== "boolean")
56
+ throw new TypeError("pinned must be a boolean.");
57
+ const record = this.#regions.get(regionId);
58
+ if (!record || Boolean(record.pinned) === pinned)
59
+ return false;
60
+ record.pinned = pinned || undefined;
61
+ this.#advance();
62
+ return true;
63
+ }
64
+ async pruneExpired(now = this.#now()) {
65
+ if (!(now instanceof Date) || !Number.isFinite(now.getTime()))
66
+ throw new TypeError("now must be a valid Date.");
67
+ const cutoff = now.getTime();
68
+ const expired = [...this.#regions.entries()]
69
+ .filter(([, record]) => record.expiresAt !== undefined && Date.parse(record.expiresAt) <= cutoff)
70
+ .map(([id]) => id)
71
+ .sort(compareCodeUnits);
72
+ if (expired.length === 0)
73
+ return expired;
74
+ for (const id of expired)
75
+ this.#regions.delete(id);
76
+ this.#advance();
77
+ return expired;
78
+ }
79
+ async beginWrite(regionId) {
80
+ requireId(regionId, "regionId");
81
+ assertCredentialFreeIdentity(regionId, "regionId");
82
+ const evictions = [];
83
+ const staged = this.#staging.get(regionId) ?? new Map();
84
+ this.#staging.set(regionId, staged);
85
+ return {
86
+ evict: async (id) => {
87
+ if (!evictions.includes(id))
88
+ evictions.push(id);
89
+ },
90
+ readStaged: async (resource) => {
91
+ const bytes = staged.get(resource.id);
92
+ if (!bytes || bytes.byteLength !== resource.byteLength)
93
+ return undefined;
94
+ return Uint8Array.from(bytes);
95
+ },
96
+ discardStaged: async (resource) => {
97
+ staged.delete(resource.id);
98
+ },
99
+ write: async (resource, bytes) => {
100
+ assertCredentialFreeIdentity(resource.id, "resource.id");
101
+ const copy = Uint8Array.from(bytes);
102
+ if (copy.byteLength !== resource.byteLength) {
103
+ throw new HonuaOfflineRegionError("integrity-mismatch", `Resource ${resource.id} byte length does not match its descriptor.`, { resourceId: resource.id });
104
+ }
105
+ staged.set(resource.id, copy);
106
+ },
107
+ commit: async (manifest, receipt, guard) => {
108
+ // `commit` is public, so the credential-free manifest invariant is
109
+ // re-enforced here rather than trusted from the coordinator.
110
+ assertCredentialFreeManifest(manifest);
111
+ if (this.#revision !== guard.expectedInventoryRevision)
112
+ return "inventory-changed";
113
+ const existing = this.#regions.get(manifest.id);
114
+ const evicted = new Set(evictions);
115
+ let logicalBytesAfter = manifest.totalLogicalBytes;
116
+ for (const [id, record] of this.#regions) {
117
+ if (id === manifest.id || evicted.has(id))
118
+ continue;
119
+ logicalBytesAfter += record.logicalByteLength;
120
+ }
121
+ if (logicalBytesAfter > guard.logicalQuotaBytes ||
122
+ logicalBytesAfter !== guard.admission.logicalBytesAfter ||
123
+ guard.admission.requiredLogicalBytes !== manifest.totalLogicalBytes ||
124
+ guard.admission.replacementLogicalBytes !== (existing?.logicalByteLength ?? 0) ||
125
+ !sameIds(guard.admission.evictRegionIds, evictions)) {
126
+ throw new HonuaOfflineRegionError("store-failed", "Memory store rejected inconsistent offline-region admission.");
127
+ }
128
+ const expected = new Set(manifest.resources.map((resource) => resource.id));
129
+ for (const resource of manifest.resources) {
130
+ const bytes = staged.get(resource.id);
131
+ if (!bytes || bytes.byteLength !== resource.byteLength) {
132
+ throw new HonuaOfflineRegionError("store-failed", "Memory store requires every manifest resource to be written before commit.", { resourceId: resource.id });
133
+ }
134
+ }
135
+ for (const id of staged.keys()) {
136
+ if (!expected.has(id)) {
137
+ throw new HonuaOfflineRegionError("store-failed", "Memory store found an unexpected staged resource.", {
138
+ resourceId: id,
139
+ });
140
+ }
141
+ }
142
+ for (const id of evictions)
143
+ this.#regions.delete(id);
144
+ this.#regions.set(manifest.id, {
145
+ manifest,
146
+ receipt,
147
+ logicalByteLength: manifest.totalLogicalBytes,
148
+ lastAccessedAt: receipt.completedAt,
149
+ ...(manifest.expiresAt ? { expiresAt: manifest.expiresAt } : {}),
150
+ ...(existing?.pinned ? { pinned: true } : {}),
151
+ resources: new Map(staged),
152
+ });
153
+ this.#staging.delete(manifest.id);
154
+ this.#advance();
155
+ return "committed";
156
+ },
157
+ rollback: async (options) => {
158
+ if (!options?.preserveStaged)
159
+ this.#staging.delete(regionId);
160
+ evictions.length = 0;
161
+ },
162
+ };
163
+ }
164
+ #advance() {
165
+ this.#sequence += 1;
166
+ this.#revision = `${this.#sequence}`;
167
+ }
168
+ }
169
+ export function createMemoryOfflineRegionStore(options = {}) {
170
+ return new MemoryOfflineRegionStore(options);
171
+ }
172
+ function requireId(value, name) {
173
+ if (typeof value !== "string" || value.length === 0)
174
+ throw new TypeError(`${name} must be non-empty.`);
175
+ }
176
+ function assertCredentialFreeIdentity(value, path) {
177
+ const reason = screenPersistedString(value, "identity");
178
+ if (reason)
179
+ throw new HonuaOfflineRegionError("invalid-manifest", credentialScreenMessage(path, reason), { path });
180
+ }
181
+ function sameIds(left, right) {
182
+ return left.length === right.length && left.every((id, index) => id === right[index]);
183
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Real browser storage-quota interrogation for offline-region admission.
3
+ *
4
+ * `logicalQuotaBytes` is an honest accounting of declared payload lengths, but
5
+ * it has no relationship to the space a browser will actually grant. This module
6
+ * closes that gap in the only way the platform supports: it reads
7
+ * `navigator.storage.estimate()` through an injectable interface, derives a
8
+ * conservative logical budget with an explicit reserve, and reports an explicit
9
+ * `unavailable` result rather than inventing a number when the platform cannot
10
+ * estimate.
11
+ *
12
+ * The estimate is deliberately imprecise and origin-scoped, and the accounting
13
+ * unit stays logical payload bytes: physical occupancy, deduplication, and index
14
+ * overhead are outside the contract and are exactly why a reserve exists. The
15
+ * derived budget is advisory, never a guarantee, so a device can still refuse a
16
+ * write that the budget admitted — which is why storage-pressure failures are
17
+ * classified here too.
18
+ *
19
+ * Nothing in this module performs network I/O, mutates the store, or reads or
20
+ * returns a credential, origin secret, or request URL. Persistence is never
21
+ * requested implicitly: `requestOfflinePersistentStorage()` is the only path to
22
+ * `navigator.storage.persist()` and it exists solely because the caller asked.
23
+ */
24
+ /** Stable discriminator and version for storage-budget probe results. */
25
+ export declare const HONUA_OFFLINE_STORAGE_BUDGET_KIND: "honua.offline-storage-budget";
26
+ export declare const HONUA_OFFLINE_STORAGE_BUDGET_VERSION: "1.0";
27
+ /** Stable discriminator and version for explicit persistence-request results. */
28
+ export declare const HONUA_OFFLINE_STORAGE_PERSISTENCE_KIND: "honua.offline-storage-persistence";
29
+ export declare const HONUA_OFFLINE_STORAGE_PERSISTENCE_VERSION: "1.0";
30
+ /** Fraction of the remaining estimate held back from the derived logical budget. */
31
+ export declare const DEFAULT_OFFLINE_STORAGE_HEADROOM_RATIO = 0.2;
32
+ /** Floor for the reserve, so a large-quota origin still keeps real headroom. */
33
+ export declare const DEFAULT_OFFLINE_STORAGE_MIN_RESERVE_BYTES: number;
34
+ /**
35
+ * Whether the origin's storage survives eviction pressure. `unknown` is a real
36
+ * answer: a platform without `StorageManager.persisted()` has not told us.
37
+ */
38
+ export type OfflineStoragePersistence = "persisted" | "best-effort" | "unknown";
39
+ /** Why no budget could be derived. Never accompanied by a fabricated number. */
40
+ export type OfflineStorageBudgetUnavailableReason = "storage-manager-unavailable" | "estimate-unsupported" | "estimate-failed" | "estimate-incomplete";
41
+ /**
42
+ * Injectable subset of the platform `StorageManager`.
43
+ *
44
+ * Every member is optional so a partial host implementation is describable
45
+ * rather than assumed; the probe degrades to an explicit `unavailable` result
46
+ * instead of throwing. A real `navigator.storage` satisfies this structurally.
47
+ */
48
+ export interface OfflineStorageManagerLike {
49
+ estimate?: () => Promise<{
50
+ readonly quota?: number;
51
+ readonly usage?: number;
52
+ } | undefined>;
53
+ persisted?: () => Promise<boolean>;
54
+ persist?: () => Promise<boolean>;
55
+ }
56
+ /** A derived, advisory logical budget backed by a real platform estimate. */
57
+ export interface OfflineStorageBudgetAvailableV1 {
58
+ readonly kind: typeof HONUA_OFFLINE_STORAGE_BUDGET_KIND;
59
+ readonly version: typeof HONUA_OFFLINE_STORAGE_BUDGET_VERSION;
60
+ readonly status: "available";
61
+ readonly observedAt: string;
62
+ /** Platform-reported origin quota. Imprecise and subject to change. */
63
+ readonly quotaBytes: number;
64
+ readonly usageBytes: number;
65
+ /** `max(0, quota - usage)`; the hard upper bound on any derived budget. */
66
+ readonly remainingBytes: number;
67
+ readonly reserveBytes: number;
68
+ readonly headroomRatio: number;
69
+ /** Pass to `logicalQuotaBytes`. Never exceeds `remainingBytes`. */
70
+ readonly logicalBudgetBytes: number;
71
+ readonly persistence: OfflineStoragePersistence;
72
+ }
73
+ /** An explicit refusal to estimate. Carries no budget of any kind. */
74
+ export interface OfflineStorageBudgetUnavailableV1 {
75
+ readonly kind: typeof HONUA_OFFLINE_STORAGE_BUDGET_KIND;
76
+ readonly version: typeof HONUA_OFFLINE_STORAGE_BUDGET_VERSION;
77
+ readonly status: "unavailable";
78
+ readonly observedAt: string;
79
+ readonly reason: OfflineStorageBudgetUnavailableReason;
80
+ readonly persistence: OfflineStoragePersistence;
81
+ }
82
+ export type OfflineStorageBudgetV1 = OfflineStorageBudgetAvailableV1 | OfflineStorageBudgetUnavailableV1;
83
+ export interface ProbeOfflineStorageBudgetOptions {
84
+ /** Defaults to `navigator.storage`. Inject for tests and non-browser hosts. */
85
+ readonly storage?: OfflineStorageManagerLike;
86
+ /** Fraction of remaining space held in reserve. `0` to `1`; defaults to `0.2`. */
87
+ readonly headroomRatio?: number;
88
+ /** Reserve floor in bytes; defaults to 16 MiB. Clamped by remaining space. */
89
+ readonly minimumReserveBytes?: number;
90
+ /** Explicit clock input so a persisted probe result stays reproducible. */
91
+ readonly now?: Date;
92
+ }
93
+ export type OfflineStoragePersistenceRequestStatus = "granted" | "denied" | "unavailable";
94
+ export type OfflineStoragePersistenceUnavailableReason = "storage-manager-unavailable" | "persist-unsupported" | "persist-failed";
95
+ /** Result of an explicit, caller-initiated persistence request. */
96
+ export interface OfflineStoragePersistenceRequestV1 {
97
+ readonly kind: typeof HONUA_OFFLINE_STORAGE_PERSISTENCE_KIND;
98
+ readonly version: typeof HONUA_OFFLINE_STORAGE_PERSISTENCE_VERSION;
99
+ readonly status: OfflineStoragePersistenceRequestStatus;
100
+ readonly persistence: OfflineStoragePersistence;
101
+ readonly reason?: OfflineStoragePersistenceUnavailableReason;
102
+ }
103
+ export interface RequestOfflinePersistentStorageOptions {
104
+ /** Defaults to `navigator.storage`. Inject for tests and non-browser hosts. */
105
+ readonly storage?: OfflineStorageManagerLike;
106
+ }
107
+ /**
108
+ * Read the origin's real storage estimate and derive a conservative logical
109
+ * budget, or report explicitly that the platform cannot estimate.
110
+ *
111
+ * Performs no network I/O, writes nothing, and never calls
112
+ * `StorageManager.persist()`. `persisted()` is read when the platform offers it
113
+ * because persisted state changes the eviction risk of every cached region; a
114
+ * platform that does not offer it yields `persistence: "unknown"` rather than an
115
+ * assumption.
116
+ *
117
+ * Derivation is deterministic integer arithmetic over the reported values:
118
+ *
119
+ * ```text
120
+ * remaining = max(0, quota - usage)
121
+ * reserve = min(remaining, max(minimumReserveBytes, floor(remaining * headroomRatio)))
122
+ * budget = remaining - reserve
123
+ * ```
124
+ *
125
+ * so the budget can never exceed the platform-reported remaining quota, and a
126
+ * nearly full origin yields `0` instead of a number that cannot be honoured.
127
+ */
128
+ export declare function probeOfflineStorageBudget(options?: ProbeOfflineStorageBudgetOptions): Promise<OfflineStorageBudgetV1>;
129
+ /**
130
+ * Ask the browser to make this origin's storage persistent.
131
+ *
132
+ * This is the only call site of `StorageManager.persist()` in the SDK and it
133
+ * runs only because an application called it. It is never invoked by
134
+ * `downloadOfflineRegion`, by the probe, or by any store adapter: the request
135
+ * can show a permission prompt, and a download is not consent to ask.
136
+ */
137
+ export declare function requestOfflinePersistentStorage(options?: RequestOfflinePersistentStorageOptions): Promise<OfflineStoragePersistenceRequestV1>;
138
+ /**
139
+ * Report whether a thrown value is, or wraps, a platform storage-pressure
140
+ * failure — the device refusing a write for lack of space.
141
+ *
142
+ * Recognizes the `QuotaExceededError` `DOMException` every engine raises, the
143
+ * Firefox `NS_ERROR_DOM_QUOTA_REACHED` name, and the legacy numeric
144
+ * `DOMException` codes, then walks a bounded `cause` and `AggregateError` graph
145
+ * so a store adapter that wrapped the failure is still classified correctly.
146
+ * Bounded and cycle-safe: it inspects at most 32 nodes and visits none twice.
147
+ *
148
+ * Exported because a host-supplied `OfflineRegionStore` must classify the same
149
+ * condition the same way; without a shared predicate an adapter's wrapper would
150
+ * silently downgrade a full device to an internal store failure.
151
+ */
152
+ export declare function isStorageQuotaPressureError(error: unknown): boolean;
153
+ /**
154
+ * Validate and take ownership of a caller-supplied budget envelope.
155
+ *
156
+ * Internal to the offline area: the diagnostic accepts a budget produced by
157
+ * `probeOfflineStorageBudget()`, and a public entry point cannot assume it was
158
+ * not hand-built.
159
+ */
160
+ export declare function captureOfflineStorageBudget(value: unknown, path: string): OfflineStorageBudgetV1;
161
+ //# sourceMappingURL=quota.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../../src/offline/quota.ts"],"names":[],"mappings":"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"}