@tileflow/react-native 0.1.0-alpha.6 → 0.1.0-alpha.7
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.
- package/README.md +64 -1
- package/android/src/main/java/dev/tileflow/reactnative/AdmissionBootstrap.kt +7 -3
- package/android/src/main/java/dev/tileflow/reactnative/AdmissionBootstrapOkHttpNetwork.kt +3 -1
- package/android/src/main/java/dev/tileflow/reactnative/AdmissionEngine.kt +4 -2
- package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeAdmissionModule.kt +1 -1
- package/dist/{contract-By3URNxS.d.ts → contract-BaK24QZD.d.ts} +38 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +461 -46
- package/dist/internal/camera.d.ts +1 -1
- package/dist/internal/initial-view.d.ts +1 -1
- package/dist/internal/native-admission-bridge.js +8 -3
- package/dist/internal/native-admission.d.ts +14 -1
- package/dist/internal/native-admission.js +173 -12
- package/dist/internal/native-appearance.d.ts +1 -1
- package/dist/internal/session.d.ts +5 -1
- package/dist/internal/session.js +143 -12
- package/dist/internal/source-state.d.ts +1 -1
- package/docs/native-admission.md +17 -1
- package/ios/TFAdmissionBootstrap.mm +12 -6
- package/ios/TFAdmissionEngine.mm +4 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
Fragment,
|
|
19
19
|
useImperativeHandle,
|
|
20
|
-
useLayoutEffect as
|
|
21
|
-
useMemo as
|
|
20
|
+
useLayoutEffect as useLayoutEffect3,
|
|
21
|
+
useMemo as useMemo3,
|
|
22
22
|
useRef,
|
|
23
|
-
useState,
|
|
23
|
+
useState as useState2,
|
|
24
24
|
useSyncExternalStore
|
|
25
25
|
} from "react";
|
|
26
26
|
import { AppState, findNodeHandle, View as View2 } from "react-native";
|
|
@@ -1268,6 +1268,9 @@ function createMapLifecycle(create, retire) {
|
|
|
1268
1268
|
getSourceState() {
|
|
1269
1269
|
return active?.owner.getSourceState();
|
|
1270
1270
|
},
|
|
1271
|
+
retry() {
|
|
1272
|
+
active?.owner.retry();
|
|
1273
|
+
},
|
|
1271
1274
|
subscribe(listener) {
|
|
1272
1275
|
listeners.add(listener);
|
|
1273
1276
|
return () => listeners.delete(listener);
|
|
@@ -1350,9 +1353,157 @@ function createMapLifecycle(create, retire) {
|
|
|
1350
1353
|
});
|
|
1351
1354
|
}
|
|
1352
1355
|
|
|
1356
|
+
// src/native-boundary-feature-state.tsx
|
|
1357
|
+
import { VectorSource } from "@maplibre/maplibre-react-native";
|
|
1358
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
1359
|
+
import {
|
|
1360
|
+
createBoundaryFeatureState,
|
|
1361
|
+
tileflowBoundaryFamilies
|
|
1362
|
+
} from "@tileflow/core/native";
|
|
1363
|
+
|
|
1364
|
+
// src/native-boundary-feature-state-owner.ts
|
|
1365
|
+
var retryDelaysMs = [16, 50, 150, 400, 1e3];
|
|
1366
|
+
var timer = (run, delayMs) => {
|
|
1367
|
+
const handle = setTimeout(run, delayMs);
|
|
1368
|
+
return () => clearTimeout(handle);
|
|
1369
|
+
};
|
|
1370
|
+
function createNativeBoundaryFeatureStateOwner(sourceId, schedule = timer) {
|
|
1371
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1372
|
+
let source = null;
|
|
1373
|
+
let style = {};
|
|
1374
|
+
let loaded = false;
|
|
1375
|
+
let replacing = false;
|
|
1376
|
+
const present = () => !replacing && loaded && source !== null && declaredVectorSource(style, sourceId);
|
|
1377
|
+
let notified = false;
|
|
1378
|
+
const notify = () => {
|
|
1379
|
+
const next = present();
|
|
1380
|
+
if (next === notified) return;
|
|
1381
|
+
notified = next;
|
|
1382
|
+
for (const listener of [...listeners]) listener();
|
|
1383
|
+
};
|
|
1384
|
+
const absentThenNotify = () => {
|
|
1385
|
+
replacing = true;
|
|
1386
|
+
notify();
|
|
1387
|
+
replacing = false;
|
|
1388
|
+
notify();
|
|
1389
|
+
};
|
|
1390
|
+
let attempt = 0;
|
|
1391
|
+
let cancelRetry;
|
|
1392
|
+
const reset = () => {
|
|
1393
|
+
cancelRetry?.();
|
|
1394
|
+
cancelRetry = void 0;
|
|
1395
|
+
attempt = 0;
|
|
1396
|
+
};
|
|
1397
|
+
const rejected = (target) => {
|
|
1398
|
+
if (cancelRetry || target !== source || !present() || attempt >= retryDelaysMs.length) return;
|
|
1399
|
+
cancelRetry = schedule(() => {
|
|
1400
|
+
cancelRetry = void 0;
|
|
1401
|
+
if (target === source && present()) absentThenNotify();
|
|
1402
|
+
}, retryDelaysMs[attempt++]);
|
|
1403
|
+
};
|
|
1404
|
+
const write = (operation) => {
|
|
1405
|
+
const target = source;
|
|
1406
|
+
if (!target) return;
|
|
1407
|
+
void operation(target).then(
|
|
1408
|
+
() => {
|
|
1409
|
+
if (target === source && !cancelRetry) attempt = 0;
|
|
1410
|
+
},
|
|
1411
|
+
() => rejected(target)
|
|
1412
|
+
);
|
|
1413
|
+
};
|
|
1414
|
+
const map = {
|
|
1415
|
+
getSource: (id) => id === sourceId && present() ? {} : void 0,
|
|
1416
|
+
getStyle: () => style,
|
|
1417
|
+
on: (_type, listener) => listeners.add(listener),
|
|
1418
|
+
off: (_type, listener) => listeners.delete(listener),
|
|
1419
|
+
setFeatureState({ id, sourceLayer }, state) {
|
|
1420
|
+
if (!sourceLayer) return;
|
|
1421
|
+
write((target) => target.setFeatureState({ id, sourceLayer }, state));
|
|
1422
|
+
},
|
|
1423
|
+
removeFeatureState({ id, sourceLayer }, key2) {
|
|
1424
|
+
if (!sourceLayer || id === void 0) return;
|
|
1425
|
+
write((target) => target.removeFeatureState({ id, sourceLayer }, key2));
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
return Object.freeze({
|
|
1429
|
+
map,
|
|
1430
|
+
/** A newly committed style is loading until MapLibre reports it loaded. */
|
|
1431
|
+
setStyle(next, nextLoaded) {
|
|
1432
|
+
if (next !== style) {
|
|
1433
|
+
reset();
|
|
1434
|
+
replacing = true;
|
|
1435
|
+
notify();
|
|
1436
|
+
replacing = false;
|
|
1437
|
+
}
|
|
1438
|
+
style = next;
|
|
1439
|
+
loaded = nextLoaded;
|
|
1440
|
+
notify();
|
|
1441
|
+
},
|
|
1442
|
+
setSource(next) {
|
|
1443
|
+
if (next !== source) reset();
|
|
1444
|
+
source = next;
|
|
1445
|
+
notify();
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
function declaredVectorSource(style, id) {
|
|
1450
|
+
const value = style.sources?.[id];
|
|
1451
|
+
return value?.type === "vector";
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// src/native-boundary-feature-state.tsx
|
|
1455
|
+
import { jsx } from "react/jsx-runtime";
|
|
1456
|
+
function NativeBoundaryFeatureState({
|
|
1457
|
+
input,
|
|
1458
|
+
style,
|
|
1459
|
+
loaded
|
|
1460
|
+
}) {
|
|
1461
|
+
const family = input.family ?? "boundaries";
|
|
1462
|
+
const sourceId = Object.hasOwn(tileflowBoundaryFamilies, family) ? tileflowBoundaryFamilies[family].sourceId : family;
|
|
1463
|
+
const owner = useMemo(() => createNativeBoundaryFeatureStateOwner(sourceId), [sourceId]);
|
|
1464
|
+
const [controller, setController] = useState(null);
|
|
1465
|
+
const [adopted, setAdopted] = useState(false);
|
|
1466
|
+
const bind2 = useCallback(
|
|
1467
|
+
(node) => {
|
|
1468
|
+
owner.setSource(node);
|
|
1469
|
+
setAdopted(node !== null);
|
|
1470
|
+
},
|
|
1471
|
+
[owner]
|
|
1472
|
+
);
|
|
1473
|
+
useLayoutEffect(() => owner.setStyle(style, loaded), [owner, style, loaded]);
|
|
1474
|
+
const { labels, state, values } = input;
|
|
1475
|
+
useEffect(() => {
|
|
1476
|
+
const created = createBoundaryFeatureState(owner.map, {
|
|
1477
|
+
family,
|
|
1478
|
+
...labels === void 0 ? {} : { labels },
|
|
1479
|
+
...state === void 0 ? {} : { state }
|
|
1480
|
+
});
|
|
1481
|
+
setController(created);
|
|
1482
|
+
return () => {
|
|
1483
|
+
setController(null);
|
|
1484
|
+
created.dispose();
|
|
1485
|
+
};
|
|
1486
|
+
}, [owner, family, labels, state]);
|
|
1487
|
+
useEffect(() => {
|
|
1488
|
+
if (!controller) return;
|
|
1489
|
+
if (values) controller.set(values);
|
|
1490
|
+
else controller.clear();
|
|
1491
|
+
}, [controller, values]);
|
|
1492
|
+
if (!declaredVectorSource(style, sourceId) || !loaded && !adopted) return null;
|
|
1493
|
+
const tiles = (style.sources?.[sourceId]).tiles;
|
|
1494
|
+
return /* @__PURE__ */ jsx(
|
|
1495
|
+
VectorSource,
|
|
1496
|
+
{
|
|
1497
|
+
id: sourceId,
|
|
1498
|
+
ref: bind2,
|
|
1499
|
+
...Array.isArray(tiles) ? { tiles } : {}
|
|
1500
|
+
}
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1353
1504
|
// src/native-marker.tsx
|
|
1354
1505
|
import { Marker as NativeMarker } from "@maplibre/maplibre-react-native";
|
|
1355
|
-
import { useLayoutEffect, useMemo } from "react";
|
|
1506
|
+
import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2 } from "react";
|
|
1356
1507
|
import { Pressable, processColor, View } from "react-native";
|
|
1357
1508
|
|
|
1358
1509
|
// src/native-marker-model.ts
|
|
@@ -1399,7 +1550,7 @@ function resolveNativeMarkerContent(context, render) {
|
|
|
1399
1550
|
}
|
|
1400
1551
|
|
|
1401
1552
|
// src/native-marker.tsx
|
|
1402
|
-
import { jsx } from "react/jsx-runtime";
|
|
1553
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1403
1554
|
var button = Object.freeze({
|
|
1404
1555
|
minWidth: 44,
|
|
1405
1556
|
minHeight: 44,
|
|
@@ -1421,7 +1572,7 @@ function NativeAnnotationMarker({
|
|
|
1421
1572
|
lifecycle,
|
|
1422
1573
|
renderMarker
|
|
1423
1574
|
}) {
|
|
1424
|
-
const model =
|
|
1575
|
+
const model = useMemo2(() => nativeMarkerModel(annotation, enabled), [annotation, enabled]);
|
|
1425
1576
|
const rendered = resolveNativeMarkerContent(model.context, renderMarker);
|
|
1426
1577
|
let color = defaultColor;
|
|
1427
1578
|
let invalidColor = false;
|
|
@@ -1435,14 +1586,14 @@ function NativeAnnotationMarker({
|
|
|
1435
1586
|
}
|
|
1436
1587
|
}
|
|
1437
1588
|
const diagnostic = rendered.diagnostic ?? (!rendered.content && invalidColor ? "INVALID_FIELD" : void 0);
|
|
1438
|
-
|
|
1589
|
+
useLayoutEffect2(() => {
|
|
1439
1590
|
if (diagnostic) lifecycle.interactionDiagnostic(sceneKey, diagnostic);
|
|
1440
1591
|
}, [annotation, diagnostic, lifecycle, sceneKey]);
|
|
1441
1592
|
const press = (event) => {
|
|
1442
1593
|
event?.stopPropagation();
|
|
1443
1594
|
lifecycle.markerPress(sceneKey, annotation);
|
|
1444
1595
|
};
|
|
1445
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ jsx2(
|
|
1446
1597
|
NativeMarker,
|
|
1447
1598
|
{
|
|
1448
1599
|
id: `tileflow-annotation-${model.key}`,
|
|
@@ -1453,7 +1604,7 @@ function NativeAnnotationMarker({
|
|
|
1453
1604
|
lifecycle.beginTouch(sceneKey);
|
|
1454
1605
|
press();
|
|
1455
1606
|
},
|
|
1456
|
-
children: /* @__PURE__ */
|
|
1607
|
+
children: /* @__PURE__ */ jsx2(
|
|
1457
1608
|
Pressable,
|
|
1458
1609
|
{
|
|
1459
1610
|
accessible: false,
|
|
@@ -1464,14 +1615,14 @@ function NativeAnnotationMarker({
|
|
|
1464
1615
|
event.stopPropagation();
|
|
1465
1616
|
},
|
|
1466
1617
|
onPress: press,
|
|
1467
|
-
children: /* @__PURE__ */
|
|
1618
|
+
children: /* @__PURE__ */ jsx2(
|
|
1468
1619
|
View,
|
|
1469
1620
|
{
|
|
1470
1621
|
pointerEvents: "none",
|
|
1471
1622
|
accessible: false,
|
|
1472
1623
|
accessibilityElementsHidden: true,
|
|
1473
1624
|
importantForAccessibility: "no-hide-descendants",
|
|
1474
|
-
children: rendered.content ?? /* @__PURE__ */
|
|
1625
|
+
children: rendered.content ?? /* @__PURE__ */ jsx2(View, { style: [dot, { backgroundColor: color }] })
|
|
1475
1626
|
}
|
|
1476
1627
|
)
|
|
1477
1628
|
}
|
|
@@ -1809,6 +1960,11 @@ var REFRESH_MARGIN_MS = 3e4;
|
|
|
1809
1960
|
var ROTATION_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
1810
1961
|
var ROTATION_REQUEST_LIMIT = 1e4;
|
|
1811
1962
|
var FUTURE_ISSUE_SKEW_MS = 3e4;
|
|
1963
|
+
var RETRY_INITIAL_MS = 1e3;
|
|
1964
|
+
var RETRY_LIMIT_MS = 6e4;
|
|
1965
|
+
var RETRY_AFTER_LIMIT_MS = 15 * 60 * 1e3;
|
|
1966
|
+
var RETRY_AFTER_SECONDS = /^\d{1,10}$/u;
|
|
1967
|
+
var QUOTA_CODES = /* @__PURE__ */ new Set(["COMMERCIAL_MAP_LIMIT_REACHED", "COMMERCIAL_SPENDING_LIMIT_REACHED"]);
|
|
1812
1968
|
var MOBILE_CREDENTIAL = /^tf_public_[0-9a-f]{48}$/u;
|
|
1813
1969
|
var MAP_ID = /^map_[A-Za-z0-9_-]{16}$/u;
|
|
1814
1970
|
var CREDENTIAL_ID = /^key_[A-Za-z0-9_-]{8,80}$/u;
|
|
@@ -1847,6 +2003,13 @@ var SUCCESS_KEYS = /* @__PURE__ */ new Set([
|
|
|
1847
2003
|
"tilesetIds"
|
|
1848
2004
|
]);
|
|
1849
2005
|
var RESTART_KEYS = /* @__PURE__ */ new Set(["code", "error", "retryWithNewSession", "sessionId"]);
|
|
2006
|
+
var TERMINAL_CODES = /* @__PURE__ */ new Set([
|
|
2007
|
+
"NATIVE_SESSION_INPUT_INVALID",
|
|
2008
|
+
"NATIVE_SESSION_CREDENTIAL_REJECTED",
|
|
2009
|
+
"NATIVE_SESSION_MAP_MISMATCH",
|
|
2010
|
+
"NATIVE_SESSION_MAP_NOT_FOUND",
|
|
2011
|
+
"NATIVE_SESSION_QUOTA_EXCEEDED"
|
|
2012
|
+
]);
|
|
1850
2013
|
var ERROR_MESSAGES = {
|
|
1851
2014
|
NATIVE_SESSION_INPUT_INVALID: "Native session configuration is invalid.",
|
|
1852
2015
|
NATIVE_SESSION_RESPONSE_TOO_LARGE: "Native session response exceeded its byte limit.",
|
|
@@ -1858,6 +2021,7 @@ var ERROR_MESSAGES = {
|
|
|
1858
2021
|
NATIVE_SESSION_MAP_NOT_FOUND: "Native session Map was not found.",
|
|
1859
2022
|
NATIVE_SESSION_QUOTA_EXCEEDED: "Native session quota was exceeded.",
|
|
1860
2023
|
NATIVE_SESSION_REJECTED: "Native session request was rejected.",
|
|
2024
|
+
NATIVE_SESSION_RATE_LIMITED: "Native session requests are rate limited.",
|
|
1861
2025
|
NATIVE_SESSION_UNAVAILABLE: "Native session service is unavailable.",
|
|
1862
2026
|
NATIVE_SESSION_REPLACED: "Native session binding was replaced.",
|
|
1863
2027
|
NATIVE_SESSION_DISPOSED: "Native session controller is disposed."
|
|
@@ -1979,7 +2143,29 @@ function createHostedNativeSessionController(input) {
|
|
|
1979
2143
|
})
|
|
1980
2144
|
);
|
|
1981
2145
|
}
|
|
2146
|
+
let terminal = null;
|
|
2147
|
+
let failures = 0;
|
|
2148
|
+
let retryAt = null;
|
|
2149
|
+
let retryError = null;
|
|
2150
|
+
function clearRetry() {
|
|
2151
|
+
failures = 0;
|
|
2152
|
+
retryAt = null;
|
|
2153
|
+
retryError = null;
|
|
2154
|
+
}
|
|
2155
|
+
function delayRetry(error) {
|
|
2156
|
+
let at;
|
|
2157
|
+
try {
|
|
2158
|
+
at = readClock();
|
|
2159
|
+
} catch {
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
if (retryAt === null || at >= retryAt) failures = Math.min(failures + 1, 16);
|
|
2163
|
+
const backoff = Math.min(RETRY_INITIAL_MS * 2 ** (failures - 1), RETRY_LIMIT_MS);
|
|
2164
|
+
retryAt = Math.max(retryAt ?? at, at + Math.max(backoff, retryAfterDelays.get(error) ?? 0));
|
|
2165
|
+
retryError = Object.freeze({ code: error.code, kind: error.kind });
|
|
2166
|
+
}
|
|
1982
2167
|
function rotateIfRequired(at) {
|
|
2168
|
+
if (terminal) throw new HostedNativeSessionError(terminal.code);
|
|
1983
2169
|
if (!activeSession || binding.kind !== "hosted") return;
|
|
1984
2170
|
if (at - activeSession.startedAt < ROTATION_AGE_MS && activeSession.requestCount < ROTATION_REQUEST_LIMIT) {
|
|
1985
2171
|
return;
|
|
@@ -2013,6 +2199,7 @@ function createHostedNativeSessionController(input) {
|
|
|
2013
2199
|
return current;
|
|
2014
2200
|
}
|
|
2015
2201
|
async function ensureAuthority(session, at) {
|
|
2202
|
+
if (terminal) throw new HostedNativeSessionError(terminal.code);
|
|
2016
2203
|
if (session.authority && session.authority.clockEpoch === clock.epoch && session.authority.validUntil - at > REFRESH_MARGIN_MS) {
|
|
2017
2204
|
return { authority: session.authority, session };
|
|
2018
2205
|
}
|
|
@@ -2020,20 +2207,28 @@ function createHostedNativeSessionController(input) {
|
|
|
2020
2207
|
if (binding.kind !== "hosted") {
|
|
2021
2208
|
throw new HostedNativeSessionError("NATIVE_SESSION_REPLACED");
|
|
2022
2209
|
}
|
|
2210
|
+
if (retryAt !== null && retryError && at < retryAt) {
|
|
2211
|
+
if (session.authority && session.authority.clockEpoch === clock.epoch && session.authority.validUntil > at) {
|
|
2212
|
+
return { authority: session.authority, session };
|
|
2213
|
+
}
|
|
2214
|
+
throw new HostedNativeSessionError(retryError.code);
|
|
2215
|
+
}
|
|
2023
2216
|
const operationBinding = binding;
|
|
2024
2217
|
const abort = new SessionAbortController();
|
|
2025
2218
|
operations.add(abort);
|
|
2026
2219
|
const operation = {
|
|
2027
2220
|
abort,
|
|
2028
2221
|
promise: Promise.resolve().then(async () => {
|
|
2029
|
-
session.error = null;
|
|
2030
|
-
publishSession(session, "loading");
|
|
2031
|
-
if (abort.signal.aborted) throw abort.error();
|
|
2032
2222
|
try {
|
|
2223
|
+
if (abort.signal.aborted) throw abort.error();
|
|
2224
|
+
session.error = null;
|
|
2225
|
+
publishSession(session, "loading");
|
|
2226
|
+
if (abort.signal.aborted) throw abort.error();
|
|
2033
2227
|
const result = await bootstrap(session, operationBinding, abort, true, true);
|
|
2034
2228
|
if (abort.signal.aborted) throw abort.error();
|
|
2035
2229
|
result.session.authority = result.authority;
|
|
2036
2230
|
result.session.error = null;
|
|
2231
|
+
if (binding === operationBinding) clearRetry();
|
|
2037
2232
|
if (activeSession === result.session) {
|
|
2038
2233
|
publishSession(result.session, "ready");
|
|
2039
2234
|
if (abort.signal.aborted) throw abort.error();
|
|
@@ -2042,6 +2237,22 @@ function createHostedNativeSessionController(input) {
|
|
|
2042
2237
|
} catch (error) {
|
|
2043
2238
|
const normalized = abort.signal.aborted ? abort.error() : normalizeError(error);
|
|
2044
2239
|
const target = latestSession(session);
|
|
2240
|
+
if (normalized.kind === "terminal" && binding === operationBinding && !terminal) {
|
|
2241
|
+
const failure3 = Object.freeze({ code: normalized.code, kind: normalized.kind });
|
|
2242
|
+
terminal = failure3;
|
|
2243
|
+
const rejectedSession = activeSession;
|
|
2244
|
+
if (rejectedSession) {
|
|
2245
|
+
rejectedSession.authority = null;
|
|
2246
|
+
rejectedSession.error = failure3;
|
|
2247
|
+
}
|
|
2248
|
+
for (const pending of [...operations]) pending.abort(failure3.code);
|
|
2249
|
+
if (binding === operationBinding && rejectedSession) {
|
|
2250
|
+
publishSession(rejectedSession, "error", failure3);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
if (normalized.kind === "recoverable" && binding === operationBinding && !terminal) {
|
|
2254
|
+
delayRetry(normalized);
|
|
2255
|
+
}
|
|
2045
2256
|
if (normalized.code !== "NATIVE_SESSION_REPLACED" && normalized.code !== "NATIVE_SESSION_DISPOSED") {
|
|
2046
2257
|
target.error = Object.freeze({ code: normalized.code, kind: normalized.kind });
|
|
2047
2258
|
publishSession(target, "error", target.error);
|
|
@@ -2074,7 +2285,10 @@ function createHostedNativeSessionController(input) {
|
|
|
2074
2285
|
try {
|
|
2075
2286
|
body = source.length ? JSON.parse(source) : null;
|
|
2076
2287
|
} catch {
|
|
2077
|
-
|
|
2288
|
+
if (response.status === 201) {
|
|
2289
|
+
throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_JSON_INVALID");
|
|
2290
|
+
}
|
|
2291
|
+
body = null;
|
|
2078
2292
|
}
|
|
2079
2293
|
if (response.status === 409 && allowRestart && isExactRestart(body, session.sessionId)) {
|
|
2080
2294
|
assertCurrentSession(session, hostedBinding);
|
|
@@ -2082,7 +2296,12 @@ function createHostedNativeSessionController(input) {
|
|
|
2082
2296
|
if (abort.signal.aborted) throw abort.error();
|
|
2083
2297
|
return bootstrap(replacement, hostedBinding, abort, false, false);
|
|
2084
2298
|
}
|
|
2085
|
-
if (response.status !== 201)
|
|
2299
|
+
if (response.status !== 201) {
|
|
2300
|
+
const failure3 = statusError(response.status, body);
|
|
2301
|
+
const delay = retryAfterMs(response.headers, now);
|
|
2302
|
+
if (delay !== null) retryAfterDelays.set(failure3, delay);
|
|
2303
|
+
throw failure3;
|
|
2304
|
+
}
|
|
2086
2305
|
if (!hasNoStore(response.headers.get("cache-control"))) {
|
|
2087
2306
|
throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_INVALID");
|
|
2088
2307
|
}
|
|
@@ -2155,6 +2374,16 @@ function createHostedNativeSessionController(input) {
|
|
|
2155
2374
|
if (binding.kind === "direct" || !activeSession) {
|
|
2156
2375
|
return Object.freeze({ status: "idle", source: "direct", lifecycle });
|
|
2157
2376
|
}
|
|
2377
|
+
if (terminal) {
|
|
2378
|
+
return Object.freeze({
|
|
2379
|
+
status: "error",
|
|
2380
|
+
source: "hosted",
|
|
2381
|
+
lifecycle,
|
|
2382
|
+
mapId: binding.mapId,
|
|
2383
|
+
sessionId: activeSession.sessionId,
|
|
2384
|
+
error: terminal
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2158
2387
|
if (activeSession.operation) {
|
|
2159
2388
|
return Object.freeze({
|
|
2160
2389
|
status: "loading",
|
|
@@ -2206,6 +2435,7 @@ function createHostedNativeSessionController(input) {
|
|
|
2206
2435
|
if (bindingGeneration !== expectedGeneration) {
|
|
2207
2436
|
throw new HostedNativeSessionError("NATIVE_SESSION_REPLACED");
|
|
2208
2437
|
}
|
|
2438
|
+
if (terminal) throw new HostedNativeSessionError(terminal.code);
|
|
2209
2439
|
checkedAt = readClock();
|
|
2210
2440
|
if (resolved.authority.clockEpoch === clock.epoch && resolved.authority.validUntil > checkedAt) {
|
|
2211
2441
|
admittedAuthorities.set(resolved.authority.public, resolved.authority);
|
|
@@ -2260,7 +2490,7 @@ function createHostedNativeSessionController(input) {
|
|
|
2260
2490
|
}
|
|
2261
2491
|
},
|
|
2262
2492
|
transportBudget(authority) {
|
|
2263
|
-
if (disposed || lifecycle !== "foreground") return 0;
|
|
2493
|
+
if (disposed || terminal || lifecycle !== "foreground") return 0;
|
|
2264
2494
|
const stored = admittedAuthorities.get(authority);
|
|
2265
2495
|
if (!stored || stored.bindingGeneration !== bindingGeneration) return 0;
|
|
2266
2496
|
try {
|
|
@@ -2279,6 +2509,11 @@ function createHostedNativeSessionController(input) {
|
|
|
2279
2509
|
async resume() {
|
|
2280
2510
|
if (disposed) throw new HostedNativeSessionError("NATIVE_SESSION_DISPOSED");
|
|
2281
2511
|
lifecycle = "foreground";
|
|
2512
|
+
if (terminal) {
|
|
2513
|
+
const error = new HostedNativeSessionError(terminal.code);
|
|
2514
|
+
publish(currentSnapshot());
|
|
2515
|
+
throw error;
|
|
2516
|
+
}
|
|
2282
2517
|
if (binding.kind === "direct" || !activeSession) {
|
|
2283
2518
|
publish(currentSnapshot());
|
|
2284
2519
|
return;
|
|
@@ -2301,9 +2536,27 @@ function createHostedNativeSessionController(input) {
|
|
|
2301
2536
|
bindingGeneration += 1;
|
|
2302
2537
|
for (const operation of operations) operation.abort("NATIVE_SESSION_REPLACED");
|
|
2303
2538
|
binding = next;
|
|
2539
|
+
terminal = null;
|
|
2540
|
+
clearRetry();
|
|
2304
2541
|
activeSession = next.kind === "hosted" ? createSession(next.surfaceId, readClock()) : null;
|
|
2305
2542
|
publish(snapshotIdle(binding, activeSession, lifecycle));
|
|
2306
2543
|
},
|
|
2544
|
+
retry() {
|
|
2545
|
+
if (disposed) throw new HostedNativeSessionError("NATIVE_SESSION_DISPOSED");
|
|
2546
|
+
if (!terminal && retryAt === null) return;
|
|
2547
|
+
terminal = null;
|
|
2548
|
+
clearRetry();
|
|
2549
|
+
if (activeSession && !activeSession.operation) activeSession.error = null;
|
|
2550
|
+
publish(currentSnapshot());
|
|
2551
|
+
},
|
|
2552
|
+
retryDelay() {
|
|
2553
|
+
if (disposed || terminal || retryAt === null) return null;
|
|
2554
|
+
try {
|
|
2555
|
+
return Math.max(0, Math.ceil(retryAt - readClock()));
|
|
2556
|
+
} catch {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
},
|
|
2307
2560
|
subscribe(listener) {
|
|
2308
2561
|
if (typeof listener !== "function") {
|
|
2309
2562
|
throw new HostedNativeSessionError("NATIVE_SESSION_INPUT_INVALID");
|
|
@@ -2398,7 +2651,8 @@ function parseSuccess(value, binding, expectedSessionId, expectedSurfaceId) {
|
|
|
2398
2651
|
const issuedAtMs = parseExactIso(value.issuedAt);
|
|
2399
2652
|
const serverTimeMs = parseExactIso(value.serverTime);
|
|
2400
2653
|
const expiresAtMs = parseExactIso(value.expiresAt);
|
|
2401
|
-
if (issuedAtMs > serverTimeMs + FUTURE_ISSUE_SKEW_MS || expiresAtMs <= serverTimeMs || expiresAtMs <= issuedAtMs || expiresAtMs - issuedAtMs > MAX_GRANT_LIFETIME_MS ||
|
|
2654
|
+
if (issuedAtMs > serverTimeMs + FUTURE_ISSUE_SKEW_MS || expiresAtMs <= serverTimeMs || expiresAtMs <= issuedAtMs || expiresAtMs - issuedAtMs > MAX_GRANT_LIFETIME_MS || // The local deadline is derived from serverTime, so this also bounds the transport budget.
|
|
2655
|
+
expiresAtMs - serverTimeMs > MAX_GRANT_LIFETIME_MS) {
|
|
2402
2656
|
throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_INVALID");
|
|
2403
2657
|
}
|
|
2404
2658
|
const authority = {
|
|
@@ -2577,18 +2831,46 @@ function decodeUtf8(bytes) {
|
|
|
2577
2831
|
function hasNoStore(value) {
|
|
2578
2832
|
return typeof value === "string" && value.split(",").some((part) => part.trim().toLowerCase() === "no-store");
|
|
2579
2833
|
}
|
|
2580
|
-
function statusError(status) {
|
|
2834
|
+
function statusError(status, body) {
|
|
2581
2835
|
if (status === 401) return new HostedNativeSessionError("NATIVE_SESSION_CREDENTIAL_REJECTED");
|
|
2582
2836
|
if (status === 403) return new HostedNativeSessionError("NATIVE_SESSION_MAP_MISMATCH");
|
|
2583
2837
|
if (status === 404) return new HostedNativeSessionError("NATIVE_SESSION_MAP_NOT_FOUND");
|
|
2584
|
-
if (status === 429)
|
|
2585
|
-
|
|
2838
|
+
if (status === 429) {
|
|
2839
|
+
return new HostedNativeSessionError(
|
|
2840
|
+
isRecord(body) && typeof body.code === "string" && QUOTA_CODES.has(body.code) ? "NATIVE_SESSION_QUOTA_EXCEEDED" : "NATIVE_SESSION_RATE_LIMITED"
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
if (status === 408 || status >= 500 && status <= 599) {
|
|
2844
|
+
return new HostedNativeSessionError("NATIVE_SESSION_UNAVAILABLE");
|
|
2845
|
+
}
|
|
2586
2846
|
return new HostedNativeSessionError("NATIVE_SESSION_REJECTED");
|
|
2587
2847
|
}
|
|
2848
|
+
var retryAfterDelays = /* @__PURE__ */ new WeakMap();
|
|
2849
|
+
function retryAfterMs(headers, now) {
|
|
2850
|
+
let value;
|
|
2851
|
+
try {
|
|
2852
|
+
value = headers.get("retry-after");
|
|
2853
|
+
} catch {
|
|
2854
|
+
return null;
|
|
2855
|
+
}
|
|
2856
|
+
if (typeof value !== "string" || value.length > 64) return null;
|
|
2857
|
+
const text = value.trim();
|
|
2858
|
+
let delay;
|
|
2859
|
+
if (RETRY_AFTER_SECONDS.test(text)) delay = Number(text) * 1e3;
|
|
2860
|
+
else {
|
|
2861
|
+
const date = Date.parse(text);
|
|
2862
|
+
if (!Number.isFinite(date)) return null;
|
|
2863
|
+
try {
|
|
2864
|
+
delay = date - readWallClock(now);
|
|
2865
|
+
} catch {
|
|
2866
|
+
return null;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
return Math.min(Math.max(0, delay), RETRY_AFTER_LIMIT_MS);
|
|
2870
|
+
}
|
|
2588
2871
|
function errorKind(code) {
|
|
2589
2872
|
if (code === "NATIVE_SESSION_DISPOSED" || code === "NATIVE_SESSION_REPLACED") return "cancelled";
|
|
2590
|
-
|
|
2591
|
-
return "terminal";
|
|
2873
|
+
return TERMINAL_CODES.has(code) ? "terminal" : "recoverable";
|
|
2592
2874
|
}
|
|
2593
2875
|
function normalizeError(error, fallback = "NATIVE_SESSION_RESPONSE_INVALID") {
|
|
2594
2876
|
return error instanceof HostedNativeSessionError ? error : new HostedNativeSessionError(fallback);
|
|
@@ -2887,6 +3169,8 @@ function createNativeAdmissionOwner(options) {
|
|
|
2887
3169
|
try {
|
|
2888
3170
|
resources = normalizeNativeResources(input.resources);
|
|
2889
3171
|
if (input.onRetired !== void 0 && typeof input.onRetired !== "function") throw cancel();
|
|
3172
|
+
if (input.onSessionError !== void 0 && typeof input.onSessionError !== "function")
|
|
3173
|
+
throw cancel();
|
|
2890
3174
|
} catch {
|
|
2891
3175
|
throw cancel("NATIVE_ADMISSION_INVALID");
|
|
2892
3176
|
}
|
|
@@ -2947,6 +3231,17 @@ function createNativeAdmissionOwner(options) {
|
|
|
2947
3231
|
controller,
|
|
2948
3232
|
handle: void 0
|
|
2949
3233
|
};
|
|
3234
|
+
let reported = null;
|
|
3235
|
+
const releaseSession = controller.subscribe((state) => {
|
|
3236
|
+
const code = state.status === "error" ? state.error.code : null;
|
|
3237
|
+
if (!context.live || code === reported) return;
|
|
3238
|
+
reported = code;
|
|
3239
|
+
if (state.status !== "error") return;
|
|
3240
|
+
try {
|
|
3241
|
+
input.onSessionError?.(Object.freeze({ code: state.error.code, kind: state.error.kind }));
|
|
3242
|
+
} catch {
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
2950
3245
|
context.handle = Object.freeze({
|
|
2951
3246
|
context: context.id,
|
|
2952
3247
|
generation: context.generation,
|
|
@@ -2976,9 +3271,26 @@ function createNativeAdmissionOwner(options) {
|
|
|
2976
3271
|
discriminate: catalog.discriminate,
|
|
2977
3272
|
discriminateForTest: catalog.discriminate,
|
|
2978
3273
|
extendResources: catalog.extend,
|
|
3274
|
+
retry() {
|
|
3275
|
+
if (!context.live || disposed) return;
|
|
3276
|
+
try {
|
|
3277
|
+
controller.retry?.();
|
|
3278
|
+
} catch {
|
|
3279
|
+
}
|
|
3280
|
+
},
|
|
3281
|
+
retryDelay() {
|
|
3282
|
+
if (!context.live || disposed) return null;
|
|
3283
|
+
try {
|
|
3284
|
+
const delay = controller.retryDelay?.() ?? null;
|
|
3285
|
+
return delay !== null && Number.isSafeInteger(delay) && delay >= 0 ? delay : null;
|
|
3286
|
+
} catch {
|
|
3287
|
+
return null;
|
|
3288
|
+
}
|
|
3289
|
+
},
|
|
2979
3290
|
retire() {
|
|
2980
3291
|
if (retirement) return retirement;
|
|
2981
3292
|
context.live = false;
|
|
3293
|
+
releaseSession();
|
|
2982
3294
|
context.batch = null;
|
|
2983
3295
|
context.tickets.clear();
|
|
2984
3296
|
catalog.retire();
|
|
@@ -5502,6 +5814,18 @@ function themeBlendKey(blend) {
|
|
|
5502
5814
|
}
|
|
5503
5815
|
|
|
5504
5816
|
// src/mounted-map-owner.ts
|
|
5817
|
+
var timer2 = (run, delayMs) => {
|
|
5818
|
+
const handle = setTimeout(run, delayMs);
|
|
5819
|
+
return () => clearTimeout(handle);
|
|
5820
|
+
};
|
|
5821
|
+
var REOPEN_LIMIT = 3;
|
|
5822
|
+
var sessionErrorCodes = /* @__PURE__ */ new Set([
|
|
5823
|
+
"NATIVE_SESSION_CREDENTIAL_REJECTED",
|
|
5824
|
+
"NATIVE_SESSION_MAP_MISMATCH",
|
|
5825
|
+
"NATIVE_SESSION_MAP_NOT_FOUND",
|
|
5826
|
+
"NATIVE_SESSION_QUOTA_EXCEEDED",
|
|
5827
|
+
"NATIVE_SESSION_INPUT_INVALID"
|
|
5828
|
+
]);
|
|
5505
5829
|
function themeSource(source, name3) {
|
|
5506
5830
|
const theme = Object.hasOwn(source.map.themes, name3) ? source.map.themes[name3] : void 0;
|
|
5507
5831
|
if (!theme) throw new NativePreparationError();
|
|
@@ -5584,7 +5908,9 @@ function createMountedMapOwner(ports2) {
|
|
|
5584
5908
|
let sourceState;
|
|
5585
5909
|
let current;
|
|
5586
5910
|
let lastReadiness = "";
|
|
5911
|
+
let reopenings = 0;
|
|
5587
5912
|
let disposal;
|
|
5913
|
+
const schedule = ports2.schedule ?? timer2;
|
|
5588
5914
|
const retired = /* @__PURE__ */ new Set();
|
|
5589
5915
|
const tasks = /* @__PURE__ */ new Set();
|
|
5590
5916
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -5619,10 +5945,11 @@ function createMountedMapOwner(ports2) {
|
|
|
5619
5945
|
if (lastReadiness === identity) return;
|
|
5620
5946
|
lastReadiness = identity;
|
|
5621
5947
|
}
|
|
5948
|
+
if (event.type === "load") reopenings = 0;
|
|
5622
5949
|
try {
|
|
5623
5950
|
let result;
|
|
5624
5951
|
if (event.type === "load") result = props.onLoad?.(event);
|
|
5625
|
-
else if (event.type === "renderer-error" || event.type === "source-error")
|
|
5952
|
+
else if (event.type === "renderer-error" || event.type === "source-error" || event.type === "session-error")
|
|
5626
5953
|
result = props.onError?.(event);
|
|
5627
5954
|
else if (event.type === "readiness-change") result = props.onReadinessChange?.(event);
|
|
5628
5955
|
else result = props.onThemeChange?.(event);
|
|
@@ -5699,11 +6026,29 @@ function createMountedMapOwner(ports2) {
|
|
|
5699
6026
|
}
|
|
5700
6027
|
}
|
|
5701
6028
|
}
|
|
6029
|
+
function cancelRetry(epoch) {
|
|
6030
|
+
const cancel = epoch.retry;
|
|
6031
|
+
epoch.retry = void 0;
|
|
6032
|
+
cancel?.();
|
|
6033
|
+
}
|
|
6034
|
+
function retryLater(epoch, source) {
|
|
6035
|
+
const delay = epoch.map?.retryDelay();
|
|
6036
|
+
if (typeof delay !== "number") return;
|
|
6037
|
+
cancelRetry(epoch);
|
|
6038
|
+
const cancel = schedule(() => {
|
|
6039
|
+
if (epoch.retry !== cancel) return;
|
|
6040
|
+
epoch.retry = void 0;
|
|
6041
|
+
if (!disposed && foreground && current === epoch && epoch.live && core.state === source)
|
|
6042
|
+
prepare(epoch, source);
|
|
6043
|
+
}, delay);
|
|
6044
|
+
epoch.retry = cancel;
|
|
6045
|
+
}
|
|
5702
6046
|
function retire(epoch) {
|
|
5703
6047
|
if (epoch.retirement) return epoch.retirement;
|
|
5704
6048
|
epoch.live = false;
|
|
5705
6049
|
retired.add(epoch);
|
|
5706
6050
|
cancelJob(epoch);
|
|
6051
|
+
cancelRetry(epoch);
|
|
5707
6052
|
epoch.cache.clear();
|
|
5708
6053
|
epoch.binding.dispose();
|
|
5709
6054
|
const context2 = epoch.map?.retire();
|
|
@@ -5727,6 +6072,24 @@ function createMountedMapOwner(ports2) {
|
|
|
5727
6072
|
});
|
|
5728
6073
|
return track(attempt);
|
|
5729
6074
|
}
|
|
6075
|
+
function restart(epoch) {
|
|
6076
|
+
current = void 0;
|
|
6077
|
+
void retire(epoch).catch(() => void 0);
|
|
6078
|
+
current = createEpoch(epoch.developmentOrigin);
|
|
6079
|
+
select();
|
|
6080
|
+
}
|
|
6081
|
+
function reopen(epoch) {
|
|
6082
|
+
if (disposed || current !== epoch || !epoch.live || !props) return;
|
|
6083
|
+
rendererError();
|
|
6084
|
+
if (reopenings >= REOPEN_LIMIT) return;
|
|
6085
|
+
reopenings += 1;
|
|
6086
|
+
restart(epoch);
|
|
6087
|
+
}
|
|
6088
|
+
function sessionError(epoch, code) {
|
|
6089
|
+
if (disposed || current !== epoch || !epoch.live || !sessionErrorCodes.has(code)) return;
|
|
6090
|
+
const error = Object.freeze({ code, kind: "terminal" });
|
|
6091
|
+
emit({ type: "session-error", generation: generation(), error });
|
|
6092
|
+
}
|
|
5730
6093
|
function createEpoch(developmentOrigin) {
|
|
5731
6094
|
const epoch = {
|
|
5732
6095
|
live: true,
|
|
@@ -5754,7 +6117,17 @@ function createMountedMapOwner(ports2) {
|
|
|
5754
6117
|
if (!epoch.live || current !== epoch) throw new NativePreparationError();
|
|
5755
6118
|
const binding = await epoch.binding.replace(source);
|
|
5756
6119
|
if (!epoch.live || current !== epoch) throw new NativePreparationError();
|
|
5757
|
-
epoch.lease = ports2.installation.open({
|
|
6120
|
+
epoch.lease = ports2.installation.open({
|
|
6121
|
+
binding,
|
|
6122
|
+
resources: [],
|
|
6123
|
+
now: ports2.now,
|
|
6124
|
+
// Admission reports from inside its own cleanup and session publication; defer.
|
|
6125
|
+
onRetired: () => void Promise.resolve().then(() => reopen(epoch)),
|
|
6126
|
+
onSessionError: (error) => {
|
|
6127
|
+
if (error.kind !== "terminal") return;
|
|
6128
|
+
void Promise.resolve().then(() => sessionError(epoch, error.code));
|
|
6129
|
+
}
|
|
6130
|
+
});
|
|
5758
6131
|
const map = await epoch.lease.ready;
|
|
5759
6132
|
epoch.map = map;
|
|
5760
6133
|
if (!epoch.live || current !== epoch || map.state.status !== "active") {
|
|
@@ -5763,7 +6136,9 @@ function createMountedMapOwner(ports2) {
|
|
|
5763
6136
|
}
|
|
5764
6137
|
return map;
|
|
5765
6138
|
});
|
|
5766
|
-
void epoch.context.catch(() =>
|
|
6139
|
+
void epoch.context.catch(() => {
|
|
6140
|
+
epoch.contextFailed = true;
|
|
6141
|
+
});
|
|
5767
6142
|
}
|
|
5768
6143
|
return epoch.context;
|
|
5769
6144
|
}
|
|
@@ -5905,6 +6280,7 @@ function createMountedMapOwner(ports2) {
|
|
|
5905
6280
|
}
|
|
5906
6281
|
rendererError();
|
|
5907
6282
|
epoch.renderer?.preparationFailed(source.generation);
|
|
6283
|
+
retryLater(epoch, source);
|
|
5908
6284
|
}).finally(() => {
|
|
5909
6285
|
if (epoch.job === job) epoch.job = void 0;
|
|
5910
6286
|
});
|
|
@@ -5962,6 +6338,7 @@ function createMountedMapOwner(ports2) {
|
|
|
5962
6338
|
foreground = false;
|
|
5963
6339
|
if (current) {
|
|
5964
6340
|
cancelJob(current);
|
|
6341
|
+
cancelRetry(current);
|
|
5965
6342
|
current.renderer?.background();
|
|
5966
6343
|
}
|
|
5967
6344
|
return;
|
|
@@ -6060,6 +6437,22 @@ function createMountedMapOwner(ports2) {
|
|
|
6060
6437
|
if (state?.status !== "ready") select();
|
|
6061
6438
|
},
|
|
6062
6439
|
nativeLifecycle,
|
|
6440
|
+
/** Loads again after a stopped session, a failed source or a failed context. */
|
|
6441
|
+
retry() {
|
|
6442
|
+
const epoch = current;
|
|
6443
|
+
if (disposed || !props || !epoch?.live) return;
|
|
6444
|
+
reopenings = 0;
|
|
6445
|
+
const map = epoch.map;
|
|
6446
|
+
if (epoch.contextFailed || map?.state.status === "retired") {
|
|
6447
|
+
restart(epoch);
|
|
6448
|
+
return;
|
|
6449
|
+
}
|
|
6450
|
+
map?.retry();
|
|
6451
|
+
cancelRetry(epoch);
|
|
6452
|
+
const state = core.state;
|
|
6453
|
+
if (state?.status === "error") select();
|
|
6454
|
+
else if (state?.status === "ready" && map && foreground) prepare(epoch, state);
|
|
6455
|
+
},
|
|
6063
6456
|
async whenIdle() {
|
|
6064
6457
|
while (tasks.size) await Promise.allSettled([...tasks]);
|
|
6065
6458
|
await current?.renderer?.whenIdle();
|
|
@@ -6097,7 +6490,9 @@ var nativeBootstrapLimits = Object.freeze({
|
|
|
6097
6490
|
queueDepth: 32,
|
|
6098
6491
|
requestBytes: 2048,
|
|
6099
6492
|
responseBytes: 65536,
|
|
6100
|
-
|
|
6493
|
+
retryAfterCharacters: 64,
|
|
6494
|
+
// Well inside the 30 s ticket and batch deadlines.
|
|
6495
|
+
timeoutMs: 1e4
|
|
6101
6496
|
});
|
|
6102
6497
|
|
|
6103
6498
|
// src/native-admission-wire.ts
|
|
@@ -6128,10 +6523,11 @@ function decodeBoundedBase64(value) {
|
|
|
6128
6523
|
return bytes;
|
|
6129
6524
|
}
|
|
6130
6525
|
function responseFromWire(reply, isAborted, release) {
|
|
6131
|
-
if (!reply || !Number.isInteger(reply.status) || reply.status < 100 || reply.status > 599 || typeof reply.cacheControl !== "string" || reply.cacheControl.length > 1024)
|
|
6526
|
+
if (!reply || !Number.isInteger(reply.status) || reply.status < 100 || reply.status > 599 || typeof reply.cacheControl !== "string" || reply.cacheControl.length > 1024 || reply.retryAfter !== void 0 && (typeof reply.retryAfter !== "string" || reply.retryAfter.length > nativeBootstrapLimits.retryAfterCharacters))
|
|
6132
6527
|
throw invalid4();
|
|
6133
6528
|
const status = reply.status;
|
|
6134
6529
|
const cacheControl = reply.cacheControl;
|
|
6530
|
+
const retryAfter = reply.retryAfter || null;
|
|
6135
6531
|
let bytes = decodeBoundedBase64(reply.bodyBase64);
|
|
6136
6532
|
let readerTaken = false;
|
|
6137
6533
|
let done = false;
|
|
@@ -6145,7 +6541,9 @@ function responseFromWire(reply, isAborted, release) {
|
|
|
6145
6541
|
status,
|
|
6146
6542
|
headers: Object.freeze({
|
|
6147
6543
|
get(name3) {
|
|
6148
|
-
|
|
6544
|
+
const header = name3.toLowerCase();
|
|
6545
|
+
if (header === "cache-control") return cacheControl;
|
|
6546
|
+
return header === "retry-after" ? retryAfter : null;
|
|
6149
6547
|
}
|
|
6150
6548
|
}),
|
|
6151
6549
|
body: Object.freeze({
|
|
@@ -7272,7 +7670,7 @@ function createNativeMapOwner() {
|
|
|
7272
7670
|
}
|
|
7273
7671
|
|
|
7274
7672
|
// src/map.tsx
|
|
7275
|
-
import { jsx as
|
|
7673
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
7276
7674
|
var fill = Object.freeze({ flex: 1 });
|
|
7277
7675
|
function NativeScene({
|
|
7278
7676
|
scene,
|
|
@@ -7281,7 +7679,8 @@ function NativeScene({
|
|
|
7281
7679
|
children,
|
|
7282
7680
|
interactions,
|
|
7283
7681
|
interactionEnabled,
|
|
7284
|
-
renderMarker
|
|
7682
|
+
renderMarker,
|
|
7683
|
+
boundaryFeatureState
|
|
7285
7684
|
}) {
|
|
7286
7685
|
const view = useRef(null);
|
|
7287
7686
|
const camera = useRef(null);
|
|
@@ -7291,7 +7690,10 @@ function NativeScene({
|
|
|
7291
7690
|
const laidOut = useRef(false);
|
|
7292
7691
|
const styleLoaded = useRef(false);
|
|
7293
7692
|
const attachRequested = useRef(false);
|
|
7294
|
-
const
|
|
7693
|
+
const latestStyle = useRef(scene.style);
|
|
7694
|
+
latestStyle.current = scene.style;
|
|
7695
|
+
const [loadedStyle, setLoadedStyle] = useState2(void 0);
|
|
7696
|
+
const host = useMemo3(() => {
|
|
7295
7697
|
const value = Object.freeze({
|
|
7296
7698
|
key: scene.key,
|
|
7297
7699
|
style: scene.style,
|
|
@@ -7305,7 +7707,7 @@ function NativeScene({
|
|
|
7305
7707
|
});
|
|
7306
7708
|
return value;
|
|
7307
7709
|
}, [scene.key, scene.style]);
|
|
7308
|
-
|
|
7710
|
+
useLayoutEffect3(() => {
|
|
7309
7711
|
committedHost.current = host;
|
|
7310
7712
|
lifecycle.bindInteractionHost(host);
|
|
7311
7713
|
return () => {
|
|
@@ -7313,11 +7715,11 @@ function NativeScene({
|
|
|
7313
7715
|
lifecycle.unbindInteractionHost(host);
|
|
7314
7716
|
};
|
|
7315
7717
|
}, [host, lifecycle]);
|
|
7316
|
-
const mapStyle =
|
|
7718
|
+
const mapStyle = useMemo3(
|
|
7317
7719
|
() => JSON.stringify(scene.style),
|
|
7318
7720
|
[scene.style]
|
|
7319
7721
|
);
|
|
7320
|
-
const [initialViewState] =
|
|
7722
|
+
const [initialViewState] = useState2(() => {
|
|
7321
7723
|
const center = [scene.initialView.center[0], scene.initialView.center[1]];
|
|
7322
7724
|
Object.freeze(center);
|
|
7323
7725
|
return Object.freeze({
|
|
@@ -7343,7 +7745,7 @@ function NativeScene({
|
|
|
7343
7745
|
};
|
|
7344
7746
|
const annotations = interactions?.annotations;
|
|
7345
7747
|
const byId = new globalThis.Map(annotations?.map((annotation) => [annotation.id, annotation]));
|
|
7346
|
-
return /* @__PURE__ */
|
|
7748
|
+
return /* @__PURE__ */ jsx3(
|
|
7347
7749
|
View2,
|
|
7348
7750
|
{
|
|
7349
7751
|
ref: bind2,
|
|
@@ -7381,10 +7783,11 @@ function NativeScene({
|
|
|
7381
7783
|
},
|
|
7382
7784
|
onDidFinishLoadingStyle: () => {
|
|
7383
7785
|
styleLoaded.current = true;
|
|
7786
|
+
setLoadedStyle(latestStyle.current);
|
|
7384
7787
|
attach();
|
|
7385
7788
|
},
|
|
7386
7789
|
children: [
|
|
7387
|
-
/* @__PURE__ */
|
|
7790
|
+
/* @__PURE__ */ jsx3(
|
|
7388
7791
|
NativeCamera,
|
|
7389
7792
|
{
|
|
7390
7793
|
ref: camera,
|
|
@@ -7394,9 +7797,9 @@ function NativeScene({
|
|
|
7394
7797
|
},
|
|
7395
7798
|
"tileflow-camera"
|
|
7396
7799
|
),
|
|
7397
|
-
/* @__PURE__ */
|
|
7800
|
+
/* @__PURE__ */ jsx3(Fragment, { children: interactions?.plan.order.map((id) => {
|
|
7398
7801
|
const annotation = byId.get(id);
|
|
7399
|
-
return annotation ? /* @__PURE__ */
|
|
7802
|
+
return annotation ? /* @__PURE__ */ jsx3(
|
|
7400
7803
|
NativeAnnotationMarker,
|
|
7401
7804
|
{
|
|
7402
7805
|
annotation,
|
|
@@ -7408,6 +7811,15 @@ function NativeScene({
|
|
|
7408
7811
|
annotation.id
|
|
7409
7812
|
) : null;
|
|
7410
7813
|
}) }, "tileflow-annotations"),
|
|
7814
|
+
boundaryFeatureState ? /* @__PURE__ */ jsx3(
|
|
7815
|
+
NativeBoundaryFeatureState,
|
|
7816
|
+
{
|
|
7817
|
+
input: boundaryFeatureState,
|
|
7818
|
+
style: scene.style,
|
|
7819
|
+
loaded: loadedStyle === scene.style
|
|
7820
|
+
},
|
|
7821
|
+
"tileflow-boundary-feature-state"
|
|
7822
|
+
) : null,
|
|
7411
7823
|
children
|
|
7412
7824
|
]
|
|
7413
7825
|
}
|
|
@@ -7416,7 +7828,7 @@ function NativeScene({
|
|
|
7416
7828
|
);
|
|
7417
7829
|
}
|
|
7418
7830
|
function Map2(props) {
|
|
7419
|
-
const [lifecycle] =
|
|
7831
|
+
const [lifecycle] = useState2(
|
|
7420
7832
|
() => createMapLifecycle(createNativeMapOwner, retireNativeMapOwner)
|
|
7421
7833
|
);
|
|
7422
7834
|
const snapshot = useSyncExternalStore(
|
|
@@ -7424,10 +7836,12 @@ function Map2(props) {
|
|
|
7424
7836
|
lifecycle.getSnapshot,
|
|
7425
7837
|
lifecycle.getSnapshot
|
|
7426
7838
|
);
|
|
7427
|
-
useImperativeHandle(
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7839
|
+
useImperativeHandle(
|
|
7840
|
+
props.ref,
|
|
7841
|
+
() => Object.freeze({ getSourceState: lifecycle.getSourceState, retry: lifecycle.retry }),
|
|
7842
|
+
[lifecycle]
|
|
7843
|
+
);
|
|
7844
|
+
useLayoutEffect3(() => {
|
|
7431
7845
|
const unmount = lifecycle.mount();
|
|
7432
7846
|
if (AppState.currentState !== "active") lifecycle.background();
|
|
7433
7847
|
const subscription = AppState.addEventListener("change", (state) => {
|
|
@@ -7439,10 +7853,10 @@ function Map2(props) {
|
|
|
7439
7853
|
unmount();
|
|
7440
7854
|
};
|
|
7441
7855
|
}, [lifecycle]);
|
|
7442
|
-
|
|
7856
|
+
useLayoutEffect3(() => {
|
|
7443
7857
|
lifecycle.update(props);
|
|
7444
7858
|
});
|
|
7445
|
-
return /* @__PURE__ */
|
|
7859
|
+
return /* @__PURE__ */ jsx3(View2, { style: props.style, testID: props.testID, children: snapshot.renderer ? /* @__PURE__ */ jsx3(
|
|
7446
7860
|
NativeScene,
|
|
7447
7861
|
{
|
|
7448
7862
|
scene: snapshot.renderer,
|
|
@@ -7451,6 +7865,7 @@ function Map2(props) {
|
|
|
7451
7865
|
interactions: snapshot.interactions,
|
|
7452
7866
|
interactionEnabled: snapshot.interactionEnabled === true,
|
|
7453
7867
|
renderMarker: props.renderMarker,
|
|
7868
|
+
boundaryFeatureState: props.boundaryFeatureState,
|
|
7454
7869
|
children: props.children
|
|
7455
7870
|
},
|
|
7456
7871
|
snapshot.renderer.key
|