@oddington/sdk 0.1.8 → 0.1.9
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 +21 -8
- package/dist/browser.js +227 -42
- package/dist/index.js +313 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,9 +54,10 @@ an OS-launched IPv4 loopback origin. It checks `/_os/app/context` on that exact
|
|
|
54
54
|
origin for every protected operation, without reading hosted access credentials.
|
|
55
55
|
It accepts no workspace, endpoint or token option. Missing, forged, expired or
|
|
56
56
|
changed local context fails closed, as do sign-out and initialization replacement.
|
|
57
|
-
Local Data
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
Local Data selects from the Host's exact published index; a warm selection
|
|
58
|
+
neither initiates nor waits for renewal or synchronous hosted authority checks.
|
|
59
|
+
Local Files returns `unsupported_context` after checking context. Neither path
|
|
60
|
+
falls back to hosted services. Local app routine
|
|
60
61
|
execution uses the installed routine's immediate-publication authority.
|
|
61
62
|
|
|
62
63
|
Apps can assess a declared routine without starting it:
|
|
@@ -104,14 +105,26 @@ Only event reads reconnect, with bounded backoff and duplicate suppression;
|
|
|
104
105
|
launch and cancellation requests are never automatically retried. Failed
|
|
105
106
|
cancellation requests do not acknowledge cancellation. Handles expose no IDs,
|
|
106
107
|
cursors, raw output or process controls. Local sign-out, expiry, context
|
|
107
|
-
replacement and
|
|
108
|
+
replacement, origin changes and unconfirmed credential changes invalidate pending work.
|
|
108
109
|
|
|
109
110
|
The exact-origin browser credential survives reload and tab closure in local
|
|
110
|
-
storage.
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
storage. Each active initialization proactively renews before expiry, normally
|
|
112
|
+
sixty seconds early with a shorter lead for shorter lifetimes. Only the exact
|
|
113
|
+
Local Host silently refreshes the secure CLI login and confirms unchanged
|
|
114
|
+
complete access. Successful renewal rotates only the bounded browser credential;
|
|
115
|
+
it exposes no CLI credential or login prompt. Other tabs and SDK instances
|
|
116
|
+
retain only their unchanged deadline during a temporary renewal outage and
|
|
117
|
+
retry proactively without starting or delaying Data reads. They
|
|
118
|
+
confirm the stored winner against the same Host and logical session before
|
|
119
|
+
silently adopting it. Rotation preserves admitted Data/results and owned runs;
|
|
120
|
+
old credentials cannot admit new requests and failed writes, launches and
|
|
121
|
+
cancellations are never replayed. Sign-out, replacement and expiry cancel
|
|
122
|
+
renewal, including pending response bodies. Host expiry and current membership
|
|
123
|
+
remain authoritative; stored state cannot revive a stopped host. Recovery
|
|
124
|
+
covers only the operation's current logical session and bounded terminal grace,
|
|
125
|
+
without relaunching or durable history.
|
|
113
126
|
Supplied File inputs currently reject `execution_unavailable` before launch;
|
|
114
|
-
File-use transfer and local
|
|
127
|
+
File-use transfer and a local browser Files adapter are separate work.
|
|
115
128
|
|
|
116
129
|
The fixed child imports `appRun` from the same package and shares the app's
|
|
117
130
|
customer-owned input/result/step types:
|
package/dist/browser.js
CHANGED
|
@@ -1500,7 +1500,9 @@ var files = {
|
|
|
1500
1500
|
read
|
|
1501
1501
|
};
|
|
1502
1502
|
|
|
1503
|
-
var sessionKey = "_os.app.session", AppResponseReadError = class extends Error {
|
|
1503
|
+
var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap(), AppResponseReadError = class extends Error {
|
|
1504
|
+
}, AppContextDenied = class extends Error {
|
|
1505
|
+
}, AppRenewalUnavailable = class extends Error {
|
|
1504
1506
|
};
|
|
1505
1507
|
async function appResponse(response, maximum, signal) {
|
|
1506
1508
|
if (signal.aborted || !response.body || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json") throw new Error();
|
|
@@ -1519,7 +1521,11 @@ async function appResponse(response, maximum, signal) {
|
|
|
1519
1521
|
throw new AppResponseReadError();
|
|
1520
1522
|
}
|
|
1521
1523
|
if (signal.aborted) throw new Error();
|
|
1522
|
-
if (chunk.done)
|
|
1524
|
+
if (chunk.done) {
|
|
1525
|
+
let value = JSON.parse(text3 + decoder.decode());
|
|
1526
|
+
if (await responseChecks.get(response)?.(value), signal.aborted) throw new Error();
|
|
1527
|
+
return value;
|
|
1528
|
+
}
|
|
1523
1529
|
if (bytes += chunk.value.byteLength, bytes > maximum)
|
|
1524
1530
|
throw abort(), new Error();
|
|
1525
1531
|
text3 += decoder.decode(chunk.value, { stream: !0 });
|
|
@@ -1537,27 +1543,180 @@ function isAppOrigin(value) {
|
|
|
1537
1543
|
}
|
|
1538
1544
|
}
|
|
1539
1545
|
function appContext(origin, active2) {
|
|
1540
|
-
let binding, credential, denied = !1, controller = new AbortController(), expiry,
|
|
1541
|
-
|
|
1546
|
+
let binding, credential, denied = !1, controller = new AbortController(), expiry, startup, renewalTimer, renewal, observation, resolution2, disposed = !1, stored = () => globalThis.localStorage.getItem(sessionKey), alive = () => {
|
|
1547
|
+
try {
|
|
1548
|
+
return !denied && !disposed && active2() && globalThis.location.origin === origin && (!binding || binding.expiresAt > Date.now());
|
|
1549
|
+
} catch {
|
|
1550
|
+
return !1;
|
|
1551
|
+
}
|
|
1552
|
+
}, cancel = () => {
|
|
1553
|
+
controller.abort(), clearTimeout(expiry), clearTimeout(startup), clearTimeout(renewalTimer), clearInterval(monitor), globalThis.removeEventListener?.("storage", storageChanged);
|
|
1554
|
+
}, invalidate = () => {
|
|
1555
|
+
denied = !0, cancel();
|
|
1542
1556
|
try {
|
|
1543
|
-
active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential) && globalThis.localStorage.removeItem(sessionKey);
|
|
1557
|
+
!disposed && active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential) && globalThis.localStorage.removeItem(sessionKey);
|
|
1544
1558
|
} catch {
|
|
1545
1559
|
}
|
|
1546
1560
|
}, current2 = () => {
|
|
1547
1561
|
try {
|
|
1548
|
-
return
|
|
1562
|
+
return alive() && stored() === credential;
|
|
1549
1563
|
} catch {
|
|
1550
1564
|
return !1;
|
|
1551
1565
|
}
|
|
1566
|
+
}, sameSession = (value) => !binding || value.appId === binding.appId && value.memberId === binding.memberId && value.workspace === binding.workspace && value.sessionId === binding.sessionId, bounded = (value) => validBinding(value) && value.expiresAt > Date.now() && value.expiresAt <= Date.now() + 6e5 && sameSession(value);
|
|
1567
|
+
async function proof(candidate, signal = controller.signal) {
|
|
1568
|
+
if (!alive() || !/^[A-Za-z0-9_-]{43}$/.test(candidate)) throw new Error();
|
|
1569
|
+
let url = `${origin}/_os/app/context`, response = await fetch(url, {
|
|
1570
|
+
credentials: "omit",
|
|
1571
|
+
redirect: "error",
|
|
1572
|
+
cache: "no-store",
|
|
1573
|
+
referrerPolicy: "no-referrer",
|
|
1574
|
+
headers: { authorization: `Bearer ${candidate}` },
|
|
1575
|
+
signal
|
|
1576
|
+
});
|
|
1577
|
+
if (response.redirected || response.url && response.url !== url) throw new Error();
|
|
1578
|
+
if (response.status === 401 || response.status === 403) throw new AppContextDenied();
|
|
1579
|
+
if (!response.ok) throw new Error();
|
|
1580
|
+
let value = await appResponse(response, 4096, signal);
|
|
1581
|
+
if (!alive() || !bounded(value)) throw new Error();
|
|
1582
|
+
return value;
|
|
1583
|
+
}
|
|
1584
|
+
function install(value, candidate) {
|
|
1585
|
+
binding = { ...value }, credential = candidate, clearTimeout(expiry), clearTimeout(renewalTimer);
|
|
1586
|
+
let remaining = binding.expiresAt - Date.now();
|
|
1587
|
+
expiry = setTimeout(invalidate, remaining), renewalTimer = setTimeout(() => {
|
|
1588
|
+
renew();
|
|
1589
|
+
}, remaining - Math.min(6e4, remaining / 5));
|
|
1590
|
+
}
|
|
1591
|
+
async function synchronize() {
|
|
1592
|
+
if (!alive()) throw new Error();
|
|
1593
|
+
if (credential && stored() !== credential) {
|
|
1594
|
+
if (!binding) throw new Error();
|
|
1595
|
+
if (observation) return observation;
|
|
1596
|
+
observation = (async () => {
|
|
1597
|
+
for (; alive(); ) {
|
|
1598
|
+
let candidate = stored();
|
|
1599
|
+
if (!candidate) throw new Error();
|
|
1600
|
+
let value;
|
|
1601
|
+
try {
|
|
1602
|
+
value = await proof(candidate);
|
|
1603
|
+
} catch {
|
|
1604
|
+
if (stored() !== candidate) continue;
|
|
1605
|
+
throw new Error();
|
|
1606
|
+
}
|
|
1607
|
+
if (stored() === candidate) {
|
|
1608
|
+
install(value, candidate);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
throw new Error();
|
|
1613
|
+
})();
|
|
1614
|
+
try {
|
|
1615
|
+
await observation;
|
|
1616
|
+
} catch {
|
|
1617
|
+
throw invalidate(), new Error("Local app context unavailable.");
|
|
1618
|
+
} finally {
|
|
1619
|
+
observation = void 0;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
async function renew() {
|
|
1624
|
+
if (!(renewal || !binding || !alive())) {
|
|
1625
|
+
renewal = (async () => {
|
|
1626
|
+
let deadline = binding.expiresAt, requestController = new AbortController(), abort = () => requestController.abort();
|
|
1627
|
+
controller.signal.addEventListener("abort", abort, { once: !0 });
|
|
1628
|
+
let timeout = setTimeout(abort, Math.min(2e4, deadline - Date.now())), previous = credential;
|
|
1629
|
+
try {
|
|
1630
|
+
if (await synchronize(), credential !== previous) return;
|
|
1631
|
+
let url = `${origin}/_os/app/renew`, response;
|
|
1632
|
+
try {
|
|
1633
|
+
response = await fetch(url, {
|
|
1634
|
+
method: "POST",
|
|
1635
|
+
credentials: "omit",
|
|
1636
|
+
redirect: "error",
|
|
1637
|
+
cache: "no-store",
|
|
1638
|
+
referrerPolicy: "no-referrer",
|
|
1639
|
+
signal: requestController.signal,
|
|
1640
|
+
headers: { authorization: `Bearer ${previous}` }
|
|
1641
|
+
});
|
|
1642
|
+
} catch {
|
|
1643
|
+
throw new AppRenewalUnavailable();
|
|
1644
|
+
}
|
|
1645
|
+
if (response.redirected || response.url && response.url !== url) throw new Error();
|
|
1646
|
+
let value = await appResponse(response, 4096, requestController.signal);
|
|
1647
|
+
if (response.status === 503 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_unavailable")
|
|
1648
|
+
throw new AppRenewalUnavailable();
|
|
1649
|
+
if (response.status === 403 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_conflict") {
|
|
1650
|
+
for (; alive() && stored() === previous && !requestController.signal.aborted; )
|
|
1651
|
+
await new Promise((resolve2, reject) => {
|
|
1652
|
+
let abortWait = () => {
|
|
1653
|
+
clearTimeout(timer), reject(new Error());
|
|
1654
|
+
}, timer = setTimeout(() => {
|
|
1655
|
+
requestController.signal.removeEventListener("abort", abortWait), resolve2();
|
|
1656
|
+
}, 100);
|
|
1657
|
+
requestController.signal.addEventListener("abort", abortWait, { once: !0 });
|
|
1658
|
+
});
|
|
1659
|
+
if (requestController.signal.aborted) throw new Error();
|
|
1660
|
+
await synchronize();
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (!response.ok) throw new Error();
|
|
1664
|
+
if (!alive() || Date.now() >= deadline || !value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential") throw new Error();
|
|
1665
|
+
let result2 = value;
|
|
1666
|
+
if (!bounded(result2.context) || typeof result2.credential != "string" || !/^[A-Za-z0-9_-]{43}$/.test(result2.credential) || result2.credential === previous) throw new Error();
|
|
1667
|
+
if (stored() !== previous) {
|
|
1668
|
+
await synchronize();
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
globalThis.localStorage.setItem(sessionKey, result2.credential), install(result2.context, result2.credential);
|
|
1672
|
+
} catch (error) {
|
|
1673
|
+
try {
|
|
1674
|
+
if (alive() && stored() && stored() !== previous) {
|
|
1675
|
+
await synchronize();
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
} catch {
|
|
1679
|
+
}
|
|
1680
|
+
try {
|
|
1681
|
+
if (alive() && stored() && stored() !== previous) {
|
|
1682
|
+
await synchronize();
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
} catch {
|
|
1686
|
+
}
|
|
1687
|
+
(error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && current2() && Date.now() < deadline ? renewalTimer = setTimeout(() => {
|
|
1688
|
+
renew();
|
|
1689
|
+
}, Math.min(5e3, (deadline - Date.now()) / 5)) : invalidate();
|
|
1690
|
+
} finally {
|
|
1691
|
+
clearTimeout(timeout), controller.signal.removeEventListener("abort", abort);
|
|
1692
|
+
}
|
|
1693
|
+
})();
|
|
1694
|
+
try {
|
|
1695
|
+
await renewal;
|
|
1696
|
+
} finally {
|
|
1697
|
+
renewal = void 0;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
let storageChanged = () => {
|
|
1702
|
+
if (credential) {
|
|
1703
|
+
if (!alive()) {
|
|
1704
|
+
invalidate();
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
synchronize().catch(invalidate);
|
|
1708
|
+
}
|
|
1552
1709
|
}, monitor = setInterval(() => {
|
|
1553
|
-
|
|
1554
|
-
}, 100)
|
|
1555
|
-
|
|
1710
|
+
storageChanged();
|
|
1711
|
+
}, 100);
|
|
1712
|
+
globalThis.addEventListener?.("storage", storageChanged);
|
|
1713
|
+
let dispose = () => {
|
|
1714
|
+
disposed = !0, cancel();
|
|
1556
1715
|
};
|
|
1557
1716
|
async function requestData(operation2, input) {
|
|
1558
1717
|
try {
|
|
1559
1718
|
if (operation2 !== "data/query" || !current2()) throw new Error();
|
|
1560
|
-
let url = `${origin}/_os/app/data`, response = await fetch(url, {
|
|
1719
|
+
let url = `${origin}/_os/app/data`, admitted = credential, response = await fetch(url, {
|
|
1561
1720
|
method: "POST",
|
|
1562
1721
|
credentials: "omit",
|
|
1563
1722
|
redirect: "error",
|
|
@@ -1567,10 +1726,12 @@ function appContext(origin, active2) {
|
|
|
1567
1726
|
headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
|
|
1568
1727
|
body: JSON.stringify(input)
|
|
1569
1728
|
});
|
|
1729
|
+
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
1570
1730
|
if (!current2() || response.redirected || response.url && response.url !== url) throw new Error();
|
|
1571
1731
|
if (response.status === 401 || response.status === 403)
|
|
1572
|
-
throw invalidate(), new DataOperationError("access_denied");
|
|
1732
|
+
throw admitted === credential && invalidate(), new DataOperationError("access_denied");
|
|
1573
1733
|
let result2 = await appResponse(response, Number.MAX_SAFE_INTEGER, controller.signal);
|
|
1734
|
+
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
1574
1735
|
if (!current2()) throw new Error();
|
|
1575
1736
|
if (!response.ok) throw dataTransportError(response.status, result2);
|
|
1576
1737
|
return result2;
|
|
@@ -1581,7 +1742,7 @@ function appContext(origin, active2) {
|
|
|
1581
1742
|
async function requestRoutine(key, suffix, init2 = {}) {
|
|
1582
1743
|
if (!current2())
|
|
1583
1744
|
throw invalidate(), new Error("Local app context unavailable.");
|
|
1584
|
-
let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, response = await fetch(url, {
|
|
1745
|
+
let url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`, admitted = credential, response = await fetch(url, {
|
|
1585
1746
|
...init2,
|
|
1586
1747
|
credentials: "omit",
|
|
1587
1748
|
redirect: "error",
|
|
@@ -1590,45 +1751,69 @@ function appContext(origin, active2) {
|
|
|
1590
1751
|
signal: controller.signal,
|
|
1591
1752
|
headers: { ...init2.body === void 0 ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
|
|
1592
1753
|
});
|
|
1754
|
+
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
1593
1755
|
if (!current2() || response.redirected || response.url && response.url !== url)
|
|
1594
1756
|
throw invalidate(), new Error("Local app context unavailable.");
|
|
1595
|
-
return (response.status === 401 || response.status === 403) && invalidate(), response
|
|
1757
|
+
return (response.status === 401 || response.status === 403) && admitted === credential && invalidate(), responseChecks.set(response, async (value) => {
|
|
1758
|
+
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
1759
|
+
admitted === credential && value && typeof value == "object" && "error" in value && value.error === "authority_unavailable" && invalidate();
|
|
1760
|
+
}), response;
|
|
1761
|
+
}
|
|
1762
|
+
function context2() {
|
|
1763
|
+
let issued = credential;
|
|
1764
|
+
return {
|
|
1765
|
+
kind: "local",
|
|
1766
|
+
...binding,
|
|
1767
|
+
get expiresAt() {
|
|
1768
|
+
return binding.expiresAt;
|
|
1769
|
+
},
|
|
1770
|
+
isCurrent: current2,
|
|
1771
|
+
signal: controller.signal,
|
|
1772
|
+
invalidate: () => {
|
|
1773
|
+
issued === credential && invalidate();
|
|
1774
|
+
},
|
|
1775
|
+
requestRoutine,
|
|
1776
|
+
requestData,
|
|
1777
|
+
requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
|
|
1778
|
+
};
|
|
1596
1779
|
}
|
|
1597
|
-
|
|
1780
|
+
let resolveOnce = async () => {
|
|
1781
|
+
let attempted;
|
|
1598
1782
|
try {
|
|
1599
1783
|
if (denied || !active2() || globalThis.location.origin !== origin) throw new Error();
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
if (
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
invalidate,
|
|
1619
|
-
requestRoutine,
|
|
1620
|
-
requestData,
|
|
1621
|
-
requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
|
|
1622
|
-
};
|
|
1623
|
-
} catch {
|
|
1624
|
-
throw invalidate(), new Error("Local app context unavailable.");
|
|
1784
|
+
credential && await synchronize();
|
|
1785
|
+
let candidate = stored();
|
|
1786
|
+
if (!candidate || !/^[A-Za-z0-9_-]{43}$/.test(candidate)) throw new Error();
|
|
1787
|
+
if (credential = candidate, attempted = candidate, !current2()) throw new Error();
|
|
1788
|
+
let value = await proof(candidate);
|
|
1789
|
+
if (stored() !== candidate || credential !== candidate)
|
|
1790
|
+
return binding || (binding = { ...value }), await synchronize(), context2();
|
|
1791
|
+
if (!current2()) throw new Error();
|
|
1792
|
+
if (binding && value.expiresAt > binding.expiresAt) throw new Error();
|
|
1793
|
+
return (!binding || value.expiresAt !== binding.expiresAt) && install(value, candidate), context2();
|
|
1794
|
+
} catch (error) {
|
|
1795
|
+
try {
|
|
1796
|
+
if (binding && current2() && attempted && attempted !== credential) return context2();
|
|
1797
|
+
if (binding && alive() && stored() && stored() !== credential)
|
|
1798
|
+
return await synchronize(), context2();
|
|
1799
|
+
} catch {
|
|
1800
|
+
}
|
|
1801
|
+
throw error instanceof AppContextDenied && binding && alive() && renewal ? new Error("Local app context unavailable.") : (invalidate(), new Error("Local app context unavailable."));
|
|
1625
1802
|
}
|
|
1803
|
+
}, resolve = () => resolution2 || (resolution2 = resolveOnce().finally(() => {
|
|
1804
|
+
resolution2 = void 0;
|
|
1805
|
+
}), resolution2);
|
|
1806
|
+
return { resolve, start: () => {
|
|
1807
|
+
startup = setTimeout(() => {
|
|
1808
|
+
alive() && !resolution2 && !binding && resolve().catch(() => {
|
|
1809
|
+
});
|
|
1810
|
+
}, 0);
|
|
1626
1811
|
}, invalidate, dispose };
|
|
1627
1812
|
}
|
|
1628
1813
|
function validBinding(value) {
|
|
1629
|
-
if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,workspace") return !1;
|
|
1814
|
+
if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,sessionId,workspace") return !1;
|
|
1630
1815
|
let binding = value;
|
|
1631
|
-
return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
|
|
1816
|
+
return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.sessionId == "string" && /^[A-Za-z0-9_-]{43}$/.test(binding.sessionId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
|
|
1632
1817
|
}
|
|
1633
1818
|
|
|
1634
1819
|
var installedDispose;
|
|
@@ -1664,7 +1849,7 @@ function init({ auth }) {
|
|
|
1664
1849
|
}), dispose = () => {
|
|
1665
1850
|
active2 && (active2 = !1, local?.dispose(), revision++, subscription.unsubscribe(), uninstall(), installedDispose === dispose && (installedDispose = void 0));
|
|
1666
1851
|
};
|
|
1667
|
-
installedDispose = dispose;
|
|
1852
|
+
installedDispose = dispose, local?.start();
|
|
1668
1853
|
}
|
|
1669
1854
|
var os = { init };
|
|
1670
1855
|
|
package/dist/index.js
CHANGED
|
@@ -22030,7 +22030,7 @@ function privateRoutineRequest(environment = typeof __ODDINGTON_BROWSER__ !== "u
|
|
|
22030
22030
|
const tcp = /^tcp:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.exec(socket);
|
|
22031
22031
|
const endpoint = tcp ? { hostname: "127.0.0.1", port: Number(tcp[1]) } : { socketPath: socket };
|
|
22032
22032
|
const id2 = crypto.randomUUID();
|
|
22033
|
-
if (init.body instanceof ReadableStream) {
|
|
22033
|
+
if (init.body instanceof ReadableStream || suffix === "" && init.method === "POST") {
|
|
22034
22034
|
await new Promise((resolve, reject) => {
|
|
22035
22035
|
const admission = httpRequest({
|
|
22036
22036
|
...endpoint,
|
|
@@ -23114,9 +23114,16 @@ var files = {
|
|
|
23114
23114
|
};
|
|
23115
23115
|
// src/app-context.ts
|
|
23116
23116
|
var sessionKey = "_os.app.session";
|
|
23117
|
+
var responseChecks = new WeakMap;
|
|
23117
23118
|
|
|
23118
23119
|
class AppResponseReadError extends Error {
|
|
23119
23120
|
}
|
|
23121
|
+
|
|
23122
|
+
class AppContextDenied extends Error {
|
|
23123
|
+
}
|
|
23124
|
+
|
|
23125
|
+
class AppRenewalUnavailable extends Error {
|
|
23126
|
+
}
|
|
23120
23127
|
async function appResponse(response, maximum, signal) {
|
|
23121
23128
|
if (signal.aborted || !response.body || response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json")
|
|
23122
23129
|
throw new Error;
|
|
@@ -23137,8 +23144,13 @@ async function appResponse(response, maximum, signal) {
|
|
|
23137
23144
|
}
|
|
23138
23145
|
if (signal.aborted)
|
|
23139
23146
|
throw new Error;
|
|
23140
|
-
if (chunk.done)
|
|
23141
|
-
|
|
23147
|
+
if (chunk.done) {
|
|
23148
|
+
const value = JSON.parse(text3 + decoder.decode());
|
|
23149
|
+
await responseChecks.get(response)?.(value);
|
|
23150
|
+
if (signal.aborted)
|
|
23151
|
+
throw new Error;
|
|
23152
|
+
return value;
|
|
23153
|
+
}
|
|
23142
23154
|
bytes += chunk.value.byteLength;
|
|
23143
23155
|
if (bytes > maximum) {
|
|
23144
23156
|
abort();
|
|
@@ -23165,38 +23177,239 @@ function appContext(origin, active2) {
|
|
|
23165
23177
|
let denied = false;
|
|
23166
23178
|
const controller = new AbortController;
|
|
23167
23179
|
let expiry;
|
|
23168
|
-
|
|
23169
|
-
|
|
23180
|
+
let startup;
|
|
23181
|
+
let renewalTimer;
|
|
23182
|
+
let renewal;
|
|
23183
|
+
let observation;
|
|
23184
|
+
let resolution2;
|
|
23185
|
+
let disposed = false;
|
|
23186
|
+
const stored = () => globalThis.localStorage.getItem(sessionKey);
|
|
23187
|
+
const alive = () => {
|
|
23188
|
+
try {
|
|
23189
|
+
return !denied && !disposed && active2() && globalThis.location.origin === origin && (!binding || binding.expiresAt > Date.now());
|
|
23190
|
+
} catch {
|
|
23191
|
+
return false;
|
|
23192
|
+
}
|
|
23193
|
+
};
|
|
23194
|
+
const cancel = () => {
|
|
23170
23195
|
controller.abort();
|
|
23171
23196
|
clearTimeout(expiry);
|
|
23197
|
+
clearTimeout(startup);
|
|
23198
|
+
clearTimeout(renewalTimer);
|
|
23172
23199
|
clearInterval(monitor);
|
|
23200
|
+
globalThis.removeEventListener?.("storage", storageChanged);
|
|
23201
|
+
};
|
|
23202
|
+
const invalidate = () => {
|
|
23203
|
+
denied = true;
|
|
23204
|
+
cancel();
|
|
23173
23205
|
try {
|
|
23174
|
-
if (active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential)) {
|
|
23206
|
+
if (!disposed && active2() && globalThis.location.origin === origin && (!credential || globalThis.localStorage.getItem(sessionKey) === credential)) {
|
|
23175
23207
|
globalThis.localStorage.removeItem(sessionKey);
|
|
23176
23208
|
}
|
|
23177
23209
|
} catch {}
|
|
23178
23210
|
};
|
|
23179
23211
|
const current = () => {
|
|
23180
23212
|
try {
|
|
23181
|
-
return
|
|
23213
|
+
return alive() && stored() === credential;
|
|
23182
23214
|
} catch {
|
|
23183
23215
|
return false;
|
|
23184
23216
|
}
|
|
23185
23217
|
};
|
|
23186
|
-
const
|
|
23187
|
-
|
|
23218
|
+
const sameSession = (value) => !binding || value.appId === binding.appId && value.memberId === binding.memberId && value.workspace === binding.workspace && value.sessionId === binding.sessionId;
|
|
23219
|
+
const bounded = (value) => validBinding(value) && value.expiresAt > Date.now() && value.expiresAt <= Date.now() + 600000 && sameSession(value);
|
|
23220
|
+
async function proof(candidate, signal = controller.signal) {
|
|
23221
|
+
if (!alive() || !/^[A-Za-z0-9_-]{43}$/.test(candidate))
|
|
23222
|
+
throw new Error;
|
|
23223
|
+
const url = `${origin}/_os/app/context`;
|
|
23224
|
+
const response = await fetch(url, {
|
|
23225
|
+
credentials: "omit",
|
|
23226
|
+
redirect: "error",
|
|
23227
|
+
cache: "no-store",
|
|
23228
|
+
referrerPolicy: "no-referrer",
|
|
23229
|
+
headers: { authorization: `Bearer ${candidate}` },
|
|
23230
|
+
signal
|
|
23231
|
+
});
|
|
23232
|
+
if (response.redirected || response.url && response.url !== url)
|
|
23233
|
+
throw new Error;
|
|
23234
|
+
if (response.status === 401 || response.status === 403)
|
|
23235
|
+
throw new AppContextDenied;
|
|
23236
|
+
if (!response.ok)
|
|
23237
|
+
throw new Error;
|
|
23238
|
+
const value = await appResponse(response, 4096, signal);
|
|
23239
|
+
if (!alive() || !bounded(value))
|
|
23240
|
+
throw new Error;
|
|
23241
|
+
return value;
|
|
23242
|
+
}
|
|
23243
|
+
function install(value, candidate) {
|
|
23244
|
+
binding = { ...value };
|
|
23245
|
+
credential = candidate;
|
|
23246
|
+
clearTimeout(expiry);
|
|
23247
|
+
clearTimeout(renewalTimer);
|
|
23248
|
+
const remaining = binding.expiresAt - Date.now();
|
|
23249
|
+
expiry = setTimeout(invalidate, remaining);
|
|
23250
|
+
renewalTimer = setTimeout(() => {
|
|
23251
|
+
renew();
|
|
23252
|
+
}, remaining - Math.min(60000, remaining / 5));
|
|
23253
|
+
}
|
|
23254
|
+
async function synchronize() {
|
|
23255
|
+
if (!alive())
|
|
23256
|
+
throw new Error;
|
|
23257
|
+
if (!credential)
|
|
23258
|
+
return;
|
|
23259
|
+
if (stored() === credential)
|
|
23260
|
+
return;
|
|
23261
|
+
if (!binding)
|
|
23262
|
+
throw new Error;
|
|
23263
|
+
if (observation)
|
|
23264
|
+
return observation;
|
|
23265
|
+
observation = (async () => {
|
|
23266
|
+
while (alive()) {
|
|
23267
|
+
const candidate = stored();
|
|
23268
|
+
if (!candidate)
|
|
23269
|
+
throw new Error;
|
|
23270
|
+
let value;
|
|
23271
|
+
try {
|
|
23272
|
+
value = await proof(candidate);
|
|
23273
|
+
} catch {
|
|
23274
|
+
if (stored() !== candidate)
|
|
23275
|
+
continue;
|
|
23276
|
+
throw new Error;
|
|
23277
|
+
}
|
|
23278
|
+
if (stored() !== candidate)
|
|
23279
|
+
continue;
|
|
23280
|
+
install(value, candidate);
|
|
23281
|
+
return;
|
|
23282
|
+
}
|
|
23283
|
+
throw new Error;
|
|
23284
|
+
})();
|
|
23285
|
+
try {
|
|
23286
|
+
await observation;
|
|
23287
|
+
} catch {
|
|
23288
|
+
invalidate();
|
|
23289
|
+
throw new Error("Local app context unavailable.");
|
|
23290
|
+
} finally {
|
|
23291
|
+
observation = undefined;
|
|
23292
|
+
}
|
|
23293
|
+
}
|
|
23294
|
+
async function renew() {
|
|
23295
|
+
if (renewal || !binding || !alive())
|
|
23296
|
+
return;
|
|
23297
|
+
renewal = (async () => {
|
|
23298
|
+
const deadline = binding.expiresAt;
|
|
23299
|
+
const requestController = new AbortController;
|
|
23300
|
+
const abort = () => requestController.abort();
|
|
23301
|
+
controller.signal.addEventListener("abort", abort, { once: true });
|
|
23302
|
+
const timeout = setTimeout(abort, Math.min(20000, deadline - Date.now()));
|
|
23303
|
+
const previous = credential;
|
|
23304
|
+
try {
|
|
23305
|
+
await synchronize();
|
|
23306
|
+
if (credential !== previous)
|
|
23307
|
+
return;
|
|
23308
|
+
const url = `${origin}/_os/app/renew`;
|
|
23309
|
+
let response;
|
|
23310
|
+
try {
|
|
23311
|
+
response = await fetch(url, {
|
|
23312
|
+
method: "POST",
|
|
23313
|
+
credentials: "omit",
|
|
23314
|
+
redirect: "error",
|
|
23315
|
+
cache: "no-store",
|
|
23316
|
+
referrerPolicy: "no-referrer",
|
|
23317
|
+
signal: requestController.signal,
|
|
23318
|
+
headers: { authorization: `Bearer ${previous}` }
|
|
23319
|
+
});
|
|
23320
|
+
} catch {
|
|
23321
|
+
throw new AppRenewalUnavailable;
|
|
23322
|
+
}
|
|
23323
|
+
if (response.redirected || response.url && response.url !== url)
|
|
23324
|
+
throw new Error;
|
|
23325
|
+
const value = await appResponse(response, 4096, requestController.signal);
|
|
23326
|
+
if (response.status === 503 && value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_unavailable")
|
|
23327
|
+
throw new AppRenewalUnavailable;
|
|
23328
|
+
if (response.status === 403 && value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_conflict") {
|
|
23329
|
+
while (alive() && stored() === previous && !requestController.signal.aborted) {
|
|
23330
|
+
await new Promise((resolve2, reject) => {
|
|
23331
|
+
const abortWait = () => {
|
|
23332
|
+
clearTimeout(timer);
|
|
23333
|
+
reject(new Error);
|
|
23334
|
+
};
|
|
23335
|
+
const timer = setTimeout(() => {
|
|
23336
|
+
requestController.signal.removeEventListener("abort", abortWait);
|
|
23337
|
+
resolve2();
|
|
23338
|
+
}, 100);
|
|
23339
|
+
requestController.signal.addEventListener("abort", abortWait, { once: true });
|
|
23340
|
+
});
|
|
23341
|
+
}
|
|
23342
|
+
if (requestController.signal.aborted)
|
|
23343
|
+
throw new Error;
|
|
23344
|
+
await synchronize();
|
|
23345
|
+
return;
|
|
23346
|
+
}
|
|
23347
|
+
if (!response.ok)
|
|
23348
|
+
throw new Error;
|
|
23349
|
+
if (!alive() || Date.now() >= deadline || !value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential")
|
|
23350
|
+
throw new Error;
|
|
23351
|
+
const result2 = value;
|
|
23352
|
+
if (!bounded(result2.context) || typeof result2.credential !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(result2.credential) || result2.credential === previous)
|
|
23353
|
+
throw new Error;
|
|
23354
|
+
if (stored() !== previous) {
|
|
23355
|
+
await synchronize();
|
|
23356
|
+
return;
|
|
23357
|
+
}
|
|
23358
|
+
globalThis.localStorage.setItem(sessionKey, result2.credential);
|
|
23359
|
+
install(result2.context, result2.credential);
|
|
23360
|
+
} catch (error) {
|
|
23361
|
+
try {
|
|
23362
|
+
if (alive() && stored() && stored() !== previous) {
|
|
23363
|
+
await synchronize();
|
|
23364
|
+
return;
|
|
23365
|
+
}
|
|
23366
|
+
} catch {}
|
|
23367
|
+
try {
|
|
23368
|
+
if (alive() && stored() && stored() !== previous) {
|
|
23369
|
+
await synchronize();
|
|
23370
|
+
return;
|
|
23371
|
+
}
|
|
23372
|
+
} catch {}
|
|
23373
|
+
if ((error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && current() && Date.now() < deadline) {
|
|
23374
|
+
renewalTimer = setTimeout(() => {
|
|
23375
|
+
renew();
|
|
23376
|
+
}, Math.min(5000, (deadline - Date.now()) / 5));
|
|
23377
|
+
} else
|
|
23378
|
+
invalidate();
|
|
23379
|
+
} finally {
|
|
23380
|
+
clearTimeout(timeout);
|
|
23381
|
+
controller.signal.removeEventListener("abort", abort);
|
|
23382
|
+
}
|
|
23383
|
+
})();
|
|
23384
|
+
try {
|
|
23385
|
+
await renewal;
|
|
23386
|
+
} finally {
|
|
23387
|
+
renewal = undefined;
|
|
23388
|
+
}
|
|
23389
|
+
}
|
|
23390
|
+
const storageChanged = () => {
|
|
23391
|
+
if (!credential)
|
|
23392
|
+
return;
|
|
23393
|
+
if (!alive()) {
|
|
23188
23394
|
invalidate();
|
|
23395
|
+
return;
|
|
23396
|
+
}
|
|
23397
|
+
synchronize().catch(invalidate);
|
|
23398
|
+
};
|
|
23399
|
+
const monitor = setInterval(() => {
|
|
23400
|
+
storageChanged();
|
|
23189
23401
|
}, 100);
|
|
23402
|
+
globalThis.addEventListener?.("storage", storageChanged);
|
|
23190
23403
|
const dispose = () => {
|
|
23191
|
-
|
|
23192
|
-
|
|
23193
|
-
clearInterval(monitor);
|
|
23404
|
+
disposed = true;
|
|
23405
|
+
cancel();
|
|
23194
23406
|
};
|
|
23195
23407
|
async function requestData(operation2, input) {
|
|
23196
23408
|
try {
|
|
23197
23409
|
if (operation2 !== "data/query" || !current())
|
|
23198
23410
|
throw new Error;
|
|
23199
23411
|
const url = `${origin}/_os/app/data`;
|
|
23412
|
+
const admitted = credential;
|
|
23200
23413
|
const response = await fetch(url, {
|
|
23201
23414
|
method: "POST",
|
|
23202
23415
|
credentials: "omit",
|
|
@@ -23207,13 +23420,20 @@ function appContext(origin, active2) {
|
|
|
23207
23420
|
headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
|
|
23208
23421
|
body: JSON.stringify(input)
|
|
23209
23422
|
});
|
|
23423
|
+
await synchronize();
|
|
23424
|
+
while (alive() && stored() !== credential)
|
|
23425
|
+
await synchronize();
|
|
23210
23426
|
if (!current() || response.redirected || response.url && response.url !== url)
|
|
23211
23427
|
throw new Error;
|
|
23212
23428
|
if (response.status === 401 || response.status === 403) {
|
|
23213
|
-
|
|
23429
|
+
if (admitted === credential)
|
|
23430
|
+
invalidate();
|
|
23214
23431
|
throw new DataOperationError("access_denied");
|
|
23215
23432
|
}
|
|
23216
23433
|
const result2 = await appResponse(response, Number.MAX_SAFE_INTEGER, controller.signal);
|
|
23434
|
+
await synchronize();
|
|
23435
|
+
while (alive() && stored() !== credential)
|
|
23436
|
+
await synchronize();
|
|
23217
23437
|
if (!current())
|
|
23218
23438
|
throw new Error;
|
|
23219
23439
|
if (!response.ok)
|
|
@@ -23233,6 +23453,7 @@ function appContext(origin, active2) {
|
|
|
23233
23453
|
throw new Error("Local app context unavailable.");
|
|
23234
23454
|
}
|
|
23235
23455
|
const url = `${origin}/_os/app/routines/${encodeURIComponent(key)}/${suffix}`;
|
|
23456
|
+
const admitted = credential;
|
|
23236
23457
|
const response = await fetch(url, {
|
|
23237
23458
|
...init,
|
|
23238
23459
|
credentials: "omit",
|
|
@@ -23242,61 +23463,107 @@ function appContext(origin, active2) {
|
|
|
23242
23463
|
signal: controller.signal,
|
|
23243
23464
|
headers: { ...init.body === undefined ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
|
|
23244
23465
|
});
|
|
23466
|
+
await synchronize();
|
|
23467
|
+
while (alive() && stored() !== credential)
|
|
23468
|
+
await synchronize();
|
|
23245
23469
|
if (!current() || response.redirected || response.url && response.url !== url) {
|
|
23246
23470
|
invalidate();
|
|
23247
23471
|
throw new Error("Local app context unavailable.");
|
|
23248
23472
|
}
|
|
23249
|
-
if (response.status === 401 || response.status === 403)
|
|
23473
|
+
if ((response.status === 401 || response.status === 403) && admitted === credential)
|
|
23250
23474
|
invalidate();
|
|
23475
|
+
responseChecks.set(response, async (value) => {
|
|
23476
|
+
await synchronize();
|
|
23477
|
+
while (alive() && stored() !== credential)
|
|
23478
|
+
await synchronize();
|
|
23479
|
+
if (admitted === credential && value && typeof value === "object" && "error" in value && value.error === "authority_unavailable")
|
|
23480
|
+
invalidate();
|
|
23481
|
+
});
|
|
23251
23482
|
return response;
|
|
23252
23483
|
}
|
|
23253
|
-
|
|
23484
|
+
function context() {
|
|
23485
|
+
const issued = credential;
|
|
23486
|
+
return {
|
|
23487
|
+
kind: "local",
|
|
23488
|
+
...binding,
|
|
23489
|
+
get expiresAt() {
|
|
23490
|
+
return binding.expiresAt;
|
|
23491
|
+
},
|
|
23492
|
+
isCurrent: current,
|
|
23493
|
+
signal: controller.signal,
|
|
23494
|
+
invalidate: () => {
|
|
23495
|
+
if (issued === credential)
|
|
23496
|
+
invalidate();
|
|
23497
|
+
},
|
|
23498
|
+
requestRoutine,
|
|
23499
|
+
requestData,
|
|
23500
|
+
requestAvailability: (key) => requestRoutine(key, "availability", { method: "GET" })
|
|
23501
|
+
};
|
|
23502
|
+
}
|
|
23503
|
+
const resolveOnce = async () => {
|
|
23504
|
+
let attempted;
|
|
23254
23505
|
try {
|
|
23255
23506
|
if (denied || !active2() || globalThis.location.origin !== origin)
|
|
23256
23507
|
throw new Error;
|
|
23257
|
-
|
|
23258
|
-
|
|
23508
|
+
if (credential)
|
|
23509
|
+
await synchronize();
|
|
23510
|
+
const candidate = stored();
|
|
23511
|
+
if (!candidate || !/^[A-Za-z0-9_-]{43}$/.test(candidate))
|
|
23259
23512
|
throw new Error;
|
|
23260
|
-
credential =
|
|
23513
|
+
credential = candidate;
|
|
23514
|
+
attempted = candidate;
|
|
23261
23515
|
if (!current())
|
|
23262
23516
|
throw new Error;
|
|
23263
|
-
const
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
|
|
23267
|
-
|
|
23268
|
-
|
|
23269
|
-
}
|
|
23270
|
-
if (!
|
|
23517
|
+
const value = await proof(candidate);
|
|
23518
|
+
if (stored() !== candidate || credential !== candidate) {
|
|
23519
|
+
if (!binding)
|
|
23520
|
+
binding = { ...value };
|
|
23521
|
+
await synchronize();
|
|
23522
|
+
return context();
|
|
23523
|
+
}
|
|
23524
|
+
if (!current())
|
|
23271
23525
|
throw new Error;
|
|
23272
|
-
|
|
23273
|
-
if (!validBinding(value) || !current() || value.expiresAt <= Date.now() || value.expiresAt > Date.now() + 600000 || binding && (value.appId !== binding.appId || value.memberId !== binding.memberId || value.workspace !== binding.workspace || value.expiresAt > binding.expiresAt))
|
|
23526
|
+
if (binding && value.expiresAt > binding.expiresAt)
|
|
23274
23527
|
throw new Error;
|
|
23275
|
-
binding
|
|
23276
|
-
|
|
23277
|
-
|
|
23278
|
-
|
|
23279
|
-
|
|
23280
|
-
|
|
23281
|
-
|
|
23282
|
-
|
|
23283
|
-
|
|
23284
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23287
|
-
|
|
23288
|
-
|
|
23528
|
+
if (!binding || value.expiresAt !== binding.expiresAt)
|
|
23529
|
+
install(value, candidate);
|
|
23530
|
+
return context();
|
|
23531
|
+
} catch (error) {
|
|
23532
|
+
try {
|
|
23533
|
+
if (binding && current() && attempted && attempted !== credential)
|
|
23534
|
+
return context();
|
|
23535
|
+
if (binding && alive() && stored() && stored() !== credential) {
|
|
23536
|
+
await synchronize();
|
|
23537
|
+
return context();
|
|
23538
|
+
}
|
|
23539
|
+
} catch {}
|
|
23540
|
+
if (error instanceof AppContextDenied && binding && alive() && renewal)
|
|
23541
|
+
throw new Error("Local app context unavailable.");
|
|
23289
23542
|
invalidate();
|
|
23290
23543
|
throw new Error("Local app context unavailable.");
|
|
23291
23544
|
}
|
|
23292
23545
|
};
|
|
23293
|
-
|
|
23546
|
+
const resolve = () => {
|
|
23547
|
+
if (resolution2)
|
|
23548
|
+
return resolution2;
|
|
23549
|
+
resolution2 = resolveOnce().finally(() => {
|
|
23550
|
+
resolution2 = undefined;
|
|
23551
|
+
});
|
|
23552
|
+
return resolution2;
|
|
23553
|
+
};
|
|
23554
|
+
const start = () => {
|
|
23555
|
+
startup = setTimeout(() => {
|
|
23556
|
+
if (alive() && !resolution2 && !binding)
|
|
23557
|
+
resolve().catch(() => {});
|
|
23558
|
+
}, 0);
|
|
23559
|
+
};
|
|
23560
|
+
return { resolve, start, invalidate, dispose };
|
|
23294
23561
|
}
|
|
23295
23562
|
function validBinding(value) {
|
|
23296
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,workspace")
|
|
23563
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,sessionId,workspace")
|
|
23297
23564
|
return false;
|
|
23298
23565
|
const binding = value;
|
|
23299
|
-
return typeof binding.appId === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.expiresAt === "number" && Number.isFinite(binding.expiresAt);
|
|
23566
|
+
return typeof binding.appId === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && typeof binding.sessionId === "string" && /^[A-Za-z0-9_-]{43}$/.test(binding.sessionId) && typeof binding.expiresAt === "number" && Number.isFinite(binding.expiresAt);
|
|
23300
23567
|
}
|
|
23301
23568
|
|
|
23302
23569
|
// src/os.ts
|
|
@@ -23364,6 +23631,7 @@ function init({ auth }) {
|
|
|
23364
23631
|
installedDispose = undefined;
|
|
23365
23632
|
};
|
|
23366
23633
|
installedDispose = dispose;
|
|
23634
|
+
local?.start();
|
|
23367
23635
|
}
|
|
23368
23636
|
var os = { init };
|
|
23369
23637
|
// src/app-routine-browser.ts
|