stitchkit 0.107.0 → 0.108.1

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.
Files changed (102) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +2 -0
  3. package/darwin-native.cjs +1 -0
  4. package/dist/agent-runtime/sandbox-process-owner.d.ts +3 -3
  5. package/dist/agent-runtime/sandbox-process-owner.d.ts.map +1 -1
  6. package/dist/agent-runtime/sandbox-process.d.ts +2 -2
  7. package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
  8. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  9. package/dist/browser/stream.d.ts +38 -12
  10. package/dist/browser/stream.d.ts.map +1 -1
  11. package/dist/entrypoints/agent-runtime/coding-tools.js +2 -2
  12. package/dist/entrypoints/agent-runtime/sandbox.js +2 -2
  13. package/dist/entrypoints/application/diagnostic-journal.js +2 -2
  14. package/dist/entrypoints/application/directory-inbox.js +4 -4
  15. package/dist/entrypoints/bin/upgrade-cli.d.ts +32 -0
  16. package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -1
  17. package/dist/entrypoints/bin/upgrade-cli.js +451 -8
  18. package/dist/entrypoints/cli/publish.js +3 -3
  19. package/dist/entrypoints/files/packaging.d.ts +3 -1
  20. package/dist/entrypoints/files/packaging.d.ts.map +1 -1
  21. package/dist/entrypoints/files/packaging.js +105 -41
  22. package/dist/entrypoints/files.js +3 -3
  23. package/dist/entrypoints/index.d.ts +1 -1
  24. package/dist/entrypoints/index.d.ts.map +1 -1
  25. package/dist/entrypoints/index.js +7 -1
  26. package/dist/entrypoints/process.js +2 -2
  27. package/dist/entrypoints/remote.js +1 -1
  28. package/dist/entrypoints/server.js +4 -4
  29. package/dist/entrypoints/telegram/screens.js +1 -1
  30. package/dist/entrypoints/telegram.d.ts +2 -0
  31. package/dist/entrypoints/telegram.d.ts.map +1 -1
  32. package/dist/entrypoints/telegram.js +337 -3
  33. package/dist/entrypoints/testing.d.ts +1 -0
  34. package/dist/entrypoints/testing.d.ts.map +1 -1
  35. package/dist/entrypoints/testing.js +131 -1
  36. package/dist/files/native-artifact.d.ts +14 -0
  37. package/dist/files/native-artifact.d.ts.map +1 -0
  38. package/dist/files/native-packaging-layout.d.ts +5 -0
  39. package/dist/files/native-packaging-layout.d.ts.map +1 -1
  40. package/dist/files/native-packaging-plugin.d.ts +53 -0
  41. package/dist/files/native-packaging-plugin.d.ts.map +1 -0
  42. package/dist/files/native-packaging.d.ts +1 -35
  43. package/dist/files/native-packaging.d.ts.map +1 -1
  44. package/dist/index-a5qr2rj6.js +26 -0
  45. package/dist/{index-7r74re56.js → index-bh2ht9y5.js} +12 -6
  46. package/dist/{index-dwf0gymy.js → index-e35py42g.js} +1 -1
  47. package/dist/{index-4q9jvqg7.js → index-epg514ys.js} +182 -13
  48. package/dist/{index-mbf4h8kp.js → index-nx49x0tw.js} +1 -1
  49. package/dist/{index-zsqt5q98.js → index-p5kx5vqd.js} +209 -54
  50. package/dist/{index-bnf8s6v3.js → index-t2bsd0hm.js} +1 -1
  51. package/dist/{index-4f37d3bg.js → index-w9mqgnz9.js} +1 -1
  52. package/dist/{index-e05cjgtf.js → index-xvj9h8yh.js} +28 -1
  53. package/dist/internal/bounded-lines.d.ts +46 -2
  54. package/dist/internal/bounded-lines.d.ts.map +1 -1
  55. package/dist/internal/process-identity.d.ts.map +1 -1
  56. package/dist/internal/until-aborted.d.ts +9 -0
  57. package/dist/internal/until-aborted.d.ts.map +1 -0
  58. package/dist/internal/upgrade-affects.d.ts +58 -0
  59. package/dist/internal/upgrade-affects.d.ts.map +1 -0
  60. package/dist/internal/upgrade-plan.d.ts +20 -0
  61. package/dist/internal/upgrade-plan.d.ts.map +1 -1
  62. package/dist/internal/upgrade-usage.d.ts +61 -0
  63. package/dist/internal/upgrade-usage.d.ts.map +1 -0
  64. package/dist/process/bun-child.d.ts +38 -0
  65. package/dist/process/bun-child.d.ts.map +1 -0
  66. package/dist/process/command-owner.d.ts +2 -2
  67. package/dist/process/command-owner.d.ts.map +1 -1
  68. package/dist/process/launch.d.ts +2 -2
  69. package/dist/process/launch.d.ts.map +1 -1
  70. package/dist/process/owned-child.d.ts +2 -1
  71. package/dist/process/owned-child.d.ts.map +1 -1
  72. package/dist/telegram/bot-api.d.ts +10 -4
  73. package/dist/telegram/bot-api.d.ts.map +1 -1
  74. package/dist/telegram/broadcast.d.ts +2 -1
  75. package/dist/telegram/broadcast.d.ts.map +1 -1
  76. package/dist/telegram/not-dispatched.d.ts +26 -0
  77. package/dist/telegram/not-dispatched.d.ts.map +1 -0
  78. package/dist/telegram/operator-channel.d.ts +2 -1
  79. package/dist/telegram/operator-channel.d.ts.map +1 -1
  80. package/dist/telegram/send-failure.d.ts +4 -3
  81. package/dist/telegram/send-failure.d.ts.map +1 -1
  82. package/dist/telegram/transport-connect.d.ts +38 -0
  83. package/dist/telegram/transport-connect.d.ts.map +1 -0
  84. package/dist/telegram/transport.d.ts +43 -0
  85. package/dist/telegram/transport.d.ts.map +1 -0
  86. package/dist/telegram/webhook.d.ts +2 -1
  87. package/dist/telegram/webhook.d.ts.map +1 -1
  88. package/dist/testing/manual-clock.d.ts +82 -0
  89. package/dist/testing/manual-clock.d.ts.map +1 -0
  90. package/llms/files-packaging.txt +4 -1
  91. package/llms/process.txt +19 -5
  92. package/llms/stitchkit.2.txt +211 -628
  93. package/llms/stitchkit.txt +635 -10
  94. package/llms/telegram.txt +36 -3
  95. package/llms/testing.txt +47 -0
  96. package/llms/{upgrading-0.98.0-0.107.0.txt → upgrading-0.100.0-0.108.0.txt} +59 -64
  97. package/llms/{upgrading-0.72.0-0.97.0.txt → upgrading-0.72.0-0.99.0.txt} +64 -3
  98. package/llms/upgrading.txt +34 -2
  99. package/llms-full.txt +246 -17
  100. package/llms.txt +8 -8
  101. package/package.json +1 -1
  102. package/llms/stitchkit.3.txt +0 -177
package/llms-full.txt CHANGED
@@ -62,9 +62,9 @@ own, recorded as an ADR.
62
62
  | `stitchkit/cli/publish` | build and release tooling (Bun or Node, POSIX) | evolving | `publishCli` — commit a verified CLI distribution and move its public manifest last; see [CLI guide](cli.md#publishing-a-complete-version) |
63
63
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
64
64
  | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
65
- | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity and Bun packaging plugin; no runtime install policy |
65
+ | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; no runtime install policy |
66
66
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
67
- | `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 |
67
+ | `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, a Bot API transport that separates a request that never left from an unknown outcome, local Bot API files, webhook ownership and durable webhook update intake |
68
68
  | `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 |
69
69
  | `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 |
70
70
  | `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,12 +74,12 @@ own, recorded as an ADR.
74
74
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
75
75
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
76
76
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
77
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 52 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
78
- | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
77
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 53 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
78
+ | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
79
79
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
80
80
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
81
81
  | `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) |
82
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 52 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
82
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 53 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
83
83
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
84
84
  | `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 |
85
85
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -91,7 +91,7 @@ own, recorded as an ADR.
91
91
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
92
92
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
93
93
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
94
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 52 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
94
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 53 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
95
95
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
96
96
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
97
97
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -3403,6 +3403,47 @@ Set `finalLine: 'require-newline'` when the final newline is part of the
3403
3403
  protocol's truncation proof. The default `allow` continues to accept one valid
3404
3404
  final JSON document without a newline.
3405
3405
 
3406
+ ### Any byte source
3407
+
3408
+ `parseNDJSON` (and `parseSSE`) read more than a `Response`: a `ReadableStream<Uint8Array>`
3409
+ (a fetch body, a Bun child's `stdout`) or any `AsyncIterable<Uint8Array>` (a Node child's
3410
+ `stdout`). `signal` stops reading and cancels or returns the source:
3411
+
3412
+ ```ts
3413
+ import { parseNDJSON } from 'stitchkit'
3414
+
3415
+ const child = Bun.spawn(['worker'], { stdout: 'pipe' })
3416
+ for await (const progress of parseNDJSON(child.stdout, {
3417
+ maxLineBytes: 64 * 1024,
3418
+ finalLine: 'require-newline',
3419
+ })) report(progress)
3420
+ ```
3421
+
3422
+ A source that hands over bytes through a callback — a `Bun.listen` or `Bun.connect` socket's
3423
+ `data` handler — uses `createNDJSONDecoder`, the same reader driven by pushes:
3424
+
3425
+ ```ts
3426
+ import { createNDJSONDecoder } from 'stitchkit'
3427
+
3428
+ const decoder = createNDJSONDecoder<ControlRequest>({ maxLineBytes: 16 * 1024 })
3429
+ Bun.listen({
3430
+ unix: socketPath,
3431
+ socket: {
3432
+ data(socket, bytes) {
3433
+ for (const request of decoder.push(bytes)) handle(socket, request)
3434
+ },
3435
+ },
3436
+ })
3437
+ ```
3438
+
3439
+ Use one decoder per connection. Lines are split in bytes and decoded only once complete, so a
3440
+ character split across chunks is never torn. A line that crosses `maxLineBytes` throws
3441
+ `StreamLineLimitError` (`limitBytes`, `lineBytes`) at the chunk that crossed it: the reader
3442
+ retains at most the limit, however long the line. With `finalLine: 'require-newline'`, a source
3443
+ that ends inside a line throws `StreamTruncatedLineError` (`lineBytes`) instead of yielding the
3444
+ fragment as a value — the right policy for a socket or a child process, where a missing newline
3445
+ means the writer died. Both errors extend the `RangeError` / `SyntaxError` they replace.
3446
+
3406
3447
  ## Resumable streams
3407
3448
 
3408
3449
  A stream that survives a dropped connection needs four things beyond opening it:
@@ -8606,11 +8647,20 @@ spool never load it, on any operating system. The package is free of import side
8606
8647
 
8607
8648
  Check an artifact by its loader, never by whether it holds `.node` bytes: Bun can copy the addon
8608
8649
  into a compiled executable even without the plugin, and the unpackaged loader does not use it.
8609
- Search the artifact for `STITCHKIT_NATIVE_NOT_PACKAGED` in both latin1 and UTF-16LE, because Bun
8610
- stores source text with non-ASCII characters as UTF-16. Present: the unpackaged loader is inside,
8611
- and every call above refuses on macOS. Absent in a plugin build: the static loader is inside
8612
- (`/$bunfs/root/darwin-<arch>-<hash>.node` in an executable, the companion path in a JS build).
8613
- Absent without the plugin: nothing in the artifact needs the addon.
8650
+ `inspectNativeArtifact(bytes)` from `stitchkit/files/packaging` reads the marker every generated
8651
+ loader carries and answers `packaged` (the plugin's static loader is inside), `unpackaged` (the
8652
+ default loader is inside, and every call above refuses on macOS) or `no-loader` (nothing in the
8653
+ artifact loads the addon). It reads both latin1 and UTF-16LE, because Bun stores source text with
8654
+ non-ASCII characters as UTF-16, and it does not mistake the `STITCHKIT_NATIVE_NOT_PACKAGED` error
8655
+ code, which every bundle with a lock carries, for a loader. Artifacts built by 0.107.0 or earlier
8656
+ carry no marker and read `no-loader`.
8657
+
8658
+ ```ts
8659
+ import { inspectNativeArtifact } from 'stitchkit/files/packaging'
8660
+
8661
+ const loader = inspectNativeArtifact(await Bun.file('dist/cli').bytes())
8662
+ if (loader === 'unpackaged') throw new Error('dist/cli was built without native packaging')
8663
+ ```
8614
8664
 
8615
8665
  ```sh
8616
8666
  bun build src/native.ts --target=bun --minify --outfile=dist/native.js
@@ -8690,6 +8740,11 @@ addon path accepts. `entryPath` is fixed, without
8690
8740
  Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
8691
8741
  and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
8692
8742
  Choose any application layout; the framework does not prescribe an app directory or installer.
8743
+ The plugin packages the loader of the installation it was created from. A build whose entry
8744
+ imports another `stitchkit` installation — another version, or the same version under another
8745
+ path, as when a build tool carries its own copy — fails with an error naming both versions and
8746
+ roots: that installation's loader would refuse the addon at run time, so the artifact is never
8747
+ written. Call `createNativePackaging` from the `stitchkit` the entry imports.
8693
8748
  Keep every bundler output and write `asset.bytes`; never read the addon from `node_modules` a
8694
8749
  second time. An installed addon whose size or SHA256 differs from the published manifest refuses
8695
8750
  with `mismatch` / `NATIVE_ASSET_DIGEST_MISMATCH`, so a file substituted after installation never
@@ -13922,6 +13977,50 @@ The suite in `packages/core/tests` is the working reference for testing each
13922
13977
  piece. Its size changes with the public surface, so the guide does not pin a
13923
13978
  count that can drift independently from the test runner.
13924
13979
 
13980
+ ### Manual time for schedules and retries
13981
+
13982
+ `createManualClock()` from `stitchkit/testing` stands time still until the test moves it. It is
13983
+ the `clock` of `createManagedSchedule`, a durable sleep, a revision signal or bounded admission,
13984
+ and the `schedule` a retrier of your own takes:
13985
+
13986
+ ```ts
13987
+ import { createManagedSchedule } from 'stitchkit/application'
13988
+ import { createManualClock } from 'stitchkit/testing'
13989
+
13990
+ const clock = createManualClock()
13991
+ const schedule = createManagedSchedule({ id: 'sweep', everyMs: 1_000, clock, run })
13992
+ // …start and activate it…
13993
+ await clock.advance(3_500) // runs at 1 s, 2 s and 3 s of manual time; the test takes milliseconds
13994
+ ```
13995
+
13996
+ Only one `advance` (or `until`) runs at a time; a second one started before the first settled is
13997
+ refused, because two interleaved moves would run time backwards.
13998
+
13999
+ Due timers fire in deadline order, ties in scheduling order. After each firing the clock yields
14000
+ real event-loop turns, not just microtasks: a continuation that arms the next retry after an
14001
+ already-readable socket or another macrotask has armed it before the clock moves on, so
14002
+ `advance(10_000)` does not skip a retry armed at the fifth second. Real work of unknown length —
14003
+ a request to a fake server, a hash in the thread pool — outruns any number of turns; the test
14004
+ wraps the dependency it injects in a `hold`:
14005
+
14006
+ ```ts
14007
+ const request = async (body) => {
14008
+ const held = clock.hold('upload to the fake server')
14009
+ try {
14010
+ return await fakeServer.send(body)
14011
+ } finally {
14012
+ held.release()
14013
+ }
14014
+ }
14015
+ ```
14016
+
14017
+ Time does not move while a hold is open; past `holdLimitMs` of real time (default 5 s)
14018
+ `advance` fails with `ManualClockError` `MANUAL_CLOCK_HOLD_TIMEOUT`, naming every open hold. A
14019
+ held operation that waits for the clock itself — a request timing out on a deadline — `park`s
14020
+ its hold so the deadline can come. `until(done)` moves from deadline to deadline until `done()`
14021
+ holds and fails with `MANUAL_CLOCK_LIMIT` past `limitMs` of manual time; `pending()` counts the
14022
+ timers still waiting, so a timer leaked past `close()` shows as a number, not as a hang.
14023
+
13925
14024
  ### Managed-resource conformance
13926
14025
 
13927
14026
  Consumer-owned resource adapters can run the framework's deterministic
@@ -15015,8 +15114,8 @@ and pacing/progress intervals must fit native timers. The optional
15015
15114
  when the signal aborts if a custom sleeper ignores it. A send already in flight
15016
15115
  finishes and is recorded before stopping.
15017
15116
 
15018
- Without `classify`, known Bot API 429, unreachable-recipient, malformed-message
15019
- and server-error behavior remains. Unknown/no-status failures now halt as
15117
+ Without `classify`, known Bot API 429, unreachable-recipient, malformed-message,
15118
+ server-error and not-dispatched behavior remains. Unknown/no-status failures now halt as
15020
15119
  uncertain, rather than being automatically retried. Existing custom `send`
15021
15120
  implementations that relied on those retries must supply an explicit classifier
15022
15121
  for proven pre-effect failures. `report.halt` now carries
@@ -15035,6 +15134,36 @@ The installed
15035
15134
  fixture checks injected rate limits, ambiguity/no resend, wait budgets and abort
15036
15135
  on Bun and Node without a client SDK.
15037
15136
 
15137
+ ## A transport that knows whether a request left
15138
+
15139
+ `fetch` folds establishing the connection and waiting for the answer into one failure. From that
15140
+ failure nobody can say whether Telegram saw the message, so the senders above treat every network
15141
+ failure as unknown: a broadcast records the recipient `uncertain` and the operator channel drops
15142
+ the message rather than risk a duplicate. On a route that sometimes fails to connect at all, that
15143
+ loses messages that never left.
15144
+
15145
+ `createTelegramBotTransport()` connects first and writes the request only on an established
15146
+ connection. Every failure before the first byte is written is `TelegramNotDispatchedError`
15147
+ (`stage`: `lookup`, `connect` or `request`), and `classifyTelegramSendFailure` reads it as
15148
+ `not-dispatched` — `retryable`, with `evidence: 'transport'`. A broadcast retries it as
15149
+ `transient`; the operator channel sends it again. A failure after the request was written stays
15150
+ an outcome nobody can know.
15151
+
15152
+ ```ts
15153
+ import { createTelegramBotTransport, telegramOperatorSender } from 'stitchkit/telegram'
15154
+
15155
+ const transport = createTelegramBotTransport()
15156
+ const send = telegramOperatorSender({ token: env.BOT_TOKEN, fetch: transport })
15157
+ ```
15158
+
15159
+ Pass it as `fetch` to `callTelegramBotApi`, `telegramBroadcastSender`, `telegramOperatorSender`
15160
+ or the webhook calls. Addresses come in the system resolver's order with no family forced; the
15161
+ next address starts 250 ms later (Happy Eyeballs), and a round with no connection is repeated
15162
+ with new sockets inside `connectBudgetMs` (default 5 s, which also bounds the address lookup; each
15163
+ round waits `connectAttemptMs`, default 2 s). The answer must arrive within `responseTimeoutMs` (default 30 s) and fit
15164
+ `maxResponseBytes` (default 1 MiB). It speaks HTTP/1.1 over `https:`, and over `http:` for a
15165
+ local Bot API server, and it uses no proxy: where the network needs one, keep `fetch`.
15166
+
15038
15167
  ## Message markup
15039
15168
 
15040
15169
  `stitchkit/telegram/html` is Telegram's HTML parse mode as a tree, with no peer
@@ -15622,6 +15751,62 @@ analytics policy belong to the application.
15622
15751
 
15623
15752
  # Upgrading stitchkit
15624
15753
 
15754
+ ## Released migration: 0.108.0
15755
+
15756
+ ### a request that never left is its own send outcome
15757
+
15758
+ **Who must act:** code with an exhaustive `switch` or `Record` over `TelegramSendFailureReason` or
15759
+ over `TelegramSendFailure['evidence']` (`stitchkit/telegram`). Code that reads `retryable` and
15760
+ `recipientUnreachable` needs no change.
15761
+
15762
+ `classifyTelegramSendFailure` now returns `reason: 'not-dispatched'` with `evidence: 'transport'`
15763
+ for a `TelegramNotDispatchedError`: the request never left, so it is `retryable` and repeating it
15764
+ cannot duplicate a message
15765
+ ([ADR 0253](../decisions/0253-a-telegram-request-that-never-left-is-safe-to-repeat.md)). Only
15766
+ `createTelegramBotTransport` throws that error; with the default `fetch` nothing changes at run time.
15767
+
15768
+ 1. Add the reason and the evidence to every exhaustive mapping:
15769
+
15770
+ ```ts
15771
+ // before
15772
+ const code: Record<TelegramSendFailureReason, string> = { /* … */ unknown: 'E_UNKNOWN' }
15773
+ // after
15774
+ const code: Record<TelegramSendFailureReason, string> = {
15775
+ /* … */ 'not-dispatched': 'E_NOT_SENT', unknown: 'E_UNKNOWN',
15776
+ }
15777
+ ```
15778
+
15779
+ Search pattern: `TelegramSendFailureReason`, `TELEGRAM_SEND_FAILURE_REASONS` and `evidence ===`.
15780
+ 2. Type-check: a `Record` or an exhaustive `switch` without the new members fails to compile.
15781
+ 3. To use the distinction, pass `fetch: createTelegramBotTransport()` to the senders that need it
15782
+ (see the [Telegram guide](telegram.md#a-transport-that-knows-whether-a-request-left)).
15783
+
15784
+ ### a packaging plugin packages only its own installation
15785
+
15786
+ **Who must act:** builds that call `createNativePackaging` (`stitchkit/files/packaging`) from a
15787
+ `stitchkit` installation other than the one the bundled entry imports — typically a build tool that
15788
+ carries its own copy. Builds whose entry and plugin come from one installation are unchanged.
15789
+
15790
+ Such a build used to succeed and ship the other installation's default Darwin loader, which refuses
15791
+ the addon on macOS at run time. It now fails, naming both versions and roots
15792
+ ([ADR 0254](../decisions/0254-a-packaging-plugin-owns-one-loader-and-names-what-it-finds.md)).
15793
+
15794
+ 1. Create the plugin from the `stitchkit` the entry resolves, not from the build tool's copy:
15795
+
15796
+ ```ts
15797
+ // before: the build tool's own stitchkit
15798
+ import { createNativePackaging } from 'stitchkit/files/packaging'
15799
+ // after: resolved from the project that owns the entry
15800
+ const { createNativePackaging } = await import(
15801
+ Bun.resolveSync('stitchkit/files/packaging', projectRoot)
15802
+ )
15803
+ ```
15804
+
15805
+ Search pattern: `createNativePackaging` in build tooling that is installed apart from the project.
15806
+ 2. Check the artifact with `inspectNativeArtifact`: a correct build reads `packaged`. An artifact
15807
+ built by 0.107.0 or earlier carries no loader marker and reads `no-loader` whatever it holds, so
15808
+ rebuild before relying on the check; a test of `=== 'unpackaged'` alone passes such an artifact.
15809
+
15625
15810
  ## Released migration: 0.107.0
15626
15811
 
15627
15812
  ### embedded native packaging names no paths
@@ -16871,6 +17056,38 @@ of the range if you want a different one.
16871
17056
  So upgrading is: read the `### ⚠️ Breaking changes` of every version *above* your
16872
17057
  current one *up to* your target, and apply each snippet.
16873
17058
 
17059
+ ### Does an item touch your project?
17060
+
17061
+ Every breaking item since 0.104.0 carries one machine line, `**Affects:**`, naming what it changes
17062
+ in a form that is read without guessing from prose:
17063
+
17064
+ ```md
17065
+ **Affects:** `stitchkit/files/packaging` createNativePackaging(delivery: 'embedded')
17066
+ **Affects:** `stitchkit/contract`, `stitchkit/server` AppError; `stitchkit` ApiError
17067
+ **Affects:** `stitchkit/process` behaviour
17068
+ **Affects:** `stitchkit/agent-runtime/harness-tools` *
17069
+ ```
17070
+
17071
+ Targets are separated by `; `. Each starts with one or more backticked entrypoints, then lists the
17072
+ exports it changes (an optional parenthesised qualifier names the option or member), or says
17073
+ `behaviour` for a change of semantics no import name reveals, or `*` for every import of a removed
17074
+ entrypoint. `stitchkit upgrade` reads your project's imports (`--cwd`, default `.`; TypeScript, JavaScript,
17075
+ Vue and Svelte files; not `node_modules`, and not a `dist`, `build`, `out` or `coverage` folder
17076
+ beside a `package.json`, which is build output) and gives each item one verdict:
17077
+
17078
+ - **affects this project** — with every `path:line` that imports a named export, or a namespace;
17079
+ - **not used here** — nothing imported matches;
17080
+ - **behavioural — check by hand** — with the files that import the entrypoint;
17081
+ - **not declared — check by hand** — an item from before the line existed, with the files that
17082
+ import the entrypoints it leads with.
17083
+
17084
+ `--json` prints the same verdicts per item for a CI step. The scan reads static `import` and
17085
+ `export … from` declarations, bare imports and `import()` / `require()` of a literal specifier; a
17086
+ specifier built at run time is invisible to it, and so is a file of another kind (`.astro`,
17087
+ `.mdx`). A directory or file the process may not read is
17088
+ listed under **Not scanned** (`unscanned` in `--json`) instead of ending the plan: the verdicts do
17089
+ not cover it.
17090
+
16874
17091
  ## Released migration: 0.88.0
16875
17092
 
16876
17093
  Two mechanical edits, and only if your project builds these values rather than only reading them.
@@ -20358,8 +20575,11 @@ The browser-and-server entrypoint. Re-exports everything from
20358
20575
  | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
20359
20576
  | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
20360
20577
  | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
20361
- | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
20362
- | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON; blank keep-alives are skipped and `finalLine: 'require-newline'` can make the delimiter mandatory — [guide](../guide/client.md#ndjson) |
20578
+ | `parseSSE` | function | parse SSE from a `Response`, a web stream or an async byte iterable into an async generator — [guide](../guide/client.md#sse) |
20579
+ | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON from a `Response`, a `ReadableStream<Uint8Array>` or an `AsyncIterable<Uint8Array>` (a child's stdout); blank keep-alives are skipped, `finalLine: 'require-newline'` makes the delimiter mandatory and `signal` cancels the source — [guide](../guide/client.md#any-byte-source) |
20580
+ | `createNDJSONDecoder` / `NDJSONDecoder` | function / _type_ | the same bounded NDJSON reading driven by `push(chunk)` / `end()`, for a socket `data` callback |
20581
+ | `StreamByteSource` | _type_ | `Response \| ReadableStream<Uint8Array> \| AsyncIterable<Uint8Array>`, what the NDJSON and SSE readers accept |
20582
+ | `StreamLineLimitError` / `StreamTruncatedLineError` | class | a line past `maxLineBytes` (`limitBytes`, `lineBytes`; a `RangeError`) and a source ended inside a line under `require-newline` (`lineBytes`; a `SyntaxError`) |
20363
20583
  | `resumableIterator` | function | re-open a long-lived stream from the last delivered cursor, with jittered backoff, a caller-owned terminal item and prompt abort — [guide](../guide/client.md#resumable-streams) |
20364
20584
  | `createBackoff` | function | exponential backoff with subtractive jitter as a value: `next()` / `reset()` |
20365
20585
  | `ContractStreamFrameSchema` / `ContractStreamFrame` | schema / _type_ | default on-the-wire `data` / safe `error` / `end` envelope of a contract-first stream |
@@ -22173,6 +22393,9 @@ handler pipeline without opening a TCP port.
22173
22393
  |--------|------|---------|
22174
22394
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
22175
22395
  | `createHandlerTestClients` | function | exact contract-registry batch form |
22396
+ | `createManualClock` / `ManualClock` | function / _type_ | manual time for schedules, retries and deadlines: `now`, `wallNow`, `schedule`, `advance(ms)` firing due timers in order with real event-loop yields between them, `until`, `pending` and `hold`; a `ManagedScheduleClock`, `DurabilityClock`, `RevisionSignalClock` and `BoundedAdmissionClock` — [guide](../guide/testing-and-deployment.md#manual-time-for-schedules-and-retries) |
22397
+ | `ManualClockOptions` / `ManualClockUntilOptions` / `ManualClockTimer` / `ManualClockHold` | _types_ | start and wall instants, event-loop turns per firing and the real-time hold limit; `until` step and manual limit; a cancellable timer; a hold that is released or parked |
22398
+ | `ManualClockError` / `ManualClockErrorCode` | class / _type_ | `MANUAL_CLOCK_HOLD_TIMEOUT` naming the open holds, or `MANUAL_CLOCK_LIMIT` when `until` runs out |
22176
22399
  | `runManagedResourceConformance` | function | run the canonical deterministic lifecycle matrix against a fresh consumer-owned `ManagedResource` fixture; resolves `void` or throws `ManagedResourceConformanceError` with a stable scenario ID and normalized trace |
22177
22400
  | `ManagedResourceConformanceScenarioIdSchema` / `ManagedResourceConformanceScenarioId` | schema / _type_ | stable clean, rollback, readiness/completion, activation, shutdown-race and forced-cleanup scenario vocabulary |
22178
22401
  | `ManagedResourceConformanceScenarioSchema` / `ManagedResourceConformanceScenario` | schema / _type_ | discriminated scenario record including whether the controlled resource is required |
@@ -22282,7 +22505,10 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
22282
22505
  | `TelegramInitDataRefusal` | _type_ | `missing-hash` / `signature-mismatch` / `malformed` / `expired` — an expired string is not a forged one |
22283
22506
  | `classifyTelegramSendFailure` | function | name a refused Bot API send and separate "retry this send" from "stop addressing this recipient" |
22284
22507
  | `TelegramSendFailure` | _type_ | reason, `status`, Telegram-stated `retryAfterSeconds`, `retryable`, `recipientUnreachable` and which evidence produced the answer |
22285
- | `TelegramSendFailureReason` / `TELEGRAM_SEND_FAILURE_REASONS` | _type_ / constant | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `rate-limited` / `message-invalid` / `server-error` / `unknown`, and the same as a list; a `Record<TelegramSendFailureReason, …>` over them fails to compile when a reason is added |
22508
+ | `createTelegramBotTransport` / `TelegramBotTransportOptions` | function / _type_ | a `fetch` for the Bot API senders that connects before it writes, so a failure before the first byte is `TelegramNotDispatchedError`; connection attempt and budget, answer timeout and size, injectable resolver and socket factory — [guide](../guide/telegram.md#a-transport-that-knows-whether-a-request-left) |
22509
+ | `TelegramFetch` / `TelegramBotTransportOpen` / `TelegramBotTransportResolve` / `TelegramBotTransportSocket` | _types_ | the `(url, init) => Promise<Response>` the senders take; the transport's socket factory, resolver and the structural socket it needs (a Node `net.Socket` or `tls.TLSSocket` fits) |
22510
+ | `TelegramNotDispatchedError` / `TelegramNotDispatchedStage` | class / _type_ | a request that provably never left (`lookup`, `connect` or `request`), so repeating it cannot duplicate a message |
22511
+ | `TelegramSendFailureReason` / `TELEGRAM_SEND_FAILURE_REASONS` | _type_ / constant | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `rate-limited` / `message-invalid` / `server-error` / `not-dispatched` / `unknown`, and the same as a list; a `Record<TelegramSendFailureReason, …>` over them fails to compile when a reason is added |
22286
22512
  | `runTelegramBroadcast` | function | a resumable broadcast by `name` under a state `directory`: audience and journal durably recorded, pacing at `ratePerSecond` (25), exact retry delays within the finite declared budget, injected classifier, uncertain send outcomes never replayed, `dryRun`, one fenced runner per name |
22287
22513
  | `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
22288
22514
  | `TelegramBroadcastFailureSchema` / `TelegramBroadcastFailure` | schema / _type_ | injected `classify` answers retry-after/transient/permanent/ambiguous; exact provider delay is bounded by `maxRetryDelayMs` without shortening, unknown outcome remains uncertain and is never replayed |
@@ -22833,7 +23059,10 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
22833
23059
  `stitchkit/files/packaging` owns `createNativePackaging` and the types
22834
23060
  `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`,
22835
23061
  and for a standalone executable `NativePackagingEmbeddedOptions`, `NativePackagingEmbeddedAsset`,
22836
- `NativePackagingEmbeddedResult`.
23062
+ `NativePackagingEmbeddedResult`. `inspectNativeArtifact` classifies a built artifact's bytes by the
23063
+ loader it carries as a `NativeArtifactLoader`: `packaged`, `unpackaged` or `no-loader`. The plugin
23064
+ fails a build whose entry imports the default loader of another `stitchkit` installation, naming
23065
+ both versions and roots.
22837
23066
  It resolves the installed package's target/version, reads each selected addon once and checks it
22838
23067
  against the `size` and `sha256` published in the package's `native-assets.json` (`formatVersion: 2`,
22839
23068
  written when Stitchkit is built). Call it once per build and reuse the result.
package/llms.txt CHANGED
@@ -9,7 +9,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
9
9
  Load the slice for each entrypoint your code imports — `import … from 'stitchkit/server'` → `llms/server.txt`; the root `stitchkit` import → `llms/stitchkit.txt`. A slice holds that entrypoint's API reference section and the guide pages whose home it is; every guide lives in exactly one slice, and a slice that needs another's guide names it under "Also read". Each file is at most 50.0 KB; a longer one continues in numbered parts, each naming the next at its top. Moving across versions: load `llms/upgrading.txt` and the range slices it lists between your version and the target. `llms-full.txt` inlines everything; it is far larger than a context window, so do not read it whole.
10
10
 
11
11
  ## Entrypoint slices
12
- - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 17.6 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 46.2 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 17.1 KB
12
+ - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 46.5 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 36.5 KB
13
13
  - **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.2 KB
14
14
  - **stitchkit/react**: [llms/react.txt](llms/react.txt) 8.6 KB
15
15
  - **stitchkit/react/keyboard**: [llms/react-keyboard.txt](llms/react-keyboard.txt) 9.2 KB
@@ -39,10 +39,10 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
39
39
  - **stitchkit/application/directory-inbox**: [llms/application-directory-inbox.txt](llms/application-directory-inbox.txt) 0.3 KB
40
40
  - **stitchkit/application/diagnostic-journal**: [llms/application-diagnostic-journal.txt](llms/application-diagnostic-journal.txt) 1.8 KB
41
41
  - **stitchkit/application/schemas**: [llms/application-schemas.txt](llms/application-schemas.txt) 1.8 KB
42
- - **stitchkit/testing**: [llms/testing.txt](llms/testing.txt) 30.7 KB
42
+ - **stitchkit/testing**: [llms/testing.txt](llms/testing.txt) 33.6 KB
43
43
  - **stitchkit/agent-runtime/sandbox**: [llms/agent-runtime-sandbox.txt](llms/agent-runtime-sandbox.txt) 8.3 KB
44
44
  - **stitchkit/files**: [llms/files.txt](llms/files.txt) 5.5 KB
45
- - **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt) 30.1 KB
45
+ - **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt) 32.9 KB
46
46
  - **stitchkit/telegram/html**: [llms/telegram-html.txt](llms/telegram-html.txt) 2.7 KB
47
47
  - **stitchkit/telegram/screens**: [llms/telegram-screens.txt](llms/telegram-screens.txt) 21.2 KB
48
48
  - **stitchkit/tracking**: [llms/tracking.txt](llms/tracking.txt) 16.8 KB
@@ -56,13 +56,13 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
56
56
  - **stitchkit/node**: [llms/node.txt](llms/node.txt) 3.1 KB
57
57
  - **stitchkit/agent-runtime/react**: [llms/agent-runtime-react.txt](llms/agent-runtime-react.txt) 1.0 KB
58
58
  - **stitchkit/agent-runtime/realtime**: [llms/agent-runtime-realtime.txt](llms/agent-runtime-realtime.txt) 2.6 KB
59
- - **stitchkit/process**: [llms/process.txt](llms/process.txt) 42.5 KB
60
- - **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 3.2 KB
59
+ - **stitchkit/process**: [llms/process.txt](llms/process.txt) 43.3 KB
60
+ - **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 3.5 KB
61
61
 
62
62
  ## Upgrading slices
63
- - **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 10.3 KB
64
- - **Upgrading 0.98.0 → 0.107.0**: [llms/upgrading-0.98.0-0.107.0.txt](llms/upgrading-0.98.0-0.107.0.txt) 45.9 KB
65
- - **Upgrading 0.72.0 → 0.97.0**: [llms/upgrading-0.72.0-0.97.0.txt](llms/upgrading-0.72.0-0.97.0.txt) 43.8 KB
63
+ - **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 12.1 KB
64
+ - **Upgrading 0.100.0 → 0.108.0**: [llms/upgrading-0.100.0-0.108.0.txt](llms/upgrading-0.100.0-0.108.0.txt) 46.1 KB
65
+ - **Upgrading 0.72.0 → 0.99.0**: [llms/upgrading-0.72.0-0.99.0.txt](llms/upgrading-0.72.0-0.99.0.txt) 46.4 KB
66
66
  - **Upgrading 0.59.0 → 0.71.0**: [llms/upgrading-0.59.0-0.71.0.txt](llms/upgrading-0.59.0-0.71.0.txt) 45.6 KB
67
67
  - **Upgrading 0.37.0 → 0.58.0**: [llms/upgrading-0.37.0-0.58.0.txt](llms/upgrading-0.37.0-0.58.0.txt) 40.0 KB
68
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.107.0",
3
+ "version": "0.108.1",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,177 +0,0 @@
1
- # stitchkit — stitchkit docs slice, part 3 of 3
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
- Parts of this slice:
7
- - `llms/stitchkit.txt`: Guide: Getting started (docs/guide/getting-started.md)
8
- - `llms/stitchkit.2.txt`: Guide: Typed client (docs/guide/client.md)
9
- - `llms/stitchkit.3.txt`: API reference: `stitchkit` (docs/api/reference.md)
10
-
11
- Also read:
12
- - see `llms/contract.txt` for the Contracts guide
13
- - see `llms/server.txt` for the Realtime guide
14
- - see `llms/server.txt` for the Auth & errors guide
15
-
16
- ==============================================================================
17
- # API reference: `stitchkit` (docs/api/reference.md)
18
- ==============================================================================
19
-
20
- ## `stitchkit`
21
-
22
- The browser-and-server entrypoint. Re-exports everything from
23
- [`stitchkit/contract`](#stitchkitcontract), plus the client and browser realtime.
24
-
25
- ### Client
26
-
27
- | Export | Kind | Summary |
28
- |--------|------|---------|
29
- | `createClient` | function | build a typed client from a contract — [guide](../guide/client.md#createclient) |
30
- | `createClients` | function | build one exact typed client per contract from a registry; accepts the same scoped config and transports as `createClient` |
31
- | `createScopedClients` | function | build one registry routed by contract scope; arrays compose contracts into one namespace |
32
- | `createScopedUrlBuilders` | function | build one URL registry routed by contract scope; arrays compose contracts into one namespace |
33
- | `ScopeClientConfigs` | _type_ | per-scope routing configuration consumed by scoped clients and URL builders |
34
- | `ScopedClientRegistry` | _type_ | exact composed registry returned by `createScopedClients` |
35
- | `ScopedUrlBuilderRegistry` | _type_ | exact composed registry returned by `createScopedUrlBuilders` |
36
- | `ClientRegistryValue` | _type_ | one contract or a contract array composing one client namespace |
37
- | `ClientContract` | _type_ | HTTP-client-compatible contract value used by scoped registries |
38
- | `RegistryScope` | _type_ | contract-scope union inferred from a scoped client registry value |
39
- | `createUrlBuilder` | function | build synchronous browser-native URLs for all HTTP endpoints; body methods accept URL-bound args only — [guide](../guide/client.md#contract-url-builders) |
40
- | `createUrlBuilders` | function | build one exact URL builder per contract in a registry |
41
- | `UrlBuilderConfig` | _type_ | explicit `{ baseUrl }` source for a URL builder |
42
- | `ClientConfig` | _type_ | config for `createClient`'s bare-fetch mode (2nd arg, no `HttpClient`) |
43
- | `ClientFetch` | _type_ | injectable Fetch-compatible transport used by framework and application-owned adapters |
44
- | `ClientRequestOptions` | _type_ | per-call `{ signal?, onUploadProgress? }` passed through an endpoint callable's `.withOptions(...)`; caller abort is distinct from timeout — [guide](../guide/client.md#per-call-cancellation) |
45
- | `UploadProgress` | _type_ | `{ sentBytes, totalBytes, attempt }` heard by `onUploadProgress`: bytes of the encoded body handed to the network, first `0`, last `totalBytes`, counted again on each transport retry — [guide](../guide/client.md#upload-progress) |
46
- | `uploadInChunks` | function | send a file larger than one request as `init` → parts → `finalize` through three calls the app owns; client-minted id, repeats of a part on a failure that may pass, `finalize` never repeated, cancel checked between parts, progress in file bytes — [guide](../guide/client.md#chunked-uploads) |
47
- | `ChunkedUploadConfig` / `ChunkedUploadStart` / `ChunkedUploadPart` / `ChunkedUploadProgress` | _type_ | the driver's file, part size, retries and three calls; what `init` declares; one part; progress `{ sentBytes, totalBytes, index, chunkCount, attempt }` |
48
- | `ChunkSource` | _type_ | what the driver reads a file through — `size` and `slice(start, end)`; a `Blob` is one, so is a platform file that is not (an Expo `File`), and a part's `bytes` is what its `slice` returns |
49
- | `ContractClientConfig` | _type_ | per-tenant / resource-scoped client config — dynamic `pathPrefix` + `stripPrefixKeys` ([guide](../guide/client.md#contractclientconfig--per-tenant--resource-scoped-clients)) |
50
- | `contractEndpointMatchers` | function | compile exact pathname matchers for selected HTTP contract operations and expected-401 policy |
51
- | `PathPrefixArgs` | _type_ | required string-valued keys exposed to a typed dynamic `pathPrefix` callback |
52
- | `createHttpClient` | function | the Ky-based HTTP transport; on Next.js SSR its first attempt stays request-memoizable while every retry is a distinct transport attempt — [guide](../guide/client.md#createhttpclient) |
53
- | `ApiError` | class | a non-2xx or client failure, with `code` / `status` / `details` / `hint`, optional readonly `traceId` from `x-request-id`, and standard `cause` preserving an injected transport failure |
54
- | `zodIssues` | function | a `ZodError` → structured `{ path, code, message }[]` — the exact projection a `VALIDATION_ERROR` carries in `ApiError.details.issues`, so a caller renders one shape. Also on `stitchkit/server`; same name, one definition |
55
- | `ZodIssueSummary` | _type_ | one structured validation issue (`{ path, code, message }`) — the element type of `details.issues`. Also on `stitchkit/server` |
56
- | `formatZodError` | function | a `ZodError` → a readable, field-summarised string. Also on `stitchkit/server` |
57
- | `HttpClient` | _type_ | the transport interface `createClient` builds on |
58
- | `ConfiguredHttpClient` | _type_ | a framework-created `HttpClient` carrying its readonly `baseUrl` for URL builders |
59
- | `HttpClientConfig` | _type_ | config for `createHttpClient`; retry `limit` counts retries after the initial attempt (default 2 = at most 3 GET attempts), with `statusCodes: []` by default; `fetch` installs an explicit transport and is mutually exclusive with the legacy Bun-only `unix` option — [details](../guide/client.md#createhttpclient) |
60
- | `UnauthorizedMatcher` | _type_ | exact `(pathname) => boolean` policy accepted by `suppressUnauthorizedFor` |
61
- | `RequestOptions` | _type_ | per-call options — params, timeout, response type, `onUploadProgress` |
62
- | `HeaderProvider` | _type_ | static or per-request headers |
63
- | `ApiEvent` | _type_ | a client event — `unauthorized` / `network_error` / `logout` |
64
- | `ApiEventListener` | _type_ | an `ApiEvent` handler |
65
-
66
- ### Realtime (client) & streaming
67
-
68
- | Export | Kind | Summary |
69
- |--------|------|---------|
70
- | `createSocketIOClient` | function | low-level typed Socket.IO transport primitive — [guide](../guide/realtime.md#low-level-transport) |
71
- | `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
72
- | `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
73
- | `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
74
- | `createLiveStateController` | function | keep typed application state current across one source-owned snapshot/event generation with finite pre-snapshot buffering, generation fencing and explicit resync — [guide](../guide/realtime.md#snapshot--event-state-synchronization) |
75
- | `LiveStateController` | _type_ | renderer-neutral `start` / `resync` / `getSnapshot` / `subscribe` / `close` handle |
76
- | `LiveStateControllerConfig` | _type_ | typed source, reducer, explicit event/byte bounds, event sizing and isolated error hooks |
77
- | `LiveStateControllerSnapshot` / `LiveStateControllerStatus` | _types_ | current value plus phase, generation, buffer and application/duplicate/gap/refusal counters |
78
- | `LiveStateControllerStatusSchema` | schema | strict runtime validation for controller status metadata |
79
- | `LiveStatePhaseSchema` / `LiveStatePhase` | schema / _type_ | `idle`, `opening`, `live`, `resync-required`, `unavailable` or `closed` |
80
- | `LiveStateStopReasonSchema` / `LiveStateStopReason` | schema / _type_ | explicit gap, overflow, source loss, controller failure and bounded `controller-capacity` reasons |
81
- | `LiveStateEventDecision` | _type_ | provider-owned reducer result: applied state, duplicate or gap |
82
- | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
83
- | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
84
- | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
85
- | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
86
- | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON; blank keep-alives are skipped and `finalLine: 'require-newline'` can make the delimiter mandatory — [guide](../guide/client.md#ndjson) |
87
- | `resumableIterator` | function | re-open a long-lived stream from the last delivered cursor, with jittered backoff, a caller-owned terminal item and prompt abort — [guide](../guide/client.md#resumable-streams) |
88
- | `createBackoff` | function | exponential backoff with subtractive jitter as a value: `next()` / `reset()` |
89
- | `ContractStreamFrameSchema` / `ContractStreamFrame` | schema / _type_ | default on-the-wire `data` / safe `error` / `end` envelope of a contract-first stream |
90
- | `ContractStreamFraming` / `ContractStreamCompletion` | _types_ | opt-in item-vs-envelope framing and terminal-vs-stream-end completion policies |
91
- | `StreamFinalLinePolicy` | _type_ | permissive or newline-required final NDJSON line policy |
92
- | `DEFAULT_CONTRACT_STREAM_FRAME_BYTES` | const | default maximum encoded contract-stream frame: 256 KiB |
93
- | `SocketIOClient` | _type_ | low-level client handle; `emit` reports disconnected drops and `emitWithAck` exposes the native Promise primitive used by validated `request()` |
94
- | `SocketIOClientPeerLoaders` | _type_ | inject `socket.io-client` so a bundler can put it in a self-contained artifact |
95
- | `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`, `onConnectError`, `onDroppedEmit`) |
96
- | `SocketEventMap` | _type_ | the shape of an event map |
97
- | `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
98
- | `RealtimeClientOptions` | _type_ | transport options plus rejected-event and metadata-only acknowledged-request phase hooks for `createRealtimeClient` |
99
- | `BoundRealtimeClient` | _type_ | validated non-owning `on`/`emit`/`request` client with no `connect`/`disconnect` |
100
- | `RealtimeClientTransport` | _type_ | minimal existing transport capability accepted by `bindRealtimeClient` |
101
- | `BindRealtimeClientOptions` | _type_ | rejection/logger options for a bound existing transport |
102
- | `RealtimeAcknowledgedEvent` | _type_ | event-name union restricted to definitions with an `ack` schema |
103
- | `RealtimeAcknowledgement` | _type_ | validated acknowledgement output inferred from an event definition |
104
- | `RealtimeRequestArguments` | _type_ | request arguments inferred from an acknowledged event tuple |
105
- | `RealtimeRequestOptions` | _type_ | finite positive native acknowledgement `timeoutMs` plus an optional invocation-scoped `onPhase` observer |
106
- | `RealtimeRequestPhaseSchema` / `RealtimeRequestPhase` | schema / _type_ | closed `engine-handoff` / `engine-ack-received` / `settled` / `timeout` / `disconnected` lifecycle |
107
- | `RealtimeRequestPhaseEventSchema` / `RealtimeRequestPhaseEvent` | schema / _type_ | strict metadata-only `{ requestId, event, phase, elapsedMs }` observation |
108
- | `RealtimeRequestPhaseHook` | _type_ | isolated sync/async observer accepted globally by `RealtimeClientOptions.onRequestPhase` or per invocation by `RealtimeRequestOptions.onPhase` |
109
- | `RealtimeRequestTimeoutError` | class | stable `REALTIME_REQUEST_TIMEOUT` rejection |
110
- | `RealtimeRequestDisconnectedError` | class | stable `REALTIME_REQUEST_DISCONNECTED` rejection, including an immediate disconnected call |
111
- | `RealtimeRequestInvalidAcknowledgementError` | class | invalid ack was reported through `onRejected` and the request rejected |
112
- | `RealtimeRequestRejectedError` | class | the peer refused the frame against its own contract and said so — `reason`, `issues` — instead of leaving the sender to time out ([ADR 0106](../decisions/0106-a-refused-frame-answers-its-sender.md)) |
113
- | `REALTIME_REJECTION_KEY` | const | the reserved acknowledgement key a refusal travels under |
114
- | `RealtimeRejectionEnvelope` | _type_ | the wire shape of a refusal |
115
- | `RealtimeRejectionReport` | _type_ | what the sender is told: event, reason, message, issues |
116
- | `RealtimeRejectionIssue` | _type_ | one refused field, already flattened (`path: '0.v'`) |
117
- | `asRealtimeRejection` | function | recognise a refusal in an acknowledgement value, validating it |
118
- | `RealtimeContract` | _type_ | shared server-to-client and client-to-server event registries |
119
- | `RealtimeEventRegistry` | _type_ | string-keyed registry of event definitions |
120
- | `RealtimeEventDefinition` | _type_ | one tuple-shaped event and optional acknowledgement schema |
121
- | `RealtimeEventArguments` | _type_ | tuple inferred from an event definition |
122
- | `RealtimeEmitArguments` | _type_ | emit arguments including an inferred acknowledgement callback |
123
- | `RealtimeEventHandler` | _type_ | handler inferred from an event definition |
124
- | `InferRealtimeEventMap` | _type_ | inferred Socket.IO-compatible event map |
125
- | `RealtimeRejectDirection` / `RealtimeRejectPhase` / `RealtimeRejectReason` / `RealtimeRejectFault` | _types_ | canonical inferred rejection direction, validation phase, reason and fault classification |
126
- | `RealtimeRejectedEvent` | _type_ | structured rejected event with event, direction, phase, reason and fault |
127
- | `RealtimeRejectedEventHook` | _type_ | sync/async observer for structured realtime rejections |
128
- | `ValidatedRealtimeSocket` | _type_ | runtime-validating `on`/`emit` surface inferred from registries; `emit` returns "accepted by the transport" (`false` only for a client-side disconnected drop) |
129
- | `RetainedTopics` | _type_ | the `createRetainedTopics` handle |
130
- | `ParseSSEOptions` | _type_ | options for `parseSSE` |
131
- | `ParseNDJSONOptions` | _type_ | options for `parseNDJSON` |
132
- | `BackoffPolicySchema` / `BackoffPolicy` | schema / _type_ | `minDelayMs`, `maxDelayMs` and a `0`–`1` jitter fraction; a ceiling below the floor is refused |
133
- | `Backoff` | _type_ | the backoff handle returned by `createBackoff` |
134
- | `ResumableIteratorConfig` | _type_ | caller-owned `open` / `advance` / `isTerminal`, retry policy, abort signal and `onAttempt` observer |
135
- | `ResumableAttempt` | _type_ | attempt number, the delay about to be waited and the error that caused it |
136
-
137
- ### Trace (client)
138
-
139
- Browser-safe W3C trace helpers — the same functions as on
140
- [`stitchkit/observability`](#stitchkitobservability), re-exported so a client
141
- can emit / propagate a `traceparent` (see `HttpClientConfig.trace`).
142
-
143
- | Export | Kind | Summary |
144
- |--------|------|---------|
145
- | `createTraceContext` | function | a fresh root trace |
146
- | `formatTraceparent` | function | render a `traceparent` header value |
147
- | `parseTraceparent` | function | parse a `traceparent` header |
148
- | `childSpan` | function | a child span of a parent trace |
149
- | `TraceContext` | _type_ | `{ traceId, spanId, parentSpanId?, tracestate?, baggage? }` |
150
-
151
- ---
152
-
153
- ### Session lifetime and credential coordination
154
-
155
- `createSessionScope` (`stitchkit`) returns the generic `SessionScope`: `replace(context)`
156
- creates a new login lifetime (including same-account relogin), `capture()` returns
157
- its `SessionOperation<T>`, `clear()` invalidates it, `stop()` permanently closes
158
- the host, and `owns(operation)` answers whether an operation is this scope's current
159
- one — `current()` alone cannot tell an operation of another scope. Operations expose `id`, `generation`, `context`, `signal`, `current()` and
160
- `assertCurrent()`. `run(callback)` checks both success and failure across an await;
161
- `deliver(callback)` admits a synchronous final send/state change or returns false.
162
- `bindFetch(fetch, headers?)` guards every attempt and scopes its abort signal. Wrap
163
- the complete client call in `run` too: headers are not the response body boundary.
164
- Expired work throws `SessionExpiredError`, not a refresh instruction; when the work
165
- itself failed, that failure is its `cause`.
166
-
167
- `createSessionCredentials` accepts `SessionCredentialsConfig`:
168
- `scope`, `refresh(credentials, signal)`, `storage.write(session, credentials)`,
169
- `storage.clear()`, `maxPendingWrites` (128), `refreshTimeoutMs` (30000, at most one
170
- hour) and `storageTimeoutMs` (30000, at most one hour): a storage call past it
171
- rejects and frees the queue, so `logout()` still clears; when the late call settles,
172
- storage is written again from memory, or cleared when nobody is logged in. `SessionCredentials` provides `login`, `logout`, `read(operation)` and
173
- `refresh(operation)`. Concurrent refresh calls in one generation share the same
174
- promise. Logout invalidates memory before its returned persistence promise settles.
175
- Storage errors and queue-capacity refusals reject; callers must handle them. All
176
- writes must pass through the same instance. There is no cross-process/tab lock,
177
- provider error classifier or automatic retry of arbitrary application callbacks.