stitchkit 0.117.0 → 0.118.0
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/CHANGELOG.md +14 -0
- package/dist/entrypoints/telegram.js +156 -61
- package/dist/telegram/update-intake-work.d.ts +20 -1
- package/dist/telegram/update-intake-work.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +12 -3
- package/dist/telegram/update-intake.d.ts.map +1 -1
- package/dist/telegram/update-store-check.d.ts +1 -1
- package/dist/telegram/update-store-check.d.ts.map +1 -1
- package/dist/telegram/update-store-postgres.d.ts.map +1 -1
- package/dist/telegram/update-store-sqlite.d.ts.map +1 -1
- package/dist/telegram/update-store.d.ts +5 -1
- package/dist/telegram/update-store.d.ts.map +1 -1
- package/llms/stitchkit.txt +3 -3
- package/llms/telegram.txt +14 -3
- package/llms/{upgrading-0.105.0-0.117.0.txt → upgrading-0.105.0-0.118.0.txt} +26 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +40 -6
- package/llms.txt +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,20 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.118.0] - 2026-11-03
|
|
19
|
+
|
|
20
|
+
### ⚠️ Breaking changes
|
|
21
|
+
|
|
22
|
+
**Who must act:** an application with its own `TelegramUpdateStore`. A bot on a store shipped here needs no change. See the [migration](docs/guide/upgrading.md).
|
|
23
|
+
|
|
24
|
+
- `stitchkit/telegram` — **`TelegramUpdateStore.due` is paged by `afterUpdateId`.** A row stays due until it is claimed, so the first page of a long queue hid the rest. A store has to continue strictly after the cursor; one that ignores it fails the sweep by name rather than reading the same page for ever, and `checkTelegramUpdateStore` holds the rule. `// before: due({ now, pendingBefore, limit })` → `// after: due({ now, pendingBefore, limit, afterUpdateId })`
|
|
25
|
+
**Affects:** `stitchkit/telegram` TelegramUpdateStore(due), TelegramUpdateDueQuery, checkTelegramUpdateStore, createTelegramUpdateIntake(store)
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- `stitchkit/telegram` — after a crash the update intake keeps every chat in order however long the queue was. `start` took 100 rows and left the rest to `pendingGraceMs`, while a new update of the same chat ran at once; an update arriving during the start overtook even a short queue. `start` now schedules the whole queue, what arrives meanwhile waits behind it, and a sweep reaches past a full page of rows already scheduled. A `retry` that throws no longer breaks its chat's line; `onStoreError` reports it. `close` no longer works through the queue: it waits for the handlers running, as it says, and the rest stays recorded.
|
|
30
|
+
**Affects:** `stitchkit/telegram` createTelegramUpdateIntake(behaviour)
|
|
31
|
+
|
|
18
32
|
## [0.117.0] - 2026-11-02
|
|
19
33
|
|
|
20
34
|
### ⚠️ Breaking changes
|
|
@@ -1622,6 +1622,72 @@ function createTelegramUpdateWorkSlots(maxConcurrent) {
|
|
|
1622
1622
|
}
|
|
1623
1623
|
};
|
|
1624
1624
|
}
|
|
1625
|
+
function lineOf(update) {
|
|
1626
|
+
for (const [key, payload] of Object.entries(update)) {
|
|
1627
|
+
if (key === "update_id" || !isRecord(payload))
|
|
1628
|
+
continue;
|
|
1629
|
+
const message = isRecord(payload.message) ? payload.message : undefined;
|
|
1630
|
+
for (const holder of [
|
|
1631
|
+
payload.chat,
|
|
1632
|
+
message?.chat,
|
|
1633
|
+
payload.from,
|
|
1634
|
+
payload.user,
|
|
1635
|
+
payload.voter_chat
|
|
1636
|
+
]) {
|
|
1637
|
+
if (isRecord(holder) && (typeof holder.id === "number" || typeof holder.id === "string")) {
|
|
1638
|
+
return `chat:${holder.id}`;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
return `update:${String(update.update_id)}`;
|
|
1643
|
+
}
|
|
1644
|
+
function createTelegramUpdateLines(run) {
|
|
1645
|
+
const lines = new Map;
|
|
1646
|
+
const scheduled = new Set;
|
|
1647
|
+
return {
|
|
1648
|
+
has: (updateId) => scheduled.has(updateId),
|
|
1649
|
+
schedule(update) {
|
|
1650
|
+
const updateId = Number(update.update_id);
|
|
1651
|
+
if (scheduled.has(updateId))
|
|
1652
|
+
return false;
|
|
1653
|
+
scheduled.add(updateId);
|
|
1654
|
+
const line = lineOf(update);
|
|
1655
|
+
const next = (lines.get(line) ?? Promise.resolve()).then(() => run(update, updateId)).finally(() => {
|
|
1656
|
+
scheduled.delete(updateId);
|
|
1657
|
+
if (lines.get(line) === next)
|
|
1658
|
+
lines.delete(line);
|
|
1659
|
+
});
|
|
1660
|
+
lines.set(line, next);
|
|
1661
|
+
return true;
|
|
1662
|
+
},
|
|
1663
|
+
pending: () => [...lines.values()]
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
async function readTelegramUpdatesDue(store, read) {
|
|
1667
|
+
const { skip, parse, reading, ...query } = read;
|
|
1668
|
+
const rows = [];
|
|
1669
|
+
let afterUpdateId;
|
|
1670
|
+
for (;; ) {
|
|
1671
|
+
const due = await store.due({
|
|
1672
|
+
...query,
|
|
1673
|
+
...afterUpdateId !== undefined && { afterUpdateId }
|
|
1674
|
+
});
|
|
1675
|
+
for (const row of due) {
|
|
1676
|
+
if (afterUpdateId !== undefined && row.updateId <= afterUpdateId) {
|
|
1677
|
+
throw new TypeError("[stitchkit] telegram update store: due returned a row at or before afterUpdateId");
|
|
1678
|
+
}
|
|
1679
|
+
if (skip(row.updateId))
|
|
1680
|
+
continue;
|
|
1681
|
+
const update = parse(row.body);
|
|
1682
|
+
if (update)
|
|
1683
|
+
rows.push(update);
|
|
1684
|
+
}
|
|
1685
|
+
afterUpdateId = due.at(-1)?.updateId;
|
|
1686
|
+
if (due.length === 0 || !reading())
|
|
1687
|
+
return rows;
|
|
1688
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1625
1691
|
function createTelegramUpdateExhaustionScheduler(options) {
|
|
1626
1692
|
const { config, store, now, slots, parse, reportStoreError } = options;
|
|
1627
1693
|
const handler = config.handleExhaustion;
|
|
@@ -1829,25 +1895,6 @@ function defaultRetry(error, attempt) {
|
|
|
1829
1895
|
const backoff = Math.min(5000 * 4 ** (attempt - 1), 15 * 60000);
|
|
1830
1896
|
return Math.max(backoff, (failure.retryAfterSeconds ?? 0) * 1000);
|
|
1831
1897
|
}
|
|
1832
|
-
function lineOf(update) {
|
|
1833
|
-
for (const [key, payload] of Object.entries(update)) {
|
|
1834
|
-
if (key === "update_id" || !isRecord(payload))
|
|
1835
|
-
continue;
|
|
1836
|
-
const message = isRecord(payload.message) ? payload.message : undefined;
|
|
1837
|
-
for (const holder of [
|
|
1838
|
-
payload.chat,
|
|
1839
|
-
message?.chat,
|
|
1840
|
-
payload.from,
|
|
1841
|
-
payload.user,
|
|
1842
|
-
payload.voter_chat
|
|
1843
|
-
]) {
|
|
1844
|
-
if (isRecord(holder) && (typeof holder.id === "number" || typeof holder.id === "string")) {
|
|
1845
|
-
return `chat:${holder.id}`;
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
return `update:${String(update.update_id)}`;
|
|
1850
|
-
}
|
|
1851
1898
|
function parsed(body) {
|
|
1852
1899
|
try {
|
|
1853
1900
|
const value = JSON.parse(body);
|
|
@@ -1868,7 +1915,7 @@ function isTelegramUpdateFencedStore(store) {
|
|
|
1868
1915
|
function isTelegramUpdateDurableStore(store) {
|
|
1869
1916
|
return isTelegramUpdateFencedStore(store) && "exhaust" in store && typeof store.exhaust === "function" && "dueExhaustions" in store && typeof store.dueExhaustions === "function" && "acknowledgeExhaustion" in store && typeof store.acknowledgeExhaustion === "function";
|
|
1870
1917
|
}
|
|
1871
|
-
function
|
|
1918
|
+
function storeCapabilities(config) {
|
|
1872
1919
|
const { store } = config;
|
|
1873
1920
|
const exhaustionHandler = config.handleExhaustion;
|
|
1874
1921
|
const attemptAware = "handleAttempt" in config;
|
|
@@ -1880,6 +1927,11 @@ function createTelegramUpdateIntake(config) {
|
|
|
1880
1927
|
if (exhaustionHandler && !durable) {
|
|
1881
1928
|
throw new TypeError("[stitchkit] telegram update intake: handleExhaustion requires a TelegramUpdateDurableStore");
|
|
1882
1929
|
}
|
|
1930
|
+
return { fenced, durable };
|
|
1931
|
+
}
|
|
1932
|
+
function createTelegramUpdateIntake(config) {
|
|
1933
|
+
const { store } = config;
|
|
1934
|
+
const { fenced, durable } = storeCapabilities(config);
|
|
1883
1935
|
const now = config.now ?? Date.now;
|
|
1884
1936
|
const retry = config.retry ?? defaultRetry;
|
|
1885
1937
|
const maxAttempts = positive("maxAttempts", config.maxAttempts ?? 5);
|
|
@@ -1888,13 +1940,12 @@ function createTelegramUpdateIntake(config) {
|
|
|
1888
1940
|
const sweepEveryMs = positive("sweepEveryMs", config.sweepEveryMs ?? 30000);
|
|
1889
1941
|
const retainMs = config.retainMs ?? 86400000;
|
|
1890
1942
|
const maxConcurrent = positive("maxConcurrent", config.maxConcurrent ?? 32);
|
|
1891
|
-
const lines = new Map;
|
|
1892
|
-
const scheduled = new Set;
|
|
1893
1943
|
let started = false;
|
|
1894
1944
|
let starting;
|
|
1895
1945
|
let lifecycleGeneration = 0;
|
|
1896
1946
|
let timer;
|
|
1897
1947
|
let exhaustionCursor;
|
|
1948
|
+
let arrivals;
|
|
1898
1949
|
const storeError = (error, step) => {
|
|
1899
1950
|
try {
|
|
1900
1951
|
config.onStoreError?.(error, step);
|
|
@@ -1924,34 +1975,38 @@ function createTelegramUpdateIntake(config) {
|
|
|
1924
1975
|
},
|
|
1925
1976
|
ownershipLostReason: (identity) => new TelegramUpdateOwnershipLostError(identity)
|
|
1926
1977
|
});
|
|
1927
|
-
const
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
scheduled.add(updateId);
|
|
1932
|
-
const line = lineOf(update);
|
|
1933
|
-
const next = (lines.get(line) ?? Promise.resolve()).then(async () => {
|
|
1934
|
-
await slots.take();
|
|
1935
|
-
try {
|
|
1978
|
+
const lines = createTelegramUpdateLines(async (update, updateId) => {
|
|
1979
|
+
await slots.take();
|
|
1980
|
+
try {
|
|
1981
|
+
if (started)
|
|
1936
1982
|
await attempt(update, updateId);
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
lines.set(line, next);
|
|
1946
|
-
return true;
|
|
1947
|
-
};
|
|
1948
|
-
const sweep = async (pendingBefore) => {
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
storeError(error, "settle");
|
|
1985
|
+
} finally {
|
|
1986
|
+
slots.release();
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
const schedule = (update) => started && lines.schedule(update);
|
|
1990
|
+
const sweep = async (pendingBefore, waiting = []) => {
|
|
1949
1991
|
const at = now();
|
|
1950
|
-
const
|
|
1992
|
+
const generation = lifecycleGeneration;
|
|
1993
|
+
const rows = await readTelegramUpdatesDue(store, {
|
|
1994
|
+
now: at,
|
|
1995
|
+
pendingBefore,
|
|
1996
|
+
limit: SWEEP_BATCH,
|
|
1997
|
+
skip: lines.has,
|
|
1998
|
+
parse: parsed,
|
|
1999
|
+
reading: () => started
|
|
2000
|
+
});
|
|
2001
|
+
if (generation !== lifecycleGeneration)
|
|
2002
|
+
return 0;
|
|
2003
|
+
if (arrivals === waiting)
|
|
2004
|
+
arrivals = undefined;
|
|
2005
|
+
const waited = new Set(waiting);
|
|
2006
|
+
const queue = [...rows, ...waiting].sort((left, right) => Number(left.update_id) - Number(right.update_id));
|
|
1951
2007
|
let count = 0;
|
|
1952
|
-
for (const
|
|
1953
|
-
|
|
1954
|
-
if (update && schedule(update))
|
|
2008
|
+
for (const update of queue) {
|
|
2009
|
+
if (schedule(update) && !waited.has(update))
|
|
1955
2010
|
count += 1;
|
|
1956
2011
|
}
|
|
1957
2012
|
if (durable) {
|
|
@@ -1972,6 +2027,11 @@ function createTelegramUpdateIntake(config) {
|
|
|
1972
2027
|
await store.prune(at - retainMs);
|
|
1973
2028
|
return count;
|
|
1974
2029
|
};
|
|
2030
|
+
const drained = async () => {
|
|
2031
|
+
while (lines.pending().length > 0 || exhaustionScheduler.pending().length > 0) {
|
|
2032
|
+
await Promise.all([...lines.pending(), ...exhaustionScheduler.pending()]);
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
1975
2035
|
return {
|
|
1976
2036
|
async accept(body) {
|
|
1977
2037
|
const update = parsed(body);
|
|
@@ -1981,7 +2041,10 @@ function createTelegramUpdateIntake(config) {
|
|
|
1981
2041
|
const added = await store.add({ updateId, body, receivedAt: now() });
|
|
1982
2042
|
if (!added)
|
|
1983
2043
|
return "duplicate";
|
|
1984
|
-
|
|
2044
|
+
if (arrivals)
|
|
2045
|
+
arrivals.push(update);
|
|
2046
|
+
else
|
|
2047
|
+
schedule(update);
|
|
1985
2048
|
return "accepted";
|
|
1986
2049
|
},
|
|
1987
2050
|
start() {
|
|
@@ -1991,13 +2054,18 @@ function createTelegramUpdateIntake(config) {
|
|
|
1991
2054
|
return Promise.resolve();
|
|
1992
2055
|
started = true;
|
|
1993
2056
|
const generation = ++lifecycleGeneration;
|
|
2057
|
+
const waiting = [];
|
|
2058
|
+
arrivals = waiting;
|
|
1994
2059
|
const operation = (async () => {
|
|
1995
2060
|
try {
|
|
1996
|
-
await sweep(now());
|
|
2061
|
+
await sweep(now(), waiting);
|
|
1997
2062
|
} catch (error) {
|
|
1998
2063
|
if (started && lifecycleGeneration === generation)
|
|
1999
2064
|
started = false;
|
|
2000
2065
|
throw error;
|
|
2066
|
+
} finally {
|
|
2067
|
+
if (arrivals === waiting)
|
|
2068
|
+
arrivals = undefined;
|
|
2001
2069
|
}
|
|
2002
2070
|
if (!started || lifecycleGeneration !== generation)
|
|
2003
2071
|
return;
|
|
@@ -2018,9 +2086,10 @@ function createTelegramUpdateIntake(config) {
|
|
|
2018
2086
|
},
|
|
2019
2087
|
sweep: () => sweep(now() - pendingGraceMs),
|
|
2020
2088
|
async idle() {
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2089
|
+
await starting?.catch(() => {
|
|
2090
|
+
return;
|
|
2091
|
+
});
|
|
2092
|
+
await drained();
|
|
2024
2093
|
},
|
|
2025
2094
|
async close() {
|
|
2026
2095
|
started = false;
|
|
@@ -2036,9 +2105,7 @@ function createTelegramUpdateIntake(config) {
|
|
|
2036
2105
|
return;
|
|
2037
2106
|
});
|
|
2038
2107
|
}
|
|
2039
|
-
|
|
2040
|
-
await Promise.all([...lines.values(), ...exhaustionScheduler.pending()]);
|
|
2041
|
-
}
|
|
2108
|
+
await drained();
|
|
2042
2109
|
}
|
|
2043
2110
|
};
|
|
2044
2111
|
}
|
|
@@ -2121,7 +2188,7 @@ function memoryTelegramUpdateStore() {
|
|
|
2121
2188
|
settleCurrent(updateId, attempt, settlement);
|
|
2122
2189
|
},
|
|
2123
2190
|
async due(query) {
|
|
2124
|
-
return [...records.values()].filter((record) => record.state === "pending" ? record.receivedAt <= query.pendingBefore : isClaimable(record, query.now)).sort((left, right) => left.updateId - right.updateId).slice(0, query.limit).map(({ updateId, body }) => ({ updateId, body }));
|
|
2191
|
+
return [...records.values()].filter((record) => record.state === "pending" ? record.receivedAt <= query.pendingBefore : isClaimable(record, query.now)).filter((record) => query.afterUpdateId === undefined || record.updateId > query.afterUpdateId).sort((left, right) => left.updateId - right.updateId).slice(0, query.limit).map(({ updateId, body }) => ({ updateId, body }));
|
|
2125
2192
|
},
|
|
2126
2193
|
async owns(identity, at) {
|
|
2127
2194
|
const record = holding(identity.updateId, identity.attempt, identity.claimId);
|
|
@@ -2254,6 +2321,27 @@ var RULES = {
|
|
|
2254
2321
|
if (!same(await store.due({ now: 100, pendingBefore: 0, limit: 10 }), []))
|
|
2255
2322
|
violated("an update past maxAttempts was not abandoned");
|
|
2256
2323
|
},
|
|
2324
|
+
"due continues strictly after its cursor": async (store, violated) => {
|
|
2325
|
+
for (const updateId of [1, 2, 3]) {
|
|
2326
|
+
await store.add({ updateId, body: "b", receivedAt: 0 });
|
|
2327
|
+
}
|
|
2328
|
+
await store.claim(2, { ...claim, now: 0, leaseUntil: 10 });
|
|
2329
|
+
await store.settle(2, 1, { state: "failed", at: 1, retryAt: 5, error: "x" });
|
|
2330
|
+
const page = async (afterUpdateId) => (await store.due({
|
|
2331
|
+
now: 5,
|
|
2332
|
+
pendingBefore: 0,
|
|
2333
|
+
limit: 2,
|
|
2334
|
+
...afterUpdateId !== undefined && { afterUpdateId }
|
|
2335
|
+
})).map(({ updateId }) => updateId);
|
|
2336
|
+
if (!same(await page(), [1, 2]))
|
|
2337
|
+
violated("due did not begin at the first row");
|
|
2338
|
+
if (!same(await page(1), [2, 3]))
|
|
2339
|
+
violated("due lost a retry after its cursor");
|
|
2340
|
+
if (!same(await page(2), [3]))
|
|
2341
|
+
violated("due did not continue after its cursor");
|
|
2342
|
+
if (!same(await page(3), []))
|
|
2343
|
+
violated("due listed a row at or before its cursor");
|
|
2344
|
+
},
|
|
2257
2345
|
"settled updates are forgotten after they are pruned": async (store, violated) => {
|
|
2258
2346
|
await store.add({ updateId: 3, body: "b", receivedAt: 0 });
|
|
2259
2347
|
await store.claim(3, { ...claim, now: 0, leaseUntil: 100 });
|
|
@@ -2632,9 +2720,15 @@ function postgresTelegramUpdateStore(config) {
|
|
|
2632
2720
|
},
|
|
2633
2721
|
async due(options) {
|
|
2634
2722
|
const rows = await run(`SELECT update_id, body FROM ${table}
|
|
2635
|
-
WHERE (state = 'pending' AND received_at <= $2::bigint)
|
|
2636
|
-
OR (state <> 'pending' AND ${CLAIMABLE})
|
|
2637
|
-
|
|
2723
|
+
WHERE ((state = 'pending' AND received_at <= $2::bigint)
|
|
2724
|
+
OR (state <> 'pending' AND ${CLAIMABLE}))
|
|
2725
|
+
AND update_id > $4::bigint
|
|
2726
|
+
ORDER BY update_id LIMIT $3::integer`, [
|
|
2727
|
+
options.now,
|
|
2728
|
+
options.pendingBefore,
|
|
2729
|
+
options.limit,
|
|
2730
|
+
options.afterUpdateId ?? Number.MIN_SAFE_INTEGER
|
|
2731
|
+
]);
|
|
2638
2732
|
return rows.flatMap((row) => {
|
|
2639
2733
|
const updateId = integer(field(row, "update_id"));
|
|
2640
2734
|
const body = field(row, "body");
|
|
@@ -2743,7 +2837,8 @@ function prepareTelegramSqliteUpdateStatements(database, table) {
|
|
|
2743
2837
|
WHERE update_id = ?5 AND attempts = ?6 AND state = 'processing' AND due_at >= ?7
|
|
2744
2838
|
AND (?8 IS NULL OR claim_id = ?8)`),
|
|
2745
2839
|
due: database.prepare(`SELECT update_id, body FROM ${table}
|
|
2746
|
-
WHERE (state = 'pending' AND received_at <= ?2) OR (state <> 'pending' AND ${CLAIMABLE2})
|
|
2840
|
+
WHERE ((state = 'pending' AND received_at <= ?2) OR (state <> 'pending' AND ${CLAIMABLE2}))
|
|
2841
|
+
AND update_id > ?4
|
|
2747
2842
|
ORDER BY update_id LIMIT ?3`),
|
|
2748
2843
|
prune: database.prepare(`DELETE FROM ${table} WHERE state IN ('completed', 'abandoned') AND settled_at < ?1`),
|
|
2749
2844
|
owns: database.prepare(`SELECT update_id FROM ${table}
|
|
@@ -2827,7 +2922,7 @@ function sqliteTelegramUpdateStore(config) {
|
|
|
2827
2922
|
settleCurrent(updateId, attempt, settlement);
|
|
2828
2923
|
},
|
|
2829
2924
|
async due(query) {
|
|
2830
|
-
return due.all(query.now, query.pendingBefore, query.limit).flatMap((row) => rowOf(row) ?? []);
|
|
2925
|
+
return due.all(query.now, query.pendingBefore, query.limit, query.afterUpdateId ?? Number.MIN_SAFE_INTEGER).flatMap((row) => rowOf(row) ?? []);
|
|
2831
2926
|
},
|
|
2832
2927
|
async owns(identity, at) {
|
|
2833
2928
|
return owns.all(identity.updateId, identity.attempt, at, identity.claimId).length > 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TelegramUpdateAttemptIntakeConfig, TelegramUpdateEnvelope, TelegramUpdateIntakeConfig, TelegramUpdateStoreStep } from './update-intake.js';
|
|
2
|
-
import type { StoredTelegramUpdateExhaustion, TelegramUpdateAttemptIdentity, TelegramUpdateDurableStore, TelegramUpdateFencedStore, TelegramUpdateStore } from './update-store.js';
|
|
2
|
+
import type { StoredTelegramUpdateExhaustion, TelegramUpdateAttemptIdentity, TelegramUpdateDueQuery, TelegramUpdateDurableStore, TelegramUpdateFencedStore, TelegramUpdateStore } from './update-store.js';
|
|
3
3
|
type TelegramUpdateAnyIntakeConfig<TUpdate extends TelegramUpdateEnvelope> = TelegramUpdateIntakeConfig<TUpdate> | TelegramUpdateAttemptIntakeConfig<TUpdate>;
|
|
4
4
|
export type TelegramUpdateStoreErrorReporter = (error: unknown, step: TelegramUpdateStoreStep) => void;
|
|
5
5
|
/** One shared concurrency budget for ordinary attempts and exhaustion delivery. */
|
|
@@ -8,6 +8,25 @@ export interface TelegramUpdateWorkSlots {
|
|
|
8
8
|
release(): void;
|
|
9
9
|
}
|
|
10
10
|
export declare function createTelegramUpdateWorkSlots(maxConcurrent: number): TelegramUpdateWorkSlots;
|
|
11
|
+
/** The updates this process holds, each behind the earlier ones of its chat. */
|
|
12
|
+
export interface TelegramUpdateLines {
|
|
13
|
+
has(updateId: number): boolean;
|
|
14
|
+
/** Join the update's line; `false` when it is already in one. */
|
|
15
|
+
schedule(update: Record<string, unknown>): boolean;
|
|
16
|
+
pending(): readonly Promise<void>[];
|
|
17
|
+
}
|
|
18
|
+
export declare function createTelegramUpdateLines(run: (update: Record<string, unknown>, updateId: number) => Promise<void>): TelegramUpdateLines;
|
|
19
|
+
/**
|
|
20
|
+
* Every row due, read to the empty page. A scheduled row stays due until
|
|
21
|
+
* claimed, so only a cursor reaches past the first page. A store on a
|
|
22
|
+
* synchronous driver never leaves the microtask queue, so each page yields:
|
|
23
|
+
* webhooks and lease renewals run during a read.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readTelegramUpdatesDue(store: TelegramUpdateStore, read: Omit<TelegramUpdateDueQuery, 'afterUpdateId'> & {
|
|
26
|
+
readonly skip: (updateId: number) => boolean;
|
|
27
|
+
readonly parse: (body: string) => Record<string, unknown> | undefined;
|
|
28
|
+
readonly reading: () => boolean;
|
|
29
|
+
}): Promise<Record<string, unknown>[]>;
|
|
11
30
|
export interface TelegramUpdateExhaustionScheduler {
|
|
12
31
|
schedule(row: StoredTelegramUpdateExhaustion, active: boolean): boolean;
|
|
13
32
|
pending(): readonly Promise<void>[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-intake-work.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake-work.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,iCAAiC,EACjC,sBAAsB,EAEtB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,8BAA8B,EAC9B,6BAA6B,EAC7B,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAExB,KAAK,6BAA6B,CAAC,OAAO,SAAS,sBAAsB,IACrE,0BAA0B,CAAC,OAAO,CAAC,GACnC,iCAAiC,CAAC,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,gCAAgC,GAAG,CAC7C,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,uBAAuB,KAC1B,IAAI,CAAC;AAEV,mFAAmF;AACnF,MAAM,WAAW,uBAAuB;IACtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,GAAG,uBAAuB,CAiB5F;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,GAAG,EAAE,8BAA8B,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IACxE,OAAO,IAAI,SAAS,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;CACrC;AAED,uFAAuF;AACvF,wBAAgB,uCAAuC,CACrD,OAAO,SAAS,sBAAsB,EACtC,OAAO,EAAE;IACT,QAAQ,CAAC,MAAM,EAAE,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,0BAA0B,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,gBAAgB,EAAE,gCAAgC,CAAC;CAC7D,GAAG,iCAAiC,CA4CpC;AAYD,yEAAyE;AACzE,wBAAgB,iCAAiC,CAC/C,OAAO,SAAS,sBAAsB,EACtC,OAAO,EAAE;IACT,QAAQ,CAAC,MAAM,EAAE,6BAA6B,CAAC,OAAO,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,KAAK,CAAC;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,gCAAgC,CAAC;IAC5D,QAAQ,CAAC,kBAAkB,EAAE,CAAC,GAAG,EAAE,8BAA8B,KAAK,IAAI,CAAC;IAC3E,QAAQ,CAAC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,6BAA6B,KAAK,OAAO,CAAC;CACpF,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CA+JvE"}
|
|
1
|
+
{"version":3,"file":"update-intake-work.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake-work.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* `pending` whose process never got to it, `failed` whose retry is due,
|
|
18
18
|
* `processing` whose lease nobody renewed. Delivery is at least once: a
|
|
19
19
|
* handler that finished but could not be recorded as finished runs again.
|
|
20
|
+
*
|
|
21
|
+
* The start reads the whole queue into the lines of its chats, page by page,
|
|
22
|
+
* and whatever arrives meanwhile waits for it: a line is ordered by when it was
|
|
23
|
+
* joined, so the only way to keep a chat in `update_id` order after a crash is
|
|
24
|
+
* for the rows the dead process left to join first. The attempt the crash
|
|
25
|
+
* interrupted stays under its lease and comes when that lapses.
|
|
20
26
|
*/
|
|
21
27
|
import type { TelegramUpdateAttemptIdentity, TelegramUpdateFencedStore, TelegramUpdateStore } from './update-store.js';
|
|
22
28
|
import type { TelegramUpdateAcceptance } from './webhook.js';
|
|
@@ -86,7 +92,7 @@ export interface TelegramUpdateIntakeOptions<TUpdate extends TelegramUpdateEnvel
|
|
|
86
92
|
readonly handleExhaustion?: (exhaustion: TelegramUpdateExhaustion<TUpdate>) => unknown;
|
|
87
93
|
/** An observational report that `handleExhaustion` threw; it never acknowledges the row. */
|
|
88
94
|
readonly onExhaustionFailure?: (failure: TelegramUpdateExhaustionFailure<TUpdate>) => void;
|
|
89
|
-
/**
|
|
95
|
+
/** A step failed; the update stays recorded and a later sweep takes it. */
|
|
90
96
|
readonly onStoreError?: (error: unknown, step: TelegramUpdateStoreStep) => void;
|
|
91
97
|
/** Default `Date.now`. */
|
|
92
98
|
readonly now?: () => number;
|
|
@@ -106,9 +112,12 @@ export interface TelegramUpdateAttemptIntakeConfig<TUpdate extends TelegramUpdat
|
|
|
106
112
|
export interface TelegramUpdateIntake {
|
|
107
113
|
/** Record one webhook body; handling starts after, once the intake is started. */
|
|
108
114
|
accept(body: string): Promise<TelegramUpdateAcceptance>;
|
|
109
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Start handling, beginning with everything recorded before. Resolves once
|
|
117
|
+
* that queue is scheduled, not handled; `accept` works throughout.
|
|
118
|
+
*/
|
|
110
119
|
start(): Promise<void>;
|
|
111
|
-
/** Take over
|
|
120
|
+
/** Take over everything due now; how many updates were scheduled. */
|
|
112
121
|
sweep(): Promise<number>;
|
|
113
122
|
/** Resolves once nothing is scheduled. */
|
|
114
123
|
idle(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The rules of `TelegramUpdateStore`, as a check any store can be put
|
|
3
3
|
* through — the ones shipped here and an application's own over its ORM.
|
|
4
4
|
*
|
|
5
|
-
* A store is
|
|
5
|
+
* A store is six conditional statements, and each is easy to write almost
|
|
6
6
|
* right: a claim that does not check the lease, a settle that does not check
|
|
7
7
|
* the attempt, a claim read and then written in two statements so that two
|
|
8
8
|
* processes both take one update. Nothing fails in a test with one process;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-store-check.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"update-store-check.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAqYxB;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAC7D,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-store-postgres.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAIV,0BAA0B,EAE3B,MAAM,gBAAgB,CAAC;AAExB,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAAG,CAClC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,KAC5C,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAaD,mEAAmE;AACnE,wBAAgB,iCAAiC,CAAC,KAAK,SAAqB,GAAG,MAAM,CAepF;AA4HD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,iCAAiC,GACxC,0BAA0B,
|
|
1
|
+
{"version":3,"file":"update-store-postgres.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAIV,0BAA0B,EAE3B,MAAM,gBAAgB,CAAC;AAExB,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAAG,CAClC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,KAC5C,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAaD,mEAAmE;AACnE,wBAAgB,iCAAiC,CAAC,KAAK,SAAqB,GAAG,MAAM,CAepF;AA4HD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,iCAAiC,GACxC,0BAA0B,CAyK5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-store-sqlite.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAKV,0BAA0B,EAE3B,MAAM,gBAAgB,CAAC;AAExB,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACtF,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;KAC3D,CAAC;CACH;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"update-store-sqlite.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAKV,0BAA0B,EAE3B,MAAM,gBAAgB,CAAC;AAExB,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACtF,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;KAC3D,CAAC;CACH;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAgJD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,GACtC,0BAA0B,CA8I5B"}
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - `renew` and `settle` act only while the record is still `processing` under
|
|
14
14
|
* the same attempt, so a process that lost its lease cannot overwrite the
|
|
15
15
|
* one that took it over;
|
|
16
|
+
* - `due` lists a record until it is claimed, so a queue is read page by page,
|
|
17
|
+
* strictly after `afterUpdateId`;
|
|
16
18
|
* - a durable abandonment keeps `settledAt` empty until the consumer has
|
|
17
19
|
* handled its final obligation. It stays in this record and is never pruned
|
|
18
20
|
* while it is pending.
|
|
@@ -43,6 +45,8 @@ export interface TelegramUpdateDueQuery {
|
|
|
43
45
|
/** A `pending` record is due once received at or before this — its own process had its chance. */
|
|
44
46
|
readonly pendingBefore: number;
|
|
45
47
|
readonly limit: number;
|
|
48
|
+
/** Continue strictly after this `update_id`; omit to begin at the first row. */
|
|
49
|
+
readonly afterUpdateId?: number;
|
|
46
50
|
}
|
|
47
51
|
export interface StoredTelegramUpdate {
|
|
48
52
|
readonly updateId: number;
|
|
@@ -78,7 +82,7 @@ export interface TelegramUpdateStore {
|
|
|
78
82
|
now?: number): Promise<boolean>;
|
|
79
83
|
/** End this attempt; ignored when the attempt no longer holds the update. */
|
|
80
84
|
settle(updateId: number, attempt: number, settlement: TelegramUpdateSettlement): Promise<void>;
|
|
81
|
-
/**
|
|
85
|
+
/** One page of claimable updates in `update_id` order; a row stays due until it is claimed. */
|
|
82
86
|
due(query: TelegramUpdateDueQuery): Promise<readonly StoredTelegramUpdate[]>;
|
|
83
87
|
/** Forget `completed` and `abandoned` updates settled before `before`; how many were forgotten. */
|
|
84
88
|
prune(before: number): Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"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"}
|
package/llms/stitchkit.txt
CHANGED
|
@@ -89,13 +89,13 @@ own, recorded as an ADR.
|
|
|
89
89
|
| `stitchkit/xlsx` | browser **and** server | evolving | a workbook from rows and typed columns, with no dependency; text never runs as a formula |
|
|
90
90
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
91
91
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
92
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
92
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 63 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
93
93
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
|
|
94
94
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
95
95
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
96
96
|
| `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
|
|
97
97
|
| `stitchkit/react/keyboard/testing` | server (Bun or Node) | evolving | the keyboard layers and zones without a DOM — fakes for the window, elements and key events beside the two engines, to test the order of layers, `scope`/`inert` and the edges of a zone in milliseconds |
|
|
98
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the
|
|
98
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the 63 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
99
99
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
100
100
|
| `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
|
|
101
101
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -107,7 +107,7 @@ own, recorded as an ADR.
|
|
|
107
107
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
108
108
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
109
109
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
110
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the
|
|
110
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 63 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
111
111
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
112
112
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
113
113
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
package/llms/telegram.txt
CHANGED
|
@@ -478,7 +478,17 @@ intake recorded the body; the handler runs after, outside the request. Updates
|
|
|
478
478
|
of one chat run in order, other chats alongside (`maxConcurrent`, 32). An
|
|
479
479
|
attempt holds a lease it renews while the handler lives; a sweep takes over what
|
|
480
480
|
was left — `pending` whose process never got to it (`pendingGraceMs`), `failed`
|
|
481
|
-
whose retry is due, `processing` whose process died.
|
|
481
|
+
whose retry is due, `processing` whose process died. `start` schedules the whole
|
|
482
|
+
queue the last process left, page by page and without waiting for
|
|
483
|
+
`pendingGraceMs`, and an update accepted meanwhile takes its place behind it, so
|
|
484
|
+
a crash keeps each chat in order however long the queue was. The queue sits in
|
|
485
|
+
memory while it drains, as it did in the process that accepted it. `close`
|
|
486
|
+
waits for the handlers running and leaves the rest of the queue recorded. Order
|
|
487
|
+
holds inside one process for updates handled in their turn: the attempt a crash
|
|
488
|
+
interrupted comes when its lease lapses, a failed update at its retry and one
|
|
489
|
+
the store refused to claim at a later sweep, each after later updates of its
|
|
490
|
+
chat. An old release draining beside a new one shares its queue, so the two can
|
|
491
|
+
run updates of one chat side by side. A failure is retried with
|
|
482
492
|
backoff or after Telegram's `retry_after`; one Telegram said repeating cannot
|
|
483
493
|
fix (a refused message, a blocked user) is abandoned; `maxAttempts` (5) ends
|
|
484
494
|
the rest. Delivery is at least once: a handler that finished but could not be
|
|
@@ -528,7 +538,8 @@ in a `BotError`; the intake unwraps it, so `retry`, `onFailure` and the error
|
|
|
528
538
|
stored with the update are what the middleware threw — a `retry` that returns
|
|
529
539
|
`false` for the bot's own terminal error sees that error.
|
|
530
540
|
|
|
531
|
-
The base store remains the six atomic steps of `TelegramUpdateStore
|
|
541
|
+
The base store remains the six atomic steps of `TelegramUpdateStore`; `due` is
|
|
542
|
+
paged, continuing strictly after `afterUpdateId`. A
|
|
532
543
|
`TelegramUpdateFencedStore` adds `claimOwned`, `renewOwned`, `owns` and
|
|
533
544
|
`settleOwned`; `TelegramUpdateDurableStore` adds the final-obligation methods.
|
|
534
545
|
Reference stores implement both, so the bot's own database holds the fence and
|
|
@@ -659,7 +670,7 @@ injected grammY bot. → ADR 0143, ADR 0201, ADR 0274 — [guide](../guide/teleg
|
|
|
659
670
|
| `TelegramUpdateIntakeConfig` / `TelegramUpdateAttemptIntakeConfig` / `TelegramUpdateIntakeOptions` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, unchanged `handle(update)` or attempt-aware `handleAttempt(update, context)` (a grammY `BotError` is unwrapped), shared `retry`, `maxAttempts` (5), `leaseMs` (120 000), `pendingGraceMs` (60 000), `sweepEveryMs` (30 000), `retainMs` (24 h), `maxConcurrent` (32), `onFailure`, durable `handleExhaustion`, `onExhaustionFailure`, `onStoreError`, `now`; `accept(body)`, `start()`, `sweep()`, `idle()`, `close()` |
|
|
660
671
|
| `TelegramUpdateAttemptContext` / `TelegramUpdateAttemptIdentity` / `TelegramUpdateOwnershipLostError` | _type_ / class | immutable `{ updateId, attempt, claimId }` fence plus `ownerLost`; the persisted opaque claim token prevents a stale capability from matching after prune/re-add, while a store check in the consumer's DB transaction fences a domain write |
|
|
661
672
|
| `TelegramUpdateExhaustion` / `TelegramUpdateExhaustionFailure` / `StoredTelegramUpdateExhaustion` / `TelegramUpdateExhaustionQuery` | _type_ | a terminal update/attempt/error retained in the update store until `handleExhaustion` resolves; ordered `{ limit, afterUpdateId? }` pages prevent a failing first batch from starving later rows; delivery failures are observational and leave it due |
|
|
662
|
-
| `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it |
|
|
673
|
+
| `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it; `due` returns one page and continues strictly after `afterUpdateId` |
|
|
663
674
|
| `TelegramUpdateFencedStore` / `TelegramUpdateDurableStore` | _type_ | reference-store capabilities: `claimOwned` creates and returns the persisted identity, `renewOwned`, `owns(identity, at)` and `settleOwned(identity, settlement)` act only on that exact unexpired claim; `exhaust`, `dueExhaustions`, `acknowledgeExhaustion` keep a final consumer obligation in the same row until exact acknowledgement |
|
|
664
675
|
| `memoryTelegramUpdateStore` | function | the store in this process's memory — tests, or a bot that accepts losing unhandled updates to a restart |
|
|
665
676
|
| `sqliteTelegramUpdateStore` / `SqliteTelegramUpdateStoreConfig` / `TelegramSqliteDatabase` | function / _type_ | the store in SQLite over the handle the application opened — `bun:sqlite` `Database` or `node:sqlite` `DatabaseSync`; `table` (default `telegram_updates`) created when missing |
|
|
@@ -1,14 +1,37 @@
|
|
|
1
|
-
# Upgrading 0.105.0 → 0.
|
|
1
|
+
# Upgrading 0.105.0 → 0.118.0 — stitchkit docs slice
|
|
2
2
|
|
|
3
3
|
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
4
|
> `llms.txt` in this package.
|
|
5
5
|
|
|
6
|
-
Contains: Guide: Upgrading, 0.105.0 → 0.
|
|
6
|
+
Contains: Guide: Upgrading, 0.105.0 → 0.118.0 (docs/guide/upgrading.md)
|
|
7
7
|
|
|
8
8
|
==============================================================================
|
|
9
|
-
# Guide: Upgrading, 0.105.0 → 0.
|
|
9
|
+
# Guide: Upgrading, 0.105.0 → 0.118.0 (docs/guide/upgrading.md)
|
|
10
10
|
==============================================================================
|
|
11
11
|
|
|
12
|
+
## Released migration: 0.118.0
|
|
13
|
+
|
|
14
|
+
### telegram-due-cursor
|
|
15
|
+
|
|
16
|
+
**Who must act:** an application with its own `TelegramUpdateStore`. A bot on `memoryTelegramUpdateStore`,
|
|
17
|
+
`sqliteTelegramUpdateStore` or `postgresTelegramUpdateStore` needs no change.
|
|
18
|
+
|
|
19
|
+
`due` returns one page of a queue whose rows stay due until claimed, and the intake reads the next page from
|
|
20
|
+
`afterUpdateId`, until a page comes back empty. A store that ignores the cursor fails `start` and every sweep
|
|
21
|
+
with a `TypeError`, reported to `onStoreError`, as soon as a row is due. Continue strictly after it, in
|
|
22
|
+
`update_id` order:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// before
|
|
26
|
+
where: { OR: dueRows }
|
|
27
|
+
// after
|
|
28
|
+
where: { OR: dueRows, ...(query.afterUpdateId !== undefined && { updateId: { gt: query.afterUpdateId } }) }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`checkTelegramUpdateStore` reports the rule as `due continues strictly after its cursor`.
|
|
32
|
+
|
|
33
|
+
Search pattern: an object with a `due(` method passed as `store` to `createTelegramUpdateIntake`.
|
|
34
|
+
|
|
12
35
|
## Released migration: 0.117.0
|
|
13
36
|
|
|
14
37
|
### telegram-chat-pause
|
package/llms/upgrading.txt
CHANGED
|
@@ -11,7 +11,7 @@ Contains: Guide: Upgrading, general (docs/guide/upgrading.md)
|
|
|
11
11
|
|
|
12
12
|
Load this slice, then every range slice between your installed version and the target:
|
|
13
13
|
|
|
14
|
-
- 0.105.0 → 0.
|
|
14
|
+
- 0.105.0 → 0.118.0: `llms/upgrading-0.105.0-0.118.0.txt`
|
|
15
15
|
- 0.84.0 → 0.104.0: `llms/upgrading-0.84.0-0.104.0.txt`
|
|
16
16
|
- 0.63.0 → 0.83.0: `llms/upgrading-0.63.0-0.83.0.txt`
|
|
17
17
|
- 0.44.0 → 0.62.0: `llms/upgrading-0.44.0-0.62.0.txt`
|
package/llms-full.txt
CHANGED
|
@@ -81,13 +81,13 @@ own, recorded as an ADR.
|
|
|
81
81
|
| `stitchkit/xlsx` | browser **and** server | evolving | a workbook from rows and typed columns, with no dependency; text never runs as a formula |
|
|
82
82
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
83
83
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
84
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
84
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 63 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
85
85
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
|
|
86
86
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
87
87
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
88
88
|
| `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
|
|
89
89
|
| `stitchkit/react/keyboard/testing` | server (Bun or Node) | evolving | the keyboard layers and zones without a DOM — fakes for the window, elements and key events beside the two engines, to test the order of layers, `scope`/`inert` and the edges of a zone in milliseconds |
|
|
90
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the
|
|
90
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the 63 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
91
91
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
92
92
|
| `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
|
|
93
93
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -99,7 +99,7 @@ own, recorded as an ADR.
|
|
|
99
99
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
100
100
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
101
101
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
102
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the
|
|
102
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 63 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
103
103
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
104
104
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
105
105
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -16407,7 +16407,17 @@ intake recorded the body; the handler runs after, outside the request. Updates
|
|
|
16407
16407
|
of one chat run in order, other chats alongside (`maxConcurrent`, 32). An
|
|
16408
16408
|
attempt holds a lease it renews while the handler lives; a sweep takes over what
|
|
16409
16409
|
was left — `pending` whose process never got to it (`pendingGraceMs`), `failed`
|
|
16410
|
-
whose retry is due, `processing` whose process died.
|
|
16410
|
+
whose retry is due, `processing` whose process died. `start` schedules the whole
|
|
16411
|
+
queue the last process left, page by page and without waiting for
|
|
16412
|
+
`pendingGraceMs`, and an update accepted meanwhile takes its place behind it, so
|
|
16413
|
+
a crash keeps each chat in order however long the queue was. The queue sits in
|
|
16414
|
+
memory while it drains, as it did in the process that accepted it. `close`
|
|
16415
|
+
waits for the handlers running and leaves the rest of the queue recorded. Order
|
|
16416
|
+
holds inside one process for updates handled in their turn: the attempt a crash
|
|
16417
|
+
interrupted comes when its lease lapses, a failed update at its retry and one
|
|
16418
|
+
the store refused to claim at a later sweep, each after later updates of its
|
|
16419
|
+
chat. An old release draining beside a new one shares its queue, so the two can
|
|
16420
|
+
run updates of one chat side by side. A failure is retried with
|
|
16411
16421
|
backoff or after Telegram's `retry_after`; one Telegram said repeating cannot
|
|
16412
16422
|
fix (a refused message, a blocked user) is abandoned; `maxAttempts` (5) ends
|
|
16413
16423
|
the rest. Delivery is at least once: a handler that finished but could not be
|
|
@@ -16457,7 +16467,8 @@ in a `BotError`; the intake unwraps it, so `retry`, `onFailure` and the error
|
|
|
16457
16467
|
stored with the update are what the middleware threw — a `retry` that returns
|
|
16458
16468
|
`false` for the bot's own terminal error sees that error.
|
|
16459
16469
|
|
|
16460
|
-
The base store remains the six atomic steps of `TelegramUpdateStore
|
|
16470
|
+
The base store remains the six atomic steps of `TelegramUpdateStore`; `due` is
|
|
16471
|
+
paged, continuing strictly after `afterUpdateId`. A
|
|
16461
16472
|
`TelegramUpdateFencedStore` adds `claimOwned`, `renewOwned`, `owns` and
|
|
16462
16473
|
`settleOwned`; `TelegramUpdateDurableStore` adds the final-obligation methods.
|
|
16463
16474
|
Reference stores implement both, so the bot's own database holds the fence and
|
|
@@ -17037,6 +17048,29 @@ charts, no merged cells and no streaming: an application that needs them takes a
|
|
|
17037
17048
|
|
|
17038
17049
|
# Upgrading stitchkit
|
|
17039
17050
|
|
|
17051
|
+
## Released migration: 0.118.0
|
|
17052
|
+
|
|
17053
|
+
### telegram-due-cursor
|
|
17054
|
+
|
|
17055
|
+
**Who must act:** an application with its own `TelegramUpdateStore`. A bot on `memoryTelegramUpdateStore`,
|
|
17056
|
+
`sqliteTelegramUpdateStore` or `postgresTelegramUpdateStore` needs no change.
|
|
17057
|
+
|
|
17058
|
+
`due` returns one page of a queue whose rows stay due until claimed, and the intake reads the next page from
|
|
17059
|
+
`afterUpdateId`, until a page comes back empty. A store that ignores the cursor fails `start` and every sweep
|
|
17060
|
+
with a `TypeError`, reported to `onStoreError`, as soon as a row is due. Continue strictly after it, in
|
|
17061
|
+
`update_id` order:
|
|
17062
|
+
|
|
17063
|
+
```ts
|
|
17064
|
+
// before
|
|
17065
|
+
where: { OR: dueRows }
|
|
17066
|
+
// after
|
|
17067
|
+
where: { OR: dueRows, ...(query.afterUpdateId !== undefined && { updateId: { gt: query.afterUpdateId } }) }
|
|
17068
|
+
```
|
|
17069
|
+
|
|
17070
|
+
`checkTelegramUpdateStore` reports the rule as `due continues strictly after its cursor`.
|
|
17071
|
+
|
|
17072
|
+
Search pattern: an object with a `due(` method passed as `store` to `createTelegramUpdateIntake`.
|
|
17073
|
+
|
|
17040
17074
|
## Released migration: 0.117.0
|
|
17041
17075
|
|
|
17042
17076
|
### telegram-chat-pause
|
|
@@ -24178,7 +24212,7 @@ injected grammY bot. → ADR 0143, ADR 0201, ADR 0274 — [guide](../guide/teleg
|
|
|
24178
24212
|
| `TelegramUpdateIntakeConfig` / `TelegramUpdateAttemptIntakeConfig` / `TelegramUpdateIntakeOptions` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, unchanged `handle(update)` or attempt-aware `handleAttempt(update, context)` (a grammY `BotError` is unwrapped), shared `retry`, `maxAttempts` (5), `leaseMs` (120 000), `pendingGraceMs` (60 000), `sweepEveryMs` (30 000), `retainMs` (24 h), `maxConcurrent` (32), `onFailure`, durable `handleExhaustion`, `onExhaustionFailure`, `onStoreError`, `now`; `accept(body)`, `start()`, `sweep()`, `idle()`, `close()` |
|
|
24179
24213
|
| `TelegramUpdateAttemptContext` / `TelegramUpdateAttemptIdentity` / `TelegramUpdateOwnershipLostError` | _type_ / class | immutable `{ updateId, attempt, claimId }` fence plus `ownerLost`; the persisted opaque claim token prevents a stale capability from matching after prune/re-add, while a store check in the consumer's DB transaction fences a domain write |
|
|
24180
24214
|
| `TelegramUpdateExhaustion` / `TelegramUpdateExhaustionFailure` / `StoredTelegramUpdateExhaustion` / `TelegramUpdateExhaustionQuery` | _type_ | a terminal update/attempt/error retained in the update store until `handleExhaustion` resolves; ordered `{ limit, afterUpdateId? }` pages prevent a failing first batch from starving later rows; delivery failures are observational and leave it due |
|
|
24181
|
-
| `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it |
|
|
24215
|
+
| `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it; `due` returns one page and continues strictly after `afterUpdateId` |
|
|
24182
24216
|
| `TelegramUpdateFencedStore` / `TelegramUpdateDurableStore` | _type_ | reference-store capabilities: `claimOwned` creates and returns the persisted identity, `renewOwned`, `owns(identity, at)` and `settleOwned(identity, settlement)` act only on that exact unexpired claim; `exhaust`, `dueExhaustions`, `acknowledgeExhaustion` keep a final consumer obligation in the same row until exact acknowledgement |
|
|
24183
24217
|
| `memoryTelegramUpdateStore` | function | the store in this process's memory — tests, or a bot that accepts losing unhandled updates to a restart |
|
|
24184
24218
|
| `sqliteTelegramUpdateStore` / `SqliteTelegramUpdateStoreConfig` / `TelegramSqliteDatabase` | function / _type_ | the store in SQLite over the handle the application opened — `bun:sqlite` `Database` or `node:sqlite` `DatabaseSync`; `table` (default `telegram_updates`) created when missing |
|
package/llms.txt
CHANGED
|
@@ -43,7 +43,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
43
43
|
- **stitchkit/testing**: [llms/testing.txt](llms/testing.txt) 33.6 KB
|
|
44
44
|
- **stitchkit/agent-runtime/sandbox**: [llms/agent-runtime-sandbox.txt](llms/agent-runtime-sandbox.txt) 8.3 KB
|
|
45
45
|
- **stitchkit/files**: [llms/files.txt](llms/files.txt) 5.7 KB
|
|
46
|
-
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt)
|
|
46
|
+
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt) 44.8 KB
|
|
47
47
|
- **stitchkit/telegram/grammy**: [llms/telegram-grammy.txt](llms/telegram-grammy.txt) 1.2 KB
|
|
48
48
|
- **stitchkit/telegram/html**: [llms/telegram-html.txt](llms/telegram-html.txt) 2.7 KB
|
|
49
49
|
- **stitchkit/telegram/screens**: [llms/telegram-screens.txt](llms/telegram-screens.txt) 21.2 KB
|
|
@@ -69,7 +69,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
69
69
|
|
|
70
70
|
## Upgrading slices
|
|
71
71
|
- **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 12.7 KB
|
|
72
|
-
- **Upgrading 0.105.0 → 0.
|
|
72
|
+
- **Upgrading 0.105.0 → 0.118.0**: [llms/upgrading-0.105.0-0.118.0.txt](llms/upgrading-0.105.0-0.118.0.txt) 43.0 KB
|
|
73
73
|
- **Upgrading 0.84.0 → 0.104.0**: [llms/upgrading-0.84.0-0.104.0.txt](llms/upgrading-0.84.0-0.104.0.txt) 46.1 KB
|
|
74
74
|
- **Upgrading 0.63.0 → 0.83.0**: [llms/upgrading-0.63.0-0.83.0.txt](llms/upgrading-0.63.0-0.83.0.txt) 44.0 KB
|
|
75
75
|
- **Upgrading 0.44.0 → 0.62.0**: [llms/upgrading-0.44.0-0.62.0.txt](llms/upgrading-0.44.0-0.62.0.txt) 46.5 KB
|
package/package.json
CHANGED