@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
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared pieces of the Google emulator API (internal): origins, the environment, drill knobs,
|
|
3
|
+
* response and error-envelope helpers, query checks, run-scoped text, and page tokens.
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
7
|
+
import type * as Schema from 'effect/Schema'
|
|
8
|
+
import {
|
|
9
|
+
integerIn,
|
|
10
|
+
isNotEmulated,
|
|
11
|
+
notEmulated,
|
|
12
|
+
type Commit,
|
|
13
|
+
type EmulatedRequest,
|
|
14
|
+
type NotEmulated,
|
|
15
|
+
type StatefulRoute
|
|
16
|
+
} from '../stateful-emulator.ts'
|
|
17
|
+
import type { GoogleEmulatorState } from './state.ts'
|
|
18
|
+
|
|
19
|
+
/** The origin of every Gmail route (the API and the upload endpoint), as the fixtures record. */
|
|
20
|
+
export const googleEmulatorGmailOrigin = 'https://gmail.googleapis.com'
|
|
21
|
+
|
|
22
|
+
/** The origin of every Calendar and Drive route, as the fixtures record. */
|
|
23
|
+
export const googleEmulatorApisOrigin = 'https://www.googleapis.com'
|
|
24
|
+
|
|
25
|
+
/** Drill knobs (tests only): each makes the emulator disagree with exactly one conformance case. */
|
|
26
|
+
export type GoogleEmulatorDrills = {
|
|
27
|
+
/** A later `gmail.list` page starts one message early, repeating the previous page's last. */
|
|
28
|
+
readonly gmailPageRepeats?: boolean
|
|
29
|
+
/** Attachment `data` is answered in the standard base64 alphabet instead of base64url. */
|
|
30
|
+
readonly attachmentStandardBase64?: boolean
|
|
31
|
+
/** Gmail 404 envelopes carry no `error.message`. */
|
|
32
|
+
readonly notFoundWithoutMessage?: boolean
|
|
33
|
+
/** Deleting a label leaves it on the messages that carry it. */
|
|
34
|
+
readonly labelDeleteKeepsOnMessages?: boolean
|
|
35
|
+
/** A draft update keeps the earlier subject and body (it ignores the new message). */
|
|
36
|
+
readonly draftUpdateKeepsContent?: boolean
|
|
37
|
+
/** The trash answer omits `TRASH` from `labelIds` (the state still carries it). */
|
|
38
|
+
readonly trashAnswerOmitsTrash?: boolean
|
|
39
|
+
/** The sent message reads back without its `To` header. */
|
|
40
|
+
readonly sentMessageWithoutTo?: boolean
|
|
41
|
+
/** A later `calendar.list_events` page starts one event early. */
|
|
42
|
+
readonly calendarPageRepeats?: boolean
|
|
43
|
+
/** An event PATCH answers (and keeps) the earlier summary. */
|
|
44
|
+
readonly eventPatchKeepsSummary?: boolean
|
|
45
|
+
/** Deleting a cancelled event answers 409 instead of the recorded 410. */
|
|
46
|
+
readonly repeatedEventDeleteConflict?: boolean
|
|
47
|
+
/** A later `drive.list_files` page starts one file early. */
|
|
48
|
+
readonly drivePageRepeats?: boolean
|
|
49
|
+
/** `drive.get_file` answers no `parents`. */
|
|
50
|
+
readonly getFileWithoutParents?: boolean
|
|
51
|
+
/** Folder listings include trashed children. */
|
|
52
|
+
readonly listIncludesTrashed?: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const googleEmulatorDrillKnobs: ReadonlyArray<keyof GoogleEmulatorDrills> = [
|
|
56
|
+
'gmailPageRepeats',
|
|
57
|
+
'attachmentStandardBase64',
|
|
58
|
+
'notFoundWithoutMessage',
|
|
59
|
+
'labelDeleteKeepsOnMessages',
|
|
60
|
+
'draftUpdateKeepsContent',
|
|
61
|
+
'trashAnswerOmitsTrash',
|
|
62
|
+
'sentMessageWithoutTo',
|
|
63
|
+
'calendarPageRepeats',
|
|
64
|
+
'eventPatchKeepsSummary',
|
|
65
|
+
'repeatedEventDeleteConflict',
|
|
66
|
+
'drivePageRepeats',
|
|
67
|
+
'getFileWithoutParents',
|
|
68
|
+
'listIncludesTrashed'
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
/** A page token this emulator issued: where its list continues, and the list it was issued for. */
|
|
72
|
+
export type GoogleIssuedPageToken = {
|
|
73
|
+
/** The list it continues (its key names the list and its page size). */
|
|
74
|
+
readonly listKey: string
|
|
75
|
+
readonly offset: number
|
|
76
|
+
/** The whole list as rendered when the token was issued; any change is not emulated. */
|
|
77
|
+
readonly fingerprint: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type GoogleApiEnv = {
|
|
81
|
+
/** Clock in epoch milliseconds (created and updated timestamps, the sent `Date` header). */
|
|
82
|
+
readonly now: () => number
|
|
83
|
+
readonly drills: Readonly<Record<keyof GoogleEmulatorDrills, boolean>>
|
|
84
|
+
/**
|
|
85
|
+
* Issued page tokens by value (runtime-only; cleared by reset and seed). A value is bound to
|
|
86
|
+
* exactly one list, position, and rendering, and never rebound.
|
|
87
|
+
*/
|
|
88
|
+
readonly pageTokens: Map<string, GoogleIssuedPageToken>
|
|
89
|
+
/**
|
|
90
|
+
* The token generation: advanced by every reset and seed (never rewound). A page token is the
|
|
91
|
+
* fixture's value (`synthetic-<api>-page-<n>`) only in the generation that first issued that
|
|
92
|
+
* value; a later generation issues `<value>.g<generation>`, so no token value ever crosses a
|
|
93
|
+
* reset or seed.
|
|
94
|
+
*/
|
|
95
|
+
readonly generation: { current: number }
|
|
96
|
+
/** The generation that first issued each token value (never cleared). */
|
|
97
|
+
readonly firstIssued: Map<string, number>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type GoogleRoute = StatefulRoute<GoogleEmulatorState, GoogleApiEnv>
|
|
101
|
+
|
|
102
|
+
/** Route evidence of an unverified connector route on `origin`, citing `caseIds`. */
|
|
103
|
+
export const evidence = (
|
|
104
|
+
origin: string,
|
|
105
|
+
method: string,
|
|
106
|
+
path: string,
|
|
107
|
+
write: boolean,
|
|
108
|
+
caseIds: ReadonlyArray<string>,
|
|
109
|
+
params: Readonly<Record<string, RegExp>> = {}
|
|
110
|
+
) => ({
|
|
111
|
+
method,
|
|
112
|
+
path,
|
|
113
|
+
kind: 'connector' as const,
|
|
114
|
+
write,
|
|
115
|
+
caseIds,
|
|
116
|
+
evidence: 'unverified' as const,
|
|
117
|
+
origin,
|
|
118
|
+
params
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
/** A path parameter, decoded once by the wrapper (empty when absent). */
|
|
122
|
+
export const param = (request: EmulatedRequest, name: string): string => request.params[name] ?? ''
|
|
123
|
+
|
|
124
|
+
/** The `internalDate` every recorded Gmail message carries. */
|
|
125
|
+
export const recordedInternalDate = '1790000000000'
|
|
126
|
+
|
|
127
|
+
/** The raw path segment of a Gmail message, thread, label, attachment, or draft id. */
|
|
128
|
+
export const gmailIdSegment = /^[A-Za-z0-9_-]{1,100}$/
|
|
129
|
+
|
|
130
|
+
// Responses, as the fixtures record them.
|
|
131
|
+
|
|
132
|
+
const googleContentType = 'application/json; charset=UTF-8'
|
|
133
|
+
|
|
134
|
+
export const googleJson = (status: number, body: Schema.Json): Response =>
|
|
135
|
+
new Response(JSON.stringify(body), { status, headers: { 'content-type': googleContentType } })
|
|
136
|
+
|
|
137
|
+
/** The bodiless 204 every recorded delete answers. */
|
|
138
|
+
export const googleNoContent = (): Response => new Response(null, { status: 204 })
|
|
139
|
+
|
|
140
|
+
/** The Google JSON error envelope, as the fixtures write it. */
|
|
141
|
+
export const googleErrorBody = (
|
|
142
|
+
code: number,
|
|
143
|
+
message: string,
|
|
144
|
+
reason: string,
|
|
145
|
+
status: string
|
|
146
|
+
): Schema.JsonObject => ({
|
|
147
|
+
error: { code, message, errors: [{ message, domain: 'global', reason }], status }
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
/** A read-only commit. */
|
|
151
|
+
export const answer =
|
|
152
|
+
(response: () => Response): Commit =>
|
|
153
|
+
() =>
|
|
154
|
+
response()
|
|
155
|
+
|
|
156
|
+
// Queries.
|
|
157
|
+
|
|
158
|
+
/** A query value that must equal the recorded one. */
|
|
159
|
+
export const recordedValue = (
|
|
160
|
+
query: Readonly<Record<string, string>>,
|
|
161
|
+
key: string,
|
|
162
|
+
value: string
|
|
163
|
+
): NotEmulated | undefined =>
|
|
164
|
+
query[key] === value ? undefined : notEmulated(`${key} other than ${value} is not emulated`)
|
|
165
|
+
|
|
166
|
+
/** A decimal page size from `minimum` to `maximum`, or not emulated. */
|
|
167
|
+
export const pageSize = (
|
|
168
|
+
raw: string | undefined,
|
|
169
|
+
label: string,
|
|
170
|
+
minimum: number,
|
|
171
|
+
maximum: number
|
|
172
|
+
): number | NotEmulated =>
|
|
173
|
+
integerIn(
|
|
174
|
+
raw !== undefined && /^[1-9]\d{0,5}$/.test(raw) ? Number(raw) : null,
|
|
175
|
+
label,
|
|
176
|
+
minimum,
|
|
177
|
+
maximum
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
/** A request without query parameters, or not emulated. */
|
|
181
|
+
export const withoutQuery = (request: EmulatedRequest): NotEmulated | undefined =>
|
|
182
|
+
request.query.size > 0
|
|
183
|
+
? notEmulated('query parameters are not emulated on this route')
|
|
184
|
+
: undefined
|
|
185
|
+
|
|
186
|
+
// Run-scoped text.
|
|
187
|
+
|
|
188
|
+
/** The run id shape of the cases (`GoogleConformanceRunId`): at most 40 characters. */
|
|
189
|
+
const runIdPattern = /^run-[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The well-formed run id of `yolk-conformance <runId> <kind>` (or `... <kind>: <rest>`), or
|
|
193
|
+
* `undefined` when `text` has another form.
|
|
194
|
+
*/
|
|
195
|
+
export const runIdOf = (text: string, kind: string, rest?: string): string | undefined => {
|
|
196
|
+
const prefix = 'yolk-conformance '
|
|
197
|
+
const suffix = rest === undefined ? ` ${kind}` : ` ${kind}: ${rest}`
|
|
198
|
+
|
|
199
|
+
if (!text.startsWith(prefix) || !text.endsWith(suffix)) return undefined
|
|
200
|
+
|
|
201
|
+
const runId = text.slice(prefix.length, text.length - suffix.length)
|
|
202
|
+
|
|
203
|
+
return runId.length <= 40 && runIdPattern.test(runId) ? runId : undefined
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* True when `text` is `yolk-conformance <runId> <kind>` (or `... <kind>: <rest>`) for a
|
|
208
|
+
* well-formed run id: the only latitude in the run-scoped names, subjects, and summaries.
|
|
209
|
+
*/
|
|
210
|
+
export const isRunText = (text: string, kind: string, rest?: string): boolean =>
|
|
211
|
+
runIdOf(text, kind, rest) !== undefined
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The length of the fixtures' run id (`run-synthetic`). The draft and sent messages the Gmail
|
|
215
|
+
* fixtures record carry a `sizeEstimate` that covers their subject, so a draft or send subject
|
|
216
|
+
* must carry a run id of exactly this length for that recorded value to stay true.
|
|
217
|
+
*/
|
|
218
|
+
export const recordedRunIdLength = 13
|
|
219
|
+
|
|
220
|
+
// Page tokens: the fixture's value on first issuance, validated by issuance only.
|
|
221
|
+
|
|
222
|
+
/** `value` in this generation (see `GoogleApiEnv.generation`); records its first issuance. */
|
|
223
|
+
const generationValue = (env: GoogleApiEnv, value: string): string => {
|
|
224
|
+
const first = env.firstIssued.get(value)
|
|
225
|
+
const generation = env.generation.current
|
|
226
|
+
|
|
227
|
+
if (first === undefined) env.firstIssued.set(value, generation)
|
|
228
|
+
|
|
229
|
+
return first === undefined || first === generation ? value : `${value}.g${generation}`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* The page token for page `page` of a list (in a commit), bound to `issued` (its list, offset, and
|
|
234
|
+
* fingerprint). The first issuance is the fixture's value (`synthetic-<api>-page-<n>`). Token
|
|
235
|
+
* values are globally unique: a value already bound is reused only for exactly the same list,
|
|
236
|
+
* position, and rendering, and never rebound to anything else, so another list or page size, or a
|
|
237
|
+
* changed list, gets a distinct value (`synthetic-<api>-page-<n>.v<k>`) and an earlier token keeps
|
|
238
|
+
* meaning only what it was issued for.
|
|
239
|
+
*/
|
|
240
|
+
const issueToken = (
|
|
241
|
+
env: GoogleApiEnv,
|
|
242
|
+
prefix: string,
|
|
243
|
+
page: number,
|
|
244
|
+
issued: GoogleIssuedPageToken
|
|
245
|
+
): string => {
|
|
246
|
+
const base = `synthetic-${prefix}-page-${page}`
|
|
247
|
+
|
|
248
|
+
for (let variant = 1; ; variant += 1) {
|
|
249
|
+
const value = generationValue(env, variant === 1 ? base : `${base}.v${variant}`)
|
|
250
|
+
const existing = env.pageTokens.get(value)
|
|
251
|
+
|
|
252
|
+
if (existing === undefined) {
|
|
253
|
+
env.pageTokens.set(value, issued)
|
|
254
|
+
|
|
255
|
+
return value
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
existing.listKey === issued.listKey &&
|
|
260
|
+
existing.fingerprint === issued.fingerprint &&
|
|
261
|
+
existing.offset === issued.offset
|
|
262
|
+
) {
|
|
263
|
+
return value
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Where a page of a list starts: 0, or an issued token's offset; not emulated otherwise. */
|
|
269
|
+
const pageStart = (
|
|
270
|
+
env: GoogleApiEnv,
|
|
271
|
+
listKey: string,
|
|
272
|
+
fingerprint: string,
|
|
273
|
+
token: string | undefined
|
|
274
|
+
): number | NotEmulated => {
|
|
275
|
+
if (token === undefined) return 0
|
|
276
|
+
|
|
277
|
+
const issued = env.pageTokens.get(token)
|
|
278
|
+
|
|
279
|
+
if (issued === undefined || issued.listKey !== listKey) {
|
|
280
|
+
return notEmulated(
|
|
281
|
+
'a pageToken this emulator did not issue for this list since the last reset is not emulated'
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return issued.fingerprint === fingerprint
|
|
286
|
+
? issued.offset
|
|
287
|
+
: notEmulated('continuing a list that changed since its pageToken was issued is not emulated')
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* The commit answering one page of a list: `render(page, nextPageToken)` for the page's rendered
|
|
292
|
+
* items, with a next token (registered for this list) while items remain. `repeats` (a drill)
|
|
293
|
+
* starts a later page one item early.
|
|
294
|
+
*/
|
|
295
|
+
export const listPage = (
|
|
296
|
+
env: GoogleApiEnv,
|
|
297
|
+
prefix: string,
|
|
298
|
+
listKey: string,
|
|
299
|
+
all: ReadonlyArray<Schema.Json>,
|
|
300
|
+
size: number,
|
|
301
|
+
token: string | undefined,
|
|
302
|
+
repeats: boolean,
|
|
303
|
+
render: (page: ReadonlyArray<Schema.Json>, nextPageToken: string | undefined) => Response
|
|
304
|
+
): Commit | NotEmulated => {
|
|
305
|
+
const fingerprint = JSON.stringify(all)
|
|
306
|
+
const offset = pageStart(env, listKey, fingerprint, token)
|
|
307
|
+
|
|
308
|
+
if (isNotEmulated(offset)) return offset
|
|
309
|
+
|
|
310
|
+
const start = repeats && offset > 0 ? offset - 1 : offset
|
|
311
|
+
const end = start + size
|
|
312
|
+
|
|
313
|
+
return () => {
|
|
314
|
+
const next =
|
|
315
|
+
end < all.length
|
|
316
|
+
? issueToken(env, prefix, Math.floor(end / size) + 1, { listKey, offset: end, fingerprint })
|
|
317
|
+
: undefined
|
|
318
|
+
|
|
319
|
+
return render(all.slice(start, end), next)
|
|
320
|
+
}
|
|
321
|
+
}
|