stitchkit 0.100.1 → 0.100.3
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 +99 -0
- package/README.md +2 -1
- package/dist/application/process-lifecycle-transitions.d.ts +13 -2
- package/dist/application/process-lifecycle-transitions.d.ts.map +1 -1
- package/dist/application/process-lifecycle.d.ts +2 -1
- package/dist/application/process-lifecycle.d.ts.map +1 -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.d.ts +1 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +218 -214
- 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 +115 -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 +5 -1
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +548 -156
- 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 +223 -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-6k1svmjh.js +141 -0
- 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-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-yw4fzmdz.js → index-kzz6rq0w.js} +28 -17
- 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-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/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/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.map +1 -1
- package/dist/telegram/operator-channel.d.ts +36 -3
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- 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 +19 -0
- package/dist/telegram/send-failure.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +78 -0
- package/dist/telegram/update-intake.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/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/application.2.txt +26 -1
- package/llms/application.txt +18 -27
- package/llms/cli.txt +8 -2
- package/llms/observability.txt +2 -1
- package/llms/react-keyboard.txt +193 -0
- package/llms/stitchkit.txt +7 -1
- package/llms/telegram-html.txt +35 -0
- package/llms/telegram-screens.txt +365 -0
- package/llms/telegram.txt +160 -4
- package/llms-full.txt +755 -11
- package/llms.txt +11 -6
- 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
package/llms/application.txt
CHANGED
|
@@ -876,7 +876,8 @@ ever became ready:
|
|
|
876
876
|
| none | — | — | `first-boot` | — |
|
|
877
877
|
| recorded `stoppedAt` | — | — | `clean` / `forced` / `startup-failed` / `abnormal` as recorded | unchanged |
|
|
878
878
|
| still open | yes | — | `hot-reload` | `hot-reload`, closed at the new start |
|
|
879
|
-
| still open | no | changed | `handoff` | stays open; it records its own stop later |
|
|
879
|
+
| still open | no | changed | `handoff` (default) | stays open; it records its own stop later |
|
|
880
|
+
| still open | no | changed, `versionChangeOverlap: 'abnormal'` | `abnormal` / `startup-failed` | closed at the new start, as for the same version |
|
|
880
881
|
| still open, was ready | no | same | `abnormal` (default) | `abnormal`, closed at the new start — an upper bound, the crash time is unknown |
|
|
881
882
|
| still open, never ready | no | same | `startup-failed` (default) | `startup-failed`, closed at the new start |
|
|
882
883
|
|
|
@@ -918,7 +919,22 @@ zero-downtime reload of the same build — pass `sameVersionOverlap: 'handoff'`
|
|
|
918
919
|
to the ledger, and every open run stays open until it records its own
|
|
919
920
|
shutdown. The cost of that setting is symmetric: a real crash under it is
|
|
920
921
|
reported as a handoff and the dead run stays open in the ledger until
|
|
921
|
-
retention drops it.
|
|
922
|
+
retention drops it.
|
|
923
|
+
|
|
924
|
+
A version change is the other axis, and its default is the opposite:
|
|
925
|
+
`handoff`, because a release that starts the new build before stopping the
|
|
926
|
+
old one is the common zero-downtime shape. Where builds never overlap — one
|
|
927
|
+
process per deployment (a pm2 fork, a systemd unit, one container), the old
|
|
928
|
+
one stopped before the new one starts — pass `versionChangeOverlap:
|
|
929
|
+
'abnormal'`. An open predecessor of another build then died without
|
|
930
|
+
recording its exit, most often killed during the release itself: it is closed
|
|
931
|
+
as `abnormal` (or `startup-failed`) at the new start, `downtimeMs` is `null`
|
|
932
|
+
instead of `0`, and `previousVersion` / `versionChanged` still describe the
|
|
933
|
+
release. A predecessor that did record its stop is read as before, and its
|
|
934
|
+
downtime is measured. The two settings compose: `sameVersionOverlap:
|
|
935
|
+
'handoff'` keeps open only the runs of a build allowed to overlap the new one.
|
|
936
|
+
|
|
937
|
+
The list is kept in the order the transitions wrote it —
|
|
922
938
|
every write goes through one atomic update, so that order is the causal one,
|
|
923
939
|
and a successor whose clock lags its predecessor still finds it at the head;
|
|
924
940
|
`startedAt` is data, not the sort key. Retention (`retain`, default 20) drops
|
|
@@ -991,28 +1007,3 @@ so a scheduler stall on a loaded host cannot pull a live lock from under its
|
|
|
991
1007
|
holder — after which the heartbeat wins and the lock is abandoned. Temporary files a crashed writer left beside the state are swept on
|
|
992
1008
|
the store's first update. Ledger corruption may be declared reconstructable;
|
|
993
1009
|
an outbox must fail closed rather than silently discard pending delivery.
|
|
994
|
-
|
|
995
|
-
## Recovering change subscriptions
|
|
996
|
-
|
|
997
|
-
`changeSubscriptionResource` is an optional resource over a consumer-owned driver.
|
|
998
|
-
It uses a finite declared key set, subscribes before reading, reconciles after
|
|
999
|
-
reconnect and periodically repairs lost hints. `reconcileIntervalMs` is explicit;
|
|
1000
|
-
notifications alone cannot prove freshness. Within a key, hints coalesce into one
|
|
1001
|
-
trailing read and a failed read retries with backoff. Use the supplied
|
|
1002
|
-
`context.commit(() => publish(value))` after awaits to reject an obsolete connection.
|
|
1003
|
-
|
|
1004
|
-
Drivers implement `connect(signal)` and return `subscribe({ signal, hint,
|
|
1005
|
-
disconnected })` plus `close()`. Subscribe must establish the notification boundary
|
|
1006
|
-
before resolving. A database LISTEN driver owns its connection, channels, decoding
|
|
1007
|
-
and transaction ordering; no SQL or database driver is included here. The resource
|
|
1008
|
-
owns reconnect, deadlines and shutdown. Drivers must honor abort. If one ignores it,
|
|
1009
|
-
its slot remains occupied until settlement; deadlines cannot safely manufacture
|
|
1010
|
-
successful cleanup or permit an overlapping connection.
|
|
1011
|
-
|
|
1012
|
-
For watched reads, set `reconcileIntervalMs` on the existing `createWatchHub` rather
|
|
1013
|
-
than building a second read loop. Its per-key machinery is shared with managed
|
|
1014
|
-
subscriptions. Limits default to 1024 sources and 1024 subscribers per hub; set
|
|
1015
|
-
explicit finite values for larger workloads. Supply a server-verified admission
|
|
1016
|
-
scope to `attach` and abort it on logout, permission revocation or session replacement.
|
|
1017
|
-
The read callback receives the trusted scope as its third argument. A client owner
|
|
1018
|
-
ID in arguments is not authentication.
|
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
|
@@ -835,7 +835,8 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
835
835
|
| `createBoundedLogger` | function | decorate a `StitchLogger` with request context, shared sanitisation, redaction and total bounds |
|
|
836
836
|
| `DEFAULT_REDACT_PATHS` | constant | baseline credential/token paths added to the sanitizer's sensitive-key policy |
|
|
837
837
|
| `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 |
|
|
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; `sensitiveValues` masks exact secrets in every string |
|
|
839
|
+
| `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
840
|
| `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
841
|
| `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
|
|
841
842
|
| `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 |
|
|
@@ -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/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 |
|
|
@@ -81,6 +83,7 @@ own, recorded as an ADR.
|
|
|
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 |
|
|
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) |
|
|
84
87
|
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 45 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 |
|
|
@@ -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
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# stitchkit/telegram/html — 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: API reference: `stitchkit/telegram/html` (docs/api/reference.md)
|
|
7
|
+
|
|
8
|
+
Also read:
|
|
9
|
+
- see `llms/telegram.txt` for the Telegram bots guide
|
|
10
|
+
|
|
11
|
+
==============================================================================
|
|
12
|
+
# API reference: `stitchkit/telegram/html` (docs/api/reference.md)
|
|
13
|
+
==============================================================================
|
|
14
|
+
|
|
15
|
+
## `stitchkit/telegram/html`
|
|
16
|
+
|
|
17
|
+
Telegram's HTML parse mode as a tree: any markup in, markup Telegram accepts
|
|
18
|
+
out, cut to its limits by what the reader sees. No peer and no DOM — the same
|
|
19
|
+
parse serves a server send and a browser preview. → ADR 0213 —
|
|
20
|
+
[guide](../guide/telegram.md#message-markup)
|
|
21
|
+
|
|
22
|
+
| Export | Kind | Summary |
|
|
23
|
+
|--------|------|---------|
|
|
24
|
+
| `parseTelegramHtml` | function | markup into nodes Telegram accepts: synonyms renamed (`strong` → `b`), block tags into line breaks, a link only with an `http`/`https`/`tg`/`mailto` `href`, an element where Telegram forbids it kept as text, a closing tag closing what is open inside it; never throws |
|
|
25
|
+
| `TelegramHtmlNode` / `TelegramHtmlElement` / `TelegramHtmlTextNode` / `TelegramHtmlTag` / `TelegramHtmlStyle` | _type_ | text nodes hold decoded text; elements `b` `i` `u` `s` `tg-spoiler` `code`, `a` with `href`, `pre` with `language`, `blockquote` with `expandable`, `tg-emoji` with `emojiId`, `tg-time` with `unix` and `format` |
|
|
26
|
+
| `renderTelegramHtml` | function | nodes back to markup Telegram parses into exactly them |
|
|
27
|
+
| `sanitizeTelegramHtml` | function | any markup as markup Telegram accepts — `renderTelegramHtml(parseTelegramHtml(markup))` |
|
|
28
|
+
| `splitTelegramHtml` / `TelegramHtmlLimitOptions` | function / _type_ | parts within `limit` visible characters (default 4096), cut at a paragraph, a line, a space, else anywhere but inside a character or a custom emoji; an element across a cut is closed and reopened with its attributes; nothing visible is no parts |
|
|
29
|
+
| `truncateTelegramHtml` / `TelegramHtmlTruncateOptions` | function / _type_ | one part: cut to `limit` with `ellipsis` (`…`) inside it, still valid |
|
|
30
|
+
| `telegramHtmlText` | function | the text a reader sees, entities decoded |
|
|
31
|
+
| `checkTelegramHtml` / `TelegramHtmlCheck` / `TelegramHtmlProblem` | function / _type_ | the first thing Telegram would refuse, unrepaired — `bare-character` / `unsupported-tag` / `unmatched-end-tag` / `unclosed-tag` — and the visible text before it |
|
|
32
|
+
| `escapeTelegramHtml` | function | `&`, `<`, `>` and `"` escaped for Telegram's HTML |
|
|
33
|
+
| `TELEGRAM_TEXT_LIMIT` / `TELEGRAM_CAPTION_LIMIT` | constant | 4096 and 1024, counted after the markup is parsed, in UTF-16 code units |
|
|
34
|
+
|
|
35
|
+
---
|