@yolk-sdk/emulators 0.1.0-canary.96
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/LICENSE +21 -0
- package/README.md +2135 -0
- package/dist/anthropic.d.mts +269 -0
- package/dist/anthropic.d.mts.map +1 -0
- package/dist/anthropic.mjs +177 -0
- package/dist/anthropic.mjs.map +1 -0
- package/dist/chat-completions.d.mts +288 -0
- package/dist/chat-completions.d.mts.map +1 -0
- package/dist/chat-completions.mjs +451 -0
- package/dist/chat-completions.mjs.map +1 -0
- package/dist/codex.d.mts +265 -0
- package/dist/codex.d.mts.map +1 -0
- package/dist/codex.mjs +199 -0
- package/dist/codex.mjs.map +1 -0
- package/dist/dropbox/api.d.mts +63 -0
- package/dist/dropbox/api.d.mts.map +1 -0
- package/dist/dropbox/api.mjs +565 -0
- package/dist/dropbox/api.mjs.map +1 -0
- package/dist/dropbox/state.d.mts +136 -0
- package/dist/dropbox/state.d.mts.map +1 -0
- package/dist/dropbox/state.mjs +209 -0
- package/dist/dropbox/state.mjs.map +1 -0
- package/dist/dropbox.d.mts +79 -0
- package/dist/dropbox.d.mts.map +1 -0
- package/dist/dropbox.mjs +124 -0
- package/dist/dropbox.mjs.map +1 -0
- package/dist/email-fixtures.d.mts +36 -0
- package/dist/email-fixtures.d.mts.map +1 -0
- package/dist/email-fixtures.mjs +1080 -0
- package/dist/email-fixtures.mjs.map +1 -0
- package/dist/email.d.mts +160 -0
- package/dist/email.d.mts.map +1 -0
- package/dist/email.mjs +608 -0
- package/dist/email.mjs.map +1 -0
- package/dist/emulator-compose.d.mts +40 -0
- package/dist/emulator-compose.d.mts.map +1 -0
- package/dist/emulator-compose.mjs +91 -0
- package/dist/emulator-compose.mjs.map +1 -0
- package/dist/emulator-http.d.mts +53 -0
- package/dist/emulator-http.d.mts.map +1 -0
- package/dist/emulator-http.mjs +117 -0
- package/dist/emulator-http.mjs.map +1 -0
- package/dist/emulator-kernel.d.mts +176 -0
- package/dist/emulator-kernel.d.mts.map +1 -0
- package/dist/emulator-kernel.mjs +413 -0
- package/dist/emulator-kernel.mjs.map +1 -0
- package/dist/fixture-route.d.mts +129 -0
- package/dist/fixture-route.d.mts.map +1 -0
- package/dist/fixture-route.mjs +257 -0
- package/dist/fixture-route.mjs.map +1 -0
- package/dist/fortnox/api.d.mts +92 -0
- package/dist/fortnox/api.d.mts.map +1 -0
- package/dist/fortnox/api.mjs +750 -0
- package/dist/fortnox/api.mjs.map +1 -0
- package/dist/fortnox/state.d.mts +533 -0
- package/dist/fortnox/state.d.mts.map +1 -0
- package/dist/fortnox/state.mjs +612 -0
- package/dist/fortnox/state.mjs.map +1 -0
- package/dist/fortnox.d.mts +128 -0
- package/dist/fortnox.d.mts.map +1 -0
- package/dist/fortnox.mjs +403 -0
- package/dist/fortnox.mjs.map +1 -0
- package/dist/gateway-evaluate-recordings.d.mts +12 -0
- package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
- package/dist/gateway-evaluate-recordings.mjs +128 -0
- package/dist/gateway-evaluate-recordings.mjs.map +1 -0
- package/dist/gateway.d.mts +272 -0
- package/dist/gateway.d.mts.map +1 -0
- package/dist/gateway.mjs +400 -0
- package/dist/gateway.mjs.map +1 -0
- package/dist/github/api.d.mts +63 -0
- package/dist/github/api.d.mts.map +1 -0
- package/dist/github/api.mjs +527 -0
- package/dist/github/api.mjs.map +1 -0
- package/dist/github/state.d.mts +272 -0
- package/dist/github/state.d.mts.map +1 -0
- package/dist/github/state.mjs +303 -0
- package/dist/github/state.mjs.map +1 -0
- package/dist/github.d.mts +77 -0
- package/dist/github.d.mts.map +1 -0
- package/dist/github.mjs +180 -0
- package/dist/github.mjs.map +1 -0
- package/dist/google/calendar.d.mts +10 -0
- package/dist/google/calendar.d.mts.map +1 -0
- package/dist/google/calendar.mjs +252 -0
- package/dist/google/calendar.mjs.map +1 -0
- package/dist/google/drive.d.mts +8 -0
- package/dist/google/drive.d.mts.map +1 -0
- package/dist/google/drive.mjs +252 -0
- package/dist/google/drive.mjs.map +1 -0
- package/dist/google/gmail.d.mts +10 -0
- package/dist/google/gmail.d.mts.map +1 -0
- package/dist/google/gmail.mjs +591 -0
- package/dist/google/gmail.mjs.map +1 -0
- package/dist/google/shared.d.mts +107 -0
- package/dist/google/shared.d.mts.map +1 -0
- package/dist/google/shared.mjs +146 -0
- package/dist/google/shared.mjs.map +1 -0
- package/dist/google/state.d.mts +385 -0
- package/dist/google/state.d.mts.map +1 -0
- package/dist/google/state.mjs +496 -0
- package/dist/google/state.mjs.map +1 -0
- package/dist/google.d.mts +75 -0
- package/dist/google.d.mts.map +1 -0
- package/dist/google.mjs +210 -0
- package/dist/google.mjs.map +1 -0
- package/dist/linkedin-search/api.d.mts +45 -0
- package/dist/linkedin-search/api.d.mts.map +1 -0
- package/dist/linkedin-search/api.mjs +181 -0
- package/dist/linkedin-search/api.mjs.map +1 -0
- package/dist/linkedin-search/state.d.mts +123 -0
- package/dist/linkedin-search/state.d.mts.map +1 -0
- package/dist/linkedin-search/state.mjs +261 -0
- package/dist/linkedin-search/state.mjs.map +1 -0
- package/dist/linkedin-search.d.mts +69 -0
- package/dist/linkedin-search.d.mts.map +1 -0
- package/dist/linkedin-search.mjs +158 -0
- package/dist/linkedin-search.mjs.map +1 -0
- package/dist/mcp/api.d.mts +44 -0
- package/dist/mcp/api.d.mts.map +1 -0
- package/dist/mcp/api.mjs +557 -0
- package/dist/mcp/api.mjs.map +1 -0
- package/dist/mcp/recordings.d.mts +40 -0
- package/dist/mcp/recordings.d.mts.map +1 -0
- package/dist/mcp/recordings.mjs +2390 -0
- package/dist/mcp/recordings.mjs.map +1 -0
- package/dist/mcp/state.d.mts +71 -0
- package/dist/mcp/state.d.mts.map +1 -0
- package/dist/mcp/state.mjs +83 -0
- package/dist/mcp/state.mjs.map +1 -0
- package/dist/mcp.d.mts +84 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +241 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/messages.d.mts +183 -0
- package/dist/messages.d.mts.map +1 -0
- package/dist/messages.mjs +532 -0
- package/dist/messages.mjs.map +1 -0
- package/dist/microsoft/api.d.mts +47 -0
- package/dist/microsoft/api.d.mts.map +1 -0
- package/dist/microsoft/api.mjs +178 -0
- package/dist/microsoft/api.mjs.map +1 -0
- package/dist/microsoft/calendar.d.mts +30 -0
- package/dist/microsoft/calendar.d.mts.map +1 -0
- package/dist/microsoft/calendar.mjs +271 -0
- package/dist/microsoft/calendar.mjs.map +1 -0
- package/dist/microsoft/drive.d.mts +36 -0
- package/dist/microsoft/drive.d.mts.map +1 -0
- package/dist/microsoft/drive.mjs +298 -0
- package/dist/microsoft/drive.mjs.map +1 -0
- package/dist/microsoft/graph.d.mts +198 -0
- package/dist/microsoft/graph.d.mts.map +1 -0
- package/dist/microsoft/graph.mjs +264 -0
- package/dist/microsoft/graph.mjs.map +1 -0
- package/dist/microsoft/mail.d.mts +48 -0
- package/dist/microsoft/mail.d.mts.map +1 -0
- package/dist/microsoft/mail.mjs +488 -0
- package/dist/microsoft/mail.mjs.map +1 -0
- package/dist/microsoft/state.d.mts +480 -0
- package/dist/microsoft/state.d.mts.map +1 -0
- package/dist/microsoft/state.mjs +625 -0
- package/dist/microsoft/state.mjs.map +1 -0
- package/dist/microsoft.d.mts +168 -0
- package/dist/microsoft.d.mts.map +1 -0
- package/dist/microsoft.mjs +483 -0
- package/dist/microsoft.mjs.map +1 -0
- package/dist/node.d.mts +47 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +178 -0
- package/dist/node.mjs.map +1 -0
- package/dist/notion/api.d.mts +51 -0
- package/dist/notion/api.d.mts.map +1 -0
- package/dist/notion/api.mjs +507 -0
- package/dist/notion/api.mjs.map +1 -0
- package/dist/notion/state.d.mts +329 -0
- package/dist/notion/state.d.mts.map +1 -0
- package/dist/notion/state.mjs +432 -0
- package/dist/notion/state.mjs.map +1 -0
- package/dist/notion.d.mts +72 -0
- package/dist/notion.d.mts.map +1 -0
- package/dist/notion.mjs +127 -0
- package/dist/notion.mjs.map +1 -0
- package/dist/openai.d.mts +165 -0
- package/dist/openai.d.mts.map +1 -0
- package/dist/openai.mjs +141 -0
- package/dist/openai.mjs.map +1 -0
- package/dist/opencode-recordings.d.mts +7 -0
- package/dist/opencode-recordings.d.mts.map +1 -0
- package/dist/opencode-recordings.mjs +222 -0
- package/dist/opencode-recordings.mjs.map +1 -0
- package/dist/opencode.d.mts +70 -0
- package/dist/opencode.d.mts.map +1 -0
- package/dist/opencode.mjs +217 -0
- package/dist/opencode.mjs.map +1 -0
- package/dist/r2-fixtures.d.mts +36 -0
- package/dist/r2-fixtures.d.mts.map +1 -0
- package/dist/r2-fixtures.mjs +245 -0
- package/dist/r2-fixtures.mjs.map +1 -0
- package/dist/r2-guard.d.mts +43 -0
- package/dist/r2-guard.d.mts.map +1 -0
- package/dist/r2-guard.mjs +215 -0
- package/dist/r2-guard.mjs.map +1 -0
- package/dist/r2.d.mts +166 -0
- package/dist/r2.d.mts.map +1 -0
- package/dist/r2.mjs +517 -0
- package/dist/r2.mjs.map +1 -0
- package/dist/responses.d.mts +231 -0
- package/dist/responses.d.mts.map +1 -0
- package/dist/responses.mjs +556 -0
- package/dist/responses.mjs.map +1 -0
- package/dist/route-evidence.d.mts +45 -0
- package/dist/route-evidence.d.mts.map +1 -0
- package/dist/route-evidence.mjs +56 -0
- package/dist/route-evidence.mjs.map +1 -0
- package/dist/router.d.mts +93 -0
- package/dist/router.d.mts.map +1 -0
- package/dist/router.mjs +259 -0
- package/dist/router.mjs.map +1 -0
- package/dist/stateful-core.d.mts +17 -0
- package/dist/stateful-core.d.mts.map +1 -0
- package/dist/stateful-core.mjs +41 -0
- package/dist/stateful-core.mjs.map +1 -0
- package/dist/stateful-emulator.d.mts +565 -0
- package/dist/stateful-emulator.d.mts.map +1 -0
- package/dist/stateful-emulator.mjs +1228 -0
- package/dist/stateful-emulator.mjs.map +1 -0
- package/dist/stateful-secrets.d.mts +84 -0
- package/dist/stateful-secrets.d.mts.map +1 -0
- package/dist/stateful-secrets.mjs +216 -0
- package/dist/stateful-secrets.mjs.map +1 -0
- package/dist/subscription-usage-recordings.d.mts +9 -0
- package/dist/subscription-usage-recordings.d.mts.map +1 -0
- package/dist/subscription-usage-recordings.mjs +53 -0
- package/dist/subscription-usage-recordings.mjs.map +1 -0
- package/dist/subscription-usage.d.mts +53 -0
- package/dist/subscription-usage.d.mts.map +1 -0
- package/dist/subscription-usage.mjs +21 -0
- package/dist/subscription-usage.mjs.map +1 -0
- package/dist/telegram/api.d.mts +34 -0
- package/dist/telegram/api.d.mts.map +1 -0
- package/dist/telegram/api.mjs +257 -0
- package/dist/telegram/api.mjs.map +1 -0
- package/dist/telegram/state.d.mts +128 -0
- package/dist/telegram/state.d.mts.map +1 -0
- package/dist/telegram/state.mjs +154 -0
- package/dist/telegram/state.mjs.map +1 -0
- package/dist/telegram.d.mts +62 -0
- package/dist/telegram.d.mts.map +1 -0
- package/dist/telegram.mjs +127 -0
- package/dist/telegram.mjs.map +1 -0
- package/dist/todoist/api.d.mts +55 -0
- package/dist/todoist/api.d.mts.map +1 -0
- package/dist/todoist/api.mjs +415 -0
- package/dist/todoist/api.mjs.map +1 -0
- package/dist/todoist/state.d.mts +275 -0
- package/dist/todoist/state.d.mts.map +1 -0
- package/dist/todoist/state.mjs +345 -0
- package/dist/todoist/state.mjs.map +1 -0
- package/dist/todoist.d.mts +68 -0
- package/dist/todoist.d.mts.map +1 -0
- package/dist/todoist.mjs +181 -0
- package/dist/todoist.mjs.map +1 -0
- package/dist/xai.d.mts +267 -0
- package/dist/xai.d.mts.map +1 -0
- package/dist/xai.mjs +252 -0
- package/dist/xai.mjs.map +1 -0
- package/package.json +157 -0
- package/src/anthropic.ts +289 -0
- package/src/chat-completions.ts +924 -0
- package/src/codex.ts +296 -0
- package/src/dropbox/api.ts +1084 -0
- package/src/dropbox/state.ts +364 -0
- package/src/dropbox.ts +203 -0
- package/src/email-fixtures.ts +1297 -0
- package/src/email.ts +1108 -0
- package/src/emulator-compose.ts +147 -0
- package/src/emulator-http.ts +184 -0
- package/src/emulator-kernel.ts +844 -0
- package/src/fixture-route.ts +561 -0
- package/src/fortnox/api.ts +1352 -0
- package/src/fortnox/state.ts +798 -0
- package/src/fortnox.ts +801 -0
- package/src/gateway-evaluate-recordings.ts +153 -0
- package/src/gateway.ts +530 -0
- package/src/github/api.ts +986 -0
- package/src/github/state.ts +439 -0
- package/src/github.ts +271 -0
- package/src/google/calendar.ts +486 -0
- package/src/google/drive.ts +471 -0
- package/src/google/gmail.ts +1011 -0
- package/src/google/shared.ts +321 -0
- package/src/google/state.ts +686 -0
- package/src/google.ts +298 -0
- package/src/linkedin-search/api.ts +363 -0
- package/src/linkedin-search/state.ts +373 -0
- package/src/linkedin-search.ts +241 -0
- package/src/mcp/api.ts +995 -0
- package/src/mcp/recordings.ts +2665 -0
- package/src/mcp/state.ts +125 -0
- package/src/mcp.ts +319 -0
- package/src/messages.ts +844 -0
- package/src/microsoft/api.ts +426 -0
- package/src/microsoft/calendar.ts +491 -0
- package/src/microsoft/drive.ts +541 -0
- package/src/microsoft/graph.ts +550 -0
- package/src/microsoft/mail.ts +833 -0
- package/src/microsoft/state.ts +822 -0
- package/src/microsoft.ts +982 -0
- package/src/node.ts +293 -0
- package/src/notion/api.ts +976 -0
- package/src/notion/state.ts +512 -0
- package/src/notion.ts +210 -0
- package/src/openai.ts +198 -0
- package/src/opencode-recordings.ts +257 -0
- package/src/opencode.ts +271 -0
- package/src/r2-fixtures.ts +295 -0
- package/src/r2-guard.ts +323 -0
- package/src/r2.ts +890 -0
- package/src/responses.ts +901 -0
- package/src/route-evidence.ts +90 -0
- package/src/router.ts +490 -0
- package/src/stateful-core.ts +70 -0
- package/src/stateful-emulator.ts +2571 -0
- package/src/stateful-secrets.ts +299 -0
- package/src/subscription-usage-recordings.ts +77 -0
- package/src/subscription-usage.ts +68 -0
- package/src/telegram/api.ts +437 -0
- package/src/telegram/state.ts +229 -0
- package/src/telegram.ts +227 -0
- package/src/todoist/api.ts +736 -0
- package/src/todoist/state.ts +456 -0
- package/src/todoist.ts +316 -0
- package/src/xai.ts +341 -0
package/src/github.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateful GitHub emulator (the REST routes of the GitHub conformance cases on
|
|
3
|
+
* `https://api.github.com`, API version 2026-03-10), built on the upstream `@emulators/core`
|
|
4
|
+
* custom runtime, with a request ledger, status faults, and an `/_emulate/*` control plane.
|
|
5
|
+
*
|
|
6
|
+
* It never imports SDK code: its wire shapes, error bodies, and default seed are copied as data
|
|
7
|
+
* from the synthetic GitHub conformance fixtures, and every route names the conformance cases it
|
|
8
|
+
* follows in `githubEmulatorRoutes`. Only the routes those seven cases send are emulated. Response
|
|
9
|
+
* behaviour comes only from the fixtures: anything they do not show answers one ledgered 400
|
|
10
|
+
* not-emulated, writes nothing, and uses up no fault. That includes the leftover lookup's
|
|
11
|
+
* open-issue listing (`GET /repos/{owner}/{repo}/issues`, no fixture), so the read-only lookup
|
|
12
|
+
* fails and the live runner prints its lookup-failed WARN. Every route answers only on the
|
|
13
|
+
* recorded origin `https://api.github.com` (`fetchOn(origin)` serves it behind a loopback
|
|
14
|
+
* rewrite).
|
|
15
|
+
*
|
|
16
|
+
* Fail closed: a request is recognised only when its raw path is exactly an emulated route shape
|
|
17
|
+
* (every path parameter matches its raw pattern in full) under that route's method, and any
|
|
18
|
+
* `Authorization` header is exactly `Bearer <token>` (a recognisable bearer, below). Every other
|
|
19
|
+
* request is ledgered and answered with constant text only (`/<unrecognised>`, a standard method or
|
|
20
|
+
* `<other>`, an empty query, no body, a constant reason). The bearer value is never compared
|
|
21
|
+
* against anything, stored, forwarded, or ledgered. A recognised bearer must match the RFC 6750
|
|
22
|
+
* `b64token` syntax exactly (`^[A-Za-z0-9\-._~+/]+=*$`, at least 8 characters), start with a
|
|
23
|
+
* character in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u`, and hold at least one character
|
|
24
|
+
* outside the JSON-number alphabet `[0-9.eE+-]` (every GitHub and Google token form does: `ghp_…`,
|
|
25
|
+
* `github_pat_…`, `gho_…`, `ya29.…`). So no number's text can contain it; it holds no escape
|
|
26
|
+
* introducer (`%`, `\`, `"`), so no escape starts inside it; and its first character is no hex
|
|
27
|
+
* digit and no JSON escape letter, so no stray `%`, `\`, or partial escape to its left can complete
|
|
28
|
+
* with it, and its characters always decode in place. An `Authorization` header with any other
|
|
29
|
+
* value is unrecognisable. A recognised request that repeats the bearer value in its raw path, any
|
|
30
|
+
* path segment, the raw query or any query key or value, any recorded header, or its body is
|
|
31
|
+
* refused and ledgered with constant text only: a standard method, the path `/<unrecognised>`, its
|
|
32
|
+
* route template, an empty query, no headers or body, and a constant reason
|
|
33
|
+
* (`the query repeats the credential`, for example). Each part is checked through the closure of
|
|
34
|
+
* two total, lexical transforms that cannot fail: a tolerant percent-decode (every `%XX` below
|
|
35
|
+
* `%80` becomes its ASCII character; any other `%` sequence is left as it is) and a tolerant
|
|
36
|
+
* JSON-unescape (in any text, whether or not it parses as JSON, `\uXXXX` below `\u0080` and `\"`,
|
|
37
|
+
* `\\`, `\/`, `\b`, `\f`, `\n`, `\r`, `\t` become their characters). Starting from each part's raw
|
|
38
|
+
* text, either transform is applied to every text of the previous step, deduplicated, until no new
|
|
39
|
+
* text appears (a fixpoint), and every text is checked for the bearer as a substring; both
|
|
40
|
+
* transforms never lengthen a text and shorten it whenever they change it. So any depth of
|
|
41
|
+
* percent-encoding or JSON escaping, in any order, is seen through in every part: the raw path and
|
|
42
|
+
* each raw path segment, the raw query and each query key and value (already decoded once by
|
|
43
|
+
* `URLSearchParams`), each recorded header, and the raw body. The work is capped at 64 rounds, 1024
|
|
44
|
+
* distinct texts, or 8 Mi characters read by the transforms, whichever comes first; a part whose
|
|
45
|
+
* closure hits a cap before its fixpoint counts as repeating the credential and is refused with the
|
|
46
|
+
* same constant entry (uncertainty refuses, it never admits; so any part over 4 Mi characters is
|
|
47
|
+
* always refused). Any other recognised request has the bearer value scrubbed from its ledgered
|
|
48
|
+
* fields and every not-emulated reason (plan-time reasons included); its recorded query is keyed by
|
|
49
|
+
* recorded key; a key recorded more than once lists its values in order (as a JSON array); and
|
|
50
|
+
* recorded headers and query keys and values that start like JSON (`{`, `[`, `"`) are recorded
|
|
51
|
+
* parsed with credential-named keys redacted at any depth, or as `<redacted>` when they do not
|
|
52
|
+
* parse, whatever the header's declared format. Refusals never echo a request's own query or body
|
|
53
|
+
* keys.
|
|
54
|
+
*
|
|
55
|
+
* Request-shape latitude (`/github`, the only accepted deviations): any bearer value in the RFC
|
|
56
|
+
* 6750 `b64token` syntax (`[A-Za-z0-9\-._~+/]+=*`) of at least 8 characters, starting with a
|
|
57
|
+
* character in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u` (so a legacy all-hex token is
|
|
58
|
+
* refused), with at least one outside `[0-9.eE+-]`, that occurs nowhere else in the request (never
|
|
59
|
+
* compared against anything, stored, or ledgered); extra request headers; JSON key order;
|
|
60
|
+
* `content-type` media-type parameters; the order of query parameters; any non-empty issue title
|
|
61
|
+
* and comment body, and any issue body text; any comment listing `since` of the form
|
|
62
|
+
* `YYYY-MM-DDTHH:MM:SSZ`; any label listing `per_page` from 1 to 100, with no `page` or a `page`
|
|
63
|
+
* from 2 to one past the last page; any issue search `q` that starts with the seeded
|
|
64
|
+
* `repo:<owner>/<repo>` qualifier and whose query after it is longer than 256 characters (answered
|
|
65
|
+
* the recorded 422); any issue number the repository has not reached (answered the recorded 404);
|
|
66
|
+
* and any issue, comment, repository label, or file the state holds where a fixture has one, under
|
|
67
|
+
* the per-route state rules. `Authorization` must be exactly `Bearer <token>` (that spelling, one
|
|
68
|
+
* space), `Accept` `application/vnd.github+json`, and `X-GitHub-Api-Version` `2026-03-10`.
|
|
69
|
+
* Everything else (other keys and values, query parameters, empty query components such as a bare
|
|
70
|
+
* `?` or a stray `&`, another origin or repository, a repeated query key, an explicit `page=1`, and
|
|
71
|
+
* a comment listing `per_page` other than 100) is not emulated.
|
|
72
|
+
*
|
|
73
|
+
* Node-only: `@emulators/core` imports Node builtins, so the core is loaded lazily by
|
|
74
|
+
* `makeGithubEmulator` (importing this module has no side effects).
|
|
75
|
+
*
|
|
76
|
+
* @experimental
|
|
77
|
+
*/
|
|
78
|
+
import { Data, Predicate } from 'effect'
|
|
79
|
+
import {
|
|
80
|
+
githubApiRoutes,
|
|
81
|
+
githubEmulatorAccept,
|
|
82
|
+
githubEmulatorApiVersion,
|
|
83
|
+
githubEmulatorDrillKnobs,
|
|
84
|
+
type GithubApiEnv,
|
|
85
|
+
type GithubEmulatorDrills
|
|
86
|
+
} from './github/api.ts'
|
|
87
|
+
import {
|
|
88
|
+
buildSeedState,
|
|
89
|
+
decodeState,
|
|
90
|
+
type GithubEmulatorSeed,
|
|
91
|
+
type GithubEmulatorState
|
|
92
|
+
} from './github/state.ts'
|
|
93
|
+
import type { EmulatorRouteEvidence } from './route-evidence.ts'
|
|
94
|
+
import {
|
|
95
|
+
StatefulFault,
|
|
96
|
+
StatefulFaultMatch,
|
|
97
|
+
checkBooleanDrills,
|
|
98
|
+
makeStatefulEmulator,
|
|
99
|
+
routeEvidence,
|
|
100
|
+
type StatefulCoverage,
|
|
101
|
+
type StatefulEmulatorApi,
|
|
102
|
+
type StatefulFaultState,
|
|
103
|
+
type StatefulInputKind,
|
|
104
|
+
type StatefulLedgerEntry
|
|
105
|
+
} from './stateful-emulator.ts'
|
|
106
|
+
import { statefulCoreRuntime } from './stateful-core.ts'
|
|
107
|
+
|
|
108
|
+
export type { EmulatorEvidence, EmulatorRouteEvidence } from './route-evidence.ts'
|
|
109
|
+
|
|
110
|
+
export { emulatorEvidenceHeader } from './route-evidence.ts'
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
githubEmulatorAccept,
|
|
114
|
+
githubEmulatorApiVersion,
|
|
115
|
+
githubEmulatorErrorBodies,
|
|
116
|
+
githubEmulatorOrigin,
|
|
117
|
+
type GithubEmulatorDrills
|
|
118
|
+
} from './github/api.ts'
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
GithubEmulatorComment,
|
|
122
|
+
GithubEmulatorFile,
|
|
123
|
+
GithubEmulatorIssue,
|
|
124
|
+
GithubEmulatorLabel,
|
|
125
|
+
GithubEmulatorProfile,
|
|
126
|
+
GithubEmulatorRepository,
|
|
127
|
+
GithubEmulatorSeed,
|
|
128
|
+
GithubEmulatorSeedIssue,
|
|
129
|
+
GithubEmulatorStateSchema,
|
|
130
|
+
GithubEmulatorUser,
|
|
131
|
+
type GithubEmulatorState
|
|
132
|
+
} from './github/state.ts'
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Route evidence manifest: every emulated route, whether it writes, and the conformance cases
|
|
136
|
+
* whose (currently synthetic, unverified) wire claims it follows. Kept in sync with the handlers
|
|
137
|
+
* by construction (both come from one route table).
|
|
138
|
+
*/
|
|
139
|
+
export const githubEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence> =
|
|
140
|
+
githubApiRoutes.map(routeEvidence)
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Optional fault filter; an omitted field matches every request. `method` is the HTTP method;
|
|
144
|
+
* `path` is the raw request path (ending in `*`, a prefix); `route` is a route template of the
|
|
145
|
+
* manifest (another value is rejected when the fault is added).
|
|
146
|
+
*/
|
|
147
|
+
export const GithubFaultMatch = StatefulFaultMatch
|
|
148
|
+
|
|
149
|
+
export type GithubFaultMatch = typeof StatefulFaultMatch.Type
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* A status fault (400-599): answer matching requests with this status, headers, and body instead
|
|
153
|
+
* of the route's write, so nothing is written. The body defaults to
|
|
154
|
+
* `{ error: { type: 'emulator_fault', message } }`; `count` limits how many requests it answers.
|
|
155
|
+
* Only a request the emulator would answer reaches a fault: a request that is not emulated, by
|
|
156
|
+
* its shape or by the state, never uses one up.
|
|
157
|
+
*/
|
|
158
|
+
export const GithubFault = StatefulFault
|
|
159
|
+
|
|
160
|
+
export type GithubFault = StatefulFault
|
|
161
|
+
|
|
162
|
+
export type GithubFaultState = StatefulFaultState
|
|
163
|
+
|
|
164
|
+
export type GithubLedgerEntry = StatefulLedgerEntry
|
|
165
|
+
|
|
166
|
+
export type GithubCoverage = StatefulCoverage
|
|
167
|
+
|
|
168
|
+
/** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
|
|
169
|
+
export class GithubEmulatorInputInvalid extends Data.TaggedError('GithubEmulatorInputInvalid')<{
|
|
170
|
+
readonly input: StatefulInputKind
|
|
171
|
+
readonly reason: string
|
|
172
|
+
}> {
|
|
173
|
+
override get message(): string {
|
|
174
|
+
return `Invalid GitHub emulator ${this.input}: ${this.reason}`
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export type GithubEmulatorOptions = {
|
|
179
|
+
/** Typed seed; defaults to the fixture entities (`profile: 'default'`). */
|
|
180
|
+
readonly seed?: GithubEmulatorSeed
|
|
181
|
+
/**
|
|
182
|
+
* Clock in epoch milliseconds (created issue and comment times, close times). Defaults to
|
|
183
|
+
* `Date.now`.
|
|
184
|
+
*/
|
|
185
|
+
readonly now?: () => number
|
|
186
|
+
/** Drill knobs (tests only): make the emulator disagree with one conformance claim. */
|
|
187
|
+
readonly drills?: GithubEmulatorDrills
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export type GithubEmulator = StatefulEmulatorApi<GithubEmulatorState, GithubEmulatorSeed>
|
|
191
|
+
|
|
192
|
+
const inputInvalid = (input: StatefulInputKind, reason: string) =>
|
|
193
|
+
new GithubEmulatorInputInvalid({ input, reason })
|
|
194
|
+
|
|
195
|
+
/** The constant reason of a request on no emulated route shape. */
|
|
196
|
+
const githubUnrecognisedReason = 'no emulated GitHub route for this method and path'
|
|
197
|
+
|
|
198
|
+
/** The constant reason of a request whose `Authorization` header is not one recognisable bearer. */
|
|
199
|
+
const githubUnrecognisedAuthorizationReason =
|
|
200
|
+
'an unrecognisable Authorization header is not emulated'
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create a stateful GitHub emulator on the `@emulators/core` custom runtime. Each call has its own
|
|
204
|
+
* state, ledger, and faults. Rejects with `GithubEmulatorInputInvalid` for an invalid seed or
|
|
205
|
+
* option. Every request needs `Accept: application/vnd.github+json` and
|
|
206
|
+
* `X-GitHub-Api-Version: 2026-03-10` (else not emulated). See `src/stateful-emulator.ts` for the
|
|
207
|
+
* request precedence.
|
|
208
|
+
*/
|
|
209
|
+
export const makeGithubEmulator = async (
|
|
210
|
+
options: GithubEmulatorOptions = {}
|
|
211
|
+
): Promise<GithubEmulator> => {
|
|
212
|
+
const initial = buildSeedState(options.seed ?? {})
|
|
213
|
+
|
|
214
|
+
if (Predicate.isString(initial)) {
|
|
215
|
+
throw inputInvalid('seed', initial)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
checkBooleanDrills(options.drills, githubEmulatorDrillKnobs, inputInvalid)
|
|
219
|
+
|
|
220
|
+
const drills = options.drills ?? {}
|
|
221
|
+
|
|
222
|
+
const env: GithubApiEnv = {
|
|
223
|
+
now: options.now ?? (() => Date.now()),
|
|
224
|
+
drills: {
|
|
225
|
+
linkOmitsNext: drills.linkOmitsNext === true,
|
|
226
|
+
notFoundOmitsDocumentationUrl: drills.notFoundOmitsDocumentationUrl === true,
|
|
227
|
+
validationWithoutErrors: drills.validationWithoutErrors === true,
|
|
228
|
+
contentUnfolded: drills.contentUnfolded === true,
|
|
229
|
+
sinceExcludesEqual: drills.sinceExcludesEqual === true,
|
|
230
|
+
addAnswerOmitsLabel: drills.addAnswerOmitsLabel === true,
|
|
231
|
+
closeWithoutClosedAt: drills.closeWithoutClosedAt === true
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return makeStatefulEmulator<GithubEmulatorState, GithubApiEnv, GithubEmulatorSeed>(
|
|
236
|
+
{
|
|
237
|
+
routes: githubApiRoutes,
|
|
238
|
+
env,
|
|
239
|
+
initial,
|
|
240
|
+
buildSeed: buildSeedState,
|
|
241
|
+
requestProblem: header => {
|
|
242
|
+
// The values every fixture sends.
|
|
243
|
+
if (header('accept') !== githubEmulatorAccept) {
|
|
244
|
+
return `Accept other than ${githubEmulatorAccept} is not emulated`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return header('x-github-api-version') === githubEmulatorApiVersion
|
|
248
|
+
? undefined
|
|
249
|
+
: `X-GitHub-Api-Version other than ${githubEmulatorApiVersion} is not emulated`
|
|
250
|
+
},
|
|
251
|
+
recordHeaders: [
|
|
252
|
+
{ name: 'accept', json: false },
|
|
253
|
+
{ name: 'x-github-api-version', json: false }
|
|
254
|
+
],
|
|
255
|
+
failClosed: {
|
|
256
|
+
unrecognised: githubUnrecognisedReason,
|
|
257
|
+
unrecognisedAuthorization: githubUnrecognisedAuthorizationReason
|
|
258
|
+
},
|
|
259
|
+
// No cursors: label pages are page numbers, as GitHub's are.
|
|
260
|
+
clearRuntime: () => undefined,
|
|
261
|
+
runtimeState: () => ({}),
|
|
262
|
+
seedSummary: state => ({
|
|
263
|
+
labels: state.labels.length,
|
|
264
|
+
issues: state.issues.length,
|
|
265
|
+
files: state.files.length
|
|
266
|
+
}),
|
|
267
|
+
inputInvalid
|
|
268
|
+
},
|
|
269
|
+
statefulCoreRuntime({ name: 'github', initial, decodeState, inputInvalid })
|
|
270
|
+
)
|
|
271
|
+
}
|