@yolk-sdk/emulators 0.1.0-canary.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +2135 -0
- package/dist/anthropic.d.mts +269 -0
- package/dist/anthropic.d.mts.map +1 -0
- package/dist/anthropic.mjs +177 -0
- package/dist/anthropic.mjs.map +1 -0
- package/dist/chat-completions.d.mts +288 -0
- package/dist/chat-completions.d.mts.map +1 -0
- package/dist/chat-completions.mjs +451 -0
- package/dist/chat-completions.mjs.map +1 -0
- package/dist/codex.d.mts +265 -0
- package/dist/codex.d.mts.map +1 -0
- package/dist/codex.mjs +199 -0
- package/dist/codex.mjs.map +1 -0
- package/dist/dropbox/api.d.mts +63 -0
- package/dist/dropbox/api.d.mts.map +1 -0
- package/dist/dropbox/api.mjs +565 -0
- package/dist/dropbox/api.mjs.map +1 -0
- package/dist/dropbox/state.d.mts +136 -0
- package/dist/dropbox/state.d.mts.map +1 -0
- package/dist/dropbox/state.mjs +209 -0
- package/dist/dropbox/state.mjs.map +1 -0
- package/dist/dropbox.d.mts +79 -0
- package/dist/dropbox.d.mts.map +1 -0
- package/dist/dropbox.mjs +124 -0
- package/dist/dropbox.mjs.map +1 -0
- package/dist/email-fixtures.d.mts +36 -0
- package/dist/email-fixtures.d.mts.map +1 -0
- package/dist/email-fixtures.mjs +1080 -0
- package/dist/email-fixtures.mjs.map +1 -0
- package/dist/email.d.mts +160 -0
- package/dist/email.d.mts.map +1 -0
- package/dist/email.mjs +608 -0
- package/dist/email.mjs.map +1 -0
- package/dist/emulator-compose.d.mts +40 -0
- package/dist/emulator-compose.d.mts.map +1 -0
- package/dist/emulator-compose.mjs +91 -0
- package/dist/emulator-compose.mjs.map +1 -0
- package/dist/emulator-http.d.mts +53 -0
- package/dist/emulator-http.d.mts.map +1 -0
- package/dist/emulator-http.mjs +117 -0
- package/dist/emulator-http.mjs.map +1 -0
- package/dist/emulator-kernel.d.mts +176 -0
- package/dist/emulator-kernel.d.mts.map +1 -0
- package/dist/emulator-kernel.mjs +413 -0
- package/dist/emulator-kernel.mjs.map +1 -0
- package/dist/fixture-route.d.mts +129 -0
- package/dist/fixture-route.d.mts.map +1 -0
- package/dist/fixture-route.mjs +257 -0
- package/dist/fixture-route.mjs.map +1 -0
- package/dist/fortnox/api.d.mts +92 -0
- package/dist/fortnox/api.d.mts.map +1 -0
- package/dist/fortnox/api.mjs +750 -0
- package/dist/fortnox/api.mjs.map +1 -0
- package/dist/fortnox/state.d.mts +533 -0
- package/dist/fortnox/state.d.mts.map +1 -0
- package/dist/fortnox/state.mjs +612 -0
- package/dist/fortnox/state.mjs.map +1 -0
- package/dist/fortnox.d.mts +128 -0
- package/dist/fortnox.d.mts.map +1 -0
- package/dist/fortnox.mjs +403 -0
- package/dist/fortnox.mjs.map +1 -0
- package/dist/gateway-evaluate-recordings.d.mts +12 -0
- package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
- package/dist/gateway-evaluate-recordings.mjs +128 -0
- package/dist/gateway-evaluate-recordings.mjs.map +1 -0
- package/dist/gateway.d.mts +272 -0
- package/dist/gateway.d.mts.map +1 -0
- package/dist/gateway.mjs +400 -0
- package/dist/gateway.mjs.map +1 -0
- package/dist/github/api.d.mts +63 -0
- package/dist/github/api.d.mts.map +1 -0
- package/dist/github/api.mjs +527 -0
- package/dist/github/api.mjs.map +1 -0
- package/dist/github/state.d.mts +272 -0
- package/dist/github/state.d.mts.map +1 -0
- package/dist/github/state.mjs +303 -0
- package/dist/github/state.mjs.map +1 -0
- package/dist/github.d.mts +77 -0
- package/dist/github.d.mts.map +1 -0
- package/dist/github.mjs +180 -0
- package/dist/github.mjs.map +1 -0
- package/dist/google/calendar.d.mts +10 -0
- package/dist/google/calendar.d.mts.map +1 -0
- package/dist/google/calendar.mjs +252 -0
- package/dist/google/calendar.mjs.map +1 -0
- package/dist/google/drive.d.mts +8 -0
- package/dist/google/drive.d.mts.map +1 -0
- package/dist/google/drive.mjs +252 -0
- package/dist/google/drive.mjs.map +1 -0
- package/dist/google/gmail.d.mts +10 -0
- package/dist/google/gmail.d.mts.map +1 -0
- package/dist/google/gmail.mjs +591 -0
- package/dist/google/gmail.mjs.map +1 -0
- package/dist/google/shared.d.mts +107 -0
- package/dist/google/shared.d.mts.map +1 -0
- package/dist/google/shared.mjs +146 -0
- package/dist/google/shared.mjs.map +1 -0
- package/dist/google/state.d.mts +385 -0
- package/dist/google/state.d.mts.map +1 -0
- package/dist/google/state.mjs +496 -0
- package/dist/google/state.mjs.map +1 -0
- package/dist/google.d.mts +75 -0
- package/dist/google.d.mts.map +1 -0
- package/dist/google.mjs +210 -0
- package/dist/google.mjs.map +1 -0
- package/dist/linkedin-search/api.d.mts +45 -0
- package/dist/linkedin-search/api.d.mts.map +1 -0
- package/dist/linkedin-search/api.mjs +181 -0
- package/dist/linkedin-search/api.mjs.map +1 -0
- package/dist/linkedin-search/state.d.mts +123 -0
- package/dist/linkedin-search/state.d.mts.map +1 -0
- package/dist/linkedin-search/state.mjs +261 -0
- package/dist/linkedin-search/state.mjs.map +1 -0
- package/dist/linkedin-search.d.mts +69 -0
- package/dist/linkedin-search.d.mts.map +1 -0
- package/dist/linkedin-search.mjs +158 -0
- package/dist/linkedin-search.mjs.map +1 -0
- package/dist/mcp/api.d.mts +44 -0
- package/dist/mcp/api.d.mts.map +1 -0
- package/dist/mcp/api.mjs +557 -0
- package/dist/mcp/api.mjs.map +1 -0
- package/dist/mcp/recordings.d.mts +40 -0
- package/dist/mcp/recordings.d.mts.map +1 -0
- package/dist/mcp/recordings.mjs +2390 -0
- package/dist/mcp/recordings.mjs.map +1 -0
- package/dist/mcp/state.d.mts +71 -0
- package/dist/mcp/state.d.mts.map +1 -0
- package/dist/mcp/state.mjs +83 -0
- package/dist/mcp/state.mjs.map +1 -0
- package/dist/mcp.d.mts +84 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +241 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/messages.d.mts +183 -0
- package/dist/messages.d.mts.map +1 -0
- package/dist/messages.mjs +532 -0
- package/dist/messages.mjs.map +1 -0
- package/dist/microsoft/api.d.mts +47 -0
- package/dist/microsoft/api.d.mts.map +1 -0
- package/dist/microsoft/api.mjs +178 -0
- package/dist/microsoft/api.mjs.map +1 -0
- package/dist/microsoft/calendar.d.mts +30 -0
- package/dist/microsoft/calendar.d.mts.map +1 -0
- package/dist/microsoft/calendar.mjs +271 -0
- package/dist/microsoft/calendar.mjs.map +1 -0
- package/dist/microsoft/drive.d.mts +36 -0
- package/dist/microsoft/drive.d.mts.map +1 -0
- package/dist/microsoft/drive.mjs +298 -0
- package/dist/microsoft/drive.mjs.map +1 -0
- package/dist/microsoft/graph.d.mts +198 -0
- package/dist/microsoft/graph.d.mts.map +1 -0
- package/dist/microsoft/graph.mjs +264 -0
- package/dist/microsoft/graph.mjs.map +1 -0
- package/dist/microsoft/mail.d.mts +48 -0
- package/dist/microsoft/mail.d.mts.map +1 -0
- package/dist/microsoft/mail.mjs +488 -0
- package/dist/microsoft/mail.mjs.map +1 -0
- package/dist/microsoft/state.d.mts +480 -0
- package/dist/microsoft/state.d.mts.map +1 -0
- package/dist/microsoft/state.mjs +625 -0
- package/dist/microsoft/state.mjs.map +1 -0
- package/dist/microsoft.d.mts +168 -0
- package/dist/microsoft.d.mts.map +1 -0
- package/dist/microsoft.mjs +483 -0
- package/dist/microsoft.mjs.map +1 -0
- package/dist/node.d.mts +47 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +178 -0
- package/dist/node.mjs.map +1 -0
- package/dist/notion/api.d.mts +51 -0
- package/dist/notion/api.d.mts.map +1 -0
- package/dist/notion/api.mjs +507 -0
- package/dist/notion/api.mjs.map +1 -0
- package/dist/notion/state.d.mts +329 -0
- package/dist/notion/state.d.mts.map +1 -0
- package/dist/notion/state.mjs +432 -0
- package/dist/notion/state.mjs.map +1 -0
- package/dist/notion.d.mts +72 -0
- package/dist/notion.d.mts.map +1 -0
- package/dist/notion.mjs +127 -0
- package/dist/notion.mjs.map +1 -0
- package/dist/openai.d.mts +165 -0
- package/dist/openai.d.mts.map +1 -0
- package/dist/openai.mjs +141 -0
- package/dist/openai.mjs.map +1 -0
- package/dist/opencode-recordings.d.mts +7 -0
- package/dist/opencode-recordings.d.mts.map +1 -0
- package/dist/opencode-recordings.mjs +222 -0
- package/dist/opencode-recordings.mjs.map +1 -0
- package/dist/opencode.d.mts +70 -0
- package/dist/opencode.d.mts.map +1 -0
- package/dist/opencode.mjs +217 -0
- package/dist/opencode.mjs.map +1 -0
- package/dist/r2-fixtures.d.mts +36 -0
- package/dist/r2-fixtures.d.mts.map +1 -0
- package/dist/r2-fixtures.mjs +245 -0
- package/dist/r2-fixtures.mjs.map +1 -0
- package/dist/r2-guard.d.mts +43 -0
- package/dist/r2-guard.d.mts.map +1 -0
- package/dist/r2-guard.mjs +215 -0
- package/dist/r2-guard.mjs.map +1 -0
- package/dist/r2.d.mts +166 -0
- package/dist/r2.d.mts.map +1 -0
- package/dist/r2.mjs +517 -0
- package/dist/r2.mjs.map +1 -0
- package/dist/responses.d.mts +231 -0
- package/dist/responses.d.mts.map +1 -0
- package/dist/responses.mjs +556 -0
- package/dist/responses.mjs.map +1 -0
- package/dist/route-evidence.d.mts +45 -0
- package/dist/route-evidence.d.mts.map +1 -0
- package/dist/route-evidence.mjs +56 -0
- package/dist/route-evidence.mjs.map +1 -0
- package/dist/router.d.mts +93 -0
- package/dist/router.d.mts.map +1 -0
- package/dist/router.mjs +259 -0
- package/dist/router.mjs.map +1 -0
- package/dist/stateful-core.d.mts +17 -0
- package/dist/stateful-core.d.mts.map +1 -0
- package/dist/stateful-core.mjs +41 -0
- package/dist/stateful-core.mjs.map +1 -0
- package/dist/stateful-emulator.d.mts +565 -0
- package/dist/stateful-emulator.d.mts.map +1 -0
- package/dist/stateful-emulator.mjs +1228 -0
- package/dist/stateful-emulator.mjs.map +1 -0
- package/dist/stateful-secrets.d.mts +84 -0
- package/dist/stateful-secrets.d.mts.map +1 -0
- package/dist/stateful-secrets.mjs +216 -0
- package/dist/stateful-secrets.mjs.map +1 -0
- package/dist/subscription-usage-recordings.d.mts +9 -0
- package/dist/subscription-usage-recordings.d.mts.map +1 -0
- package/dist/subscription-usage-recordings.mjs +53 -0
- package/dist/subscription-usage-recordings.mjs.map +1 -0
- package/dist/subscription-usage.d.mts +53 -0
- package/dist/subscription-usage.d.mts.map +1 -0
- package/dist/subscription-usage.mjs +21 -0
- package/dist/subscription-usage.mjs.map +1 -0
- package/dist/telegram/api.d.mts +34 -0
- package/dist/telegram/api.d.mts.map +1 -0
- package/dist/telegram/api.mjs +257 -0
- package/dist/telegram/api.mjs.map +1 -0
- package/dist/telegram/state.d.mts +128 -0
- package/dist/telegram/state.d.mts.map +1 -0
- package/dist/telegram/state.mjs +154 -0
- package/dist/telegram/state.mjs.map +1 -0
- package/dist/telegram.d.mts +62 -0
- package/dist/telegram.d.mts.map +1 -0
- package/dist/telegram.mjs +127 -0
- package/dist/telegram.mjs.map +1 -0
- package/dist/todoist/api.d.mts +55 -0
- package/dist/todoist/api.d.mts.map +1 -0
- package/dist/todoist/api.mjs +415 -0
- package/dist/todoist/api.mjs.map +1 -0
- package/dist/todoist/state.d.mts +275 -0
- package/dist/todoist/state.d.mts.map +1 -0
- package/dist/todoist/state.mjs +345 -0
- package/dist/todoist/state.mjs.map +1 -0
- package/dist/todoist.d.mts +68 -0
- package/dist/todoist.d.mts.map +1 -0
- package/dist/todoist.mjs +181 -0
- package/dist/todoist.mjs.map +1 -0
- package/dist/xai.d.mts +267 -0
- package/dist/xai.d.mts.map +1 -0
- package/dist/xai.mjs +252 -0
- package/dist/xai.mjs.map +1 -0
- package/package.json +157 -0
- package/src/anthropic.ts +289 -0
- package/src/chat-completions.ts +924 -0
- package/src/codex.ts +296 -0
- package/src/dropbox/api.ts +1084 -0
- package/src/dropbox/state.ts +364 -0
- package/src/dropbox.ts +203 -0
- package/src/email-fixtures.ts +1297 -0
- package/src/email.ts +1108 -0
- package/src/emulator-compose.ts +147 -0
- package/src/emulator-http.ts +184 -0
- package/src/emulator-kernel.ts +844 -0
- package/src/fixture-route.ts +561 -0
- package/src/fortnox/api.ts +1352 -0
- package/src/fortnox/state.ts +798 -0
- package/src/fortnox.ts +801 -0
- package/src/gateway-evaluate-recordings.ts +153 -0
- package/src/gateway.ts +530 -0
- package/src/github/api.ts +986 -0
- package/src/github/state.ts +439 -0
- package/src/github.ts +271 -0
- package/src/google/calendar.ts +486 -0
- package/src/google/drive.ts +471 -0
- package/src/google/gmail.ts +1011 -0
- package/src/google/shared.ts +321 -0
- package/src/google/state.ts +686 -0
- package/src/google.ts +298 -0
- package/src/linkedin-search/api.ts +363 -0
- package/src/linkedin-search/state.ts +373 -0
- package/src/linkedin-search.ts +241 -0
- package/src/mcp/api.ts +995 -0
- package/src/mcp/recordings.ts +2665 -0
- package/src/mcp/state.ts +125 -0
- package/src/mcp.ts +319 -0
- package/src/messages.ts +844 -0
- package/src/microsoft/api.ts +426 -0
- package/src/microsoft/calendar.ts +491 -0
- package/src/microsoft/drive.ts +541 -0
- package/src/microsoft/graph.ts +550 -0
- package/src/microsoft/mail.ts +833 -0
- package/src/microsoft/state.ts +822 -0
- package/src/microsoft.ts +982 -0
- package/src/node.ts +293 -0
- package/src/notion/api.ts +976 -0
- package/src/notion/state.ts +512 -0
- package/src/notion.ts +210 -0
- package/src/openai.ts +198 -0
- package/src/opencode-recordings.ts +257 -0
- package/src/opencode.ts +271 -0
- package/src/r2-fixtures.ts +295 -0
- package/src/r2-guard.ts +323 -0
- package/src/r2.ts +890 -0
- package/src/responses.ts +901 -0
- package/src/route-evidence.ts +90 -0
- package/src/router.ts +490 -0
- package/src/stateful-core.ts +70 -0
- package/src/stateful-emulator.ts +2571 -0
- package/src/stateful-secrets.ts +299 -0
- package/src/subscription-usage-recordings.ts +77 -0
- package/src/subscription-usage.ts +68 -0
- package/src/telegram/api.ts +437 -0
- package/src/telegram/state.ts +229 -0
- package/src/telegram.ts +227 -0
- package/src/todoist/api.ts +736 -0
- package/src/todoist/state.ts +456 -0
- package/src/todoist.ts +316 -0
- package/src/xai.ts +341 -0
package/src/opencode.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Go emulator: one plain fetch handler for the origin `https://opencode.ai`, serving the
|
|
3
|
+
* routes the OpenCode Go provider and usage fetcher call under `/zen/go/v1`:
|
|
4
|
+
*
|
|
5
|
+
* - `POST /zen/go/v1/chat/completions` (Bearer, `max_tokens`, streamed with usage);
|
|
6
|
+
* - `POST /zen/go/v1/messages` (`x-api-key`, `anthropic-version: 2023-06-01`, streamed);
|
|
7
|
+
* - `POST /zen/go/v1/responses` (Bearer, `max_output_tokens`, streamed; plain text or a replayed
|
|
8
|
+
* tool turn with commentary-tagged text);
|
|
9
|
+
* - `GET /zen/go/v1/usage` (Bearer).
|
|
10
|
+
*
|
|
11
|
+
* Fixture-only (the owner rule for new routes; `fixture-route.ts`): a request matching a recorded
|
|
12
|
+
* request shape, within the documented request-shape latitude, gets that Go conformance fixture's
|
|
13
|
+
* recorded response, copied as data (`opencode-recordings.ts`); everything else (unknown routes,
|
|
14
|
+
* missing credentials or headers, other models, non-streamed modes, tools other than the recorded
|
|
15
|
+
* replay, reasoning, extra fields) answers one ledgered 400 not-emulated.
|
|
16
|
+
*
|
|
17
|
+
* Each route keeps its own ledger, faults, scripted turns, and control plane (`emulator.chat`,
|
|
18
|
+
* `emulator.messages`, `emulator.responses`, `emulator.usage`; over HTTP `/_emulate/<part>/*`);
|
|
19
|
+
* `emulator.coverage()` and `GET /_emulate/coverage` combine them, and `emulator.reset()` and
|
|
20
|
+
* `POST /_emulate/reset` reset all four. Requests on no route are ledgered by the chat part.
|
|
21
|
+
*
|
|
22
|
+
* It never imports SDK code. All routes are `unverified` (the fixtures are synthetic).
|
|
23
|
+
*
|
|
24
|
+
* Runtime-portable Web APIs only (`Request`, `Response`, `ReadableStream`, `TextEncoder`, `URL`);
|
|
25
|
+
* no Effect runtime is required to use it.
|
|
26
|
+
*
|
|
27
|
+
* @experimental
|
|
28
|
+
*/
|
|
29
|
+
import { Data } from 'effect'
|
|
30
|
+
import type * as Schema from 'effect/Schema'
|
|
31
|
+
import { combinedCoverage, composeFetch, type ComposedPart } from './emulator-compose.ts'
|
|
32
|
+
import { jsonResponse, stringField, type EmulatorCoverage } from './emulator-kernel.ts'
|
|
33
|
+
import {
|
|
34
|
+
makeFixtureRouteEmulator,
|
|
35
|
+
type FixtureRecording,
|
|
36
|
+
type FixtureRouteEmulator
|
|
37
|
+
} from './fixture-route.ts'
|
|
38
|
+
import { openCodeGoRecordings } from './opencode-recordings.ts'
|
|
39
|
+
import type { EmulatorRouteEvidence } from './route-evidence.ts'
|
|
40
|
+
import { makeSubscriptionUsageEmulator, recordedUsageBody } from './subscription-usage.ts'
|
|
41
|
+
|
|
42
|
+
export type { EmulatorEvidence, EmulatorRouteEvidence } from './route-evidence.ts'
|
|
43
|
+
|
|
44
|
+
export { emulatorEvidenceHeader } from './route-evidence.ts'
|
|
45
|
+
|
|
46
|
+
export type {
|
|
47
|
+
FixtureRouteFault as OpenCodeGoFault,
|
|
48
|
+
FixtureRouteLedgerEntry as OpenCodeGoLedgerEntry,
|
|
49
|
+
FixtureRouteScriptedTurn as OpenCodeGoScriptedTurn
|
|
50
|
+
} from './fixture-route.ts'
|
|
51
|
+
|
|
52
|
+
export const openCodeGoChatCompletionsPath = '/zen/go/v1/chat/completions'
|
|
53
|
+
|
|
54
|
+
export const openCodeGoMessagesPath = '/zen/go/v1/messages'
|
|
55
|
+
|
|
56
|
+
export const openCodeGoResponsesPath = '/zen/go/v1/responses'
|
|
57
|
+
|
|
58
|
+
/** The Go subscription-usage path (`openCodeGoSubscriptionUsageUrl` in the SDK). */
|
|
59
|
+
export const openCodeGoUsagePath = '/zen/go/v1/usage'
|
|
60
|
+
|
|
61
|
+
const recordingsFor = (path: string): ReadonlyArray<FixtureRecording> =>
|
|
62
|
+
openCodeGoRecordings.filter(recording => recording.request.path === path)
|
|
63
|
+
|
|
64
|
+
const usageRecording: FixtureRecording = (() => {
|
|
65
|
+
const [recording] = recordingsFor(openCodeGoUsagePath)
|
|
66
|
+
|
|
67
|
+
// Packaging-data invariant: the Go usage fixture is copied into this package.
|
|
68
|
+
if (recording === undefined) throw new Error('the Go usage recording is missing')
|
|
69
|
+
|
|
70
|
+
return recording
|
|
71
|
+
})()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The model ids the Go fixtures record, one per protocol (synthetic placeholders). A protocol
|
|
75
|
+
* route answers only its recorded model; any other model is not emulated.
|
|
76
|
+
*/
|
|
77
|
+
export const openCodeGoEmulatorDefaultModels: ReadonlyArray<string> = [
|
|
78
|
+
...new Set(
|
|
79
|
+
openCodeGoRecordings.flatMap(recording => {
|
|
80
|
+
const model = stringField(recording.request.body, 'model')
|
|
81
|
+
|
|
82
|
+
return model === undefined ? [] : [model]
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Route evidence manifest: every emulated OpenCode Go route and the conformance cases whose
|
|
89
|
+
* (synthetic, unverified) fixtures it answers from.
|
|
90
|
+
*/
|
|
91
|
+
export const openCodeGoEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence> = [
|
|
92
|
+
{
|
|
93
|
+
method: 'POST',
|
|
94
|
+
path: openCodeGoChatCompletionsPath,
|
|
95
|
+
kind: 'provider',
|
|
96
|
+
write: false,
|
|
97
|
+
caseIds: ['opencode.go.chat.stream.plain-text'],
|
|
98
|
+
evidence: 'unverified',
|
|
99
|
+
observedAt: undefined
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
method: 'POST',
|
|
103
|
+
path: openCodeGoMessagesPath,
|
|
104
|
+
kind: 'provider',
|
|
105
|
+
write: false,
|
|
106
|
+
caseIds: ['opencode.go.messages.stream.plain-text'],
|
|
107
|
+
evidence: 'unverified',
|
|
108
|
+
observedAt: undefined
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
method: 'POST',
|
|
112
|
+
path: openCodeGoResponsesPath,
|
|
113
|
+
kind: 'provider',
|
|
114
|
+
write: false,
|
|
115
|
+
caseIds: [
|
|
116
|
+
'opencode.go.responses.stream.plain-text',
|
|
117
|
+
'opencode.go.responses.stream.commentary-replay'
|
|
118
|
+
],
|
|
119
|
+
evidence: 'unverified',
|
|
120
|
+
observedAt: undefined
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
method: 'GET',
|
|
124
|
+
path: openCodeGoUsagePath,
|
|
125
|
+
kind: 'provider',
|
|
126
|
+
write: false,
|
|
127
|
+
caseIds: ['opencode.go.usage.snapshot'],
|
|
128
|
+
evidence: 'unverified',
|
|
129
|
+
observedAt: undefined
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The recorded Go usage body (the synthetic `opencode.go.usage.snapshot` fixture, copied as
|
|
135
|
+
* data): `usage.rolling`, `usage.weekly`, and `usage.monthly` as `{ percent, resetsAt }`.
|
|
136
|
+
*/
|
|
137
|
+
export const openCodeGoUsageDefault: Schema.Json = recordedUsageBody(usageRecording)
|
|
138
|
+
|
|
139
|
+
/** Thrown by the JS API (`faults.add`, `script.enqueue`) and at construction for invalid input. */
|
|
140
|
+
export class OpenCodeGoEmulatorInputInvalid extends Data.TaggedError(
|
|
141
|
+
'OpenCodeGoEmulatorInputInvalid'
|
|
142
|
+
)<{
|
|
143
|
+
readonly protocol: 'chat-completions' | 'messages' | 'responses' | 'usage'
|
|
144
|
+
readonly input: 'fault' | 'turn'
|
|
145
|
+
readonly reason: string
|
|
146
|
+
}> {
|
|
147
|
+
override get message(): string {
|
|
148
|
+
return `Invalid OpenCode Go ${this.protocol} emulator ${this.input}: ${this.reason}`
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type OpenCodeGoEmulatorOptions = {
|
|
153
|
+
/**
|
|
154
|
+
* Replacement usage-route body; must have the recorded JSON shape (same keys and value kinds).
|
|
155
|
+
* Defaults to the recorded body (`openCodeGoUsageDefault`).
|
|
156
|
+
*/
|
|
157
|
+
readonly subscriptionUsage?: Schema.Json
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** One Go route: a fixture-only emulator API (ledger, faults, scripted turns, coverage). */
|
|
161
|
+
export type OpenCodeGoRouteEmulator = FixtureRouteEmulator
|
|
162
|
+
|
|
163
|
+
export type OpenCodeGoEmulator = {
|
|
164
|
+
/** The fetch handler for every route and the `/_emulate/*` control plane. Never rejects. */
|
|
165
|
+
readonly fetch: (request: Request) => Promise<Response>
|
|
166
|
+
/** Reset every part (ledgers, faults, and scripted turns). */
|
|
167
|
+
readonly reset: () => void
|
|
168
|
+
/** Every route's coverage, in manifest order. */
|
|
169
|
+
readonly coverage: () => EmulatorCoverage
|
|
170
|
+
/** `POST /zen/go/v1/chat/completions` (control plane `/_emulate/chat/*`). */
|
|
171
|
+
readonly chat: OpenCodeGoRouteEmulator
|
|
172
|
+
/** `POST /zen/go/v1/messages` (control plane `/_emulate/messages/*`). */
|
|
173
|
+
readonly messages: OpenCodeGoRouteEmulator
|
|
174
|
+
/** `POST /zen/go/v1/responses` (control plane `/_emulate/responses/*`). */
|
|
175
|
+
readonly responses: OpenCodeGoRouteEmulator
|
|
176
|
+
/** `GET /zen/go/v1/usage` (control plane `/_emulate/usage/*`). */
|
|
177
|
+
readonly usage: OpenCodeGoRouteEmulator
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const routesFor = (path: string): ReadonlyArray<EmulatorRouteEvidence> =>
|
|
181
|
+
openCodeGoEmulatorRoutes.filter(route => route.path === path)
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create an OpenCode Go emulator. Each call has independent state for every part. Throws
|
|
185
|
+
* `OpenCodeGoEmulatorInputInvalid` when `subscriptionUsage` does not have the recorded shape.
|
|
186
|
+
*
|
|
187
|
+
* Per route, a request carrying the credential and headers the Go provider or usage fetcher sends
|
|
188
|
+
* (Bearer for chat, Responses, and usage; `x-api-key` and `anthropic-version: 2023-06-01` for
|
|
189
|
+
* Messages), the recorded `accept` and `content-type`, and a body matching a recorded request
|
|
190
|
+
* shape gets the recorded response; anything else answers 400 not-emulated. Test controls per
|
|
191
|
+
* part: the shared kernel faults and scripted error turns (and, on usage, a scripted same-shaped
|
|
192
|
+
* `{ usage }` body).
|
|
193
|
+
*/
|
|
194
|
+
export const makeOpenCodeGoEmulator = (
|
|
195
|
+
options: OpenCodeGoEmulatorOptions = {}
|
|
196
|
+
): OpenCodeGoEmulator => {
|
|
197
|
+
const inputInvalid =
|
|
198
|
+
(protocol: OpenCodeGoEmulatorInputInvalid['protocol']) =>
|
|
199
|
+
(input: 'fault' | 'turn', reason: string) =>
|
|
200
|
+
new OpenCodeGoEmulatorInputInvalid({ protocol, input, reason })
|
|
201
|
+
|
|
202
|
+
const chat = makeFixtureRouteEmulator({
|
|
203
|
+
method: 'POST',
|
|
204
|
+
path: openCodeGoChatCompletionsPath,
|
|
205
|
+
routes: routesFor(openCodeGoChatCompletionsPath),
|
|
206
|
+
recordings: recordingsFor(openCodeGoChatCompletionsPath),
|
|
207
|
+
credential: 'bearer',
|
|
208
|
+
headers: [],
|
|
209
|
+
inputInvalid: inputInvalid('chat-completions')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const messages = makeFixtureRouteEmulator({
|
|
213
|
+
method: 'POST',
|
|
214
|
+
path: openCodeGoMessagesPath,
|
|
215
|
+
routes: routesFor(openCodeGoMessagesPath),
|
|
216
|
+
recordings: recordingsFor(openCodeGoMessagesPath),
|
|
217
|
+
credential: 'x-api-key',
|
|
218
|
+
headers: [
|
|
219
|
+
{ name: 'anthropic-version', record: true, accepts: value => value === '2023-06-01' }
|
|
220
|
+
],
|
|
221
|
+
inputInvalid: inputInvalid('messages')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const responses = makeFixtureRouteEmulator({
|
|
225
|
+
method: 'POST',
|
|
226
|
+
path: openCodeGoResponsesPath,
|
|
227
|
+
routes: routesFor(openCodeGoResponsesPath),
|
|
228
|
+
recordings: recordingsFor(openCodeGoResponsesPath),
|
|
229
|
+
credential: 'bearer',
|
|
230
|
+
headers: [],
|
|
231
|
+
inputInvalid: inputInvalid('responses')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const usage = makeSubscriptionUsageEmulator({
|
|
235
|
+
path: openCodeGoUsagePath,
|
|
236
|
+
routes: routesFor(openCodeGoUsagePath),
|
|
237
|
+
recording: usageRecording,
|
|
238
|
+
headers: [],
|
|
239
|
+
subscriptionUsage: options.subscriptionUsage,
|
|
240
|
+
inputInvalid: inputInvalid('usage')
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const parts: ReadonlyArray<ComposedPart> = [chat, messages, responses, usage]
|
|
244
|
+
const coverage = () => combinedCoverage(parts)
|
|
245
|
+
|
|
246
|
+
const reset = () => {
|
|
247
|
+
for (const part of parts) part.reset()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const fetch = composeFetch({
|
|
251
|
+
routes: [
|
|
252
|
+
{ name: 'chat', paths: [openCodeGoChatCompletionsPath], part: chat },
|
|
253
|
+
{ name: 'messages', paths: [openCodeGoMessagesPath], part: messages },
|
|
254
|
+
{ name: 'responses', paths: [openCodeGoResponsesPath], part: responses },
|
|
255
|
+
{ name: 'usage', paths: [openCodeGoUsagePath], part: usage }
|
|
256
|
+
],
|
|
257
|
+
fallback: chat,
|
|
258
|
+
control: async (request, path) =>
|
|
259
|
+
path === '/_emulate/coverage' && request.method === 'GET'
|
|
260
|
+
? jsonResponse(200, coverage())
|
|
261
|
+
: jsonResponse(404, {
|
|
262
|
+
error: {
|
|
263
|
+
message:
|
|
264
|
+
'unknown control-plane route: use /_emulate/{chat,messages,responses,usage}/*, GET /_emulate/coverage, or POST /_emulate/reset',
|
|
265
|
+
type: 'emulator_error'
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
return { fetch, reset, coverage, chat, messages, responses, usage }
|
|
271
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The R2 conformance fixtures, copied as data (internal; re-exported by `r2.ts`).
|
|
3
|
+
*
|
|
4
|
+
* Emulators never import SDK code: this is a verbatim copy of `r2ConformanceFixtures` from
|
|
5
|
+
* `@yolk-sdk/connectors/r2-storage/conformance` (synthetic `PortFixture`s; none observed live).
|
|
6
|
+
* The presigned URL carries only the canonical synthetic SigV4 placeholders
|
|
7
|
+
* (`syntheticPortCredentialParams` of `@yolk-sdk/conformance`). `test/r2-conformance.test.ts`
|
|
8
|
+
* fails when the two drift apart; update both together.
|
|
9
|
+
*/
|
|
10
|
+
import type * as Schema from 'effect/Schema'
|
|
11
|
+
|
|
12
|
+
/** A port failure as a fixture records it (mirrors `PortFailure` in `@yolk-sdk/conformance`). */
|
|
13
|
+
export type R2EmulatorFailure = {
|
|
14
|
+
readonly kind: 'expected' | 'error'
|
|
15
|
+
readonly code: string
|
|
16
|
+
readonly message: string
|
|
17
|
+
readonly status?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type R2EmulatorFixtureFields = {
|
|
21
|
+
readonly id: string
|
|
22
|
+
readonly port: string
|
|
23
|
+
readonly method: string
|
|
24
|
+
readonly request: Schema.Json
|
|
25
|
+
readonly observed?: { readonly account: string; readonly date: string }
|
|
26
|
+
readonly note?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* One recorded `R2Presigner` or `R2ObjectClient` call (the plain-JSON shape of a `PortFixture`):
|
|
31
|
+
* exactly one of `response` or `failure`.
|
|
32
|
+
*/
|
|
33
|
+
export type R2EmulatorFixture =
|
|
34
|
+
| (R2EmulatorFixtureFields & { readonly response: Schema.Json; readonly failure?: never })
|
|
35
|
+
| (R2EmulatorFixtureFields & {
|
|
36
|
+
readonly failure: R2EmulatorFailure
|
|
37
|
+
readonly response?: never
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export const r2EmulatorFixtures: ReadonlyArray<R2EmulatorFixture> = [
|
|
41
|
+
{
|
|
42
|
+
id: 'r2.presign.put-upload-url.presign.synthetic',
|
|
43
|
+
port: 'R2Presigner',
|
|
44
|
+
method: 'presignPutObject',
|
|
45
|
+
request: {
|
|
46
|
+
endpoint: 'https://synthetic-account.r2.example.test',
|
|
47
|
+
bucket: 'yolk-synthetic-bucket',
|
|
48
|
+
key: 'yolk-conformance/run-synthetic/presign.txt',
|
|
49
|
+
contentType: 'text/plain'
|
|
50
|
+
},
|
|
51
|
+
response: {
|
|
52
|
+
uploadUrl:
|
|
53
|
+
'https://synthetic-account.r2.example.test/yolk-synthetic-bucket/yolk-conformance/run-synthetic/presign.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=yolk-synthetic-access-key-id%2F20260930%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20260930T120000Z&X-Amz-Expires=900&X-Amz-Signature=yolk-synthetic-signature&X-Amz-SignedHeaders=content-type%3Bhost&x-id=PutObject'
|
|
54
|
+
},
|
|
55
|
+
note: 'Local SigV4 query signing (15-minute expiry, content-type signed); nothing sent to R2. Synthetic.'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'r2.objects.get-max-bytes.within-budget.synthetic',
|
|
59
|
+
port: 'R2ObjectClient',
|
|
60
|
+
method: 'get',
|
|
61
|
+
request: {
|
|
62
|
+
bucket: 'yolk-synthetic-bucket',
|
|
63
|
+
key: 'fixtures/synthetic-object.txt',
|
|
64
|
+
maxBytes: 1048576
|
|
65
|
+
},
|
|
66
|
+
response: {
|
|
67
|
+
etag: '"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"',
|
|
68
|
+
size: 44,
|
|
69
|
+
bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='
|
|
70
|
+
},
|
|
71
|
+
note: 'The seeded object within the budget. Synthetic.'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'r2.objects.get-max-bytes.over-budget.synthetic',
|
|
75
|
+
port: 'R2ObjectClient',
|
|
76
|
+
method: 'get',
|
|
77
|
+
request: {
|
|
78
|
+
bucket: 'yolk-synthetic-bucket',
|
|
79
|
+
key: 'fixtures/synthetic-object.txt',
|
|
80
|
+
maxBytes: 43
|
|
81
|
+
},
|
|
82
|
+
failure: {
|
|
83
|
+
kind: 'error',
|
|
84
|
+
code: 'response_too_large',
|
|
85
|
+
message: 'The object is larger than maxBytes; no bytes were returned.'
|
|
86
|
+
},
|
|
87
|
+
note: 'The host stops at maxBytes (Content-Length or streamed count). Synthetic.'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'r2.objects.get-expected-etag.plain.synthetic',
|
|
91
|
+
port: 'R2ObjectClient',
|
|
92
|
+
method: 'get',
|
|
93
|
+
request: {
|
|
94
|
+
bucket: 'yolk-synthetic-bucket',
|
|
95
|
+
key: 'fixtures/synthetic-object.txt',
|
|
96
|
+
maxBytes: 1048576
|
|
97
|
+
},
|
|
98
|
+
response: {
|
|
99
|
+
etag: '"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"',
|
|
100
|
+
size: 44,
|
|
101
|
+
bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='
|
|
102
|
+
},
|
|
103
|
+
note: 'A plain read that learns the etag. Synthetic.'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'r2.objects.get-expected-etag.current.synthetic',
|
|
107
|
+
port: 'R2ObjectClient',
|
|
108
|
+
method: 'get',
|
|
109
|
+
request: {
|
|
110
|
+
bucket: 'yolk-synthetic-bucket',
|
|
111
|
+
key: 'fixtures/synthetic-object.txt',
|
|
112
|
+
expectedEtag: '"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"',
|
|
113
|
+
maxBytes: 1048576
|
|
114
|
+
},
|
|
115
|
+
response: {
|
|
116
|
+
etag: '"a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"',
|
|
117
|
+
size: 44,
|
|
118
|
+
bodyBase64: 'U3ludGhldGljIGNvbmZvcm1hbmNlIG9iamVjdDogc2FmZSB0byByZWFkLgo='
|
|
119
|
+
},
|
|
120
|
+
note: 'If-Match with the current etag. Synthetic.'
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: 'r2.objects.get-expected-etag.stale.synthetic',
|
|
124
|
+
port: 'R2ObjectClient',
|
|
125
|
+
method: 'get',
|
|
126
|
+
request: {
|
|
127
|
+
bucket: 'yolk-synthetic-bucket',
|
|
128
|
+
key: 'fixtures/synthetic-object.txt',
|
|
129
|
+
expectedEtag: '"00000000000000000000000000000000"',
|
|
130
|
+
maxBytes: 1048576
|
|
131
|
+
},
|
|
132
|
+
failure: {
|
|
133
|
+
kind: 'error',
|
|
134
|
+
code: 'conflict',
|
|
135
|
+
message: 'Precondition failed (If-Match).'
|
|
136
|
+
},
|
|
137
|
+
note: 'If-Match with a stale etag: HTTP 412 or a binding onlyIf without body. Synthetic.'
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'r2.objects.get-missing-not-found.get.synthetic',
|
|
141
|
+
port: 'R2ObjectClient',
|
|
142
|
+
method: 'get',
|
|
143
|
+
request: {
|
|
144
|
+
bucket: 'yolk-synthetic-bucket',
|
|
145
|
+
key: 'yolk-conformance/run-synthetic/absent.txt',
|
|
146
|
+
maxBytes: 1048576
|
|
147
|
+
},
|
|
148
|
+
failure: {
|
|
149
|
+
kind: 'error',
|
|
150
|
+
code: 'not_found',
|
|
151
|
+
message: 'No object with that key.'
|
|
152
|
+
},
|
|
153
|
+
note: 'HTTP 404 NoSuchKey, or a binding get answering null. Synthetic.'
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: 'r2.objects.create-if-absent.create.synthetic',
|
|
157
|
+
port: 'R2ObjectClient',
|
|
158
|
+
method: 'put',
|
|
159
|
+
request: {
|
|
160
|
+
bucket: 'yolk-synthetic-bucket',
|
|
161
|
+
key: 'yolk-conformance/run-synthetic/create-if-absent.txt',
|
|
162
|
+
condition: {
|
|
163
|
+
kind: 'absent'
|
|
164
|
+
},
|
|
165
|
+
bodyBase64:
|
|
166
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',
|
|
167
|
+
maxUploadBytes: 1048576
|
|
168
|
+
},
|
|
169
|
+
response: {
|
|
170
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee01"',
|
|
171
|
+
size: 68
|
|
172
|
+
},
|
|
173
|
+
note: 'PUT with If-None-Match: * on a fresh key. Synthetic.'
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'r2.objects.create-if-absent.create-again.synthetic',
|
|
177
|
+
port: 'R2ObjectClient',
|
|
178
|
+
method: 'put',
|
|
179
|
+
request: {
|
|
180
|
+
bucket: 'yolk-synthetic-bucket',
|
|
181
|
+
key: 'yolk-conformance/run-synthetic/create-if-absent.txt',
|
|
182
|
+
condition: {
|
|
183
|
+
kind: 'absent'
|
|
184
|
+
},
|
|
185
|
+
bodyBase64:
|
|
186
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBzZWNvbmQgc3ludGhldGljIGJvZHksIG5ldmVyIHN0b3JlZA==',
|
|
187
|
+
maxUploadBytes: 1048576
|
|
188
|
+
},
|
|
189
|
+
failure: {
|
|
190
|
+
kind: 'error',
|
|
191
|
+
code: 'conflict',
|
|
192
|
+
message: 'Precondition failed (If-None-Match).'
|
|
193
|
+
},
|
|
194
|
+
note: 'The same key again: HTTP 412, or a binding put answering null. Synthetic.'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'r2.objects.create-if-absent.read.synthetic',
|
|
198
|
+
port: 'R2ObjectClient',
|
|
199
|
+
method: 'get',
|
|
200
|
+
request: {
|
|
201
|
+
bucket: 'yolk-synthetic-bucket',
|
|
202
|
+
key: 'yolk-conformance/run-synthetic/create-if-absent.txt',
|
|
203
|
+
expectedEtag: '"c0ffee00c0ffee00c0ffee00c0ffee01"',
|
|
204
|
+
maxBytes: 1048576
|
|
205
|
+
},
|
|
206
|
+
response: {
|
|
207
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee01"',
|
|
208
|
+
size: 68,
|
|
209
|
+
bodyBase64:
|
|
210
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBmaXJzdCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU='
|
|
211
|
+
},
|
|
212
|
+
note: 'The first bytes, unchanged. Synthetic.'
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'r2.objects.update-if-match.create.synthetic',
|
|
216
|
+
port: 'R2ObjectClient',
|
|
217
|
+
method: 'put',
|
|
218
|
+
request: {
|
|
219
|
+
bucket: 'yolk-synthetic-bucket',
|
|
220
|
+
key: 'yolk-conformance/run-synthetic/update-if-match.txt',
|
|
221
|
+
condition: {
|
|
222
|
+
kind: 'absent'
|
|
223
|
+
},
|
|
224
|
+
bodyBase64:
|
|
225
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiBvcmlnaW5hbCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',
|
|
226
|
+
maxUploadBytes: 1048576
|
|
227
|
+
},
|
|
228
|
+
response: {
|
|
229
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee02"',
|
|
230
|
+
size: 71
|
|
231
|
+
},
|
|
232
|
+
note: 'PUT with If-None-Match: * on a fresh key. Synthetic.'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: 'r2.objects.update-if-match.stale.synthetic',
|
|
236
|
+
port: 'R2ObjectClient',
|
|
237
|
+
method: 'put',
|
|
238
|
+
request: {
|
|
239
|
+
bucket: 'yolk-synthetic-bucket',
|
|
240
|
+
key: 'yolk-conformance/run-synthetic/update-if-match.txt',
|
|
241
|
+
condition: {
|
|
242
|
+
kind: 'etag',
|
|
243
|
+
etag: '"00000000000000000000000000000000"'
|
|
244
|
+
},
|
|
245
|
+
bodyBase64:
|
|
246
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',
|
|
247
|
+
maxUploadBytes: 1048576
|
|
248
|
+
},
|
|
249
|
+
failure: {
|
|
250
|
+
kind: 'error',
|
|
251
|
+
code: 'conflict',
|
|
252
|
+
message: 'Precondition failed (If-Match).'
|
|
253
|
+
},
|
|
254
|
+
note: 'PUT with a stale If-Match: HTTP 412, or a binding put answering null. Synthetic.'
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'r2.objects.update-if-match.current.synthetic',
|
|
258
|
+
port: 'R2ObjectClient',
|
|
259
|
+
method: 'put',
|
|
260
|
+
request: {
|
|
261
|
+
bucket: 'yolk-synthetic-bucket',
|
|
262
|
+
key: 'yolk-conformance/run-synthetic/update-if-match.txt',
|
|
263
|
+
condition: {
|
|
264
|
+
kind: 'etag',
|
|
265
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee02"'
|
|
266
|
+
},
|
|
267
|
+
bodyBase64:
|
|
268
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU=',
|
|
269
|
+
maxUploadBytes: 1048576
|
|
270
|
+
},
|
|
271
|
+
response: {
|
|
272
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee03"',
|
|
273
|
+
size: 74
|
|
274
|
+
},
|
|
275
|
+
note: 'PUT with the current If-Match: a new MD5 etag. Synthetic.'
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'r2.objects.update-if-match.read.synthetic',
|
|
279
|
+
port: 'R2ObjectClient',
|
|
280
|
+
method: 'get',
|
|
281
|
+
request: {
|
|
282
|
+
bucket: 'yolk-synthetic-bucket',
|
|
283
|
+
key: 'yolk-conformance/run-synthetic/update-if-match.txt',
|
|
284
|
+
expectedEtag: '"c0ffee00c0ffee00c0ffee00c0ffee03"',
|
|
285
|
+
maxBytes: 1048576
|
|
286
|
+
},
|
|
287
|
+
response: {
|
|
288
|
+
etag: '"c0ffee00c0ffee00c0ffee00c0ffee03"',
|
|
289
|
+
size: 74,
|
|
290
|
+
bodyBase64:
|
|
291
|
+
'eW9say1jb25mb3JtYW5jZSBydW4tc3ludGhldGljOiByZXBsYWNlbWVudCBzeW50aGV0aWMgYm9keSwgc2FmZSB0byBkZWxldGU='
|
|
292
|
+
},
|
|
293
|
+
note: 'The replacement bytes under the new etag. Synthetic.'
|
|
294
|
+
}
|
|
295
|
+
]
|