stitchkit 0.100.2 → 0.101.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 +153 -0
- package/README.md +2 -1
- package/dist/entrypoints/agent-runtime/browser.js +119 -119
- package/dist/entrypoints/agent-runtime/coding-tools.js +15 -20
- package/dist/entrypoints/agent-runtime/harness.js +55 -63
- package/dist/entrypoints/agent-runtime/openrouter.js +6 -6
- package/dist/entrypoints/agent-runtime/sandbox.js +20 -25
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +11 -13
- package/dist/entrypoints/agent-runtime/sqlite/node.js +11 -13
- package/dist/entrypoints/agent-runtime/testing.js +32 -34
- package/dist/entrypoints/agent-runtime.js +395 -402
- package/dist/entrypoints/application/diagnostic-journal.js +26 -27
- package/dist/entrypoints/application/directory-inbox.js +16 -17
- package/dist/entrypoints/application/grammy.js +13 -13
- package/dist/entrypoints/application/opentelemetry.js +13 -13
- package/dist/entrypoints/application/schemas.js +24 -24
- package/dist/entrypoints/application.js +209 -210
- package/dist/entrypoints/bin/upgrade-cli.js +2 -2
- package/dist/entrypoints/cli.js +81 -84
- package/dist/entrypoints/contract.js +54 -57
- package/dist/entrypoints/declaration.js +22 -22
- package/dist/entrypoints/files.js +18 -21
- package/dist/entrypoints/geo.js +7 -8
- package/dist/entrypoints/google.js +2 -2
- package/dist/entrypoints/index.js +150 -154
- package/dist/entrypoints/live.js +45 -47
- package/dist/entrypoints/node.js +68 -74
- package/dist/entrypoints/oauth.js +6 -6
- package/dist/entrypoints/observability.d.ts +1 -0
- package/dist/entrypoints/observability.d.ts.map +1 -1
- package/dist/entrypoints/observability.js +116 -87
- package/dist/entrypoints/primitives.js +35 -35
- package/dist/entrypoints/react/keyboard.d.ts +4 -0
- package/dist/entrypoints/react/keyboard.d.ts.map +1 -0
- package/dist/entrypoints/react/keyboard.js +397 -0
- package/dist/entrypoints/react.js +10 -10
- package/dist/entrypoints/release.js +7 -7
- package/dist/entrypoints/remote.js +15 -17
- package/dist/entrypoints/server.js +198 -200
- package/dist/entrypoints/telegram/html.d.ts +5 -0
- package/dist/entrypoints/telegram/html.d.ts.map +1 -0
- package/dist/entrypoints/telegram/html.js +26 -0
- package/dist/entrypoints/telegram/screens.d.ts +14 -0
- package/dist/entrypoints/telegram/screens.d.ts.map +1 -0
- package/dist/entrypoints/telegram/screens.js +2028 -0
- package/dist/entrypoints/telegram.d.ts +11 -3
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +779 -159
- package/dist/entrypoints/testing.js +75 -79
- package/dist/entrypoints/tools/connections.js +30 -32
- package/dist/entrypoints/tools/contract.js +18 -20
- package/dist/entrypoints/tools/invoker.js +12 -17
- package/dist/entrypoints/tools.js +224 -229
- package/dist/entrypoints/tracking/server.js +6 -6
- package/dist/entrypoints/tracking.js +40 -42
- package/dist/entrypoints/voice.js +3 -3
- package/dist/{index-eaysmdtq.js → index-0ndk4555.js} +56 -56
- package/dist/{index-2bxbyab0.js → index-1bx1xv1k.js} +1 -1
- package/dist/{index-p5r7qpdd.js → index-1eeqpadm.js} +4 -4
- package/dist/{index-hadmp193.js → index-3ct7hhq6.js} +210 -210
- package/dist/{index-e01r054k.js → index-45tdvqa3.js} +26 -26
- package/dist/{index-dce5wxcq.js → index-4s0fep5q.js} +8 -8
- package/dist/index-50ejfz75.js +9 -0
- package/dist/{index-m9zn6cpq.js → index-5107q189.js} +12 -12
- package/dist/{index-sbdmyz75.js → index-5cc6r990.js} +3 -3
- package/dist/{index-c6bxbsnc.js → index-7kxw1awn.js} +2 -2
- package/dist/{index-0r0fbk7z.js → index-7z9ym7t8.js} +28 -28
- package/dist/{index-p7hgdyhb.js → index-814xpr6d.js} +2 -2
- package/dist/{index-gtj3zc53.js → index-8kzct1d6.js} +99 -99
- package/dist/{index-1zbbzgfh.js → index-9pw0a51a.js} +4 -4
- package/dist/{index-79z7m7dt.js → index-a9pd417t.js} +35 -35
- package/dist/{index-ham5sz8m.js → index-aaj97r4y.js} +45 -45
- package/dist/{index-s812ycat.js → index-aan6hqb9.js} +3 -3
- package/dist/{index-10x2hbx0.js → index-adhgd4pe.js} +44 -44
- package/dist/{index-2gxzjvyr.js → index-afjvjyck.js} +23 -23
- package/dist/{index-qmy6zq75.js → index-ahvsksw5.js} +16 -16
- package/dist/{index-hr4tzbwq.js → index-ap3gqe56.js} +3 -3
- package/dist/{index-znfs20ss.js → index-b2bz0kdm.js} +16 -16
- package/dist/{index-r066jx76.js → index-c6aagrbd.js} +148 -120
- package/dist/{index-k30h66yd.js → index-ck71tbcf.js} +9 -9
- package/dist/{index-eqqvgdb3.js → index-ct75c3at.js} +2 -2
- package/dist/{index-gw8bmyaj.js → index-djjhhgjk.js} +41 -41
- package/dist/{index-8qt2m66q.js → index-dvwj1f43.js} +43 -43
- package/dist/index-e05cjgtf.js +144 -0
- package/dist/{index-yw4fzmdz.js → index-e6c2spsd.js} +79 -30
- package/dist/{index-fhwqwxe0.js → index-ej331hah.js} +1 -1
- package/dist/{index-9t3dffv2.js → index-ewbaywk5.js} +5 -5
- package/dist/{index-7p1f0vp2.js → index-fyphpknh.js} +25 -25
- package/dist/{index-83bdg97s.js → index-g149e9pk.js} +1 -1
- package/dist/{index-ngfhvfpd.js → index-g6a3na27.js} +4 -4
- package/dist/{index-qwhynv6x.js → index-h124epcf.js} +20 -20
- package/dist/{index-h6eeys1v.js → index-h97tqqp1.js} +9 -9
- package/dist/{index-zcgf3gqf.js → index-hby4qhax.js} +10 -10
- package/dist/{index-5jrd0yng.js → index-j8aw2c0e.js} +4 -4
- package/dist/{index-d8m8zpd8.js → index-jzczzcsb.js} +14 -14
- package/dist/index-kqnd6a85.js +26 -0
- package/dist/{index-35mh5bhj.js → index-kw1ws0cs.js} +12 -12
- package/dist/{index-4gcnakhx.js → index-m38ej7e5.js} +2 -2
- package/dist/{index-ndddxtyx.js → index-m79yr938.js} +19 -19
- package/dist/{index-5p2m7we1.js → index-m9fndta6.js} +11 -11
- package/dist/index-mnkk1ffz.js +150 -0
- package/dist/index-n15jny5b.js +4 -0
- package/dist/{index-2vtcmrj9.js → index-n53mswt3.js} +20 -20
- package/dist/{index-bt14acb5.js → index-n5yqxd3d.js} +21 -21
- package/dist/{index-bm5rx7n6.js → index-n9tyz8z3.js} +12 -12
- package/dist/index-nmaqxdx7.js +463 -0
- package/dist/{index-mqv3b5rh.js → index-nx6eazt0.js} +30 -30
- package/dist/{index-3z73fh2c.js → index-q0th8q0z.js} +9 -9
- package/dist/{index-v260df89.js → index-qjeggp8w.js} +20 -20
- package/dist/{index-zfrhfn82.js → index-r1z0rd9c.js} +20 -20
- package/dist/{index-wqgjfstn.js → index-r5c4fedv.js} +3 -3
- package/dist/{index-cyktc4bp.js → index-r5hsgkz5.js} +118 -118
- package/dist/{index-0b1ess5e.js → index-r7a7r7ha.js} +1 -1
- package/dist/{index-1j8pm5t0.js → index-rfkh44ss.js} +15 -15
- package/dist/index-rz3z13e9.js +47 -0
- package/dist/{index-k6gpggb9.js → index-s80awwsw.js} +35 -35
- package/dist/{index-vqr7f7v8.js → index-ssmd41kh.js} +124 -124
- package/dist/index-sznqf8fx.js +20 -0
- package/dist/{index-ensd0r74.js → index-v14qqmys.js} +41 -41
- package/dist/{index-paz6sgdp.js → index-v4a2fjtk.js} +36 -36
- package/dist/{index-d9pk3td4.js → index-w0eqdhyd.js} +14 -14
- package/dist/{index-5qn1kx34.js → index-w575evhq.js} +29 -29
- package/dist/{index-3gye9wzb.js → index-w5s3xftz.js} +26 -26
- package/dist/{index-tm88avnj.js → index-wykebpn9.js} +3 -3
- package/dist/{index-sh3s86na.js → index-xkhk5d6g.js} +17 -17
- package/dist/{index-6k1937bx.js → index-xqhede6x.js} +4 -4
- package/dist/{index-f4fx2tza.js → index-zs4ms3th.js} +181 -181
- package/dist/{index-k5hsfc3j.js → index-zsvzhjtk.js} +1 -1
- package/dist/internal/secret-shapes.d.ts +21 -0
- package/dist/internal/secret-shapes.d.ts.map +1 -0
- package/dist/observability/bounded-logger.d.ts +2 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/observability/sanitize.d.ts +6 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/observability/secret-values.d.ts +25 -0
- package/dist/observability/secret-values.d.ts.map +1 -0
- package/dist/react/keyboard/hooks.d.ts +71 -0
- package/dist/react/keyboard/hooks.d.ts.map +1 -0
- package/dist/react/keyboard/layers.d.ts +98 -0
- package/dist/react/keyboard/layers.d.ts.map +1 -0
- package/dist/react/keyboard/zone.d.ts +139 -0
- package/dist/react/keyboard/zone.d.ts.map +1 -0
- package/dist/telegram/bot-api.d.ts +7 -4
- package/dist/telegram/bot-api.d.ts.map +1 -1
- package/dist/telegram/crypto.d.ts +16 -0
- package/dist/telegram/crypto.d.ts.map +1 -0
- package/dist/telegram/html/check.d.ts +26 -0
- package/dist/telegram/html/check.d.ts.map +1 -0
- package/dist/telegram/html/nodes.d.ts +70 -0
- package/dist/telegram/html/nodes.d.ts.map +1 -0
- package/dist/telegram/html/parse.d.ts +35 -0
- package/dist/telegram/html/parse.d.ts.map +1 -0
- package/dist/telegram/html/render.d.ts +39 -0
- package/dist/telegram/html/render.d.ts.map +1 -0
- package/dist/telegram/init-data.d.ts +3 -44
- package/dist/telegram/init-data.d.ts.map +1 -1
- package/dist/telegram/operator-channel.d.ts +19 -3
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- package/dist/telegram/operator-dedupe.d.ts +44 -0
- package/dist/telegram/operator-dedupe.d.ts.map +1 -0
- package/dist/telegram/screens/callback-codec.d.ts +34 -0
- package/dist/telegram/screens/callback-codec.d.ts.map +1 -0
- package/dist/telegram/screens/dispatch.d.ts +22 -0
- package/dist/telegram/screens/dispatch.d.ts.map +1 -0
- package/dist/telegram/screens/display.d.ts +70 -0
- package/dist/telegram/screens/display.d.ts.map +1 -0
- package/dist/telegram/screens/execute.d.ts +57 -0
- package/dist/telegram/screens/execute.d.ts.map +1 -0
- package/dist/telegram/screens/html.d.ts +36 -0
- package/dist/telegram/screens/html.d.ts.map +1 -0
- package/dist/telegram/screens/outcome.d.ts +74 -0
- package/dist/telegram/screens/outcome.d.ts.map +1 -0
- package/dist/telegram/screens/path.d.ts +29 -0
- package/dist/telegram/screens/path.d.ts.map +1 -0
- package/dist/telegram/screens/reconcile.d.ts +67 -0
- package/dist/telegram/screens/reconcile.d.ts.map +1 -0
- package/dist/telegram/screens/registry.d.ts +23 -0
- package/dist/telegram/screens/registry.d.ts.map +1 -0
- package/dist/telegram/screens/render.d.ts +70 -0
- package/dist/telegram/screens/render.d.ts.map +1 -0
- package/dist/telegram/screens/runtime.d.ts +133 -0
- package/dist/telegram/screens/runtime.d.ts.map +1 -0
- package/dist/telegram/screens/screen-types.d.ts +130 -0
- package/dist/telegram/screens/screen-types.d.ts.map +1 -0
- package/dist/telegram/screens/screen.d.ts +138 -0
- package/dist/telegram/screens/screen.d.ts.map +1 -0
- package/dist/telegram/screens/state.d.ts +115 -0
- package/dist/telegram/screens/state.d.ts.map +1 -0
- package/dist/telegram/screens/telegram-screens.d.ts +18 -0
- package/dist/telegram/screens/telegram-screens.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts +47 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat.d.ts +66 -0
- package/dist/telegram/screens/test-chat.d.ts.map +1 -0
- package/dist/telegram/screens/view.d.ts +89 -0
- package/dist/telegram/screens/view.d.ts.map +1 -0
- package/dist/telegram/send-failure.d.ts +25 -0
- package/dist/telegram/send-failure.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +82 -0
- package/dist/telegram/update-intake.d.ts.map +1 -0
- package/dist/telegram/update-store-check.d.ts +22 -0
- package/dist/telegram/update-store-check.d.ts.map +1 -0
- package/dist/telegram/update-store-postgres.d.ts +33 -0
- package/dist/telegram/update-store-postgres.d.ts.map +1 -0
- package/dist/telegram/update-store-sqlite.d.ts +26 -0
- package/dist/telegram/update-store-sqlite.d.ts.map +1 -0
- package/dist/telegram/update-store.d.ts +67 -0
- package/dist/telegram/update-store.d.ts.map +1 -0
- package/dist/telegram/user.d.ts +51 -0
- package/dist/telegram/user.d.ts.map +1 -0
- package/dist/telegram/webhook.d.ts +105 -0
- package/dist/telegram/webhook.d.ts.map +1 -0
- package/dist/tools/cli/args-fields.d.ts +8 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -1
- package/dist/tools/cli/args-route.d.ts.map +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/cli/help.d.ts.map +1 -1
- package/llms/cli.txt +8 -2
- package/llms/observability.txt +15 -2
- package/llms/react-keyboard.txt +193 -0
- package/llms/server.4.txt +16 -0
- package/llms/stitchkit.txt +10 -4
- package/llms/telegram-html.txt +35 -0
- package/llms/telegram-screens.txt +365 -0
- package/llms/telegram.txt +245 -11
- package/llms/{upgrading-0.74.0-0.100.0.txt → upgrading-0.74.0-0.101.0.txt} +39 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +889 -19
- package/llms.txt +12 -7
- package/package.json +17 -2
- package/dist/index-2k4yrqkc.js +0 -9
- package/dist/index-crykmqkr.js +0 -26
- package/dist/index-y64k3s0h.js +0 -150
- package/dist/index-y91ry9h2.js +0 -4
- /package/dist/{index-hffjpmq9.js → index-9jfdm5tg.js} +0 -0
- /package/dist/{index-zjjyynm3.js → index-d4pa2bgs.js} +0 -0
- /package/dist/{index-rxfy4cq7.js → index-dwfv9a85.js} +0 -0
- /package/dist/{index-5jm36zv1.js → index-gfk7nrht.js} +0 -0
- /package/dist/{index-b9aefrqd.js → index-n1dqr7gf.js} +0 -0
- /package/dist/{index-q3pyb9n0.js → index-sykb5ag7.js} +0 -0
- /package/dist/{index-48ffdxgk.js → index-xhzq2qvj.js} +0 -0
- /package/dist/{index-kzfs85xp.js → index-zsarb5xp.js} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where received updates wait, and the rules a store keeps for them.
|
|
3
|
+
*
|
|
4
|
+
* The intake decides; the store only has to make each decision atomic, so two
|
|
5
|
+
* processes sharing one store — an old release still draining beside a new one
|
|
6
|
+
* — never run one update at once. Every rule is a condition on one record:
|
|
7
|
+
*
|
|
8
|
+
* - **claimable** at `now`: `pending`; `failed` whose `dueAt` has come;
|
|
9
|
+
* `processing` whose lease (`dueAt`) has lapsed — its process died;
|
|
10
|
+
* - `claim` takes a claimable record whose `attempts` are under the limit —
|
|
11
|
+
* `processing`, `attempts + 1`, lease until `leaseUntil` — and settles one
|
|
12
|
+
* whose attempts are spent as `abandoned`;
|
|
13
|
+
* - `renew` and `settle` act only while the record is still `processing` under
|
|
14
|
+
* the same attempt, so a process that lost its lease cannot overwrite the
|
|
15
|
+
* one that took it over.
|
|
16
|
+
*/
|
|
17
|
+
export type TelegramUpdateState = 'pending' | 'processing' | 'failed' | 'completed' | 'abandoned';
|
|
18
|
+
export type TelegramUpdateSettlement = {
|
|
19
|
+
readonly state: 'completed';
|
|
20
|
+
readonly at: number;
|
|
21
|
+
} | {
|
|
22
|
+
readonly state: 'failed';
|
|
23
|
+
readonly at: number;
|
|
24
|
+
readonly retryAt: number;
|
|
25
|
+
readonly error: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly state: 'abandoned';
|
|
28
|
+
readonly at: number;
|
|
29
|
+
readonly error: string;
|
|
30
|
+
};
|
|
31
|
+
export interface TelegramUpdateClaimOptions {
|
|
32
|
+
readonly now: number;
|
|
33
|
+
readonly leaseUntil: number;
|
|
34
|
+
readonly maxAttempts: number;
|
|
35
|
+
}
|
|
36
|
+
export interface TelegramUpdateDueQuery {
|
|
37
|
+
readonly now: number;
|
|
38
|
+
/** A `pending` record is due once received at or before this — its own process had its chance. */
|
|
39
|
+
readonly pendingBefore: number;
|
|
40
|
+
readonly limit: number;
|
|
41
|
+
}
|
|
42
|
+
export interface StoredTelegramUpdate {
|
|
43
|
+
readonly updateId: number;
|
|
44
|
+
readonly body: string;
|
|
45
|
+
}
|
|
46
|
+
export interface TelegramUpdateStore {
|
|
47
|
+
/** Record a received update as `pending`; `false` when this `update_id` is already recorded. */
|
|
48
|
+
add(update: StoredTelegramUpdate & {
|
|
49
|
+
readonly receivedAt: number;
|
|
50
|
+
}): Promise<boolean>;
|
|
51
|
+
/** Take a claimable update for one attempt; its number, or `undefined` when not taken. */
|
|
52
|
+
claim(updateId: number, options: TelegramUpdateClaimOptions): Promise<number | undefined>;
|
|
53
|
+
/** Extend the lease of this attempt; `false` when the attempt no longer holds the update. */
|
|
54
|
+
renew(updateId: number, attempt: number, leaseUntil: number): Promise<boolean>;
|
|
55
|
+
/** End this attempt; ignored when the attempt no longer holds the update. */
|
|
56
|
+
settle(updateId: number, attempt: number, settlement: TelegramUpdateSettlement): Promise<void>;
|
|
57
|
+
/** Claimable updates in `update_id` order. */
|
|
58
|
+
due(query: TelegramUpdateDueQuery): Promise<readonly StoredTelegramUpdate[]>;
|
|
59
|
+
/** Forget `completed` and `abandoned` updates settled before `before`; how many were forgotten. */
|
|
60
|
+
prune(before: number): Promise<number>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A store in this process's memory — for tests, and for a bot that accepts
|
|
64
|
+
* losing what was received but not handled when it restarts.
|
|
65
|
+
*/
|
|
66
|
+
export declare function memoryTelegramUpdateStore(): TelegramUpdateStore;
|
|
67
|
+
//# sourceMappingURL=update-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,0FAA0F;IAC1F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1F,6FAA6F;IAC7F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,6EAA6E;IAC7E,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,wBAAwB,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC7E,mGAAmG;IACnG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC;AAsBD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,mBAAmB,CAsE/D"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Telegram user in one shape, wherever it came from.
|
|
3
|
+
*
|
|
4
|
+
* Telegram sends a user in `snake_case` twice over — the Bot API's `from` on
|
|
5
|
+
* every update, the Mini App's signed `user` — and every other name this
|
|
6
|
+
* package publishes is `camelCase`. An application with one `ensureAccount`
|
|
7
|
+
* receives both, and a surface that switched convention by source would make
|
|
8
|
+
* it write the same translation twice. So both pass through this schema and
|
|
9
|
+
* arrive as the same `TelegramUser`.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
declare const TelegramUserSchema: z.ZodPipe<z.ZodObject<{
|
|
13
|
+
id: z.ZodInt;
|
|
14
|
+
is_bot: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
first_name: z.ZodString;
|
|
16
|
+
last_name: z.ZodOptional<z.ZodString>;
|
|
17
|
+
username: z.ZodOptional<z.ZodString>;
|
|
18
|
+
language_code: z.ZodOptional<z.ZodString>;
|
|
19
|
+
is_premium: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
allows_write_to_pm: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
photo_url: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
23
|
+
id: number;
|
|
24
|
+
firstName: string;
|
|
25
|
+
isBot?: boolean | undefined;
|
|
26
|
+
lastName?: string | undefined;
|
|
27
|
+
username?: string | undefined;
|
|
28
|
+
languageCode?: string | undefined;
|
|
29
|
+
isPremium?: boolean | undefined;
|
|
30
|
+
allowsWriteToPm?: boolean | undefined;
|
|
31
|
+
photoUrl?: string | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
id: number;
|
|
34
|
+
is_bot?: boolean | undefined;
|
|
35
|
+
first_name: string;
|
|
36
|
+
last_name?: string | undefined;
|
|
37
|
+
username?: string | undefined;
|
|
38
|
+
language_code?: string | undefined;
|
|
39
|
+
is_premium?: boolean | undefined;
|
|
40
|
+
allows_write_to_pm?: boolean | undefined;
|
|
41
|
+
photo_url?: string | undefined;
|
|
42
|
+
}>>;
|
|
43
|
+
/** A Telegram user as this package names it; fields Telegram did not send are absent. */
|
|
44
|
+
export type TelegramUser = z.infer<typeof TelegramUserSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* Telegram's user record — grammY's `ctx.from`, a Bot API `User`, a Mini App's
|
|
47
|
+
* `user` — as a `TelegramUser`; `undefined` when it is not one.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseTelegramUser(value: unknown): TelegramUser | undefined;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/telegram/user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBnB,CAAC;AAEN,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAG1E"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bot's webhook: one place in all of Telegram, and who may take it.
|
|
3
|
+
*
|
|
4
|
+
* `setWebhook` moves a bot's updates without asking whose they were. A process
|
|
5
|
+
* started anywhere with the production token — a developer's machine, a
|
|
6
|
+
* staging host, a rehearsal — takes the production bot with one call, and the
|
|
7
|
+
* production process simply stops hearing from Telegram. So a claim sets the
|
|
8
|
+
* webhook only where it already points here, or where the caller named the
|
|
9
|
+
* current owner's host as the one it takes over from (`'none'` when there is no
|
|
10
|
+
* webhook — a bot on long polling is somebody's bot too).
|
|
11
|
+
*
|
|
12
|
+
* "Here" is the address *and* the secret. Telegram never returns the secret,
|
|
13
|
+
* so the address carries a tag derived from it: an `owner` query parameter
|
|
14
|
+
* holding an HMAC of the secret. A process with the production address but a
|
|
15
|
+
* different secret has a different address and is refused, instead of quietly
|
|
16
|
+
* installing its secret and leaving production answering 403 to every update.
|
|
17
|
+
* Routers match the path, which the tag leaves alone; the secret cannot be
|
|
18
|
+
* recovered from it. Errors and reports name hosts only — a foreign webhook's
|
|
19
|
+
* path may itself be a secret.
|
|
20
|
+
*/
|
|
21
|
+
/** `takeoverFrom` when there is no webhook to take over — the bot polls, or is new. */
|
|
22
|
+
export declare const TELEGRAM_WEBHOOK_NONE = "none";
|
|
23
|
+
export interface TelegramWebhookConfig {
|
|
24
|
+
readonly token: string;
|
|
25
|
+
/** Where Telegram should post updates, without the owner tag. */
|
|
26
|
+
readonly url: string;
|
|
27
|
+
/** Telegram's `secret_token`: 1–256 of `A-Z a-z 0-9 _ -`. */
|
|
28
|
+
readonly secret: string;
|
|
29
|
+
/** A local Bot API server. Default: Telegram's. */
|
|
30
|
+
readonly apiRoot?: string;
|
|
31
|
+
readonly fetch?: typeof fetch;
|
|
32
|
+
readonly signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export interface ClaimTelegramWebhookConfig extends TelegramWebhookConfig {
|
|
35
|
+
/**
|
|
36
|
+
* The host the webhook may be taken from — the current owner's, as the
|
|
37
|
+
* refusal names it — or `'none'` to set one where there is none. Set it for
|
|
38
|
+
* one switch and remove it: left in place it is a standing permission.
|
|
39
|
+
*/
|
|
40
|
+
readonly takeoverFrom?: string;
|
|
41
|
+
/** Telegram's `allowed_updates`. Default: whatever the webhook had. */
|
|
42
|
+
readonly allowedUpdates?: readonly string[];
|
|
43
|
+
/** Telegram's `max_connections`, 1–100. */
|
|
44
|
+
readonly maxConnections?: number;
|
|
45
|
+
/** Default `false`: updates waiting for the previous owner are this owner's now. */
|
|
46
|
+
readonly dropPendingUpdates?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface TelegramWebhookClaim {
|
|
49
|
+
/** The address set, owner tag included. */
|
|
50
|
+
readonly url: string;
|
|
51
|
+
/** The host it was taken over from; absent when it was already this one's. */
|
|
52
|
+
readonly takenFrom?: string;
|
|
53
|
+
readonly pendingUpdates: number;
|
|
54
|
+
}
|
|
55
|
+
export type TelegramWebhookRefusal =
|
|
56
|
+
/** Another host holds the webhook. */
|
|
57
|
+
'owned-elsewhere'
|
|
58
|
+
/** This address holds it, registered with another secret. */
|
|
59
|
+
| 'other-secret'
|
|
60
|
+
/** Telegram accepted `setWebhook` but reports another address after it. */
|
|
61
|
+
| 'not-confirmed';
|
|
62
|
+
export declare class TelegramWebhookClaimError extends Error {
|
|
63
|
+
readonly reason: TelegramWebhookRefusal;
|
|
64
|
+
/** Where the webhook points, as a host; `'none'` when nowhere. */
|
|
65
|
+
readonly ownerHost: string;
|
|
66
|
+
constructor(reason: TelegramWebhookRefusal, ownerHost: string);
|
|
67
|
+
}
|
|
68
|
+
export interface TelegramWebhookState {
|
|
69
|
+
/** The webhook points to this address with this secret. */
|
|
70
|
+
readonly owned: boolean;
|
|
71
|
+
readonly ownerHost: string;
|
|
72
|
+
readonly pendingUpdates: number;
|
|
73
|
+
/** Telegram's last failure to deliver, if it reported one. */
|
|
74
|
+
readonly lastError?: {
|
|
75
|
+
readonly at: number;
|
|
76
|
+
readonly message: string;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The address with its owner tag: the same for every process with this secret. */
|
|
80
|
+
export declare function telegramWebhookUrl(url: string, secret: string): Promise<string>;
|
|
81
|
+
/**
|
|
82
|
+
* Set the webhook if it is this process's to set, and confirm Telegram holds
|
|
83
|
+
* it. Refused — without `setWebhook` — when another host or another secret
|
|
84
|
+
* holds it and `takeoverFrom` does not name that holder.
|
|
85
|
+
*/
|
|
86
|
+
export declare function claimTelegramWebhook(config: ClaimTelegramWebhookConfig): Promise<TelegramWebhookClaim>;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the webhook still points here. A webhook moved to another host, or
|
|
89
|
+
* re-registered with another secret, stops this process hearing from Telegram
|
|
90
|
+
* without any error of its own — a periodic check is how it is noticed.
|
|
91
|
+
*/
|
|
92
|
+
export declare function checkTelegramWebhook(config: TelegramWebhookConfig): Promise<TelegramWebhookState>;
|
|
93
|
+
/** What becomes of an update body: recorded, seen before, or not an update. */
|
|
94
|
+
export type TelegramUpdateAcceptance = 'accepted' | 'duplicate' | 'invalid';
|
|
95
|
+
export interface ReceiveTelegramWebhookOptions {
|
|
96
|
+
readonly secret: string;
|
|
97
|
+
/** Records the update; answered to Telegram as soon as this resolves. */
|
|
98
|
+
readonly accept: (body: string) => Promise<TelegramUpdateAcceptance>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Answer one webhook request: 403 without this secret, 400 for a body that is
|
|
102
|
+
* not an update, 200 once the update is recorded — before it is handled.
|
|
103
|
+
*/
|
|
104
|
+
export declare function receiveTelegramWebhook(request: Request, options: ReceiveTelegramWebhookOptions): Promise<Response>;
|
|
105
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/telegram/webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA2B,SAAQ,qBAAqB;IACvE;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,oFAAoF;IACpF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,sBAAsB;AAChC,sCAAsC;AACpC,iBAAiB;AACnB,6DAA6D;GAC3D,cAAc;AAChB,2EAA2E;GACzE,eAAe,CAAC;AAEpB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,YAAY,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,EAa5D;CACF;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAcD,mFAAmF;AACnF,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUrF;AA+BD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,oBAAoB,CAAC,CAgC/B;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAS/B;AAED,+EAA+E;AAC/E,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5E,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACtE;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,QAAQ,CAAC,CAUnB"}
|
|
@@ -20,6 +20,14 @@ export interface FieldInfo {
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function describeSchemaFields(schema: z.ZodType | undefined): Map<string, FieldInfo>;
|
|
22
22
|
/** Strict boolean for a RESERVED option — an unrecognised value is a usage error, never a silent `true`. */
|
|
23
|
+
/**
|
|
24
|
+
* The token after a bare boolean flag, when it is the flag's value: exactly
|
|
25
|
+
* `true` or `false` — `--verbose false` is a common habit, and reading it as
|
|
26
|
+
* "on, plus a positional `false`" silently does the opposite of what was said.
|
|
27
|
+
* Only those two words: `1`, `yes` or `no` there stay positionals, since a
|
|
28
|
+
* flag followed by an ordinary value must not swallow it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function separateBoolValue(next: string | undefined): boolean | undefined;
|
|
23
31
|
/** True for a value the reserved-boolean grammar already claims. */
|
|
24
32
|
export declare function isReservedBoolWord(value: string): boolean;
|
|
25
33
|
export declare function parseReservedBool(name: string, value: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6ED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAKD,4GAA4G;AAC5G,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
|
|
1
|
+
{"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6ED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAKD,4GAA4G;AAC5G;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAI/E;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args-route.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-route.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAGD,eAAO,MAAM,YAAY,aAA6D,CAAC;AAEvF,eAAO,MAAM,oBAAoB,aAA+C,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAcrF;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"args-route.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-route.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAGD,eAAO,MAAM,YAAY,aAA6D,CAAC;AAEvF,eAAO,MAAM,oBAAoB,aAA+C,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAcrF;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,YAAY,CAuHlF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiB7B,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AAGjE,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;CACxB;AA4RD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,MAAM,GAAE;IACN,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,aAAa,CAUf;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS,GAC9B,qBAAqB,CAkDvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,+BAA+B,CAAC;AAEvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AA6DvD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC7C,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CASrC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAqCT;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,EAClC,kBAAkB,GAAE,SAAS,eAAe,EAAO,GAClD,MAAM,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,+BAA+B,CAAC;AAEvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AA6DvD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC7C,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CASrC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAqCT;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,EAClC,kBAAkB,GAAE,SAAS,eAAe,EAAO,GAClD,MAAM,CA6DR"}
|
package/llms/cli.txt
CHANGED
|
@@ -194,12 +194,18 @@ string, the schema says what it should be:
|
|
|
194
194
|
| -------------------- | ---------------------------------------------- |
|
|
195
195
|
| `z.string()` | `--name "box"` or a positional |
|
|
196
196
|
| `z.number()` | `--count 3` → `3` |
|
|
197
|
-
| `z.boolean()` | `--active` (presence) / `--no-active`
|
|
197
|
+
| `z.boolean()` | `--active` (presence) / `--active false` / `--no-active` |
|
|
198
198
|
| `z.enum([...])` | `--size large` |
|
|
199
199
|
| `z.array(z.string())`| `--tag a --tag b` → `["a","b"]` |
|
|
200
200
|
| `z.object({...})` | `--opts '{"k":"v"}'` (JSON) or `--opts.k v` |
|
|
201
201
|
| `.optional()` / `.default()` | not required |
|
|
202
202
|
|
|
203
|
+
After a bare boolean flag, the next token is its value when it is exactly
|
|
204
|
+
`true` or `false` — `--active false` turns it off, as it reads. Any other word
|
|
205
|
+
there stays a positional, and `--` makes a literal `false` one too. The same
|
|
206
|
+
holds for a short alias, a framework flag such as `--json false` and an
|
|
207
|
+
application option.
|
|
208
|
+
|
|
203
209
|
Without presentation configuration, positional arguments fill non-boolean
|
|
204
210
|
fields in declaration order, so `myapp generate "a fox"` is
|
|
205
211
|
`--prompt "a fox"`. A piped value fills the first required unset field:
|
|
@@ -258,7 +264,7 @@ framework globals may precede that explicit command. Top-level `--help`, `-h`
|
|
|
258
264
|
and `--version` never execute the default, and top-level help marks it.
|
|
259
265
|
|
|
260
266
|
Aliases are command-local, one ASCII letter and validated against the resolved
|
|
261
|
-
command schema. `-f` / `-f=false` are boolean forms; values accept `-n 100` and
|
|
267
|
+
command schema. `-f` / `-f=false` / `-f false` are boolean forms; values accept `-n 100` and
|
|
262
268
|
`-n=100`. Arrays accumulate across short and long forms. `-h` is reserved,
|
|
263
269
|
bundles such as `-fn`, attached values such as `-n100`, `--no-f` and unknown
|
|
264
270
|
short flags are rejected. Canonical `--no-follow` remains available.
|
package/llms/observability.txt
CHANGED
|
@@ -577,6 +577,18 @@ A payload goes into an audit row only after `sanitizePayload`:
|
|
|
577
577
|
`tokenizer`) survive. This applies wherever a key exists — an object field and
|
|
578
578
|
a `Map` entry alike, at any depth. A `Set` member has no key, so nothing there
|
|
579
579
|
is masked by name;
|
|
580
|
+
- **secrets are masked by their shape** in every string, whatever key holds
|
|
581
|
+
it — the secret half of a Telegram bot token (`123456:[redacted]`: the bot's
|
|
582
|
+
id stays, it tells two bots apart), the password in
|
|
583
|
+
`postgresql://user:password@host` (user, host and path stay), and the value
|
|
584
|
+
of a query parameter named as a secret (`?access_token=`, `?api-key=`,
|
|
585
|
+
`?key=`, `?sig=`). A URL in an error message or a request log line is the
|
|
586
|
+
usual way a token reaches a journal;
|
|
587
|
+
- **an error keeps what it says about itself** — its own fields (`error_code`,
|
|
588
|
+
`parameters`, a provider's `status`) are written beside `name`, `message`,
|
|
589
|
+
`stack` and `cause`, masked by the same rules; a field holding a class
|
|
590
|
+
instance other than an error (grammY's `BotError.ctx`) is named, not written,
|
|
591
|
+
so one error cannot push its line past the bound and lose the stack;
|
|
580
592
|
- **binary blobs** (`Uint8Array`, `Blob`, `FormData`) collapse to metadata —
|
|
581
593
|
never the bytes;
|
|
582
594
|
- the result is **capped** — anything over the byte limit becomes a preview.
|
|
@@ -835,7 +847,8 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
835
847
|
| `createBoundedLogger` | function | decorate a `StitchLogger` with request context, shared sanitisation, redaction and total bounds |
|
|
836
848
|
| `DEFAULT_REDACT_PATHS` | constant | baseline credential/token paths added to the sanitizer's sensitive-key policy |
|
|
837
849
|
| `createJsonLogger` | function | the process journal: one JSON line per call in pino's shape (numeric `level`, epoch `time`, `msg`) on standard output, through `createBoundedLogger` — an `Error` in any field keeps name, message, stack and cause; secrets masked; `level` threshold, static `fields`, injectable `write` |
|
|
838
|
-
| `JsonLoggerOptions` / `JsonLogLevel` / `JSON_LOG_LEVELS` | _type_ / constant | the bounded-logger options plus `level`, `fields`, `write`, `now`; pino's numbers `debug` 20 … `error` 50, which a line reader matches on |
|
|
850
|
+
| `JsonLoggerOptions` / `JsonLogLevel` / `JSON_LOG_LEVELS` | _type_ / constant | the bounded-logger options plus `level`, `fields`, `write`, `now`; pino's numbers `debug` 20 … `error` 50, which a line reader matches on; `sensitiveValues` masks exact secrets in every string |
|
|
851
|
+
| `secretValuesFromEnv` / `SecretValuesOptions` | function / _type_ | every secret an environment holds, for `sensitiveValues`: variables named as a token, secret, key, password or credential, the password and secret query values in any URL variable, each URL-encoded too, longest first; values under `minLength` (8) skipped |
|
|
839
852
|
| `BoundedLoggerBounds` / `BoundedLoggerOptions` | _type_ | per-value and total record ceilings (`stringLengthByKey` raises the string bound for named keys such as `stack`) plus sink/redaction configuration |
|
|
840
853
|
| `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
|
|
841
854
|
| `runUnitOfWork` / `RunUnitOfWorkOptions` | function / _type_ | run work that did not arrive over a transport — an agent loop, a scheduled broadcast — inside one context, with the same completion record a request gets |
|
|
@@ -888,7 +901,7 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
888
901
|
| Export | Kind | Summary |
|
|
889
902
|
|--------|------|---------|
|
|
890
903
|
| `sanitizePayload` | function | redact secrets and cap size — [guide](../guide/observability.md#sanitisation) |
|
|
891
|
-
| `redact` | function | mask secret-named keys, drop binary blobs |
|
|
904
|
+
| `redact` | function | mask secret-named keys and secrets recognised by shape in every string — the secret half of a bot token, a password in an address, a query parameter named as a secret; drop binary blobs; an error keeps its own fields (`error_code`, `parameters`), a class instance on one is named, not written |
|
|
892
905
|
| `truncatePreview` | function | cap a value by serialised size |
|
|
893
906
|
| `measureSize` | function | item count + byte size of a result |
|
|
894
907
|
| `JsonValue` | _type_ | a JSON-serialisable value |
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# stitchkit/react/keyboard — stitchkit docs slice
|
|
2
|
+
|
|
3
|
+
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
|
+
> `llms.txt` in this package.
|
|
5
|
+
|
|
6
|
+
Contains: Guide: Keyboard layers and list zones (docs/guide/keyboard.md) · API reference: `stitchkit/react/keyboard` (docs/api/reference.md)
|
|
7
|
+
|
|
8
|
+
==============================================================================
|
|
9
|
+
# Guide: Keyboard layers and list zones (docs/guide/keyboard.md)
|
|
10
|
+
==============================================================================
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
title: Keyboard layers and list zones
|
|
14
|
+
description: One listener decides which part of a screen gets a key; lists move real focus with the arrows
|
|
15
|
+
type: guide
|
|
16
|
+
status: active
|
|
17
|
+
created: 2026-09-29
|
|
18
|
+
updated: 2026-09-29
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Keyboard layers and list zones
|
|
22
|
+
|
|
23
|
+
`stitchkit/react/keyboard` answers one question for a React screen: **who gets
|
|
24
|
+
this key?** It is headless — three hooks and no component, no styles, no
|
|
25
|
+
router. The application keeps its URL, its sections and its markup; the hooks
|
|
26
|
+
decide which part of the screen hears a key and move focus through lists.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun add react # the only peer
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Why layers
|
|
33
|
+
|
|
34
|
+
A screen with a menu, a dialog, a list and section shortcuts usually grows a
|
|
35
|
+
`keydown` listener per concern, and they run in the order they mounted:
|
|
36
|
+
Escape closes the dialog *and* the page behind it, an arrow in a dialog also
|
|
37
|
+
switches the section underneath, a hidden panel still reacts. Here every
|
|
38
|
+
concern is a layer on one stack, and the stack asks them in a fixed order until
|
|
39
|
+
one takes the key:
|
|
40
|
+
|
|
41
|
+
| Kind | What it is | Example |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `overlay` | something modal; while one is live nothing below hears any key | a dialog |
|
|
44
|
+
| `local` | a component with the user's attention, not modal | an open command menu |
|
|
45
|
+
| `route` | the page's "up one level" | Escape closes the open entity |
|
|
46
|
+
| `zone` | a list the arrows move through (see below) | the chat list |
|
|
47
|
+
| `global` | shortcuts of the whole screen | ← / → switch sections |
|
|
48
|
+
|
|
49
|
+
Within a kind the innermost layer wins: of two layers whose elements nest, the
|
|
50
|
+
inner one, otherwise the one activated last. One press of Escape closes one
|
|
51
|
+
level, and holding Escape does not close several.
|
|
52
|
+
|
|
53
|
+
The stack listens on the window, in the bubbling phase. Everything closer to the
|
|
54
|
+
key hears it first — a widget's own `onKeyDown`, a text field, a popover that
|
|
55
|
+
closes on Escape — and keeps it with `preventDefault()` or `stopPropagation()`.
|
|
56
|
+
A layer only gets what nobody nearer wanted, so third-party components keep
|
|
57
|
+
working unchanged.
|
|
58
|
+
|
|
59
|
+
## Shortcuts and Escape
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { useRef } from 'react'
|
|
63
|
+
import { useEscapeLayer, useKeyLayer } from 'stitchkit/react/keyboard'
|
|
64
|
+
|
|
65
|
+
function Workspace({ next, previous, children }) {
|
|
66
|
+
useKeyLayer({
|
|
67
|
+
kind: 'global',
|
|
68
|
+
onKey: (event) => {
|
|
69
|
+
if (event.key === 'ArrowRight') next()
|
|
70
|
+
else if (event.key === 'ArrowLeft') previous()
|
|
71
|
+
else return false // not taken: the key keeps its default
|
|
72
|
+
return true
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
return children
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function EntityPanel({ close }) {
|
|
79
|
+
const panel = useRef<HTMLElement>(null)
|
|
80
|
+
useEscapeLayer({ kind: 'route', scope: panel, onEscape: close })
|
|
81
|
+
return <section ref={panel}>…</section>
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`onKey` returns `true` when it took the key: its default is prevented and no
|
|
86
|
+
layer below hears it. `onEscape` returns `false` when there was nothing to close,
|
|
87
|
+
and the next layer is asked.
|
|
88
|
+
|
|
89
|
+
**`scope`** ties a layer to an element. A scoped layer is heard only while the
|
|
90
|
+
element is mounted, visible and outside any `inert` subtree — a panel kept
|
|
91
|
+
mounted in the background stops hearing keys without an `active` flag — and it
|
|
92
|
+
outranks layers of its kind whose elements contain it. React runs a child's
|
|
93
|
+
effects before its parent's, so when a page and the panel inside it mount
|
|
94
|
+
together, only `scope` tells the stack which one is inside.
|
|
95
|
+
|
|
96
|
+
**`active`** (default `true`) switches a layer off without unmounting it;
|
|
97
|
+
becoming active again puts it on top of its kind.
|
|
98
|
+
|
|
99
|
+
**Fields keep their keys.** Keys pressed in an input, a text area, a select, an
|
|
100
|
+
editable region, a combobox, a slider or any area marked `data-local-keys` do not
|
|
101
|
+
reach layers — except Escape, which a field that wants it keeps with
|
|
102
|
+
`preventDefault()`. `fields: 'include'` lets a layer hear them anyway.
|
|
103
|
+
`isKeyFieldTarget(event.target)` is the same test, for a handler of your own.
|
|
104
|
+
|
|
105
|
+
## List zones
|
|
106
|
+
|
|
107
|
+
A zone is a list the arrows move through with **real focus**: one item is the
|
|
108
|
+
tab stop (roving `tabIndex`), Tab enters the list at it and leaves the list, the
|
|
109
|
+
arrows move DOM focus between items. Screen readers follow it, `:focus-visible`
|
|
110
|
+
draws it, the browser scrolls to it.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { useNavZone } from 'stitchkit/react/keyboard'
|
|
114
|
+
|
|
115
|
+
function ChatList({ chats, openId, open }) {
|
|
116
|
+
const zone = useNavZone({
|
|
117
|
+
items: chats.map((chat) => chat.id),
|
|
118
|
+
value: openId,
|
|
119
|
+
onChange: (id) => open(id),
|
|
120
|
+
label: 'Chats',
|
|
121
|
+
})
|
|
122
|
+
return (
|
|
123
|
+
<ul {...zone.zoneProps}>
|
|
124
|
+
{chats.map((chat) => (
|
|
125
|
+
<li key={chat.id} {...zone.itemProps(chat.id)}>
|
|
126
|
+
{chat.title}
|
|
127
|
+
</li>
|
|
128
|
+
))}
|
|
129
|
+
</ul>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Option | Default | Meaning |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `items`, `value`, `onChange` | — | the ids in order, the chosen one, and the report of a choice; the zone never holds the value |
|
|
137
|
+
| `mode` | `'select'` | `'select'`: an arrow chooses (`onChange(id, 'arrow')`). `'highlight'`: an arrow only moves focus, Enter chooses (`onChange(id, 'confirm')`) |
|
|
138
|
+
| `orientation` | `'vertical'` | which arrows move; the other axis enters and exits |
|
|
139
|
+
| `role` | `'listbox'` | `'listbox'` of options or `'tablist'` of tabs, marked `aria-selected`; `'none'` keeps the items' own semantics (links, buttons) and marks the chosen one `aria-current` |
|
|
140
|
+
| `loop` | `false` | wrap past the ends instead of calling `onBoundary` |
|
|
141
|
+
| `onEnter(id)` | — | → in a vertical zone (↓ in a horizontal one), and Enter in select mode: go into what the item opens |
|
|
142
|
+
| `onExit()` | — | ← in a vertical zone (↑ in a horizontal one) |
|
|
143
|
+
| `onBoundary(side)` | — | a move past `'start'` or `'end'`: hand focus to the neighbouring zone |
|
|
144
|
+
| `active` | `true` | whether arrows pressed outside every zone land here |
|
|
145
|
+
| `label` | — | the zone's accessible name |
|
|
146
|
+
| `onReveal(id)` | — | focus went to an item with no element yet — a row of a virtualized list outside the rendered range; scroll it in, and focus lands on it when it mounts |
|
|
147
|
+
|
|
148
|
+
The callbacks return `false` to leave the key to the page, anything else takes
|
|
149
|
+
it. A key the zone does not take bubbles on to the layers — in a vertical list
|
|
150
|
+
← and → are not the list's keys, so the section switch above hears them.
|
|
151
|
+
|
|
152
|
+
Each zone keeps its own focused item; two zones never share one. When focus is
|
|
153
|
+
in no zone at all — right after the page loads — the first arrow on the zone's
|
|
154
|
+
axis puts focus on the active zone's current item. To move between zones
|
|
155
|
+
(sidebar → table), call `zone.focus(id?)` of the other zone from `onEnter` or
|
|
156
|
+
`onBoundary`. `zone.element` is the zone's element, for a layer scoped to it.
|
|
157
|
+
|
|
158
|
+
## What stays in the application
|
|
159
|
+
|
|
160
|
+
The URL, the router, sections and which screen is open. A layer calls the
|
|
161
|
+
application's own functions — `router.push`, `close()` — and nothing here reads
|
|
162
|
+
or writes a location. Keep entity coordinates in the path and filters in the
|
|
163
|
+
query, parse and build them in one pure module with tests, and use explicit
|
|
164
|
+
`push` / `replace` rather than a positional flag.
|
|
165
|
+
|
|
166
|
+
==============================================================================
|
|
167
|
+
# API reference: `stitchkit/react/keyboard` (docs/api/reference.md)
|
|
168
|
+
==============================================================================
|
|
169
|
+
|
|
170
|
+
## `stitchkit/react/keyboard`
|
|
171
|
+
|
|
172
|
+
Browser-only, headless keyboard handling for React: which part of a screen gets a
|
|
173
|
+
key, and lists the arrows move through with real focus. Needs the `react` peer;
|
|
174
|
+
no component, no styles, no router — see the [keyboard guide](../guide/keyboard.md).
|
|
175
|
+
|
|
176
|
+
| Export | Kind | Summary |
|
|
177
|
+
|--------|------|---------|
|
|
178
|
+
| `useKeyLayer` | hook | put a layer on the window's one key stack — asked `overlay` → `local` → `route` → `zone` → `global`, innermost first within a kind |
|
|
179
|
+
| `useEscapeLayer` | hook | an `overlay`, `local` or `route` layer for Escape: one press closes one level |
|
|
180
|
+
| `useNavZone` | hook | a list zone — roving tab stop, arrows move DOM focus, select or highlight mode, cross-axis enter/exit, boundaries |
|
|
181
|
+
| `nextZoneIndex` | function | where a move lands in a list of a given length: an index, `'before-start'` or `'after-end'` |
|
|
182
|
+
| `isKeyFieldTarget` | function | whether a key was pressed in a field or a `data-local-keys` area, the test layers use |
|
|
183
|
+
| `KeyLayerOptions` | _type_ | `kind`, `onKey`, `active`, `scope`, `fields` |
|
|
184
|
+
| `KeyLayerKind` | _type_ | `'overlay' \| 'local' \| 'route' \| 'zone' \| 'global'` |
|
|
185
|
+
| `EscapeLayerOptions` | _type_ | `kind`, `onEscape`, `active`, `scope` |
|
|
186
|
+
| `NavZoneOptions` | _type_ | `items`, `value`, `onChange`, `mode`, `orientation`, `role`, `loop`, `onEnter`, `onExit`, `onBoundary`, `active`, `label`, `onReveal` |
|
|
187
|
+
| `NavZone` | _type_ | what `useNavZone` returns — `zoneProps`, `itemProps(id)`, `element`, `current`, `focus(id?)` |
|
|
188
|
+
| `NavZoneProps` / `NavZoneItemProps` | _type_ | the props spread on the zone's element and on each item |
|
|
189
|
+
| `NextZoneIndexInput` | _type_ | `length`, `index`, `move`, `loop` |
|
|
190
|
+
| `ZoneItemId` | _type_ | an item id — `string \| number` |
|
|
191
|
+
| `ZoneChangeCause` | _type_ | `'arrow' \| 'confirm'` |
|
|
192
|
+
| `ZoneMove` | _type_ | `'next' \| 'previous' \| 'first' \| 'last'` |
|
|
193
|
+
| `ZoneOrientation` | _type_ | `'vertical' \| 'horizontal'` |
|
package/llms/server.4.txt
CHANGED
|
@@ -357,6 +357,11 @@ Three things it does that a hand-written check usually does not:
|
|
|
357
357
|
- `raw` keeps every signed pair, so a field Telegram adds after this release
|
|
358
358
|
does not need a release to reach.
|
|
359
359
|
|
|
360
|
+
The user arrives `camelCase` — `firstName`, `languageCode` — as `TelegramUser`.
|
|
361
|
+
A bot meets the same person as grammY's `ctx.from`, in Telegram's `snake_case`;
|
|
362
|
+
`parseTelegramUser(ctx.from)` gives the same `TelegramUser`, so one
|
|
363
|
+
`ensureAccount(user)` serves the Mini App and the chat.
|
|
364
|
+
|
|
360
365
|
`maxAgeSeconds` is optional and there is no default: only the application knows
|
|
361
366
|
how long its own session is worth. Omitting it means the string never expires,
|
|
362
367
|
which is a decision rather than an oversight.
|
|
@@ -386,6 +391,17 @@ and our payload is wrong, which is the case a list of substrings quietly counts
|
|
|
386
391
|
against the user. An unrecognised refusal leaves the recipient reachable: losing
|
|
387
392
|
a working subscriber forever costs more than one wasted send.
|
|
388
393
|
|
|
394
|
+
`TELEGRAM_SEND_FAILURE_REASONS` lists every reason. A table keyed by
|
|
395
|
+
`TelegramSendFailureReason` — error codes, the text a person reads — fails to
|
|
396
|
+
compile when a release adds one, instead of falling through to a default:
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
const codes: Record<TelegramSendFailureReason, string> = {
|
|
400
|
+
'blocked-by-user': 'E_BLOCKED',
|
|
401
|
+
/* … every reason … */
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
389
405
|
The rest of what a bot is assembled from — its resources, journal, operator
|
|
390
406
|
channel, broadcasts and local Bot API files — is in [Telegram bots](telegram.md).
|
|
391
407
|
|
package/llms/stitchkit.txt
CHANGED
|
@@ -69,7 +69,9 @@ own, recorded as an ADR.
|
|
|
69
69
|
| `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
|
|
70
70
|
| `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
71
71
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
72
|
-
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel
|
|
72
|
+
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
|
|
73
|
+
| `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
|
|
74
|
+
| `stitchkit/telegram/screens` | server (Bun or Node) | evolving | a bot's menus as declared screens over grammY — typed paths, actions and input, message reconciliation, state in any `StorageAdapter`, a test chat |
|
|
73
75
|
| `stitchkit/tracking` | browser **and** server | evolving | visitor-tracking mechanics — `createTrackingClient`, the tab-shared outbox, the page-leave beacon, attribution, the contract factory; no event vocabulary, no React |
|
|
74
76
|
| `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
|
|
75
77
|
| `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
|
|
@@ -77,11 +79,12 @@ own, recorded as an ADR.
|
|
|
77
79
|
| `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
|
|
78
80
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
79
81
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
80
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
82
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 46 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
81
83
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
|
|
82
84
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
83
85
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
84
|
-
| `stitchkit/
|
|
86
|
+
| `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) |
|
|
87
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 46 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
85
88
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
86
89
|
| `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 |
|
|
87
90
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -91,7 +94,7 @@ own, recorded as an ADR.
|
|
|
91
94
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
92
95
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
93
96
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
94
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 10 of the
|
|
97
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 10 of the 46 minors since 0.56.2, most recently 0.100.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
95
98
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
96
99
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
97
100
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -203,6 +206,7 @@ map — feature → packages:
|
|
|
203
206
|
| MCP host/client tests | `@modelcontextprotocol/client` |
|
|
204
207
|
| MCP Apps UI widgets | `@modelcontextprotocol/ext-apps` |
|
|
205
208
|
| React data layer (`stitchkit/react`) | `@tanstack/react-query` `react-query-kit` |
|
|
209
|
+
| Keyboard layers and list zones (`stitchkit/react/keyboard`) | `react` |
|
|
206
210
|
| MaxMind GeoIP (`stitchkit/geo`) | `maxmind` |
|
|
207
211
|
| Browser OAuth client (`stitchkit/oauth`) | — |
|
|
208
212
|
| Google OIDC verifier (`stitchkit/google`) | `google-auth-library` |
|
|
@@ -211,6 +215,8 @@ map — feature → packages:
|
|
|
211
215
|
| Socket.IO client | `socket.io-client` (runtime peer; unrelated root declarations remain peer-free) |
|
|
212
216
|
| grammY lifecycle adapters (`stitchkit/application/grammy`) | `grammy` |
|
|
213
217
|
| Telegram platform primitives (`stitchkit/telegram`) | — (peer-free) |
|
|
218
|
+
| Telegram message markup (`stitchkit/telegram/html`) | — (peer-free) |
|
|
219
|
+
| Telegram screens (`stitchkit/telegram/screens`) | `grammy` (types only; the bot brings it) |
|
|
214
220
|
| OpenTelemetry gauges (`stitchkit/application/opentelemetry`) | `@opentelemetry/api` |
|
|
215
221
|
|
|
216
222
|
```bash
|