@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.
Files changed (333) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2135 -0
  3. package/dist/anthropic.d.mts +269 -0
  4. package/dist/anthropic.d.mts.map +1 -0
  5. package/dist/anthropic.mjs +177 -0
  6. package/dist/anthropic.mjs.map +1 -0
  7. package/dist/chat-completions.d.mts +288 -0
  8. package/dist/chat-completions.d.mts.map +1 -0
  9. package/dist/chat-completions.mjs +451 -0
  10. package/dist/chat-completions.mjs.map +1 -0
  11. package/dist/codex.d.mts +265 -0
  12. package/dist/codex.d.mts.map +1 -0
  13. package/dist/codex.mjs +199 -0
  14. package/dist/codex.mjs.map +1 -0
  15. package/dist/dropbox/api.d.mts +63 -0
  16. package/dist/dropbox/api.d.mts.map +1 -0
  17. package/dist/dropbox/api.mjs +565 -0
  18. package/dist/dropbox/api.mjs.map +1 -0
  19. package/dist/dropbox/state.d.mts +136 -0
  20. package/dist/dropbox/state.d.mts.map +1 -0
  21. package/dist/dropbox/state.mjs +209 -0
  22. package/dist/dropbox/state.mjs.map +1 -0
  23. package/dist/dropbox.d.mts +79 -0
  24. package/dist/dropbox.d.mts.map +1 -0
  25. package/dist/dropbox.mjs +124 -0
  26. package/dist/dropbox.mjs.map +1 -0
  27. package/dist/email-fixtures.d.mts +36 -0
  28. package/dist/email-fixtures.d.mts.map +1 -0
  29. package/dist/email-fixtures.mjs +1080 -0
  30. package/dist/email-fixtures.mjs.map +1 -0
  31. package/dist/email.d.mts +160 -0
  32. package/dist/email.d.mts.map +1 -0
  33. package/dist/email.mjs +608 -0
  34. package/dist/email.mjs.map +1 -0
  35. package/dist/emulator-compose.d.mts +40 -0
  36. package/dist/emulator-compose.d.mts.map +1 -0
  37. package/dist/emulator-compose.mjs +91 -0
  38. package/dist/emulator-compose.mjs.map +1 -0
  39. package/dist/emulator-http.d.mts +53 -0
  40. package/dist/emulator-http.d.mts.map +1 -0
  41. package/dist/emulator-http.mjs +117 -0
  42. package/dist/emulator-http.mjs.map +1 -0
  43. package/dist/emulator-kernel.d.mts +176 -0
  44. package/dist/emulator-kernel.d.mts.map +1 -0
  45. package/dist/emulator-kernel.mjs +413 -0
  46. package/dist/emulator-kernel.mjs.map +1 -0
  47. package/dist/fixture-route.d.mts +129 -0
  48. package/dist/fixture-route.d.mts.map +1 -0
  49. package/dist/fixture-route.mjs +257 -0
  50. package/dist/fixture-route.mjs.map +1 -0
  51. package/dist/fortnox/api.d.mts +92 -0
  52. package/dist/fortnox/api.d.mts.map +1 -0
  53. package/dist/fortnox/api.mjs +750 -0
  54. package/dist/fortnox/api.mjs.map +1 -0
  55. package/dist/fortnox/state.d.mts +533 -0
  56. package/dist/fortnox/state.d.mts.map +1 -0
  57. package/dist/fortnox/state.mjs +612 -0
  58. package/dist/fortnox/state.mjs.map +1 -0
  59. package/dist/fortnox.d.mts +128 -0
  60. package/dist/fortnox.d.mts.map +1 -0
  61. package/dist/fortnox.mjs +403 -0
  62. package/dist/fortnox.mjs.map +1 -0
  63. package/dist/gateway-evaluate-recordings.d.mts +12 -0
  64. package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
  65. package/dist/gateway-evaluate-recordings.mjs +128 -0
  66. package/dist/gateway-evaluate-recordings.mjs.map +1 -0
  67. package/dist/gateway.d.mts +272 -0
  68. package/dist/gateway.d.mts.map +1 -0
  69. package/dist/gateway.mjs +400 -0
  70. package/dist/gateway.mjs.map +1 -0
  71. package/dist/github/api.d.mts +63 -0
  72. package/dist/github/api.d.mts.map +1 -0
  73. package/dist/github/api.mjs +527 -0
  74. package/dist/github/api.mjs.map +1 -0
  75. package/dist/github/state.d.mts +272 -0
  76. package/dist/github/state.d.mts.map +1 -0
  77. package/dist/github/state.mjs +303 -0
  78. package/dist/github/state.mjs.map +1 -0
  79. package/dist/github.d.mts +77 -0
  80. package/dist/github.d.mts.map +1 -0
  81. package/dist/github.mjs +180 -0
  82. package/dist/github.mjs.map +1 -0
  83. package/dist/google/calendar.d.mts +10 -0
  84. package/dist/google/calendar.d.mts.map +1 -0
  85. package/dist/google/calendar.mjs +252 -0
  86. package/dist/google/calendar.mjs.map +1 -0
  87. package/dist/google/drive.d.mts +8 -0
  88. package/dist/google/drive.d.mts.map +1 -0
  89. package/dist/google/drive.mjs +252 -0
  90. package/dist/google/drive.mjs.map +1 -0
  91. package/dist/google/gmail.d.mts +10 -0
  92. package/dist/google/gmail.d.mts.map +1 -0
  93. package/dist/google/gmail.mjs +591 -0
  94. package/dist/google/gmail.mjs.map +1 -0
  95. package/dist/google/shared.d.mts +107 -0
  96. package/dist/google/shared.d.mts.map +1 -0
  97. package/dist/google/shared.mjs +146 -0
  98. package/dist/google/shared.mjs.map +1 -0
  99. package/dist/google/state.d.mts +385 -0
  100. package/dist/google/state.d.mts.map +1 -0
  101. package/dist/google/state.mjs +496 -0
  102. package/dist/google/state.mjs.map +1 -0
  103. package/dist/google.d.mts +75 -0
  104. package/dist/google.d.mts.map +1 -0
  105. package/dist/google.mjs +210 -0
  106. package/dist/google.mjs.map +1 -0
  107. package/dist/linkedin-search/api.d.mts +45 -0
  108. package/dist/linkedin-search/api.d.mts.map +1 -0
  109. package/dist/linkedin-search/api.mjs +181 -0
  110. package/dist/linkedin-search/api.mjs.map +1 -0
  111. package/dist/linkedin-search/state.d.mts +123 -0
  112. package/dist/linkedin-search/state.d.mts.map +1 -0
  113. package/dist/linkedin-search/state.mjs +261 -0
  114. package/dist/linkedin-search/state.mjs.map +1 -0
  115. package/dist/linkedin-search.d.mts +69 -0
  116. package/dist/linkedin-search.d.mts.map +1 -0
  117. package/dist/linkedin-search.mjs +158 -0
  118. package/dist/linkedin-search.mjs.map +1 -0
  119. package/dist/mcp/api.d.mts +44 -0
  120. package/dist/mcp/api.d.mts.map +1 -0
  121. package/dist/mcp/api.mjs +557 -0
  122. package/dist/mcp/api.mjs.map +1 -0
  123. package/dist/mcp/recordings.d.mts +40 -0
  124. package/dist/mcp/recordings.d.mts.map +1 -0
  125. package/dist/mcp/recordings.mjs +2390 -0
  126. package/dist/mcp/recordings.mjs.map +1 -0
  127. package/dist/mcp/state.d.mts +71 -0
  128. package/dist/mcp/state.d.mts.map +1 -0
  129. package/dist/mcp/state.mjs +83 -0
  130. package/dist/mcp/state.mjs.map +1 -0
  131. package/dist/mcp.d.mts +84 -0
  132. package/dist/mcp.d.mts.map +1 -0
  133. package/dist/mcp.mjs +241 -0
  134. package/dist/mcp.mjs.map +1 -0
  135. package/dist/messages.d.mts +183 -0
  136. package/dist/messages.d.mts.map +1 -0
  137. package/dist/messages.mjs +532 -0
  138. package/dist/messages.mjs.map +1 -0
  139. package/dist/microsoft/api.d.mts +47 -0
  140. package/dist/microsoft/api.d.mts.map +1 -0
  141. package/dist/microsoft/api.mjs +178 -0
  142. package/dist/microsoft/api.mjs.map +1 -0
  143. package/dist/microsoft/calendar.d.mts +30 -0
  144. package/dist/microsoft/calendar.d.mts.map +1 -0
  145. package/dist/microsoft/calendar.mjs +271 -0
  146. package/dist/microsoft/calendar.mjs.map +1 -0
  147. package/dist/microsoft/drive.d.mts +36 -0
  148. package/dist/microsoft/drive.d.mts.map +1 -0
  149. package/dist/microsoft/drive.mjs +298 -0
  150. package/dist/microsoft/drive.mjs.map +1 -0
  151. package/dist/microsoft/graph.d.mts +198 -0
  152. package/dist/microsoft/graph.d.mts.map +1 -0
  153. package/dist/microsoft/graph.mjs +264 -0
  154. package/dist/microsoft/graph.mjs.map +1 -0
  155. package/dist/microsoft/mail.d.mts +48 -0
  156. package/dist/microsoft/mail.d.mts.map +1 -0
  157. package/dist/microsoft/mail.mjs +488 -0
  158. package/dist/microsoft/mail.mjs.map +1 -0
  159. package/dist/microsoft/state.d.mts +480 -0
  160. package/dist/microsoft/state.d.mts.map +1 -0
  161. package/dist/microsoft/state.mjs +625 -0
  162. package/dist/microsoft/state.mjs.map +1 -0
  163. package/dist/microsoft.d.mts +168 -0
  164. package/dist/microsoft.d.mts.map +1 -0
  165. package/dist/microsoft.mjs +483 -0
  166. package/dist/microsoft.mjs.map +1 -0
  167. package/dist/node.d.mts +47 -0
  168. package/dist/node.d.mts.map +1 -0
  169. package/dist/node.mjs +178 -0
  170. package/dist/node.mjs.map +1 -0
  171. package/dist/notion/api.d.mts +51 -0
  172. package/dist/notion/api.d.mts.map +1 -0
  173. package/dist/notion/api.mjs +507 -0
  174. package/dist/notion/api.mjs.map +1 -0
  175. package/dist/notion/state.d.mts +329 -0
  176. package/dist/notion/state.d.mts.map +1 -0
  177. package/dist/notion/state.mjs +432 -0
  178. package/dist/notion/state.mjs.map +1 -0
  179. package/dist/notion.d.mts +72 -0
  180. package/dist/notion.d.mts.map +1 -0
  181. package/dist/notion.mjs +127 -0
  182. package/dist/notion.mjs.map +1 -0
  183. package/dist/openai.d.mts +165 -0
  184. package/dist/openai.d.mts.map +1 -0
  185. package/dist/openai.mjs +141 -0
  186. package/dist/openai.mjs.map +1 -0
  187. package/dist/opencode-recordings.d.mts +7 -0
  188. package/dist/opencode-recordings.d.mts.map +1 -0
  189. package/dist/opencode-recordings.mjs +222 -0
  190. package/dist/opencode-recordings.mjs.map +1 -0
  191. package/dist/opencode.d.mts +70 -0
  192. package/dist/opencode.d.mts.map +1 -0
  193. package/dist/opencode.mjs +217 -0
  194. package/dist/opencode.mjs.map +1 -0
  195. package/dist/r2-fixtures.d.mts +36 -0
  196. package/dist/r2-fixtures.d.mts.map +1 -0
  197. package/dist/r2-fixtures.mjs +245 -0
  198. package/dist/r2-fixtures.mjs.map +1 -0
  199. package/dist/r2-guard.d.mts +43 -0
  200. package/dist/r2-guard.d.mts.map +1 -0
  201. package/dist/r2-guard.mjs +215 -0
  202. package/dist/r2-guard.mjs.map +1 -0
  203. package/dist/r2.d.mts +166 -0
  204. package/dist/r2.d.mts.map +1 -0
  205. package/dist/r2.mjs +517 -0
  206. package/dist/r2.mjs.map +1 -0
  207. package/dist/responses.d.mts +231 -0
  208. package/dist/responses.d.mts.map +1 -0
  209. package/dist/responses.mjs +556 -0
  210. package/dist/responses.mjs.map +1 -0
  211. package/dist/route-evidence.d.mts +45 -0
  212. package/dist/route-evidence.d.mts.map +1 -0
  213. package/dist/route-evidence.mjs +56 -0
  214. package/dist/route-evidence.mjs.map +1 -0
  215. package/dist/router.d.mts +93 -0
  216. package/dist/router.d.mts.map +1 -0
  217. package/dist/router.mjs +259 -0
  218. package/dist/router.mjs.map +1 -0
  219. package/dist/stateful-core.d.mts +17 -0
  220. package/dist/stateful-core.d.mts.map +1 -0
  221. package/dist/stateful-core.mjs +41 -0
  222. package/dist/stateful-core.mjs.map +1 -0
  223. package/dist/stateful-emulator.d.mts +565 -0
  224. package/dist/stateful-emulator.d.mts.map +1 -0
  225. package/dist/stateful-emulator.mjs +1228 -0
  226. package/dist/stateful-emulator.mjs.map +1 -0
  227. package/dist/stateful-secrets.d.mts +84 -0
  228. package/dist/stateful-secrets.d.mts.map +1 -0
  229. package/dist/stateful-secrets.mjs +216 -0
  230. package/dist/stateful-secrets.mjs.map +1 -0
  231. package/dist/subscription-usage-recordings.d.mts +9 -0
  232. package/dist/subscription-usage-recordings.d.mts.map +1 -0
  233. package/dist/subscription-usage-recordings.mjs +53 -0
  234. package/dist/subscription-usage-recordings.mjs.map +1 -0
  235. package/dist/subscription-usage.d.mts +53 -0
  236. package/dist/subscription-usage.d.mts.map +1 -0
  237. package/dist/subscription-usage.mjs +21 -0
  238. package/dist/subscription-usage.mjs.map +1 -0
  239. package/dist/telegram/api.d.mts +34 -0
  240. package/dist/telegram/api.d.mts.map +1 -0
  241. package/dist/telegram/api.mjs +257 -0
  242. package/dist/telegram/api.mjs.map +1 -0
  243. package/dist/telegram/state.d.mts +128 -0
  244. package/dist/telegram/state.d.mts.map +1 -0
  245. package/dist/telegram/state.mjs +154 -0
  246. package/dist/telegram/state.mjs.map +1 -0
  247. package/dist/telegram.d.mts +62 -0
  248. package/dist/telegram.d.mts.map +1 -0
  249. package/dist/telegram.mjs +127 -0
  250. package/dist/telegram.mjs.map +1 -0
  251. package/dist/todoist/api.d.mts +55 -0
  252. package/dist/todoist/api.d.mts.map +1 -0
  253. package/dist/todoist/api.mjs +415 -0
  254. package/dist/todoist/api.mjs.map +1 -0
  255. package/dist/todoist/state.d.mts +275 -0
  256. package/dist/todoist/state.d.mts.map +1 -0
  257. package/dist/todoist/state.mjs +345 -0
  258. package/dist/todoist/state.mjs.map +1 -0
  259. package/dist/todoist.d.mts +68 -0
  260. package/dist/todoist.d.mts.map +1 -0
  261. package/dist/todoist.mjs +181 -0
  262. package/dist/todoist.mjs.map +1 -0
  263. package/dist/xai.d.mts +267 -0
  264. package/dist/xai.d.mts.map +1 -0
  265. package/dist/xai.mjs +252 -0
  266. package/dist/xai.mjs.map +1 -0
  267. package/package.json +157 -0
  268. package/src/anthropic.ts +289 -0
  269. package/src/chat-completions.ts +924 -0
  270. package/src/codex.ts +296 -0
  271. package/src/dropbox/api.ts +1084 -0
  272. package/src/dropbox/state.ts +364 -0
  273. package/src/dropbox.ts +203 -0
  274. package/src/email-fixtures.ts +1297 -0
  275. package/src/email.ts +1108 -0
  276. package/src/emulator-compose.ts +147 -0
  277. package/src/emulator-http.ts +184 -0
  278. package/src/emulator-kernel.ts +844 -0
  279. package/src/fixture-route.ts +561 -0
  280. package/src/fortnox/api.ts +1352 -0
  281. package/src/fortnox/state.ts +798 -0
  282. package/src/fortnox.ts +801 -0
  283. package/src/gateway-evaluate-recordings.ts +153 -0
  284. package/src/gateway.ts +530 -0
  285. package/src/github/api.ts +986 -0
  286. package/src/github/state.ts +439 -0
  287. package/src/github.ts +271 -0
  288. package/src/google/calendar.ts +486 -0
  289. package/src/google/drive.ts +471 -0
  290. package/src/google/gmail.ts +1011 -0
  291. package/src/google/shared.ts +321 -0
  292. package/src/google/state.ts +686 -0
  293. package/src/google.ts +298 -0
  294. package/src/linkedin-search/api.ts +363 -0
  295. package/src/linkedin-search/state.ts +373 -0
  296. package/src/linkedin-search.ts +241 -0
  297. package/src/mcp/api.ts +995 -0
  298. package/src/mcp/recordings.ts +2665 -0
  299. package/src/mcp/state.ts +125 -0
  300. package/src/mcp.ts +319 -0
  301. package/src/messages.ts +844 -0
  302. package/src/microsoft/api.ts +426 -0
  303. package/src/microsoft/calendar.ts +491 -0
  304. package/src/microsoft/drive.ts +541 -0
  305. package/src/microsoft/graph.ts +550 -0
  306. package/src/microsoft/mail.ts +833 -0
  307. package/src/microsoft/state.ts +822 -0
  308. package/src/microsoft.ts +982 -0
  309. package/src/node.ts +293 -0
  310. package/src/notion/api.ts +976 -0
  311. package/src/notion/state.ts +512 -0
  312. package/src/notion.ts +210 -0
  313. package/src/openai.ts +198 -0
  314. package/src/opencode-recordings.ts +257 -0
  315. package/src/opencode.ts +271 -0
  316. package/src/r2-fixtures.ts +295 -0
  317. package/src/r2-guard.ts +323 -0
  318. package/src/r2.ts +890 -0
  319. package/src/responses.ts +901 -0
  320. package/src/route-evidence.ts +90 -0
  321. package/src/router.ts +490 -0
  322. package/src/stateful-core.ts +70 -0
  323. package/src/stateful-emulator.ts +2571 -0
  324. package/src/stateful-secrets.ts +299 -0
  325. package/src/subscription-usage-recordings.ts +77 -0
  326. package/src/subscription-usage.ts +68 -0
  327. package/src/telegram/api.ts +437 -0
  328. package/src/telegram/state.ts +229 -0
  329. package/src/telegram.ts +227 -0
  330. package/src/todoist/api.ts +736 -0
  331. package/src/todoist/state.ts +456 -0
  332. package/src/todoist.ts +316 -0
  333. package/src/xai.ts +341 -0
@@ -0,0 +1,77 @@
1
+ import { EmulatorEvidence, EmulatorRouteEvidence, emulatorEvidenceHeader } from "./route-evidence.mjs";
2
+ import { StatefulCoverage, StatefulEmulatorApi, StatefulFault, StatefulFaultMatch, StatefulFaultState, StatefulInputKind, StatefulLedgerEntry } from "./stateful-emulator.mjs";
3
+ import { GithubEmulatorComment, GithubEmulatorFile, GithubEmulatorIssue, GithubEmulatorLabel, GithubEmulatorProfile, GithubEmulatorRepository, GithubEmulatorSeed, GithubEmulatorSeedIssue, GithubEmulatorState, GithubEmulatorStateSchema, GithubEmulatorUser } from "./github/state.mjs";
4
+ import { GithubEmulatorDrills, githubEmulatorAccept, githubEmulatorApiVersion, githubEmulatorErrorBodies, githubEmulatorOrigin } from "./github/api.mjs";
5
+
6
+ //#region src/github.d.ts
7
+ /**
8
+ * Route evidence manifest: every emulated route, whether it writes, and the conformance cases
9
+ * whose (currently synthetic, unverified) wire claims it follows. Kept in sync with the handlers
10
+ * by construction (both come from one route table).
11
+ */
12
+ declare const githubEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence>;
13
+ /**
14
+ * Optional fault filter; an omitted field matches every request. `method` is the HTTP method;
15
+ * `path` is the raw request path (ending in `*`, a prefix); `route` is a route template of the
16
+ * manifest (another value is rejected when the fault is added).
17
+ */
18
+ declare const GithubFaultMatch: import("effect/Schema").Struct<{
19
+ readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
20
+ readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
21
+ readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
22
+ }>;
23
+ type GithubFaultMatch = typeof StatefulFaultMatch.Type;
24
+ /**
25
+ * A status fault (400-599): answer matching requests with this status, headers, and body instead
26
+ * of the route's write, so nothing is written. The body defaults to
27
+ * `{ error: { type: 'emulator_fault', message } }`; `count` limits how many requests it answers.
28
+ * Only a request the emulator would answer reaches a fault: a request that is not emulated, by
29
+ * its shape or by the state, never uses one up.
30
+ */
31
+ declare const GithubFault: import("effect/Schema").Struct<{
32
+ readonly kind: import("effect/Schema").Literal<"status">;
33
+ readonly status: import("effect/Schema").Int;
34
+ readonly headers: import("effect/Schema").optionalKey<import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").String>>;
35
+ readonly body: import("effect/Schema").optionalKey<import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>>;
36
+ readonly match: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
37
+ readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
38
+ readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
39
+ readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
40
+ }>>;
41
+ readonly count: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
42
+ }>;
43
+ type GithubFault = StatefulFault;
44
+ type GithubFaultState = StatefulFaultState;
45
+ type GithubLedgerEntry = StatefulLedgerEntry;
46
+ type GithubCoverage = StatefulCoverage;
47
+ declare const GithubEmulatorInputInvalid_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
48
+ readonly _tag: "GithubEmulatorInputInvalid";
49
+ } & Readonly<A>;
50
+ /** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
51
+ declare class GithubEmulatorInputInvalid extends GithubEmulatorInputInvalid_base<{
52
+ readonly input: StatefulInputKind;
53
+ readonly reason: string;
54
+ }> {
55
+ get message(): string;
56
+ }
57
+ type GithubEmulatorOptions = {
58
+ /** Typed seed; defaults to the fixture entities (`profile: 'default'`). */readonly seed?: GithubEmulatorSeed;
59
+ /**
60
+ * Clock in epoch milliseconds (created issue and comment times, close times). Defaults to
61
+ * `Date.now`.
62
+ */
63
+ readonly now?: () => number; /** Drill knobs (tests only): make the emulator disagree with one conformance claim. */
64
+ readonly drills?: GithubEmulatorDrills;
65
+ };
66
+ type GithubEmulator = StatefulEmulatorApi<GithubEmulatorState, GithubEmulatorSeed>;
67
+ /**
68
+ * Create a stateful GitHub emulator on the `@emulators/core` custom runtime. Each call has its own
69
+ * state, ledger, and faults. Rejects with `GithubEmulatorInputInvalid` for an invalid seed or
70
+ * option. Every request needs `Accept: application/vnd.github+json` and
71
+ * `X-GitHub-Api-Version: 2026-03-10` (else not emulated). See `src/stateful-emulator.ts` for the
72
+ * request precedence.
73
+ */
74
+ declare const makeGithubEmulator: (options?: GithubEmulatorOptions) => Promise<GithubEmulator>;
75
+ //#endregion
76
+ export { type EmulatorEvidence, type EmulatorRouteEvidence, GithubCoverage, GithubEmulator, GithubEmulatorComment, type GithubEmulatorDrills, GithubEmulatorFile, GithubEmulatorInputInvalid, GithubEmulatorIssue, GithubEmulatorLabel, GithubEmulatorOptions, GithubEmulatorProfile, GithubEmulatorRepository, GithubEmulatorSeed, GithubEmulatorSeedIssue, type GithubEmulatorState, GithubEmulatorStateSchema, GithubEmulatorUser, GithubFault, GithubFaultMatch, GithubFaultState, GithubLedgerEntry, emulatorEvidenceHeader, githubEmulatorAccept, githubEmulatorApiVersion, githubEmulatorErrorBodies, githubEmulatorOrigin, githubEmulatorRoutes, makeGithubEmulator };
77
+ //# sourceMappingURL=github.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.mts","names":[],"sources":["../src/github.ts"],"mappings":";;;;;;;;AA0IsE;AAQtE;;cARa,oBAAA,EAAsB,aAAa,CAAC,qBAAA;;;;;;cAQpC,gBAAA,0BAAgB,MAAA;EAAA;;;;KAEjB,gBAAA,UAA0B,kBAAA,CAAmB,IAAI;;;AAA7D;;;;AAA6D;cAShD,WAAA,0BAAW,MAAA;EAAA;;;;;;;;;;;KAEZ,WAAA,GAAc,aAAa;AAAA,KAE3B,gBAAA,GAAmB,kBAAkB;AAAA,KAErC,iBAAA,GAAoB,mBAAmB;AAAA,KAEvC,cAAA,GAAiB,gBAAgB;AAAA,cAAA,+BAAA;;;;cAGhC,0BAAA,SAAmC,+BAAA;EAAA,SACrC,KAAA,EAAO,iBAAA;EAAA,SACP,MAAA;AAAA;EAAA,IAEI,OAAA,CAAA;AAAA;AAAA,KAKH,qBAAA;sFAED,IAAA,GAAO,kBAAA;;;;;WAKP,GAAA;WAEA,MAAA,GAAS,oBAAoB;AAAA;AAAA,KAG5B,cAAA,GAAiB,mBAAA,CAAoB,mBAAA,EAAqB,kBAAA;;;AA9B/B;AAEvC;;;;cA+Ca,kBAAA,GACX,OAAA,GAAS,qBAAA,KACR,OAAA,CAAQ,cAAA"}
@@ -0,0 +1,180 @@
1
+ import { emulatorEvidenceHeader } from "./route-evidence.mjs";
2
+ import { StatefulFault, StatefulFaultMatch, checkBooleanDrills, makeStatefulEmulator, routeEvidence } from "./stateful-emulator.mjs";
3
+ import { statefulCoreRuntime } from "./stateful-core.mjs";
4
+ import { GithubEmulatorComment, GithubEmulatorFile, GithubEmulatorIssue, GithubEmulatorLabel, GithubEmulatorProfile, GithubEmulatorRepository, GithubEmulatorSeed, GithubEmulatorSeedIssue, GithubEmulatorStateSchema, GithubEmulatorUser, buildSeedState, decodeState } from "./github/state.mjs";
5
+ import { githubApiRoutes, githubEmulatorAccept, githubEmulatorApiVersion, githubEmulatorDrillKnobs, githubEmulatorErrorBodies, githubEmulatorOrigin } from "./github/api.mjs";
6
+ import { Data, Predicate } from "effect";
7
+ //#region src/github.ts
8
+ /**
9
+ * Stateful GitHub emulator (the REST routes of the GitHub conformance cases on
10
+ * `https://api.github.com`, API version 2026-03-10), built on the upstream `@emulators/core`
11
+ * custom runtime, with a request ledger, status faults, and an `/_emulate/*` control plane.
12
+ *
13
+ * It never imports SDK code: its wire shapes, error bodies, and default seed are copied as data
14
+ * from the synthetic GitHub conformance fixtures, and every route names the conformance cases it
15
+ * follows in `githubEmulatorRoutes`. Only the routes those seven cases send are emulated. Response
16
+ * behaviour comes only from the fixtures: anything they do not show answers one ledgered 400
17
+ * not-emulated, writes nothing, and uses up no fault. That includes the leftover lookup's
18
+ * open-issue listing (`GET /repos/{owner}/{repo}/issues`, no fixture), so the read-only lookup
19
+ * fails and the live runner prints its lookup-failed WARN. Every route answers only on the
20
+ * recorded origin `https://api.github.com` (`fetchOn(origin)` serves it behind a loopback
21
+ * rewrite).
22
+ *
23
+ * Fail closed: a request is recognised only when its raw path is exactly an emulated route shape
24
+ * (every path parameter matches its raw pattern in full) under that route's method, and any
25
+ * `Authorization` header is exactly `Bearer <token>` (a recognisable bearer, below). Every other
26
+ * request is ledgered and answered with constant text only (`/<unrecognised>`, a standard method or
27
+ * `<other>`, an empty query, no body, a constant reason). The bearer value is never compared
28
+ * against anything, stored, forwarded, or ledgered. A recognised bearer must match the RFC 6750
29
+ * `b64token` syntax exactly (`^[A-Za-z0-9\-._~+/]+=*$`, at least 8 characters), start with a
30
+ * character in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u`, and hold at least one character
31
+ * outside the JSON-number alphabet `[0-9.eE+-]` (every GitHub and Google token form does: `ghp_…`,
32
+ * `github_pat_…`, `gho_…`, `ya29.…`). So no number's text can contain it; it holds no escape
33
+ * introducer (`%`, `\`, `"`), so no escape starts inside it; and its first character is no hex
34
+ * digit and no JSON escape letter, so no stray `%`, `\`, or partial escape to its left can complete
35
+ * with it, and its characters always decode in place. An `Authorization` header with any other
36
+ * value is unrecognisable. A recognised request that repeats the bearer value in its raw path, any
37
+ * path segment, the raw query or any query key or value, any recorded header, or its body is
38
+ * refused and ledgered with constant text only: a standard method, the path `/<unrecognised>`, its
39
+ * route template, an empty query, no headers or body, and a constant reason
40
+ * (`the query repeats the credential`, for example). Each part is checked through the closure of
41
+ * two total, lexical transforms that cannot fail: a tolerant percent-decode (every `%XX` below
42
+ * `%80` becomes its ASCII character; any other `%` sequence is left as it is) and a tolerant
43
+ * JSON-unescape (in any text, whether or not it parses as JSON, `\uXXXX` below `\u0080` and `\"`,
44
+ * `\\`, `\/`, `\b`, `\f`, `\n`, `\r`, `\t` become their characters). Starting from each part's raw
45
+ * text, either transform is applied to every text of the previous step, deduplicated, until no new
46
+ * text appears (a fixpoint), and every text is checked for the bearer as a substring; both
47
+ * transforms never lengthen a text and shorten it whenever they change it. So any depth of
48
+ * percent-encoding or JSON escaping, in any order, is seen through in every part: the raw path and
49
+ * each raw path segment, the raw query and each query key and value (already decoded once by
50
+ * `URLSearchParams`), each recorded header, and the raw body. The work is capped at 64 rounds, 1024
51
+ * distinct texts, or 8 Mi characters read by the transforms, whichever comes first; a part whose
52
+ * closure hits a cap before its fixpoint counts as repeating the credential and is refused with the
53
+ * same constant entry (uncertainty refuses, it never admits; so any part over 4 Mi characters is
54
+ * always refused). Any other recognised request has the bearer value scrubbed from its ledgered
55
+ * fields and every not-emulated reason (plan-time reasons included); its recorded query is keyed by
56
+ * recorded key; a key recorded more than once lists its values in order (as a JSON array); and
57
+ * recorded headers and query keys and values that start like JSON (`{`, `[`, `"`) are recorded
58
+ * parsed with credential-named keys redacted at any depth, or as `<redacted>` when they do not
59
+ * parse, whatever the header's declared format. Refusals never echo a request's own query or body
60
+ * keys.
61
+ *
62
+ * Request-shape latitude (`/github`, the only accepted deviations): any bearer value in the RFC
63
+ * 6750 `b64token` syntax (`[A-Za-z0-9\-._~+/]+=*`) of at least 8 characters, starting with a
64
+ * character in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u` (so a legacy all-hex token is
65
+ * refused), with at least one outside `[0-9.eE+-]`, that occurs nowhere else in the request (never
66
+ * compared against anything, stored, or ledgered); extra request headers; JSON key order;
67
+ * `content-type` media-type parameters; the order of query parameters; any non-empty issue title
68
+ * and comment body, and any issue body text; any comment listing `since` of the form
69
+ * `YYYY-MM-DDTHH:MM:SSZ`; any label listing `per_page` from 1 to 100, with no `page` or a `page`
70
+ * from 2 to one past the last page; any issue search `q` that starts with the seeded
71
+ * `repo:<owner>/<repo>` qualifier and whose query after it is longer than 256 characters (answered
72
+ * the recorded 422); any issue number the repository has not reached (answered the recorded 404);
73
+ * and any issue, comment, repository label, or file the state holds where a fixture has one, under
74
+ * the per-route state rules. `Authorization` must be exactly `Bearer <token>` (that spelling, one
75
+ * space), `Accept` `application/vnd.github+json`, and `X-GitHub-Api-Version` `2026-03-10`.
76
+ * Everything else (other keys and values, query parameters, empty query components such as a bare
77
+ * `?` or a stray `&`, another origin or repository, a repeated query key, an explicit `page=1`, and
78
+ * a comment listing `per_page` other than 100) is not emulated.
79
+ *
80
+ * Node-only: `@emulators/core` imports Node builtins, so the core is loaded lazily by
81
+ * `makeGithubEmulator` (importing this module has no side effects).
82
+ *
83
+ * @experimental
84
+ */
85
+ /**
86
+ * Route evidence manifest: every emulated route, whether it writes, and the conformance cases
87
+ * whose (currently synthetic, unverified) wire claims it follows. Kept in sync with the handlers
88
+ * by construction (both come from one route table).
89
+ */
90
+ const githubEmulatorRoutes = githubApiRoutes.map(routeEvidence);
91
+ /**
92
+ * Optional fault filter; an omitted field matches every request. `method` is the HTTP method;
93
+ * `path` is the raw request path (ending in `*`, a prefix); `route` is a route template of the
94
+ * manifest (another value is rejected when the fault is added).
95
+ */
96
+ const GithubFaultMatch = StatefulFaultMatch;
97
+ /**
98
+ * A status fault (400-599): answer matching requests with this status, headers, and body instead
99
+ * of the route's write, so nothing is written. The body defaults to
100
+ * `{ error: { type: 'emulator_fault', message } }`; `count` limits how many requests it answers.
101
+ * Only a request the emulator would answer reaches a fault: a request that is not emulated, by
102
+ * its shape or by the state, never uses one up.
103
+ */
104
+ const GithubFault = StatefulFault;
105
+ /** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
106
+ var GithubEmulatorInputInvalid = class extends Data.TaggedError("GithubEmulatorInputInvalid") {
107
+ get message() {
108
+ return `Invalid GitHub emulator ${this.input}: ${this.reason}`;
109
+ }
110
+ };
111
+ const inputInvalid = (input, reason) => new GithubEmulatorInputInvalid({
112
+ input,
113
+ reason
114
+ });
115
+ /** The constant reason of a request on no emulated route shape. */
116
+ const githubUnrecognisedReason = "no emulated GitHub route for this method and path";
117
+ /** The constant reason of a request whose `Authorization` header is not one recognisable bearer. */
118
+ const githubUnrecognisedAuthorizationReason = "an unrecognisable Authorization header is not emulated";
119
+ /**
120
+ * Create a stateful GitHub emulator on the `@emulators/core` custom runtime. Each call has its own
121
+ * state, ledger, and faults. Rejects with `GithubEmulatorInputInvalid` for an invalid seed or
122
+ * option. Every request needs `Accept: application/vnd.github+json` and
123
+ * `X-GitHub-Api-Version: 2026-03-10` (else not emulated). See `src/stateful-emulator.ts` for the
124
+ * request precedence.
125
+ */
126
+ const makeGithubEmulator = async (options = {}) => {
127
+ const initial = buildSeedState(options.seed ?? {});
128
+ if (Predicate.isString(initial)) throw inputInvalid("seed", initial);
129
+ checkBooleanDrills(options.drills, githubEmulatorDrillKnobs, inputInvalid);
130
+ const drills = options.drills ?? {};
131
+ return makeStatefulEmulator({
132
+ routes: githubApiRoutes,
133
+ env: {
134
+ now: options.now ?? (() => Date.now()),
135
+ drills: {
136
+ linkOmitsNext: drills.linkOmitsNext === true,
137
+ notFoundOmitsDocumentationUrl: drills.notFoundOmitsDocumentationUrl === true,
138
+ validationWithoutErrors: drills.validationWithoutErrors === true,
139
+ contentUnfolded: drills.contentUnfolded === true,
140
+ sinceExcludesEqual: drills.sinceExcludesEqual === true,
141
+ addAnswerOmitsLabel: drills.addAnswerOmitsLabel === true,
142
+ closeWithoutClosedAt: drills.closeWithoutClosedAt === true
143
+ }
144
+ },
145
+ initial,
146
+ buildSeed: buildSeedState,
147
+ requestProblem: (header) => {
148
+ if (header("accept") !== "application/vnd.github+json") return `Accept other than ${githubEmulatorAccept} is not emulated`;
149
+ return header("x-github-api-version") === "2026-03-10" ? void 0 : `X-GitHub-Api-Version other than ${githubEmulatorApiVersion} is not emulated`;
150
+ },
151
+ recordHeaders: [{
152
+ name: "accept",
153
+ json: false
154
+ }, {
155
+ name: "x-github-api-version",
156
+ json: false
157
+ }],
158
+ failClosed: {
159
+ unrecognised: githubUnrecognisedReason,
160
+ unrecognisedAuthorization: githubUnrecognisedAuthorizationReason
161
+ },
162
+ clearRuntime: () => void 0,
163
+ runtimeState: () => ({}),
164
+ seedSummary: (state) => ({
165
+ labels: state.labels.length,
166
+ issues: state.issues.length,
167
+ files: state.files.length
168
+ }),
169
+ inputInvalid
170
+ }, statefulCoreRuntime({
171
+ name: "github",
172
+ initial,
173
+ decodeState,
174
+ inputInvalid
175
+ }));
176
+ };
177
+ //#endregion
178
+ export { GithubEmulatorComment, GithubEmulatorFile, GithubEmulatorInputInvalid, GithubEmulatorIssue, GithubEmulatorLabel, GithubEmulatorProfile, GithubEmulatorRepository, GithubEmulatorSeed, GithubEmulatorSeedIssue, GithubEmulatorStateSchema, GithubEmulatorUser, GithubFault, GithubFaultMatch, emulatorEvidenceHeader, githubEmulatorAccept, githubEmulatorApiVersion, githubEmulatorErrorBodies, githubEmulatorOrigin, githubEmulatorRoutes, makeGithubEmulator };
179
+
180
+ //# sourceMappingURL=github.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.mjs","names":[],"sources":["../src/github.ts"],"sourcesContent":["/**\n * Stateful GitHub emulator (the REST routes of the GitHub conformance cases on\n * `https://api.github.com`, API version 2026-03-10), built on the upstream `@emulators/core`\n * custom runtime, with a request ledger, status faults, and an `/_emulate/*` control plane.\n *\n * It never imports SDK code: its wire shapes, error bodies, and default seed are copied as data\n * from the synthetic GitHub conformance fixtures, and every route names the conformance cases it\n * follows in `githubEmulatorRoutes`. Only the routes those seven cases send are emulated. Response\n * behaviour comes only from the fixtures: anything they do not show answers one ledgered 400\n * not-emulated, writes nothing, and uses up no fault. That includes the leftover lookup's\n * open-issue listing (`GET /repos/{owner}/{repo}/issues`, no fixture), so the read-only lookup\n * fails and the live runner prints its lookup-failed WARN. Every route answers only on the\n * recorded origin `https://api.github.com` (`fetchOn(origin)` serves it behind a loopback\n * rewrite).\n *\n * Fail closed: a request is recognised only when its raw path is exactly an emulated route shape\n * (every path parameter matches its raw pattern in full) under that route's method, and any\n * `Authorization` header is exactly `Bearer <token>` (a recognisable bearer, below). Every other\n * request is ledgered and answered with constant text only (`/<unrecognised>`, a standard method or\n * `<other>`, an empty query, no body, a constant reason). The bearer value is never compared\n * against anything, stored, forwarded, or ledgered. A recognised bearer must match the RFC 6750\n * `b64token` syntax exactly (`^[A-Za-z0-9\\-._~+/]+=*$`, at least 8 characters), start with a\n * character in `[G-Zg-z\\-._~+/]` other than `n`, `r`, `t`, `u`, and hold at least one character\n * outside the JSON-number alphabet `[0-9.eE+-]` (every GitHub and Google token form does: `ghp_…`,\n * `github_pat_…`, `gho_…`, `ya29.…`). So no number's text can contain it; it holds no escape\n * introducer (`%`, `\\`, `\"`), so no escape starts inside it; and its first character is no hex\n * digit and no JSON escape letter, so no stray `%`, `\\`, or partial escape to its left can complete\n * with it, and its characters always decode in place. An `Authorization` header with any other\n * value is unrecognisable. A recognised request that repeats the bearer value in its raw path, any\n * path segment, the raw query or any query key or value, any recorded header, or its body is\n * refused and ledgered with constant text only: a standard method, the path `/<unrecognised>`, its\n * route template, an empty query, no headers or body, and a constant reason\n * (`the query repeats the credential`, for example). Each part is checked through the closure of\n * two total, lexical transforms that cannot fail: a tolerant percent-decode (every `%XX` below\n * `%80` becomes its ASCII character; any other `%` sequence is left as it is) and a tolerant\n * JSON-unescape (in any text, whether or not it parses as JSON, `\\uXXXX` below `\\u0080` and `\\\"`,\n * `\\\\`, `\\/`, `\\b`, `\\f`, `\\n`, `\\r`, `\\t` become their characters). Starting from each part's raw\n * text, either transform is applied to every text of the previous step, deduplicated, until no new\n * text appears (a fixpoint), and every text is checked for the bearer as a substring; both\n * transforms never lengthen a text and shorten it whenever they change it. So any depth of\n * percent-encoding or JSON escaping, in any order, is seen through in every part: the raw path and\n * each raw path segment, the raw query and each query key and value (already decoded once by\n * `URLSearchParams`), each recorded header, and the raw body. The work is capped at 64 rounds, 1024\n * distinct texts, or 8 Mi characters read by the transforms, whichever comes first; a part whose\n * closure hits a cap before its fixpoint counts as repeating the credential and is refused with the\n * same constant entry (uncertainty refuses, it never admits; so any part over 4 Mi characters is\n * always refused). Any other recognised request has the bearer value scrubbed from its ledgered\n * fields and every not-emulated reason (plan-time reasons included); its recorded query is keyed by\n * recorded key; a key recorded more than once lists its values in order (as a JSON array); and\n * recorded headers and query keys and values that start like JSON (`{`, `[`, `\"`) are recorded\n * parsed with credential-named keys redacted at any depth, or as `<redacted>` when they do not\n * parse, whatever the header's declared format. Refusals never echo a request's own query or body\n * keys.\n *\n * Request-shape latitude (`/github`, the only accepted deviations): any bearer value in the RFC\n * 6750 `b64token` syntax (`[A-Za-z0-9\\-._~+/]+=*`) of at least 8 characters, starting with a\n * character in `[G-Zg-z\\-._~+/]` other than `n`, `r`, `t`, `u` (so a legacy all-hex token is\n * refused), with at least one outside `[0-9.eE+-]`, that occurs nowhere else in the request (never\n * compared against anything, stored, or ledgered); extra request headers; JSON key order;\n * `content-type` media-type parameters; the order of query parameters; any non-empty issue title\n * and comment body, and any issue body text; any comment listing `since` of the form\n * `YYYY-MM-DDTHH:MM:SSZ`; any label listing `per_page` from 1 to 100, with no `page` or a `page`\n * from 2 to one past the last page; any issue search `q` that starts with the seeded\n * `repo:<owner>/<repo>` qualifier and whose query after it is longer than 256 characters (answered\n * the recorded 422); any issue number the repository has not reached (answered the recorded 404);\n * and any issue, comment, repository label, or file the state holds where a fixture has one, under\n * the per-route state rules. `Authorization` must be exactly `Bearer <token>` (that spelling, one\n * space), `Accept` `application/vnd.github+json`, and `X-GitHub-Api-Version` `2026-03-10`.\n * Everything else (other keys and values, query parameters, empty query components such as a bare\n * `?` or a stray `&`, another origin or repository, a repeated query key, an explicit `page=1`, and\n * a comment listing `per_page` other than 100) is not emulated.\n *\n * Node-only: `@emulators/core` imports Node builtins, so the core is loaded lazily by\n * `makeGithubEmulator` (importing this module has no side effects).\n *\n * @experimental\n */\nimport { Data, Predicate } from 'effect'\nimport {\n githubApiRoutes,\n githubEmulatorAccept,\n githubEmulatorApiVersion,\n githubEmulatorDrillKnobs,\n type GithubApiEnv,\n type GithubEmulatorDrills\n} from './github/api.ts'\nimport {\n buildSeedState,\n decodeState,\n type GithubEmulatorSeed,\n type GithubEmulatorState\n} from './github/state.ts'\nimport type { EmulatorRouteEvidence } from './route-evidence.ts'\nimport {\n StatefulFault,\n StatefulFaultMatch,\n checkBooleanDrills,\n makeStatefulEmulator,\n routeEvidence,\n type StatefulCoverage,\n type StatefulEmulatorApi,\n type StatefulFaultState,\n type StatefulInputKind,\n type StatefulLedgerEntry\n} from './stateful-emulator.ts'\nimport { statefulCoreRuntime } from './stateful-core.ts'\n\nexport type { EmulatorEvidence, EmulatorRouteEvidence } from './route-evidence.ts'\n\nexport { emulatorEvidenceHeader } from './route-evidence.ts'\n\nexport {\n githubEmulatorAccept,\n githubEmulatorApiVersion,\n githubEmulatorErrorBodies,\n githubEmulatorOrigin,\n type GithubEmulatorDrills\n} from './github/api.ts'\n\nexport {\n GithubEmulatorComment,\n GithubEmulatorFile,\n GithubEmulatorIssue,\n GithubEmulatorLabel,\n GithubEmulatorProfile,\n GithubEmulatorRepository,\n GithubEmulatorSeed,\n GithubEmulatorSeedIssue,\n GithubEmulatorStateSchema,\n GithubEmulatorUser,\n type GithubEmulatorState\n} from './github/state.ts'\n\n/**\n * Route evidence manifest: every emulated route, whether it writes, and the conformance cases\n * whose (currently synthetic, unverified) wire claims it follows. Kept in sync with the handlers\n * by construction (both come from one route table).\n */\nexport const githubEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence> =\n githubApiRoutes.map(routeEvidence)\n\n/**\n * Optional fault filter; an omitted field matches every request. `method` is the HTTP method;\n * `path` is the raw request path (ending in `*`, a prefix); `route` is a route template of the\n * manifest (another value is rejected when the fault is added).\n */\nexport const GithubFaultMatch = StatefulFaultMatch\n\nexport type GithubFaultMatch = typeof StatefulFaultMatch.Type\n\n/**\n * A status fault (400-599): answer matching requests with this status, headers, and body instead\n * of the route's write, so nothing is written. The body defaults to\n * `{ error: { type: 'emulator_fault', message } }`; `count` limits how many requests it answers.\n * Only a request the emulator would answer reaches a fault: a request that is not emulated, by\n * its shape or by the state, never uses one up.\n */\nexport const GithubFault = StatefulFault\n\nexport type GithubFault = StatefulFault\n\nexport type GithubFaultState = StatefulFaultState\n\nexport type GithubLedgerEntry = StatefulLedgerEntry\n\nexport type GithubCoverage = StatefulCoverage\n\n/** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */\nexport class GithubEmulatorInputInvalid extends Data.TaggedError('GithubEmulatorInputInvalid')<{\n readonly input: StatefulInputKind\n readonly reason: string\n}> {\n override get message(): string {\n return `Invalid GitHub emulator ${this.input}: ${this.reason}`\n }\n}\n\nexport type GithubEmulatorOptions = {\n /** Typed seed; defaults to the fixture entities (`profile: 'default'`). */\n readonly seed?: GithubEmulatorSeed\n /**\n * Clock in epoch milliseconds (created issue and comment times, close times). Defaults to\n * `Date.now`.\n */\n readonly now?: () => number\n /** Drill knobs (tests only): make the emulator disagree with one conformance claim. */\n readonly drills?: GithubEmulatorDrills\n}\n\nexport type GithubEmulator = StatefulEmulatorApi<GithubEmulatorState, GithubEmulatorSeed>\n\nconst inputInvalid = (input: StatefulInputKind, reason: string) =>\n new GithubEmulatorInputInvalid({ input, reason })\n\n/** The constant reason of a request on no emulated route shape. */\nconst githubUnrecognisedReason = 'no emulated GitHub route for this method and path'\n\n/** The constant reason of a request whose `Authorization` header is not one recognisable bearer. */\nconst githubUnrecognisedAuthorizationReason =\n 'an unrecognisable Authorization header is not emulated'\n\n/**\n * Create a stateful GitHub emulator on the `@emulators/core` custom runtime. Each call has its own\n * state, ledger, and faults. Rejects with `GithubEmulatorInputInvalid` for an invalid seed or\n * option. Every request needs `Accept: application/vnd.github+json` and\n * `X-GitHub-Api-Version: 2026-03-10` (else not emulated). See `src/stateful-emulator.ts` for the\n * request precedence.\n */\nexport const makeGithubEmulator = async (\n options: GithubEmulatorOptions = {}\n): Promise<GithubEmulator> => {\n const initial = buildSeedState(options.seed ?? {})\n\n if (Predicate.isString(initial)) {\n throw inputInvalid('seed', initial)\n }\n\n checkBooleanDrills(options.drills, githubEmulatorDrillKnobs, inputInvalid)\n\n const drills = options.drills ?? {}\n\n const env: GithubApiEnv = {\n now: options.now ?? (() => Date.now()),\n drills: {\n linkOmitsNext: drills.linkOmitsNext === true,\n notFoundOmitsDocumentationUrl: drills.notFoundOmitsDocumentationUrl === true,\n validationWithoutErrors: drills.validationWithoutErrors === true,\n contentUnfolded: drills.contentUnfolded === true,\n sinceExcludesEqual: drills.sinceExcludesEqual === true,\n addAnswerOmitsLabel: drills.addAnswerOmitsLabel === true,\n closeWithoutClosedAt: drills.closeWithoutClosedAt === true\n }\n }\n\n return makeStatefulEmulator<GithubEmulatorState, GithubApiEnv, GithubEmulatorSeed>(\n {\n routes: githubApiRoutes,\n env,\n initial,\n buildSeed: buildSeedState,\n requestProblem: header => {\n // The values every fixture sends.\n if (header('accept') !== githubEmulatorAccept) {\n return `Accept other than ${githubEmulatorAccept} is not emulated`\n }\n\n return header('x-github-api-version') === githubEmulatorApiVersion\n ? undefined\n : `X-GitHub-Api-Version other than ${githubEmulatorApiVersion} is not emulated`\n },\n recordHeaders: [\n { name: 'accept', json: false },\n { name: 'x-github-api-version', json: false }\n ],\n failClosed: {\n unrecognised: githubUnrecognisedReason,\n unrecognisedAuthorization: githubUnrecognisedAuthorizationReason\n },\n // No cursors: label pages are page numbers, as GitHub's are.\n clearRuntime: () => undefined,\n runtimeState: () => ({}),\n seedSummary: state => ({\n labels: state.labels.length,\n issues: state.issues.length,\n files: state.files.length\n }),\n inputInvalid\n },\n statefulCoreRuntime({ name: 'github', initial, decodeState, inputInvalid })\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0IA,MAAa,uBACX,gBAAgB,IAAI,aAAa;;;;;;AAOnC,MAAa,mBAAmB;;;;;;;;AAWhC,MAAa,cAAc;;AAW3B,IAAa,6BAAb,cAAgD,KAAK,YAAY,4BAA4B,EAG1F;CACD,IAAa,UAAkB;EAC7B,OAAO,2BAA2B,KAAK,MAAM,IAAI,KAAK;CACxD;AACF;AAgBA,MAAM,gBAAgB,OAA0B,WAC9C,IAAI,2BAA2B;CAAE;CAAO;AAAO,CAAC;;AAGlD,MAAM,2BAA2B;;AAGjC,MAAM,wCACJ;;;;;;;;AASF,MAAa,qBAAqB,OAChC,UAAiC,CAAC,MACN;CAC5B,MAAM,UAAU,eAAe,QAAQ,QAAQ,CAAC,CAAC;CAEjD,IAAI,UAAU,SAAS,OAAO,GAC5B,MAAM,aAAa,QAAQ,OAAO;CAGpC,mBAAmB,QAAQ,QAAQ,0BAA0B,YAAY;CAEzE,MAAM,SAAS,QAAQ,UAAU,CAAC;CAelC,OAAO,qBACL;EACE,QAAQ;EACR,KAAA;GAfF,KAAK,QAAQ,cAAc,KAAK,IAAI;GACpC,QAAQ;IACN,eAAe,OAAO,kBAAkB;IACxC,+BAA+B,OAAO,kCAAkC;IACxE,yBAAyB,OAAO,4BAA4B;IAC5D,iBAAiB,OAAO,oBAAoB;IAC5C,oBAAoB,OAAO,uBAAuB;IAClD,qBAAqB,OAAO,wBAAwB;IACpD,sBAAsB,OAAO,yBAAyB;GACxD;EAMI;EACF;EACA,WAAW;EACX,iBAAgB,WAAU;GAExB,IAAI,OAAO,QAAQ,MAAA,+BACjB,OAAO,qBAAqB,qBAAqB;GAGnD,OAAO,OAAO,sBAAsB,MAAA,eAChC,KAAA,IACA,mCAAmC,yBAAyB;EAClE;EACA,eAAe,CACb;GAAE,MAAM;GAAU,MAAM;EAAM,GAC9B;GAAE,MAAM;GAAwB,MAAM;EAAM,CAC9C;EACA,YAAY;GACV,cAAc;GACd,2BAA2B;EAC7B;EAEA,oBAAoB,KAAA;EACpB,qBAAqB,CAAC;EACtB,cAAa,WAAU;GACrB,QAAQ,MAAM,OAAO;GACrB,QAAQ,MAAM,OAAO;GACrB,OAAO,MAAM,MAAM;EACrB;EACA;CACF,GACA,oBAAoB;EAAE,MAAM;EAAU;EAAS;EAAa;CAAa,CAAC,CAC5E;AACF"}
@@ -0,0 +1,10 @@
1
+ import { GoogleRoute } from "./shared.mjs";
2
+
3
+ //#region src/google/calendar.d.ts
4
+ /** Path of the events collection of `{calendarId}`. */
5
+ declare const calendarEventsPath = "/calendar/v3/calendars/{calendarId}/events";
6
+ /** The Calendar routes, in manifest order. */
7
+ declare const calendarRoutes: ReadonlyArray<GoogleRoute>;
8
+ //#endregion
9
+ export { calendarEventsPath, calendarRoutes };
10
+ //# sourceMappingURL=calendar.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.mts","names":[],"sources":["../../src/google/calendar.ts"],"mappings":";;;;cAgDa,kBAAA;AAAb;AAAA,cA+aa,cAAA,EAAgB,aAAa,CAAC,WAAA"}
@@ -0,0 +1,252 @@
1
+ import { exactBodyKeys, exactQuery, isNotEmulated, notEmulated, statefulRoute } from "../stateful-emulator.mjs";
2
+ import { answer, evidence, googleEmulatorApisOrigin, googleErrorBody, googleJson, googleNoContent, isRunText, listPage, pageSize, param, recordedValue, withoutQuery } from "./shared.mjs";
3
+ import { eventFields, mintedEventId, mintedEventIdPattern } from "./state.mjs";
4
+ import { Predicate } from "effect";
5
+ //#region src/google/calendar.ts
6
+ /**
7
+ * Calendar routes of the Google emulator (internal): the time-range event listing with page
8
+ * tokens, and the lifecycle of a run-scoped event without attendees (create, read, PATCH rename,
9
+ * delete to `cancelled`, and the recorded 410 of a repeated delete).
10
+ *
11
+ * Every answer comes from the Calendar fixtures, through the seed or the request, or is minted
12
+ * (event ids, page tokens, `created` / `updated` from the injectable clock). A deleted event stays
13
+ * in the state as `cancelled`, as the fixtures read it back.
14
+ *
15
+ * @experimental
16
+ */
17
+ /** Path of the events collection of `{calendarId}`. */
18
+ const calendarEventsPath = "/calendar/v3/calendars/{calendarId}/events";
19
+ const listCase = "google.calendar.list-range-paging";
20
+ const lifecycleCase = "google.calendar.event-lifecycle";
21
+ const deletedCase = "google.calendar.deleted-event-gone";
22
+ /** A raw calendar id segment: an address-like id, its `@` percent-encoded once (`%40`). */
23
+ const calendarIdSegment = /^[A-Za-z0-9._+-]{1,200}(?:%40[A-Za-z0-9.-]{1,200})?$/;
24
+ const eventIdSegment = /^[A-Za-z0-9_-]{1,1024}$/;
25
+ const calendar = (method, path, write, caseIds, withEvent) => evidence(googleEmulatorApisOrigin, method, `${calendarEventsPath}${path}`, write, caseIds, withEvent ? {
26
+ calendarId: calendarIdSegment,
27
+ eventId: eventIdSegment
28
+ } : { calendarId: calendarIdSegment });
29
+ const createdDescription = "Synthetic conformance event without attendees.";
30
+ const renamedDescription = "Synthetic conformance event, renamed.";
31
+ const eventSummaryRest = "synthetic conformance event, safe to delete";
32
+ /** The fixed half hour every recorded event create sends. */
33
+ const recordedStart = {
34
+ dateTime: "2030-01-07T09:00:00Z",
35
+ timeZone: "UTC"
36
+ };
37
+ const recordedEnd = {
38
+ dateTime: "2030-01-07T09:30:00Z",
39
+ timeZone: "UTC"
40
+ };
41
+ const instantPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|[+-](\d{2}):(\d{2}))$/;
42
+ /**
43
+ * True for an RFC 3339 instant with a `Z` or numeric offset whose components are in range (a real
44
+ * calendar date, hour 0-23, minute and second 0-59, offset hour 0-23 and minute 0-59), checked
45
+ * before `Date.parse`, which would roll an out-of-range component over instead of refusing it.
46
+ */
47
+ const isInstant = (text) => {
48
+ const parts = instantPattern.exec(text);
49
+ if (parts === null) return false;
50
+ const [year, month, day, hour, minute, second, offsetHour, offsetMinute] = parts.slice(1).map((part) => Number(part ?? "0"));
51
+ if (year === void 0 || month === void 0 || day === void 0) return false;
52
+ const days = [
53
+ 31,
54
+ year % 4 === 0 && year % 100 !== 0 || year % 400 === 0 ? 29 : 28,
55
+ 31,
56
+ 30,
57
+ 31,
58
+ 30,
59
+ 31,
60
+ 31,
61
+ 30,
62
+ 31,
63
+ 30,
64
+ 31
65
+ ][month - 1] ?? 0;
66
+ return day >= 1 && day <= days && (hour ?? 24) <= 23 && (minute ?? 60) <= 59 && (second ?? 60) <= 59 && (offsetHour ?? 24) <= 23 && (offsetMinute ?? 60) <= 59 && Number.isFinite(Date.parse(text));
67
+ };
68
+ const renderEvent = (state, owner, event) => {
69
+ const head = {
70
+ kind: "calendar#event",
71
+ etag: event.etag,
72
+ id: event.id,
73
+ status: event.status,
74
+ htmlLink: event.htmlLink,
75
+ created: event.created,
76
+ updated: event.updated,
77
+ summary: event.summary
78
+ };
79
+ if (event.description !== void 0) head.description = event.description;
80
+ return {
81
+ ...head,
82
+ creator: {
83
+ email: state.practiceAddress,
84
+ self: true
85
+ },
86
+ organizer: {
87
+ email: owner.id,
88
+ displayName: owner.summary,
89
+ self: true
90
+ },
91
+ start: { ...event.start },
92
+ end: { ...event.end },
93
+ iCalUID: event.iCalUID,
94
+ sequence: event.sequence,
95
+ reminders: { useDefault: true },
96
+ eventType: "default"
97
+ };
98
+ };
99
+ /** The instant a boundary starts (an all-day date at midnight UTC), in milliseconds. */
100
+ const instant = (boundary) => "dateTime" in boundary ? Date.parse(boundary.dateTime) : Date.parse(`${boundary.date}T00:00:00Z`);
101
+ const findCalendar = (state, id) => state.calendars.find((candidate) => candidate.id === id);
102
+ const findEvent = (state, calendarId, eventId) => state.events.find((event) => event.calendarId === calendarId && event.id === eventId);
103
+ const isCreatedHere = (event) => mintedEventIdPattern.test(event.id);
104
+ const replaceEvent = (state, next) => state.events.map((event) => event.calendarId === next.calendarId && event.id === next.id ? next : event);
105
+ /** The `{ dateTime, timeZone }` boundary a fixture records, exactly. */
106
+ const isRecordedBoundary = (value, recorded) => {
107
+ const boundary = exactBodyKeys(value, "boundary", ["dateTime", "timeZone"]);
108
+ return !isNotEmulated(boundary) && boundary.dateTime === recorded.dateTime && boundary.timeZone === recorded.timeZone;
109
+ };
110
+ const listEvents = statefulRoute(calendar("GET", "", false, [listCase], false), "none", (request) => {
111
+ const query = exactQuery(request, [
112
+ "timeMin",
113
+ "timeMax",
114
+ "maxResults",
115
+ "singleEvents",
116
+ "orderBy"
117
+ ], ["pageToken"]);
118
+ if (isNotEmulated(query)) return query;
119
+ const recorded = recordedValue(query, "singleEvents", "true") ?? recordedValue(query, "orderBy", "startTime");
120
+ if (recorded !== void 0) return recorded;
121
+ const timeMin = query["timeMin"] ?? "";
122
+ const timeMax = query["timeMax"] ?? "";
123
+ if (!isInstant(timeMin) || !isInstant(timeMax) || !(Date.parse(timeMin) < Date.parse(timeMax))) return notEmulated("timeMin and timeMax must be RFC 3339 instants, timeMin first");
124
+ const maxResults = pageSize(query["maxResults"], "maxResults", 1, 2500);
125
+ if (isNotEmulated(maxResults)) return maxResults;
126
+ const pageToken = query["pageToken"];
127
+ return pageToken === "" ? notEmulated("an empty pageToken is not emulated") : {
128
+ calendarId: param(request, "calendarId"),
129
+ timeMin,
130
+ timeMax,
131
+ maxResults,
132
+ pageToken
133
+ };
134
+ }, (state, input, { env }) => {
135
+ const owner = findCalendar(state, input.calendarId);
136
+ if (owner === void 0) return notEmulated("a calendar the state does not hold");
137
+ const from = Date.parse(input.timeMin);
138
+ const to = Date.parse(input.timeMax);
139
+ const overlapping = state.events.filter((event) => event.calendarId === owner.id && instant(event.start) < to && instant(event.end) > from).sort((left, right) => instant(left.start) - instant(right.start));
140
+ if (overlapping.length === 0) return notEmulated("a range without events is not emulated (no fixture records one)");
141
+ if (overlapping.some((event) => event.status === "cancelled")) return notEmulated("a range holding a cancelled event is not emulated");
142
+ return listPage(env, "calendar", `calendar\u0000${owner.id}\u0000${input.timeMin}\u0000${input.timeMax}\u0000${input.maxResults}`, overlapping.map((event) => renderEvent(state, owner, event)), input.maxResults, input.pageToken, env.drills.calendarPageRepeats, (page, nextPageToken) => {
143
+ const body = {
144
+ kind: "calendar#events",
145
+ summary: owner.summary,
146
+ timeZone: owner.timeZone,
147
+ accessRole: owner.accessRole,
148
+ items: [...page]
149
+ };
150
+ if (nextPageToken !== void 0) body.nextPageToken = nextPageToken;
151
+ return googleJson(200, body);
152
+ });
153
+ });
154
+ const createEvent = statefulRoute(calendar("POST", "", true, [lifecycleCase, deletedCase], false), "json", (request) => {
155
+ const body = withoutQuery(request) ?? exactBodyKeys(request.json, "the event body", [
156
+ "summary",
157
+ "description",
158
+ "start",
159
+ "end"
160
+ ]);
161
+ if (isNotEmulated(body)) return body;
162
+ const summary = Predicate.isString(body.summary) ? body.summary : "";
163
+ if (!isRunText(summary, "event", eventSummaryRest) && !isRunText(summary, "deleted event", eventSummaryRest)) return notEmulated("an event summary other than the recorded run-scoped ones");
164
+ if (body.description !== createdDescription) return notEmulated("an event description other than the recorded one is not emulated");
165
+ return isRecordedBoundary(body.start, recordedStart) && isRecordedBoundary(body.end, recordedEnd) ? {
166
+ calendarId: param(request, "calendarId"),
167
+ summary
168
+ } : notEmulated("event times other than the recorded half hour are not emulated");
169
+ }, (state, input, { env }) => {
170
+ const owner = findCalendar(state, input.calendarId);
171
+ if (owner === void 0) return notEmulated("a calendar the state does not hold");
172
+ return () => {
173
+ const at = new Date(env.now()).toISOString();
174
+ const number = state.counters.nextEventNumber;
175
+ const id = mintedEventId(number);
176
+ const event = {
177
+ calendarId: owner.id,
178
+ id,
179
+ ...eventFields(id),
180
+ status: "confirmed",
181
+ created: at,
182
+ updated: at,
183
+ summary: input.summary,
184
+ description: createdDescription,
185
+ start: { ...recordedStart },
186
+ end: { ...recordedEnd },
187
+ sequence: 0
188
+ };
189
+ state.counters = {
190
+ ...state.counters,
191
+ nextEventNumber: number + 1
192
+ };
193
+ state.events = [...state.events, event];
194
+ return googleJson(200, renderEvent(state, owner, event));
195
+ };
196
+ });
197
+ const eventRef = (request) => ({
198
+ calendarId: param(request, "calendarId"),
199
+ eventId: param(request, "eventId")
200
+ });
201
+ /** The Calendar routes, in manifest order. */
202
+ const calendarRoutes = [
203
+ listEvents,
204
+ createEvent,
205
+ statefulRoute(calendar("GET", "/{eventId}", false, [lifecycleCase, deletedCase], true), "none", (request) => withoutQuery(request) ?? eventRef(request), (state, input) => {
206
+ const owner = findCalendar(state, input.calendarId);
207
+ const event = findEvent(state, input.calendarId, input.eventId);
208
+ return owner === void 0 || event === void 0 ? notEmulated("an event the state does not hold is not emulated") : answer(() => googleJson(200, renderEvent(state, owner, event)));
209
+ }),
210
+ statefulRoute(calendar("PATCH", "/{eventId}", true, [lifecycleCase], true), "json", (request) => {
211
+ const body = withoutQuery(request) ?? exactBodyKeys(request.json, "the event patch", ["summary", "description"]);
212
+ if (isNotEmulated(body)) return body;
213
+ const summary = Predicate.isString(body.summary) ? body.summary : "";
214
+ return isRunText(summary, "event renamed", eventSummaryRest) && body.description === renamedDescription ? {
215
+ ...eventRef(request),
216
+ summary
217
+ } : notEmulated("an event patch other than the recorded rename is not emulated");
218
+ }, (state, input, { env }) => {
219
+ const owner = findCalendar(state, input.calendarId);
220
+ const event = findEvent(state, input.calendarId, input.eventId);
221
+ if (owner === void 0 || event === void 0 || !isCreatedHere(event) || event.status !== "confirmed") return notEmulated("renaming anything but a live event created here is not emulated");
222
+ return () => {
223
+ const at = new Date(env.now()).toISOString();
224
+ const next = {
225
+ ...event,
226
+ updated: at,
227
+ summary: env.drills.eventPatchKeepsSummary ? event.summary : input.summary,
228
+ description: renamedDescription
229
+ };
230
+ state.events = replaceEvent(state, next);
231
+ return googleJson(200, renderEvent(state, owner, next));
232
+ };
233
+ }),
234
+ statefulRoute(calendar("DELETE", "/{eventId}", true, [lifecycleCase, deletedCase], true), "none", (request) => withoutQuery(request) ?? eventRef(request), (state, input, { env }) => {
235
+ const event = findEvent(state, input.calendarId, input.eventId);
236
+ if (event === void 0 || !isCreatedHere(event)) return notEmulated("deleting anything but an event created here is not emulated");
237
+ if (event.status === "cancelled") return answer(() => googleJson(env.drills.repeatedEventDeleteConflict ? 409 : 410, googleErrorBody(410, "Resource has been deleted", "deleted", "GONE")));
238
+ return () => {
239
+ const at = new Date(env.now()).toISOString();
240
+ state.events = replaceEvent(state, {
241
+ ...event,
242
+ status: "cancelled",
243
+ updated: at
244
+ });
245
+ return googleNoContent();
246
+ };
247
+ })
248
+ ];
249
+ //#endregion
250
+ export { calendarEventsPath, calendarRoutes };
251
+
252
+ //# sourceMappingURL=calendar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.mjs","names":[],"sources":["../../src/google/calendar.ts"],"sourcesContent":["/**\n * Calendar routes of the Google emulator (internal): the time-range event listing with page\n * tokens, and the lifecycle of a run-scoped event without attendees (create, read, PATCH rename,\n * delete to `cancelled`, and the recorded 410 of a repeated delete).\n *\n * Every answer comes from the Calendar fixtures, through the seed or the request, or is minted\n * (event ids, page tokens, `created` / `updated` from the injectable clock). A deleted event stays\n * in the state as `cancelled`, as the fixtures read it back.\n *\n * @experimental\n */\nimport { Predicate } from 'effect'\nimport type * as Schema from 'effect/Schema'\nimport {\n exactBodyKeys,\n exactQuery,\n isNotEmulated,\n notEmulated,\n statefulRoute,\n type EmulatedRequest,\n type NotEmulated\n} from '../stateful-emulator.ts'\nimport {\n answer,\n evidence,\n googleEmulatorApisOrigin,\n googleErrorBody,\n googleJson,\n googleNoContent,\n isRunText,\n listPage,\n pageSize,\n param,\n recordedValue,\n withoutQuery,\n type GoogleRoute\n} from './shared.ts'\nimport {\n eventFields,\n mintedEventId,\n mintedEventIdPattern,\n type GoogleEmulatorCalendar,\n type GoogleEmulatorCalendarEvent,\n type GoogleEmulatorEventBoundary,\n type GoogleEmulatorState\n} from './state.ts'\n\n/** Path of the events collection of `{calendarId}`. */\nexport const calendarEventsPath = '/calendar/v3/calendars/{calendarId}/events'\n\nconst listCase = 'google.calendar.list-range-paging'\n\nconst lifecycleCase = 'google.calendar.event-lifecycle'\n\nconst deletedCase = 'google.calendar.deleted-event-gone'\n\n/** A raw calendar id segment: an address-like id, its `@` percent-encoded once (`%40`). */\nconst calendarIdSegment = /^[A-Za-z0-9._+-]{1,200}(?:%40[A-Za-z0-9.-]{1,200})?$/\n\nconst eventIdSegment = /^[A-Za-z0-9_-]{1,1024}$/\n\nconst calendar = (\n method: string,\n path: string,\n write: boolean,\n caseIds: ReadonlyArray<string>,\n withEvent: boolean\n) =>\n evidence(\n googleEmulatorApisOrigin,\n method,\n `${calendarEventsPath}${path}`,\n write,\n caseIds,\n withEvent\n ? { calendarId: calendarIdSegment, eventId: eventIdSegment }\n : { calendarId: calendarIdSegment }\n )\n\n// Recorded values.\n\nconst createdDescription = 'Synthetic conformance event without attendees.'\n\nconst renamedDescription = 'Synthetic conformance event, renamed.'\n\nconst eventSummaryRest = 'synthetic conformance event, safe to delete'\n\n/** The fixed half hour every recorded event create sends. */\nconst recordedStart = { dateTime: '2030-01-07T09:00:00Z', timeZone: 'UTC' }\n\nconst recordedEnd = { dateTime: '2030-01-07T09:30:00Z', timeZone: 'UTC' }\n\nconst instantPattern =\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,9})?(?:Z|[+-](\\d{2}):(\\d{2}))$/\n\n/**\n * True for an RFC 3339 instant with a `Z` or numeric offset whose components are in range (a real\n * calendar date, hour 0-23, minute and second 0-59, offset hour 0-23 and minute 0-59), checked\n * before `Date.parse`, which would roll an out-of-range component over instead of refusing it.\n */\nconst isInstant = (text: string): boolean => {\n const parts = instantPattern.exec(text)\n\n if (parts === null) return false\n\n const [year, month, day, hour, minute, second, offsetHour, offsetMinute] = parts\n .slice(1)\n .map(part => Number(part ?? '0'))\n\n if (year === undefined || month === undefined || day === undefined) return false\n\n const leap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0\n const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1] ?? 0\n\n return (\n day >= 1 &&\n day <= days &&\n (hour ?? 24) <= 23 &&\n (minute ?? 60) <= 59 &&\n (second ?? 60) <= 59 &&\n (offsetHour ?? 24) <= 23 &&\n (offsetMinute ?? 60) <= 59 &&\n Number.isFinite(Date.parse(text))\n )\n}\n\n// Rendering, in the fixture key order.\n\ntype EventHead = {\n readonly kind: string\n readonly etag: string\n readonly id: string\n readonly status: string\n readonly htmlLink: string\n readonly created: string\n readonly updated: string\n readonly summary: string\n description?: string\n}\n\ntype EventsPage = {\n readonly kind: string\n readonly summary: string\n readonly timeZone: string\n readonly accessRole: string\n readonly items: ReadonlyArray<Schema.Json>\n nextPageToken?: string\n}\n\nconst renderEvent = (\n state: GoogleEmulatorState,\n owner: GoogleEmulatorCalendar,\n event: GoogleEmulatorCalendarEvent\n): Schema.JsonObject => {\n const head: EventHead = {\n kind: 'calendar#event',\n etag: event.etag,\n id: event.id,\n status: event.status,\n htmlLink: event.htmlLink,\n created: event.created,\n updated: event.updated,\n summary: event.summary\n }\n\n // The fixtures show `description` only on events that have one.\n if (event.description !== undefined) head.description = event.description\n\n return {\n ...head,\n creator: { email: state.practiceAddress, self: true },\n organizer: { email: owner.id, displayName: owner.summary, self: true },\n start: { ...event.start },\n end: { ...event.end },\n iCalUID: event.iCalUID,\n sequence: event.sequence,\n reminders: { useDefault: true },\n eventType: 'default'\n }\n}\n\n/** The instant a boundary starts (an all-day date at midnight UTC), in milliseconds. */\nconst instant = (boundary: GoogleEmulatorEventBoundary): number =>\n 'dateTime' in boundary ? Date.parse(boundary.dateTime) : Date.parse(`${boundary.date}T00:00:00Z`)\n\nconst findCalendar = (state: GoogleEmulatorState, id: string) =>\n state.calendars.find(candidate => candidate.id === id)\n\nconst findEvent = (state: GoogleEmulatorState, calendarId: string, eventId: string) =>\n state.events.find(event => event.calendarId === calendarId && event.id === eventId)\n\nconst isCreatedHere = (event: GoogleEmulatorCalendarEvent): boolean =>\n mintedEventIdPattern.test(event.id)\n\nconst replaceEvent = (state: GoogleEmulatorState, next: GoogleEmulatorCalendarEvent) =>\n state.events.map(event =>\n event.calendarId === next.calendarId && event.id === next.id ? next : event\n )\n\n/** The `{ dateTime, timeZone }` boundary a fixture records, exactly. */\nconst isRecordedBoundary = (\n value: Schema.Json | undefined,\n recorded: { readonly dateTime: string; readonly timeZone: string }\n): boolean => {\n const boundary = exactBodyKeys(value, 'boundary', ['dateTime', 'timeZone'])\n\n return (\n !isNotEmulated(boundary) &&\n boundary.dateTime === recorded.dateTime &&\n boundary.timeZone === recorded.timeZone\n )\n}\n\n// Listing.\n\ntype ListInput = {\n readonly calendarId: string\n readonly timeMin: string\n readonly timeMax: string\n readonly maxResults: number\n readonly pageToken: string | undefined\n}\n\nconst listEvents: GoogleRoute = statefulRoute(\n calendar('GET', '', false, [listCase], false),\n 'none',\n (request): ListInput | NotEmulated => {\n const query = exactQuery(\n request,\n ['timeMin', 'timeMax', 'maxResults', 'singleEvents', 'orderBy'],\n ['pageToken']\n )\n\n if (isNotEmulated(query)) return query\n\n const recorded =\n recordedValue(query, 'singleEvents', 'true') ?? recordedValue(query, 'orderBy', 'startTime')\n\n if (recorded !== undefined) return recorded\n\n const timeMin = query['timeMin'] ?? ''\n const timeMax = query['timeMax'] ?? ''\n\n if (\n !isInstant(timeMin) ||\n !isInstant(timeMax) ||\n !(Date.parse(timeMin) < Date.parse(timeMax))\n ) {\n return notEmulated('timeMin and timeMax must be RFC 3339 instants, timeMin first')\n }\n\n const maxResults = pageSize(query['maxResults'], 'maxResults', 1, 2500)\n\n if (isNotEmulated(maxResults)) return maxResults\n\n const pageToken = query['pageToken']\n\n return pageToken === ''\n ? notEmulated('an empty pageToken is not emulated')\n : { calendarId: param(request, 'calendarId'), timeMin, timeMax, maxResults, pageToken }\n },\n (state, input, { env }) => {\n const owner = findCalendar(state, input.calendarId)\n\n if (owner === undefined) return notEmulated('a calendar the state does not hold')\n\n const from = Date.parse(input.timeMin)\n const to = Date.parse(input.timeMax)\n\n // By start time (`orderBy=startTime`); `sort` is stable, so equal starts keep the state order.\n const overlapping = state.events\n .filter(\n event =>\n event.calendarId === owner.id && instant(event.start) < to && instant(event.end) > from\n )\n .sort((left, right) => instant(left.start) - instant(right.start))\n\n // Every recorded listing has events, and none of them is cancelled.\n if (overlapping.length === 0) {\n return notEmulated('a range without events is not emulated (no fixture records one)')\n }\n\n if (overlapping.some(event => event.status === 'cancelled')) {\n return notEmulated('a range holding a cancelled event is not emulated')\n }\n\n return listPage(\n env,\n 'calendar',\n `calendar\\u0000${owner.id}\\u0000${input.timeMin}\\u0000${input.timeMax}\\u0000${input.maxResults}`,\n overlapping.map(event => renderEvent(state, owner, event)),\n input.maxResults,\n input.pageToken,\n env.drills.calendarPageRepeats,\n (page, nextPageToken) => {\n const body: EventsPage = {\n kind: 'calendar#events',\n summary: owner.summary,\n timeZone: owner.timeZone,\n accessRole: owner.accessRole,\n items: [...page]\n }\n\n // The last page carries no `nextPageToken`.\n if (nextPageToken !== undefined) body.nextPageToken = nextPageToken\n\n return googleJson(200, body)\n }\n )\n }\n)\n\n// The event lifecycle.\n\ntype CreateInput = { readonly calendarId: string; readonly summary: string }\n\nconst createEvent: GoogleRoute = statefulRoute(\n calendar('POST', '', true, [lifecycleCase, deletedCase], false),\n 'json',\n (request): CreateInput | NotEmulated => {\n const body =\n withoutQuery(request) ??\n exactBodyKeys(request.json, 'the event body', ['summary', 'description', 'start', 'end'])\n\n if (isNotEmulated(body)) return body\n\n const summary = Predicate.isString(body.summary) ? body.summary : ''\n\n if (\n !isRunText(summary, 'event', eventSummaryRest) &&\n !isRunText(summary, 'deleted event', eventSummaryRest)\n ) {\n return notEmulated('an event summary other than the recorded run-scoped ones')\n }\n\n if (body.description !== createdDescription) {\n return notEmulated('an event description other than the recorded one is not emulated')\n }\n\n return isRecordedBoundary(body.start, recordedStart) &&\n isRecordedBoundary(body.end, recordedEnd)\n ? { calendarId: param(request, 'calendarId'), summary }\n : notEmulated('event times other than the recorded half hour are not emulated')\n },\n (state, input, { env }) => {\n const owner = findCalendar(state, input.calendarId)\n\n if (owner === undefined) return notEmulated('a calendar the state does not hold')\n\n return () => {\n // Read the clock before any write: a failing clock writes nothing.\n const at = new Date(env.now()).toISOString()\n const number = state.counters.nextEventNumber\n const id = mintedEventId(number)\n\n const event: GoogleEmulatorCalendarEvent = {\n calendarId: owner.id,\n id,\n ...eventFields(id),\n status: 'confirmed',\n created: at,\n updated: at,\n summary: input.summary,\n description: createdDescription,\n start: { ...recordedStart },\n end: { ...recordedEnd },\n sequence: 0\n }\n\n state.counters = { ...state.counters, nextEventNumber: number + 1 }\n state.events = [...state.events, event]\n\n return googleJson(200, renderEvent(state, owner, event))\n }\n }\n)\n\ntype EventRef = { readonly calendarId: string; readonly eventId: string }\n\nconst eventRef = (request: EmulatedRequest): EventRef => ({\n calendarId: param(request, 'calendarId'),\n eventId: param(request, 'eventId')\n})\n\nconst getEvent: GoogleRoute = statefulRoute(\n calendar('GET', '/{eventId}', false, [lifecycleCase, deletedCase], true),\n 'none',\n request => withoutQuery(request) ?? eventRef(request),\n (state, input) => {\n const owner = findCalendar(state, input.calendarId)\n const event = findEvent(state, input.calendarId, input.eventId)\n\n return owner === undefined || event === undefined\n ? notEmulated('an event the state does not hold is not emulated')\n : answer(() => googleJson(200, renderEvent(state, owner, event)))\n }\n)\n\ntype PatchInput = EventRef & { readonly summary: string }\n\nconst patchEvent: GoogleRoute = statefulRoute(\n calendar('PATCH', '/{eventId}', true, [lifecycleCase], true),\n 'json',\n (request): PatchInput | NotEmulated => {\n const body =\n withoutQuery(request) ??\n exactBodyKeys(request.json, 'the event patch', ['summary', 'description'])\n\n if (isNotEmulated(body)) return body\n\n const summary = Predicate.isString(body.summary) ? body.summary : ''\n\n return isRunText(summary, 'event renamed', eventSummaryRest) &&\n body.description === renamedDescription\n ? { ...eventRef(request), summary }\n : notEmulated('an event patch other than the recorded rename is not emulated')\n },\n (state, input, { env }) => {\n const owner = findCalendar(state, input.calendarId)\n const event = findEvent(state, input.calendarId, input.eventId)\n\n if (\n owner === undefined ||\n event === undefined ||\n !isCreatedHere(event) ||\n event.status !== 'confirmed'\n ) {\n return notEmulated('renaming anything but a live event created here is not emulated')\n }\n\n return () => {\n const at = new Date(env.now()).toISOString()\n\n const next: GoogleEmulatorCalendarEvent = {\n ...event,\n updated: at,\n summary: env.drills.eventPatchKeepsSummary ? event.summary : input.summary,\n description: renamedDescription\n }\n\n state.events = replaceEvent(state, next)\n\n return googleJson(200, renderEvent(state, owner, next))\n }\n }\n)\n\nconst deleteEvent: GoogleRoute = statefulRoute(\n calendar('DELETE', '/{eventId}', true, [lifecycleCase, deletedCase], true),\n 'none',\n request => withoutQuery(request) ?? eventRef(request),\n (state, input, { env }) => {\n const event = findEvent(state, input.calendarId, input.eventId)\n\n if (event === undefined || !isCreatedHere(event)) {\n return notEmulated('deleting anything but an event created here is not emulated')\n }\n\n // The recorded repeated delete: 410 Gone (the drill answers it as 409).\n if (event.status === 'cancelled') {\n return answer(() =>\n googleJson(\n env.drills.repeatedEventDeleteConflict ? 409 : 410,\n googleErrorBody(410, 'Resource has been deleted', 'deleted', 'GONE')\n )\n )\n }\n\n return () => {\n const at = new Date(env.now()).toISOString()\n\n state.events = replaceEvent(state, { ...event, status: 'cancelled', updated: at })\n\n return googleNoContent()\n }\n }\n)\n\n/** The Calendar routes, in manifest order. */\nexport const calendarRoutes: ReadonlyArray<GoogleRoute> = [\n listEvents,\n createEvent,\n getEvent,\n patchEvent,\n deleteEvent\n]\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { GoogleRoute } from "./shared.mjs";
2
+
3
+ //#region src/google/drive.d.ts
4
+ /** The Drive routes, in manifest order. */
5
+ declare const driveRoutes: ReadonlyArray<GoogleRoute>;
6
+ //#endregion
7
+ export { driveRoutes };
8
+ //# sourceMappingURL=drive.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive.d.mts","names":[],"sources":["../../src/google/drive.ts"],"mappings":";;;;cAgda,WAAA,EAAa,aAAa,CAAC,WAAA"}