@tileflow/react-native 0.1.0-alpha.5 → 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 +66 -8
- 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-Bp-Im7tj.d.ts → contract-BaK24QZD.d.ts} +40 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +464 -48
- 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();
|
|
@@ -5466,6 +5778,7 @@ function projectMapSourceState(state) {
|
|
|
5466
5778
|
// src/theme-motion-input.ts
|
|
5467
5779
|
import { validateTileflowThemeBlendSelection } from "@tileflow/core/native";
|
|
5468
5780
|
var maximumTransitionMs = 5e3;
|
|
5781
|
+
var defaultTransitionMs = 450;
|
|
5469
5782
|
function own(value, keys) {
|
|
5470
5783
|
const prototype = Object.getPrototypeOf(value);
|
|
5471
5784
|
if (Array.isArray(value) || prototype !== Object.prototype && prototype !== null)
|
|
@@ -5480,9 +5793,9 @@ function own(value, keys) {
|
|
|
5480
5793
|
return result;
|
|
5481
5794
|
}
|
|
5482
5795
|
function snapshotThemeTransition(value) {
|
|
5483
|
-
if (value === void 0) return
|
|
5796
|
+
if (value === void 0) return defaultTransitionMs;
|
|
5484
5797
|
if (!value || typeof value !== "object") throw new NativePreparationError();
|
|
5485
|
-
const { duration =
|
|
5798
|
+
const { duration = defaultTransitionMs } = own(value, ["duration"]);
|
|
5486
5799
|
if (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0 || duration > maximumTransitionMs)
|
|
5487
5800
|
throw new NativePreparationError();
|
|
5488
5801
|
return Math.round(duration);
|
|
@@ -5501,6 +5814,18 @@ function themeBlendKey(blend) {
|
|
|
5501
5814
|
}
|
|
5502
5815
|
|
|
5503
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
|
+
]);
|
|
5504
5829
|
function themeSource(source, name3) {
|
|
5505
5830
|
const theme = Object.hasOwn(source.map.themes, name3) ? source.map.themes[name3] : void 0;
|
|
5506
5831
|
if (!theme) throw new NativePreparationError();
|
|
@@ -5583,7 +5908,9 @@ function createMountedMapOwner(ports2) {
|
|
|
5583
5908
|
let sourceState;
|
|
5584
5909
|
let current;
|
|
5585
5910
|
let lastReadiness = "";
|
|
5911
|
+
let reopenings = 0;
|
|
5586
5912
|
let disposal;
|
|
5913
|
+
const schedule = ports2.schedule ?? timer2;
|
|
5587
5914
|
const retired = /* @__PURE__ */ new Set();
|
|
5588
5915
|
const tasks = /* @__PURE__ */ new Set();
|
|
5589
5916
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -5618,10 +5945,11 @@ function createMountedMapOwner(ports2) {
|
|
|
5618
5945
|
if (lastReadiness === identity) return;
|
|
5619
5946
|
lastReadiness = identity;
|
|
5620
5947
|
}
|
|
5948
|
+
if (event.type === "load") reopenings = 0;
|
|
5621
5949
|
try {
|
|
5622
5950
|
let result;
|
|
5623
5951
|
if (event.type === "load") result = props.onLoad?.(event);
|
|
5624
|
-
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")
|
|
5625
5953
|
result = props.onError?.(event);
|
|
5626
5954
|
else if (event.type === "readiness-change") result = props.onReadinessChange?.(event);
|
|
5627
5955
|
else result = props.onThemeChange?.(event);
|
|
@@ -5698,11 +6026,29 @@ function createMountedMapOwner(ports2) {
|
|
|
5698
6026
|
}
|
|
5699
6027
|
}
|
|
5700
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
|
+
}
|
|
5701
6046
|
function retire(epoch) {
|
|
5702
6047
|
if (epoch.retirement) return epoch.retirement;
|
|
5703
6048
|
epoch.live = false;
|
|
5704
6049
|
retired.add(epoch);
|
|
5705
6050
|
cancelJob(epoch);
|
|
6051
|
+
cancelRetry(epoch);
|
|
5706
6052
|
epoch.cache.clear();
|
|
5707
6053
|
epoch.binding.dispose();
|
|
5708
6054
|
const context2 = epoch.map?.retire();
|
|
@@ -5726,6 +6072,24 @@ function createMountedMapOwner(ports2) {
|
|
|
5726
6072
|
});
|
|
5727
6073
|
return track(attempt);
|
|
5728
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
|
+
}
|
|
5729
6093
|
function createEpoch(developmentOrigin) {
|
|
5730
6094
|
const epoch = {
|
|
5731
6095
|
live: true,
|
|
@@ -5753,7 +6117,17 @@ function createMountedMapOwner(ports2) {
|
|
|
5753
6117
|
if (!epoch.live || current !== epoch) throw new NativePreparationError();
|
|
5754
6118
|
const binding = await epoch.binding.replace(source);
|
|
5755
6119
|
if (!epoch.live || current !== epoch) throw new NativePreparationError();
|
|
5756
|
-
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
|
+
});
|
|
5757
6131
|
const map = await epoch.lease.ready;
|
|
5758
6132
|
epoch.map = map;
|
|
5759
6133
|
if (!epoch.live || current !== epoch || map.state.status !== "active") {
|
|
@@ -5762,7 +6136,9 @@ function createMountedMapOwner(ports2) {
|
|
|
5762
6136
|
}
|
|
5763
6137
|
return map;
|
|
5764
6138
|
});
|
|
5765
|
-
void epoch.context.catch(() =>
|
|
6139
|
+
void epoch.context.catch(() => {
|
|
6140
|
+
epoch.contextFailed = true;
|
|
6141
|
+
});
|
|
5766
6142
|
}
|
|
5767
6143
|
return epoch.context;
|
|
5768
6144
|
}
|
|
@@ -5904,6 +6280,7 @@ function createMountedMapOwner(ports2) {
|
|
|
5904
6280
|
}
|
|
5905
6281
|
rendererError();
|
|
5906
6282
|
epoch.renderer?.preparationFailed(source.generation);
|
|
6283
|
+
retryLater(epoch, source);
|
|
5907
6284
|
}).finally(() => {
|
|
5908
6285
|
if (epoch.job === job) epoch.job = void 0;
|
|
5909
6286
|
});
|
|
@@ -5961,6 +6338,7 @@ function createMountedMapOwner(ports2) {
|
|
|
5961
6338
|
foreground = false;
|
|
5962
6339
|
if (current) {
|
|
5963
6340
|
cancelJob(current);
|
|
6341
|
+
cancelRetry(current);
|
|
5964
6342
|
current.renderer?.background();
|
|
5965
6343
|
}
|
|
5966
6344
|
return;
|
|
@@ -6059,6 +6437,22 @@ function createMountedMapOwner(ports2) {
|
|
|
6059
6437
|
if (state?.status !== "ready") select();
|
|
6060
6438
|
},
|
|
6061
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
|
+
},
|
|
6062
6456
|
async whenIdle() {
|
|
6063
6457
|
while (tasks.size) await Promise.allSettled([...tasks]);
|
|
6064
6458
|
await current?.renderer?.whenIdle();
|
|
@@ -6096,7 +6490,9 @@ var nativeBootstrapLimits = Object.freeze({
|
|
|
6096
6490
|
queueDepth: 32,
|
|
6097
6491
|
requestBytes: 2048,
|
|
6098
6492
|
responseBytes: 65536,
|
|
6099
|
-
|
|
6493
|
+
retryAfterCharacters: 64,
|
|
6494
|
+
// Well inside the 30 s ticket and batch deadlines.
|
|
6495
|
+
timeoutMs: 1e4
|
|
6100
6496
|
});
|
|
6101
6497
|
|
|
6102
6498
|
// src/native-admission-wire.ts
|
|
@@ -6127,10 +6523,11 @@ function decodeBoundedBase64(value) {
|
|
|
6127
6523
|
return bytes;
|
|
6128
6524
|
}
|
|
6129
6525
|
function responseFromWire(reply, isAborted, release) {
|
|
6130
|
-
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))
|
|
6131
6527
|
throw invalid4();
|
|
6132
6528
|
const status = reply.status;
|
|
6133
6529
|
const cacheControl = reply.cacheControl;
|
|
6530
|
+
const retryAfter = reply.retryAfter || null;
|
|
6134
6531
|
let bytes = decodeBoundedBase64(reply.bodyBase64);
|
|
6135
6532
|
let readerTaken = false;
|
|
6136
6533
|
let done = false;
|
|
@@ -6144,7 +6541,9 @@ function responseFromWire(reply, isAborted, release) {
|
|
|
6144
6541
|
status,
|
|
6145
6542
|
headers: Object.freeze({
|
|
6146
6543
|
get(name3) {
|
|
6147
|
-
|
|
6544
|
+
const header = name3.toLowerCase();
|
|
6545
|
+
if (header === "cache-control") return cacheControl;
|
|
6546
|
+
return header === "retry-after" ? retryAfter : null;
|
|
6148
6547
|
}
|
|
6149
6548
|
}),
|
|
6150
6549
|
body: Object.freeze({
|
|
@@ -7271,7 +7670,7 @@ function createNativeMapOwner() {
|
|
|
7271
7670
|
}
|
|
7272
7671
|
|
|
7273
7672
|
// src/map.tsx
|
|
7274
|
-
import { jsx as
|
|
7673
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
7275
7674
|
var fill = Object.freeze({ flex: 1 });
|
|
7276
7675
|
function NativeScene({
|
|
7277
7676
|
scene,
|
|
@@ -7280,7 +7679,8 @@ function NativeScene({
|
|
|
7280
7679
|
children,
|
|
7281
7680
|
interactions,
|
|
7282
7681
|
interactionEnabled,
|
|
7283
|
-
renderMarker
|
|
7682
|
+
renderMarker,
|
|
7683
|
+
boundaryFeatureState
|
|
7284
7684
|
}) {
|
|
7285
7685
|
const view = useRef(null);
|
|
7286
7686
|
const camera = useRef(null);
|
|
@@ -7290,7 +7690,10 @@ function NativeScene({
|
|
|
7290
7690
|
const laidOut = useRef(false);
|
|
7291
7691
|
const styleLoaded = useRef(false);
|
|
7292
7692
|
const attachRequested = useRef(false);
|
|
7293
|
-
const
|
|
7693
|
+
const latestStyle = useRef(scene.style);
|
|
7694
|
+
latestStyle.current = scene.style;
|
|
7695
|
+
const [loadedStyle, setLoadedStyle] = useState2(void 0);
|
|
7696
|
+
const host = useMemo3(() => {
|
|
7294
7697
|
const value = Object.freeze({
|
|
7295
7698
|
key: scene.key,
|
|
7296
7699
|
style: scene.style,
|
|
@@ -7304,7 +7707,7 @@ function NativeScene({
|
|
|
7304
7707
|
});
|
|
7305
7708
|
return value;
|
|
7306
7709
|
}, [scene.key, scene.style]);
|
|
7307
|
-
|
|
7710
|
+
useLayoutEffect3(() => {
|
|
7308
7711
|
committedHost.current = host;
|
|
7309
7712
|
lifecycle.bindInteractionHost(host);
|
|
7310
7713
|
return () => {
|
|
@@ -7312,11 +7715,11 @@ function NativeScene({
|
|
|
7312
7715
|
lifecycle.unbindInteractionHost(host);
|
|
7313
7716
|
};
|
|
7314
7717
|
}, [host, lifecycle]);
|
|
7315
|
-
const mapStyle =
|
|
7718
|
+
const mapStyle = useMemo3(
|
|
7316
7719
|
() => JSON.stringify(scene.style),
|
|
7317
7720
|
[scene.style]
|
|
7318
7721
|
);
|
|
7319
|
-
const [initialViewState] =
|
|
7722
|
+
const [initialViewState] = useState2(() => {
|
|
7320
7723
|
const center = [scene.initialView.center[0], scene.initialView.center[1]];
|
|
7321
7724
|
Object.freeze(center);
|
|
7322
7725
|
return Object.freeze({
|
|
@@ -7342,7 +7745,7 @@ function NativeScene({
|
|
|
7342
7745
|
};
|
|
7343
7746
|
const annotations = interactions?.annotations;
|
|
7344
7747
|
const byId = new globalThis.Map(annotations?.map((annotation) => [annotation.id, annotation]));
|
|
7345
|
-
return /* @__PURE__ */
|
|
7748
|
+
return /* @__PURE__ */ jsx3(
|
|
7346
7749
|
View2,
|
|
7347
7750
|
{
|
|
7348
7751
|
ref: bind2,
|
|
@@ -7380,10 +7783,11 @@ function NativeScene({
|
|
|
7380
7783
|
},
|
|
7381
7784
|
onDidFinishLoadingStyle: () => {
|
|
7382
7785
|
styleLoaded.current = true;
|
|
7786
|
+
setLoadedStyle(latestStyle.current);
|
|
7383
7787
|
attach();
|
|
7384
7788
|
},
|
|
7385
7789
|
children: [
|
|
7386
|
-
/* @__PURE__ */
|
|
7790
|
+
/* @__PURE__ */ jsx3(
|
|
7387
7791
|
NativeCamera,
|
|
7388
7792
|
{
|
|
7389
7793
|
ref: camera,
|
|
@@ -7393,9 +7797,9 @@ function NativeScene({
|
|
|
7393
7797
|
},
|
|
7394
7798
|
"tileflow-camera"
|
|
7395
7799
|
),
|
|
7396
|
-
/* @__PURE__ */
|
|
7800
|
+
/* @__PURE__ */ jsx3(Fragment, { children: interactions?.plan.order.map((id) => {
|
|
7397
7801
|
const annotation = byId.get(id);
|
|
7398
|
-
return annotation ? /* @__PURE__ */
|
|
7802
|
+
return annotation ? /* @__PURE__ */ jsx3(
|
|
7399
7803
|
NativeAnnotationMarker,
|
|
7400
7804
|
{
|
|
7401
7805
|
annotation,
|
|
@@ -7407,6 +7811,15 @@ function NativeScene({
|
|
|
7407
7811
|
annotation.id
|
|
7408
7812
|
) : null;
|
|
7409
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,
|
|
7410
7823
|
children
|
|
7411
7824
|
]
|
|
7412
7825
|
}
|
|
@@ -7415,7 +7828,7 @@ function NativeScene({
|
|
|
7415
7828
|
);
|
|
7416
7829
|
}
|
|
7417
7830
|
function Map2(props) {
|
|
7418
|
-
const [lifecycle] =
|
|
7831
|
+
const [lifecycle] = useState2(
|
|
7419
7832
|
() => createMapLifecycle(createNativeMapOwner, retireNativeMapOwner)
|
|
7420
7833
|
);
|
|
7421
7834
|
const snapshot = useSyncExternalStore(
|
|
@@ -7423,10 +7836,12 @@ function Map2(props) {
|
|
|
7423
7836
|
lifecycle.getSnapshot,
|
|
7424
7837
|
lifecycle.getSnapshot
|
|
7425
7838
|
);
|
|
7426
|
-
useImperativeHandle(
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7839
|
+
useImperativeHandle(
|
|
7840
|
+
props.ref,
|
|
7841
|
+
() => Object.freeze({ getSourceState: lifecycle.getSourceState, retry: lifecycle.retry }),
|
|
7842
|
+
[lifecycle]
|
|
7843
|
+
);
|
|
7844
|
+
useLayoutEffect3(() => {
|
|
7430
7845
|
const unmount = lifecycle.mount();
|
|
7431
7846
|
if (AppState.currentState !== "active") lifecycle.background();
|
|
7432
7847
|
const subscription = AppState.addEventListener("change", (state) => {
|
|
@@ -7438,10 +7853,10 @@ function Map2(props) {
|
|
|
7438
7853
|
unmount();
|
|
7439
7854
|
};
|
|
7440
7855
|
}, [lifecycle]);
|
|
7441
|
-
|
|
7856
|
+
useLayoutEffect3(() => {
|
|
7442
7857
|
lifecycle.update(props);
|
|
7443
7858
|
});
|
|
7444
|
-
return /* @__PURE__ */
|
|
7859
|
+
return /* @__PURE__ */ jsx3(View2, { style: props.style, testID: props.testID, children: snapshot.renderer ? /* @__PURE__ */ jsx3(
|
|
7445
7860
|
NativeScene,
|
|
7446
7861
|
{
|
|
7447
7862
|
scene: snapshot.renderer,
|
|
@@ -7450,6 +7865,7 @@ function Map2(props) {
|
|
|
7450
7865
|
interactions: snapshot.interactions,
|
|
7451
7866
|
interactionEnabled: snapshot.interactionEnabled === true,
|
|
7452
7867
|
renderMarker: props.renderMarker,
|
|
7868
|
+
boundaryFeatureState: props.boundaryFeatureState,
|
|
7453
7869
|
children: props.children
|
|
7454
7870
|
},
|
|
7455
7871
|
snapshot.renderer.key
|