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 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 createTelegramUpdateIntake(config) {
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 schedule = (update) => {
1928
- const updateId = Number(update.update_id);
1929
- if (!started || scheduled.has(updateId))
1930
- return false;
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
- } finally {
1938
- slots.release();
1939
- }
1940
- }).finally(() => {
1941
- scheduled.delete(updateId);
1942
- if (lines.get(line) === next)
1943
- lines.delete(line);
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 due = await store.due({ now: at, pendingBefore, limit: SWEEP_BATCH });
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 row of due) {
1953
- const update = parsed(row.body);
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
- schedule(update);
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
- while (lines.size > 0 || exhaustionScheduler.pending().length > 0) {
2022
- await Promise.all([...lines.values(), ...exhaustionScheduler.pending()]);
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
- while (lines.size > 0 || exhaustionScheduler.pending().length > 0) {
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
- ORDER BY update_id LIMIT $3::integer`, [options.now, options.pendingBefore, options.limit]);
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
- /** The store refused a step; the update is retried when its lease lapses. */
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
- /** Start handling, beginning with everything recorded before. */
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 what is due now; how many updates were scheduled. */
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":"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"}
1
+ {"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAWH,OAAO,KAAK,EACV,6BAA6B,EAE7B,yBAAyB,EACzB,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1D,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,8DAA8D;AAC9D,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B;IACjF,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,EAAE,6BAA6B,CAAC;IAC9C,+FAA+F;IAC/F,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;CACjC;AAED,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,sBAAsB,CAC9E,SAAQ,6BAA6B;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,iEAAiE;AACjE,MAAM,WAAW,+BAA+B,CAAC,OAAO,SAAS,sBAAsB;IACrF,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACvD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,kFAAkF;AAClF,qBAAa,gCAAiC,SAAQ,KAAK;aAC7B,QAAQ,EAAE,6BAA6B;IAAnE,YAA4B,QAAQ,EAAE,6BAA6B,EAKlE;CACF;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7E,qFAAqF;AACrF,MAAM,WAAW,2BAA2B,CAAC,OAAO,SAAS,sBAAsB;IACjF;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,KAAK,CAAC;IACrE,iBAAiB;IACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,sBAAsB;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC9D;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,wBAAwB,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC;IACvF,4FAA4F;IAC5F,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,+BAA+B,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC3F,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChF,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,kDAAkD;AAClD,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,CAChF,SAAQ,2BAA2B,CAAC,OAAO,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;CAC/C;AAED,gFAAgF;AAChF,MAAM,WAAW,iCAAiC,CAAC,OAAO,SAAS,sBAAsB,CACvF,SAAQ,2BAA2B,CAAC,OAAO,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC;IAC1C,mFAAmF;IACnF,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC;CAC7F;AAMD,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxD;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,qEAAqE;IACrE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,0CAA0C;IAC1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2DAA2D;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA8FD,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,EAC/E,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,GAC1C,oBAAoB,CAAC;AACxB,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,EAC/E,MAAM,EAAE,iCAAiC,CAAC,OAAO,CAAC,GACjD,oBAAoB,CAAC"}
@@ -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 five conditional statements, and each is easy to write almost
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;AAiXxB;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAC7D,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB"}
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,CAmK5B"}
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;AA+ID,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,GACtC,0BAA0B,CAyI5B"}
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
- /** Claimable updates in `update_id` order. */
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":"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"}
1
+ {"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"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"}
@@ -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 62 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
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 62 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
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 62 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
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. A failure is retried with
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`. A
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.117.0 — stitchkit docs slice
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.117.0 (docs/guide/upgrading.md)
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.117.0 (docs/guide/upgrading.md)
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
@@ -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.117.0: `llms/upgrading-0.105.0-0.117.0.txt`
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 62 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
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 62 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
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 62 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
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. A failure is retried with
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`. A
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) 43.9 KB
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.117.0**: [llms/upgrading-0.105.0-0.117.0.txt](llms/upgrading-0.105.0-0.117.0.txt) 42.1 KB
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.117.0",
3
+ "version": "0.118.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",