@volter/twin-standard 1.0.30 → 1.0.32
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/dist/src/archive-source.d.ts +13 -0
- package/dist/src/archive-source.js +176 -0
- package/dist/src/check-sources.d.ts +19 -2
- package/dist/src/check-sources.js +95 -37
- package/dist/src/cli.js +52 -9
- package/dist/src/clients/ai-gateway.d.ts +1 -0
- package/dist/src/clients/ai-gateway.js +3 -0
- package/dist/src/clients/anthropic-messages.d.ts +10 -0
- package/dist/src/clients/anthropic-messages.js +52 -0
- package/dist/src/clients/anthropic.d.ts +1 -0
- package/dist/src/clients/anthropic.js +3 -0
- package/dist/src/clients/aws.d.ts +2 -0
- package/dist/src/clients/aws.js +137 -0
- package/dist/src/clients/cerebras.d.ts +1 -0
- package/dist/src/clients/cerebras.js +3 -0
- package/dist/src/clients/clerk.d.ts +2 -0
- package/dist/src/clients/clerk.js +99 -0
- package/dist/src/clients/cloudflare.d.ts +2 -0
- package/dist/src/clients/cloudflare.js +86 -0
- package/dist/src/clients/elevenlabs.d.ts +2 -0
- package/dist/src/clients/elevenlabs.js +39 -0
- package/dist/src/clients/gemini.d.ts +2 -0
- package/dist/src/clients/gemini.js +92 -0
- package/dist/src/clients/github.d.ts +2 -0
- package/dist/src/clients/github.js +71 -0
- package/dist/src/clients/groq.d.ts +1 -0
- package/dist/src/clients/groq.js +3 -0
- package/dist/src/clients/harness.d.ts +36 -0
- package/dist/src/clients/harness.js +74 -0
- package/dist/src/clients/merge.d.ts +1 -0
- package/dist/src/clients/merge.js +8 -0
- package/dist/src/clients/openai-compatible.d.ts +9 -0
- package/dist/src/clients/openai-compatible.js +51 -0
- package/dist/src/clients/openai.d.ts +2 -0
- package/dist/src/clients/openai.js +65 -0
- package/dist/src/clients/openrouter.d.ts +1 -0
- package/dist/src/clients/openrouter.js +3 -0
- package/dist/src/clients/perplexity.d.ts +1 -0
- package/dist/src/clients/perplexity.js +3 -0
- package/dist/src/clients/planetscale.d.ts +2 -0
- package/dist/src/clients/planetscale.js +109 -0
- package/dist/src/clients/posthog.d.ts +2 -0
- package/dist/src/clients/posthog.js +145 -0
- package/dist/src/clients/run.d.ts +1 -0
- package/dist/src/clients/run.js +25 -0
- package/dist/src/clients/sentry.d.ts +2 -0
- package/dist/src/clients/sentry.js +167 -0
- package/dist/src/clients/slack.d.ts +2 -0
- package/dist/src/clients/slack.js +107 -0
- package/dist/src/clients/stripe.d.ts +2 -0
- package/dist/src/clients/stripe.js +161 -0
- package/dist/src/clients/tinybird.d.ts +2 -0
- package/dist/src/clients/tinybird.js +71 -0
- package/dist/src/clients/tunnel.d.ts +2 -0
- package/dist/src/clients/tunnel.js +99 -0
- package/dist/src/clients/upstash.d.ts +2 -0
- package/dist/src/clients/upstash.js +143 -0
- package/dist/src/clients/volteridentity.d.ts +2 -0
- package/dist/src/clients/volteridentity.js +128 -0
- package/dist/src/clients/xai.d.ts +2 -0
- package/dist/src/clients/xai.js +23 -0
- package/dist/src/conformance.d.ts +33 -0
- package/dist/src/conformance.js +520 -0
- package/dist/src/derive.js +113 -22
- package/dist/src/gate.js +22 -16
- package/dist/src/index.d.ts +5 -3
- package/dist/src/index.js +5 -3
- package/dist/src/lanes.d.ts +2 -0
- package/dist/src/lanes.js +7 -0
- package/dist/src/p3-rules.js +0 -2
- package/dist/src/protocol-3.d.ts +1 -0
- package/dist/src/protocol-3.js +76 -30
- package/dist/src/published.d.ts +6 -0
- package/dist/src/published.js +65 -11
- package/dist/src/spec-documents.js +2 -1
- package/dist/src/spec-ir-client.d.ts +5 -1
- package/dist/src/spec-ir-client.js +12 -2
- package/dist/src/spec-ir.d.ts +7 -2
- package/dist/src/spec-ir.js +42 -10
- package/dist/src/verdict.d.ts +14 -0
- package/dist/src/verdict.js +30 -0
- package/dist/src/walk/behavior-journey.d.ts +47 -0
- package/dist/src/walk/behavior-journey.js +1138 -0
- package/dist/src/walk/git-client.d.ts +26 -0
- package/dist/src/walk/git-client.js +139 -0
- package/dist/src/walk/journey-kit.d.ts +445 -0
- package/dist/src/walk/journey-kit.js +743 -0
- package/dist/src/walk/shape.d.ts +54 -0
- package/dist/src/walk/shape.js +210 -0
- package/dist/src/walk/sse-example.d.ts +2 -0
- package/dist/src/walk/sse-example.js +32 -0
- package/dist/src/walk/vendor-fetch.d.ts +20 -0
- package/dist/src/walk/vendor-fetch.js +92 -0
- package/dist/src/walk/walk-database.d.ts +12 -0
- package/dist/src/walk/walk-database.js +96 -0
- package/dist/src/walk/xml-view.d.ts +2 -0
- package/dist/src/walk/xml-view.js +55 -0
- package/package.json +39 -3
- package/src/archive-source.ts +141 -0
- package/src/check-sources.ts +83 -33
- package/src/cli.ts +40 -8
- package/src/clients/ai-gateway.ts +4 -0
- package/src/clients/anthropic-messages.ts +60 -0
- package/src/clients/anthropic.ts +4 -0
- package/src/clients/aws.ts +119 -0
- package/src/clients/cerebras.ts +4 -0
- package/src/clients/clerk.ts +97 -0
- package/src/clients/cloudflare.ts +80 -0
- package/src/clients/elevenlabs.ts +38 -0
- package/src/clients/gemini.ts +89 -0
- package/src/clients/github.ts +67 -0
- package/src/clients/groq.ts +4 -0
- package/src/clients/harness.ts +101 -0
- package/src/clients/merge.ts +9 -0
- package/src/clients/openai-compatible.ts +59 -0
- package/src/clients/openai.ts +66 -0
- package/src/clients/openrouter.ts +4 -0
- package/src/clients/perplexity.ts +4 -0
- package/src/clients/planetscale.ts +98 -0
- package/src/clients/posthog.ts +132 -0
- package/src/clients/run.ts +18 -0
- package/src/clients/sentry.ts +157 -0
- package/src/clients/slack.ts +98 -0
- package/src/clients/stripe.ts +129 -0
- package/src/clients/tinybird.ts +69 -0
- package/src/clients/tunnel.ts +79 -0
- package/src/clients/upstash.ts +137 -0
- package/src/clients/volteridentity.ts +108 -0
- package/src/clients/xai.ts +25 -0
- package/src/conformance.ts +446 -0
- package/src/derive.ts +87 -16
- package/src/gate.ts +20 -14
- package/src/index.ts +5 -3
- package/src/lanes.ts +7 -0
- package/src/p3-rules.ts +0 -2
- package/src/protocol-3.ts +71 -27
- package/src/published.ts +59 -11
- package/src/spec-documents.ts +2 -1
- package/src/spec-ir-client.ts +15 -4
- package/src/spec-ir.ts +43 -5
- package/src/verdict.ts +28 -0
- package/src/walk/behavior-journey.ts +806 -0
- package/src/walk/git-client.ts +138 -0
- package/src/walk/journey-kit.ts +858 -0
- package/src/walk/shape.ts +208 -0
- package/src/walk/sse-example.ts +25 -0
- package/src/walk/vendor-fetch.ts +80 -0
- package/src/walk/walk-database.ts +97 -0
- package/src/walk/xml-view.ts +44 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GitFile } from './journey-kit.js';
|
|
2
|
+
type Fetch = (request: Request) => Promise<Response>;
|
|
3
|
+
export type GitAnswer = {
|
|
4
|
+
status: number;
|
|
5
|
+
body: unknown;
|
|
6
|
+
text: string;
|
|
7
|
+
};
|
|
8
|
+
/** `git clone`: the ref's tip (HEAD by default) and the paths of its tree. */
|
|
9
|
+
export declare function gitClone(fetch: Fetch, base: string, step: {
|
|
10
|
+
ref?: string;
|
|
11
|
+
}, headers: Record<string, string>): Promise<GitAnswer>;
|
|
12
|
+
export type GitPush = {
|
|
13
|
+
ref: string;
|
|
14
|
+
/** a path's new content, or null to delete it */
|
|
15
|
+
files?: Record<string, GitFile>;
|
|
16
|
+
message?: string;
|
|
17
|
+
author?: string;
|
|
18
|
+
/** rewrite the tip instead of building on it (`git commit --amend`), which only a force push lands */
|
|
19
|
+
amend?: boolean;
|
|
20
|
+
/** delete the ref (`git push origin :<ref>`) */
|
|
21
|
+
delete?: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** `git push`: a commit of `files` on the ref's tip (a new ref starts from HEAD's), sent with the pack of what it made.
|
|
24
|
+
* The answer is the server's report: `unpack`, the ref's `result` (`ok`, or its `ng` reason) and the pushed `sha`. */
|
|
25
|
+
export declare function gitPush(fetch: Fetch, base: string, step: GitPush, headers: Record<string, string>): Promise<GitAnswer>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// The git client a journey's git steps are: what `git clone` and `git push` send over smart HTTP (protocol v0),
|
|
2
|
+
// built on the kernel's git library, so a life reaches a pack's git plane with a client's own bytes. It keeps no
|
|
3
|
+
// working copy between steps: a push fetches the ref's tip as a clone would have, writes its files on top and
|
|
4
|
+
// sends the ref command with a pack of what it made.
|
|
5
|
+
import { MemoryBlobStore, git } from '@volter/world-core/runtime';
|
|
6
|
+
const ENC = new TextEncoder();
|
|
7
|
+
const DEC = new TextDecoder();
|
|
8
|
+
const cat = (...parts) => git.concat(...parts);
|
|
9
|
+
/** The refs a smart-HTTP advertisement names, and the World's date the server answered at. */
|
|
10
|
+
async function advertised(fetch, base, service, headers) {
|
|
11
|
+
const res = await fetch(new Request(`${base}/info/refs?service=${service}`, { headers }));
|
|
12
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
13
|
+
const refs = new Map();
|
|
14
|
+
if (res.status !== 200)
|
|
15
|
+
return { status: res.status, refs, date: 0, text: DEC.decode(bytes) };
|
|
16
|
+
const first = git.readPktLines(bytes);
|
|
17
|
+
for (const line of git.readPktLines(first.rest).lines) {
|
|
18
|
+
const [sha, name] = line.split('\0')[0].trim().split(' ');
|
|
19
|
+
if (sha && name && sha !== git.ZERO_SHA)
|
|
20
|
+
refs.set(name, sha);
|
|
21
|
+
}
|
|
22
|
+
return { status: res.status, refs, date: Date.parse(res.headers.get('date') ?? '') || 0, text: '' };
|
|
23
|
+
}
|
|
24
|
+
/** Fetch `sha` and everything it reaches into `store`, as upload-pack sends it. */
|
|
25
|
+
async function fetchInto(fetch, base, sha, store, headers) {
|
|
26
|
+
const body = cat(git.pktLine(`want ${sha} ofs-delta no-progress\n`), git.FLUSH, git.pktLine('done\n'));
|
|
27
|
+
const res = await fetch(new Request(`${base}/git-upload-pack`, { method: 'POST', headers: { ...headers, 'content-type': 'application/x-git-upload-pack-request' }, body: body }));
|
|
28
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
29
|
+
const nak = git.readPktLines(bytes.subarray(0, 8));
|
|
30
|
+
if (!nak.lines[0]?.startsWith('NAK'))
|
|
31
|
+
throw new Error(`upload-pack answered ${DEC.decode(bytes.subarray(0, 120)).trim()}`);
|
|
32
|
+
await git.readPack(bytes.subarray(8), store);
|
|
33
|
+
}
|
|
34
|
+
/** Every file under `tree`, by path. */
|
|
35
|
+
async function flatten(store, tree, prefix = '', out = new Map()) {
|
|
36
|
+
const obj = await store.read(tree);
|
|
37
|
+
for (const e of obj ? git.decodeTree(obj.payload) : []) {
|
|
38
|
+
if (e.mode === '40000')
|
|
39
|
+
await flatten(store, e.sha, `${prefix}${e.name}/`, out);
|
|
40
|
+
else
|
|
41
|
+
out.set(`${prefix}${e.name}`, { mode: e.mode, sha: e.sha });
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** The tree of `files`, nested by directory; each object it writes is pushed onto `made`. */
|
|
46
|
+
async function build(store, files, made) {
|
|
47
|
+
const here = [];
|
|
48
|
+
const dirs = new Map();
|
|
49
|
+
for (const [path, e] of files) {
|
|
50
|
+
const slash = path.indexOf('/');
|
|
51
|
+
if (slash < 0) {
|
|
52
|
+
here.push({ mode: e.mode, name: path, sha: e.sha });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const dir = path.slice(0, slash);
|
|
56
|
+
if (!dirs.has(dir))
|
|
57
|
+
dirs.set(dir, new Map());
|
|
58
|
+
dirs.get(dir).set(path.slice(slash + 1), e);
|
|
59
|
+
}
|
|
60
|
+
for (const [dir, inner] of dirs)
|
|
61
|
+
here.push({ mode: '40000', name: dir, sha: await build(store, inner, made) });
|
|
62
|
+
const sha = await store.write('tree', git.encodeTree(git.sortTreeEntries(here)));
|
|
63
|
+
made.push(sha);
|
|
64
|
+
return sha;
|
|
65
|
+
}
|
|
66
|
+
/** `git clone`: the ref's tip (HEAD by default) and the paths of its tree. */
|
|
67
|
+
export async function gitClone(fetch, base, step, headers) {
|
|
68
|
+
const ad = await advertised(fetch, base, 'git-upload-pack', headers);
|
|
69
|
+
if (ad.status !== 200)
|
|
70
|
+
return { status: ad.status, body: ad.text, text: ad.text };
|
|
71
|
+
const ref = step.ref ?? 'HEAD';
|
|
72
|
+
const sha = ad.refs.get(ref);
|
|
73
|
+
if (!sha) {
|
|
74
|
+
const text = `fatal: Remote branch ${ref} not found in upstream origin`;
|
|
75
|
+
return { status: 404, body: text, text };
|
|
76
|
+
}
|
|
77
|
+
const store = new git.GitObjectStore(new MemoryBlobStore(), 'clone');
|
|
78
|
+
await fetchInto(fetch, base, sha, store, headers);
|
|
79
|
+
const commit = git.decodeCommit((await store.read(sha)).payload);
|
|
80
|
+
const files = await flatten(store, commit.tree);
|
|
81
|
+
const body = { sha, message: commit.message.trim(), paths: [...files.keys()].sort(), refs: [...ad.refs.keys()].sort() };
|
|
82
|
+
return { status: 200, body, text: JSON.stringify(body) };
|
|
83
|
+
}
|
|
84
|
+
/** `git push`: a commit of `files` on the ref's tip (a new ref starts from HEAD's), sent with the pack of what it made.
|
|
85
|
+
* The answer is the server's report: `unpack`, the ref's `result` (`ok`, or its `ng` reason) and the pushed `sha`. */
|
|
86
|
+
export async function gitPush(fetch, base, step, headers) {
|
|
87
|
+
const ad = await advertised(fetch, base, 'git-receive-pack', headers);
|
|
88
|
+
if (ad.status !== 200)
|
|
89
|
+
return { status: ad.status, body: ad.text, text: ad.text };
|
|
90
|
+
const old = ad.refs.get(step.ref) ?? git.ZERO_SHA;
|
|
91
|
+
const store = new git.GitObjectStore(new MemoryBlobStore(), 'push');
|
|
92
|
+
let next = git.ZERO_SHA;
|
|
93
|
+
const made = [];
|
|
94
|
+
if (!step.delete) {
|
|
95
|
+
const tip = old !== git.ZERO_SHA ? old : ad.refs.get('HEAD');
|
|
96
|
+
let files = new Map();
|
|
97
|
+
let parents = [];
|
|
98
|
+
if (tip) {
|
|
99
|
+
await fetchInto(fetch, base, tip, store, headers);
|
|
100
|
+
const commit = git.decodeCommit((await store.read(tip)).payload);
|
|
101
|
+
files = await flatten(store, commit.tree);
|
|
102
|
+
parents = step.amend ? commit.parents : [tip];
|
|
103
|
+
}
|
|
104
|
+
for (const [path, content] of Object.entries(step.files ?? {})) {
|
|
105
|
+
if (content === null) {
|
|
106
|
+
files.delete(path);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (typeof content === 'object' && content.mode === '160000') {
|
|
110
|
+
files.set(path, { mode: content.mode, sha: content.sha });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const sha = await store.write('blob', ENC.encode(typeof content === 'string' ? content : content.content));
|
|
114
|
+
made.push(sha);
|
|
115
|
+
files.set(path, { mode: typeof content === 'string' ? '100644' : content.mode, sha });
|
|
116
|
+
}
|
|
117
|
+
const tree = await build(store, files, made);
|
|
118
|
+
const [name, email] = (step.author ?? 'octocat <octocat@github.com>').split(/\s*<|>/);
|
|
119
|
+
// the commit is dated by the World's clock, as the server's Date header reads it
|
|
120
|
+
const who = { name: name.trim(), email: (email ?? '').trim(), time: Math.floor(ad.date / 1000), tz: '+0000' };
|
|
121
|
+
next = await store.write('commit', git.encodeCommit({ tree, parents, author: who, committer: who, message: `${step.message ?? 'Update'}\n` }));
|
|
122
|
+
made.push(next);
|
|
123
|
+
}
|
|
124
|
+
const objects = await Promise.all([...new Set(made)].map(async (sha) => ({ ...(await store.read(sha)) })));
|
|
125
|
+
const command = git.pktLine(`${old} ${next} ${step.ref}\0report-status\n`);
|
|
126
|
+
const body = step.delete ? cat(command, git.FLUSH) : cat(command, git.FLUSH, await git.writePack(objects));
|
|
127
|
+
const res = await fetch(new Request(`${base}/git-receive-pack`, { method: 'POST', headers: { ...headers, 'content-type': 'application/x-git-receive-pack-request' }, body: body }));
|
|
128
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
129
|
+
if (res.status !== 200) {
|
|
130
|
+
const text = DEC.decode(bytes);
|
|
131
|
+
return { status: res.status, body: text, text };
|
|
132
|
+
}
|
|
133
|
+
const lines = git.readPktLines(bytes).lines.map((l) => l.trim());
|
|
134
|
+
const unpack = lines.find((l) => l.startsWith('unpack '))?.slice(7) ?? '';
|
|
135
|
+
const verdict = lines.find((l) => l.startsWith('ok ') || l.startsWith('ng '));
|
|
136
|
+
const result = verdict?.startsWith('ok ') ? 'ok' : verdict?.slice(4 + step.ref.length).trim() ?? '';
|
|
137
|
+
const answer = { unpack, result, sha: next, before: old };
|
|
138
|
+
return { status: res.status, body: answer, text: JSON.stringify(answer) };
|
|
139
|
+
}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/** A value captured from an earlier step, written `{step.field}` — resolved in paths and bodies. */
|
|
2
|
+
export type Captured = Record<string, string>;
|
|
3
|
+
export type JourneyStep = {
|
|
4
|
+
/** What a PERSON is doing, in their words — this is what a failure report reads back. */
|
|
5
|
+
name: string;
|
|
6
|
+
method: string;
|
|
7
|
+
/** `{capture}` placeholders are substituted from earlier steps. */
|
|
8
|
+
path: string;
|
|
9
|
+
/** Start HTTP without waiting; a later step with await and the same method/path checks its answer. */
|
|
10
|
+
defer?: string;
|
|
11
|
+
/** Collect a previously deferred request once, with all ordinary response checks. */
|
|
12
|
+
await?: string;
|
|
13
|
+
body?: unknown;
|
|
14
|
+
/** A body of raw bytes (a gzip'd package, an image, an audio file), base64 in the journey and sent decoded: a
|
|
15
|
+
* string body is sent as UTF-8, which no binary format survives. A step carries `body` or `bodyBase64`, never both. */
|
|
16
|
+
bodyBase64?: string;
|
|
17
|
+
/** A large raw body written short: `text` repeated to `bytes` bytes (a part of several MiB, a body over a size limit),
|
|
18
|
+
* expanded before the step is sent, signed or compared (expandFills); never beside `body`, `bodyBase64` or `form`.
|
|
19
|
+
* Inside `body`, a value `{ "$fill": { text, bytes } }` is the same string written short. */
|
|
20
|
+
bodyFill?: {
|
|
21
|
+
text: string;
|
|
22
|
+
bytes: number;
|
|
23
|
+
};
|
|
24
|
+
/** How the client encodes `body` as it sends it, after its captures are filled: `gzip` (posthog-js's `gzip-js`, a
|
|
25
|
+
* Node SDK's gzip) or `base64-form` (a form whose `data` is the body's base64). */
|
|
26
|
+
encode?: 'gzip' | 'base64-form';
|
|
27
|
+
/** An HTML form the client posts as multipart/form-data (RFC 7578; `formBody`), as a browser posts a composer with a
|
|
28
|
+
* file from the person's machine or an SDK posts a POST-policy upload: `fields`, a template whose captures fill to a
|
|
29
|
+
* JSON `[{name, value}]` list (`"{vFields}"`), sent first and in its order (every field an S3 lease named), then
|
|
30
|
+
* `parts` in the form's order, each a text
|
|
31
|
+
* field (its `value`, captures filled: a value the page showed, read off it) or a file (`filename`, its `type`, and its
|
|
32
|
+
* bytes as `base64`, as text in `value`, or a real PNG of the size given, `png`). The walk builds the body and its boundary. A step carries
|
|
33
|
+
* one of `body`, `bodyBase64` and `form`. */
|
|
34
|
+
form?: JourneyForm;
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
/** The status this step must answer. Defaults to "any 2xx". */
|
|
37
|
+
status?: number;
|
|
38
|
+
/** Values for later steps: name → a dot path into the answer, `text:<pattern>` for a value read off a page's or mail's
|
|
39
|
+
* text, `header:<name>[:<pattern>]` for a response header (a session cookie), or `view:<path>` for a path into the
|
|
40
|
+
* pack's view of the answer (`view`: a value the client decodes before it uses it). `sse:<event>:<path>` joins
|
|
41
|
+
* matching SSE string values; `sse-json:<event>:<path>` also decodes fragmented JSON tool input. */
|
|
42
|
+
capture?: Record<string, string>;
|
|
43
|
+
/** Marks this step as creating a resource: the runner applies the ECHO and PERSISTS beats,
|
|
44
|
+
* reading it back at `readBack` (a path, `{capture}` allowed) both now and at the end.
|
|
45
|
+
* `echoOn: 'read'` when the vendor's own create answer is thin by design — Jira's returns
|
|
46
|
+
* only `{id, key, self}`, so demanding the fields echo THERE would be testing a fiction; the
|
|
47
|
+
* read-back still has to carry them. */
|
|
48
|
+
creates?: {
|
|
49
|
+
readBack: string;
|
|
50
|
+
echoOn?: 'both' | 'read';
|
|
51
|
+
/** Where the created object sits in the response, when the vendor wraps it — Slack answers
|
|
52
|
+
* `{ok, channel:{…}}` on both the create and the read, so the ECHO beat looks under
|
|
53
|
+
* `channel` rather than at the envelope. Defaults to the whole body. */
|
|
54
|
+
at?: string;
|
|
55
|
+
/** fields the vendor legitimately transforms or drops */
|
|
56
|
+
echoExcept?: string[];
|
|
57
|
+
};
|
|
58
|
+
/** This write must VISIBLY CHANGE what `GET <changes>` returns (a path, `{capture}` allowed).
|
|
59
|
+
* The runner reads that path before and after the step and fails if the answer is unchanged.
|
|
60
|
+
*
|
|
61
|
+
* This is the LANDED beat, and it exists because of the worst defect class this repo has a
|
|
62
|
+
* name for: a write that answers the vendor's own success shape and stores nothing. Jira's
|
|
63
|
+
* `PUT .../issue/{key}/assignee` answered 204 — correct, per the vendor — while writing an
|
|
64
|
+
* empty field set, and the only reason the walk caught it was that a later step happened to
|
|
65
|
+
* read the field back. Declared here, every write is asked the question. */
|
|
66
|
+
changes?: string;
|
|
67
|
+
/** An OAuth 1.0a signed request (RFC 5849, HMAC-SHA1), as twitter-api-v2 signs X's: the walk builds the
|
|
68
|
+
* `Authorization: OAuth …` header over the method, the URL the step sends to (an absolute URL, the vendor's own, is
|
|
69
|
+
* what the client signs), its query and a form-encoded body. Each value may carry `{capture}`s; `token` and
|
|
70
|
+
* `tokenSecret` are the user's, `callback` the request-token leg's and `verifier` the access-token leg's. */
|
|
71
|
+
oauth1?: {
|
|
72
|
+
consumerKey: string;
|
|
73
|
+
consumerSecret: string;
|
|
74
|
+
token?: string;
|
|
75
|
+
tokenSecret?: string;
|
|
76
|
+
callback?: string;
|
|
77
|
+
verifier?: string;
|
|
78
|
+
};
|
|
79
|
+
/** A JWT the client signs itself and sends as `Authorization: Bearer` (a GitHub App's, with the private key its
|
|
80
|
+
* registration handed it): RS256 over `claims`, with the PEM `privatePem` names (a `{capture}`); claims may carry
|
|
81
|
+
* captures too. */
|
|
82
|
+
jwt?: {
|
|
83
|
+
privatePem: string;
|
|
84
|
+
claims: Record<string, unknown>;
|
|
85
|
+
};
|
|
86
|
+
/** A SigV4-signed request (an AWS SDK's): the walk adds `authorization` and `x-amz-date` (and S3's payload hash),
|
|
87
|
+
* signed over the URL it sends to, its headers and body, at the World clock's instant, with an access key a
|
|
88
|
+
* `{capture}` names. */
|
|
89
|
+
sigv4?: {
|
|
90
|
+
accessKeyId: string;
|
|
91
|
+
secretAccessKey: string;
|
|
92
|
+
region: string;
|
|
93
|
+
service: string;
|
|
94
|
+
};
|
|
95
|
+
/** A presigned URL (the step's path) signed again with an access key a `{capture}` names: its date, expiry and scope
|
|
96
|
+
* are the URL's, its credential and signature the key's (the kernel's presignSigV4). */
|
|
97
|
+
presign?: {
|
|
98
|
+
accessKeyId: string;
|
|
99
|
+
secretAccessKey: string;
|
|
100
|
+
};
|
|
101
|
+
/** A real assertion. Return true, or a string saying what was wrong. Hand-written journeys only:
|
|
102
|
+
* a derived pack's journeys are data and say what they expect in `expect`. */
|
|
103
|
+
check?: (res: {
|
|
104
|
+
status: number;
|
|
105
|
+
body: unknown;
|
|
106
|
+
}, captured: Captured) => true | string;
|
|
107
|
+
/** What the answer must hold, as data: the value at a dot path of the body, or of the response header `header`
|
|
108
|
+
* names (an answer that is a file, not JSON, says what it is in its Content-Type), equals one given, is one of
|
|
109
|
+
* several, or exists (`{capture}` placeholders allowed in `equals`). */
|
|
110
|
+
expect?: Array<{
|
|
111
|
+
path?: string;
|
|
112
|
+
header?: string;
|
|
113
|
+
equals?: unknown;
|
|
114
|
+
oneOf?: unknown[];
|
|
115
|
+
members?: unknown[];
|
|
116
|
+
exists?: boolean;
|
|
117
|
+
contains?: string;
|
|
118
|
+
notContains?: string;
|
|
119
|
+
matches?: string;
|
|
120
|
+
shape?: JsonShape;
|
|
121
|
+
}>;
|
|
122
|
+
/** The pack's named view of the answer the step's `expect` paths read (`answerViews` in the pack's index): an answer
|
|
123
|
+
* whose values the wire encodes (a psdb result's rows, base64 values sliced by per-column lengths) is asserted by
|
|
124
|
+
* what it holds. Captures read the answer as it came. */
|
|
125
|
+
view?: string;
|
|
126
|
+
/** This step sends a vendor's published example, `<operation>#<title>` (scripts/vendor-examples.ts): its answer
|
|
127
|
+
* must hold every key path of the example's answer with the same JSON type. */
|
|
128
|
+
example?: string;
|
|
129
|
+
/** Caller-chosen values of the example's named placeholders, compared literally; captures may fill them.
|
|
130
|
+
* A binding never overrides an earlier capture with a different value. */
|
|
131
|
+
exampleBindings?: Record<string, string>;
|
|
132
|
+
/** A key path of the example's answer (or `status`) the twin answers otherwise → why the vendor's example is wrong, or
|
|
133
|
+
* why the server the twin models answers otherwise where the vendor's text allows it. */
|
|
134
|
+
exampleDiffers?: Record<string, string>;
|
|
135
|
+
/** A published body field this step leaves out (a dot path; `a[].b` in each item) → why: a rendered value naming an
|
|
136
|
+
* object the World does not hold (a Git repository behind an integration). Only a field the request schema does not
|
|
137
|
+
* require may be left out (scripts/vendor-examples.ts). */
|
|
138
|
+
unsent?: Record<string, string>;
|
|
139
|
+
/** answer paths whose value equals something the setup sent without the setup deciding it (a default the vendor applies
|
|
140
|
+
* that coincides), each with the reason: `vendor-examples.ts --sweep` does not ask them asserted, and the judge reads the reason */
|
|
141
|
+
undecided?: Record<string, string>;
|
|
142
|
+
};
|
|
143
|
+
export type JsonShape = {
|
|
144
|
+
type?: string;
|
|
145
|
+
properties?: Record<string, JsonShape>;
|
|
146
|
+
required?: string[];
|
|
147
|
+
items?: JsonShape;
|
|
148
|
+
additionalProperties?: boolean;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
};
|
|
151
|
+
/** A step's form (`JourneyStep.form`): its listed fields, then its parts. */
|
|
152
|
+
export type JourneyForm = {
|
|
153
|
+
fields?: string;
|
|
154
|
+
parts: FormPart[];
|
|
155
|
+
};
|
|
156
|
+
/** A part of a step's form: a text field and its value, or a file, its bytes as base64, as text (`value`, captures
|
|
157
|
+
* filled, sent as UTF-8: a CSV, a script) or a PNG of a stated size (an uncompressed RGB gradient, as heavy as an
|
|
158
|
+
* uncompressed export of that size). */
|
|
159
|
+
export type FormPart = {
|
|
160
|
+
name: string;
|
|
161
|
+
value: string;
|
|
162
|
+
} | {
|
|
163
|
+
name: string;
|
|
164
|
+
filename: string;
|
|
165
|
+
type?: string;
|
|
166
|
+
base64?: string;
|
|
167
|
+
value?: string;
|
|
168
|
+
png?: {
|
|
169
|
+
width: number;
|
|
170
|
+
height: number;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
/** A data journey's expectations against an answer: the failures, empty when all hold. */
|
|
174
|
+
export declare function expectFailures(expect: NonNullable<JourneyStep['expect']>, body: unknown, captured: Captured, headers?: Record<string, string>, wireText?: string): string[];
|
|
175
|
+
/** A journey with its fills written out (a step's `bodyFill`, a `{ $fill }` inside a body): what is sent, signed and
|
|
176
|
+
* compared is the whole body, while the file keeps it short. A step with no fill is the journey's own object, and a
|
|
177
|
+
* journey with none is returned itself. */
|
|
178
|
+
export declare function expandFills<J extends {
|
|
179
|
+
steps: unknown[];
|
|
180
|
+
}>(journey: J): J;
|
|
181
|
+
export type Journey = {
|
|
182
|
+
vendor: string;
|
|
183
|
+
/** The session that wrote it: a verdict from this session is no verdict (score-pack.ts). */
|
|
184
|
+
author?: string;
|
|
185
|
+
/** The host the pack serves this walk on (several packs route by host). */
|
|
186
|
+
host?: string;
|
|
187
|
+
/** Headers every step carries — auth, an API version. */
|
|
188
|
+
headers?: Record<string, string>;
|
|
189
|
+
/** The instant the story begins, where it is before the World's day one (2026-01-01): an account whose story starts
|
|
190
|
+
* while an API it used still made what it no longer makes. Its waits move the clock on from there. */
|
|
191
|
+
startsAt?: string;
|
|
192
|
+
/** The World's scenario for a generative pack's turns (a file beside the life, `journeys/<name>.json`, as a World
|
|
193
|
+
* loads `handlers/<vendor>.json`): the walk loads it, so the scripted turns and the pack's scenario code are walked. */
|
|
194
|
+
scenario?: string;
|
|
195
|
+
/** The packs of the same catalog, by vendor, that the vendor calls as its own behaviour (`ctx.vendorFetch`: a sign-in
|
|
196
|
+
* trading a code at its provider): the walk loads each over the walk's own World and root, the vendor's calls reach
|
|
197
|
+
* them in process, and a step to an absolute URL a companion's host rules claim is served by it (architecture,
|
|
198
|
+
* "Journeys"). */
|
|
199
|
+
companions?: string[];
|
|
200
|
+
/** The application's answers to what the vendor asks it (`ctx.ask`, or a `ctx.vendorFetch` URL read of an
|
|
201
|
+
* application-held file: an LLM endpoint an agent was configured with, a real-time authorization endpoint): each a
|
|
202
|
+
* request it takes (its method, POST by default, and a URL prefix; the request headers it must carry,
|
|
203
|
+
* `requestHeaders`) and the answer it gives (its status, its headers and its body), used in order among those that
|
|
204
|
+
* take the request; a question none takes is unreachable, as in a sealed World. They are given to this walk's fetch
|
|
205
|
+
* alone. `bodyError` returns the headers but fails body consumption, modeling a source disconnect. */
|
|
206
|
+
application?: Array<{
|
|
207
|
+
method?: string;
|
|
208
|
+
url: string;
|
|
209
|
+
requestHeaders?: Record<string, string>;
|
|
210
|
+
status: number;
|
|
211
|
+
headers?: Record<string, string>;
|
|
212
|
+
body?: unknown;
|
|
213
|
+
bodyError?: string;
|
|
214
|
+
}>;
|
|
215
|
+
/** What this walk is about, in one sentence: read by whoever gets the failure. */
|
|
216
|
+
about: string;
|
|
217
|
+
/** What "I don't have that" looks like for this vendor. The ISOLATED beat asks an untouched
|
|
218
|
+
* world for an id it never minted and this says whether it was refused. The default is "not
|
|
219
|
+
* 2xx", which is right for REST; Slack answers HTTP 200 with `{ok:false,error:…}` for every
|
|
220
|
+
* refusal, so a pack in that family declares its own or the beat reads a refusal as a hit. */
|
|
221
|
+
refused?: (res: {
|
|
222
|
+
status: number;
|
|
223
|
+
body: unknown;
|
|
224
|
+
}) => boolean;
|
|
225
|
+
steps: Step[];
|
|
226
|
+
};
|
|
227
|
+
export type Step = JourneyStep | WaitStep | LineStep | GitStep | SocketStep | SqlStep | RespStep;
|
|
228
|
+
/** A typed request/reply stream step. `resp` is one command or a pipeline of commands, on a named connection.
|
|
229
|
+
* Captures and expectations see `{ replies, last, error }`; binary arguments use `{ base64 }`.
|
|
230
|
+
* `read` drains pushes or a blocking command's answer without sending bytes. */
|
|
231
|
+
export type RespStep = {
|
|
232
|
+
name: string;
|
|
233
|
+
connection: string;
|
|
234
|
+
connect?: Record<string, unknown>;
|
|
235
|
+
resp?: Array<string | {
|
|
236
|
+
base64: string;
|
|
237
|
+
}> | Array<Array<string | {
|
|
238
|
+
base64: string;
|
|
239
|
+
}>>;
|
|
240
|
+
read?: boolean;
|
|
241
|
+
close?: boolean;
|
|
242
|
+
pending?: boolean;
|
|
243
|
+
refused?: string;
|
|
244
|
+
capture?: Record<string, string>;
|
|
245
|
+
expect?: JourneyStep['expect'];
|
|
246
|
+
example?: string;
|
|
247
|
+
exampleDiffers?: Record<string, string>;
|
|
248
|
+
};
|
|
249
|
+
export declare const isResp: (step: Step) => step is RespStep;
|
|
250
|
+
/** A step on a byte-stream wire (a line protocol, SMTP): lines sent on a named connection, answered by the
|
|
251
|
+
* replies they drew. `connect` opens the connection, with the options the operator started the server with
|
|
252
|
+
* (the pack's `create<Name>TwinStream`); its answer is the server's greeting. The answer is the last reply
|
|
253
|
+
* (`code`, its `lines`, their `text`) and every reply the step drew (`replies`). */
|
|
254
|
+
export type LineStep = {
|
|
255
|
+
name: string;
|
|
256
|
+
on: string;
|
|
257
|
+
connect?: Record<string, unknown>;
|
|
258
|
+
send?: string | string[];
|
|
259
|
+
/** the last reply's code; any below 400 by default */
|
|
260
|
+
status?: number;
|
|
261
|
+
capture?: Record<string, string>;
|
|
262
|
+
expect?: JourneyStep['expect'];
|
|
263
|
+
/** as a request step's: this step sends a vendor's published example */
|
|
264
|
+
example?: string;
|
|
265
|
+
exampleDiffers?: Record<string, string>;
|
|
266
|
+
};
|
|
267
|
+
export declare const isLine: (step: Step) => step is LineStep;
|
|
268
|
+
/** A step on a vendor's socket (the manifest's `sockets`, the kernel's sockets.ts: Discord's Gateway): `open` opens
|
|
269
|
+
* a named connection at a path (or an absolute `ws(s)://` url, on that host: a client that dials the vendor's socket
|
|
270
|
+
* host itself), `send` sends frames on it (an object as JSON, captures filled), `close` closes it.
|
|
271
|
+
* Its answer is every frame the server sent since the connection's last step (`frames`, each parsed from JSON when
|
|
272
|
+
* it is JSON), the last of them (`last`), and how the server closed it (`closed`: its `code` and `reason`, or null);
|
|
273
|
+
* its status is the close code, or 101 while it is open. */
|
|
274
|
+
export type SocketStep = {
|
|
275
|
+
name: string;
|
|
276
|
+
socket: string;
|
|
277
|
+
open?: string;
|
|
278
|
+
/** the opening request's own headers (a key the vendor reads at the upgrade: OpenAI Realtime's `Authorization`), filled
|
|
279
|
+
* from the captures as a request's are */
|
|
280
|
+
headers?: Record<string, string>;
|
|
281
|
+
send?: unknown;
|
|
282
|
+
close?: boolean;
|
|
283
|
+
/** 101 (open) by default; a close code when the server is to close it */
|
|
284
|
+
status?: number;
|
|
285
|
+
capture?: Record<string, string>;
|
|
286
|
+
expect?: JourneyStep['expect'];
|
|
287
|
+
};
|
|
288
|
+
export declare const isSocket: (step: Step) => step is SocketStep;
|
|
289
|
+
/** A step a git client takes on the pack's smart-HTTP plane (packages/twin-standard/src/walk/git-client.ts): `clone` reads `ref`'s tip (HEAD by
|
|
290
|
+
* default) — its `sha`, `message` and tree `paths`; `push` commits `files` (a path's content, or null to delete it) on
|
|
291
|
+
* `ref`'s tip, or rewrites the tip (`amend`), or deletes the ref (`delete`), and answers the server's report — `unpack`,
|
|
292
|
+
* the ref's `result` (`ok`, or the reason it was refused) and the pushed `sha`. `repo` is `owner/name`. */
|
|
293
|
+
export type GitFile = string | null | {
|
|
294
|
+
mode: '100644' | '100755' | '120000';
|
|
295
|
+
content: string;
|
|
296
|
+
} | {
|
|
297
|
+
mode: '160000';
|
|
298
|
+
sha: string;
|
|
299
|
+
};
|
|
300
|
+
export type GitStep = {
|
|
301
|
+
name: string;
|
|
302
|
+
git: 'clone' | 'push';
|
|
303
|
+
repo: string;
|
|
304
|
+
ref?: string;
|
|
305
|
+
files?: Record<string, GitFile>;
|
|
306
|
+
message?: string;
|
|
307
|
+
author?: string;
|
|
308
|
+
amend?: boolean;
|
|
309
|
+
delete?: boolean;
|
|
310
|
+
headers?: Record<string, string>;
|
|
311
|
+
/** the HTTP status; 200 by default */
|
|
312
|
+
status?: number;
|
|
313
|
+
capture?: Record<string, string>;
|
|
314
|
+
expect?: JourneyStep['expect'];
|
|
315
|
+
};
|
|
316
|
+
export declare const isGit: (step: Step) => step is GitStep;
|
|
317
|
+
/** Time passing in the story, with no request: the World clock moves on by `wait` (`25m`, `2h`, `3d`), so a
|
|
318
|
+
* move the vendor or the clock makes (a container idling out, a code expiring) happens as it would. */
|
|
319
|
+
export type WaitStep = {
|
|
320
|
+
name: string;
|
|
321
|
+
wait: string;
|
|
322
|
+
};
|
|
323
|
+
export declare const isWait: (step: Step) => step is WaitStep;
|
|
324
|
+
/** What an application sends the World's managed database over the database's own wire, not through the vendor's API (a
|
|
325
|
+
* pack whose data plane is the World's Postgres, the descriptor's `managedDatabase`): its migrations as its deploy applies
|
|
326
|
+
* them and the queries it sends, each on the application's one connection to the walked World's database (world-core
|
|
327
|
+
* `databaseSession`), so a transaction it begins stays open into its next steps, as on its own connection. `sql` alone is
|
|
328
|
+
* one simple Query (a migration file, a `BEGIN`); with `values` it is one extended-protocol statement, the values bind
|
|
329
|
+
* parameters (a client's `query(text, values)`), captures filled in them; the SQL itself is the application's text, sent
|
|
330
|
+
* as written. Its answer is the last statement's `{ command, rows }`, each row its
|
|
331
|
+
* columns by name in Postgres's text format, which `expect` reads; a refusal fails the step with
|
|
332
|
+
* Postgres's SQLSTATE, unless the step names that SQLSTATE as its `refused` (the application's own catch of it), when
|
|
333
|
+
* its answer is `{ code, message }`. */
|
|
334
|
+
export type SqlStep = {
|
|
335
|
+
name: string;
|
|
336
|
+
sql: string;
|
|
337
|
+
values?: Array<string | null>;
|
|
338
|
+
refused?: string;
|
|
339
|
+
expect?: JourneyStep['expect'];
|
|
340
|
+
capture?: JourneyStep['capture'];
|
|
341
|
+
};
|
|
342
|
+
export declare const isSql: (step: Step) => step is SqlStep;
|
|
343
|
+
/** A wait's length in milliseconds; an unreadable one throws. */
|
|
344
|
+
export declare function waitMs(wait: string): number;
|
|
345
|
+
/** Dot-path read that tolerates arrays (`items.0.id`) and missing links. */
|
|
346
|
+
/** What a capture takes from an answer: a dot path into it, or `text:<pattern>` — the first match of a regular expression
|
|
347
|
+
* over the answer's text (a page's HTML, a mail's body), its first group if it has one. A value a page shows a person
|
|
348
|
+
* (a token shown once, a link) is read off the page this way, never assumed from the twin's sequence. `header:<name>`
|
|
349
|
+
* takes a response header, and `header:<name>:<pattern>` the first match in it: a sign-in's session cookie is kept this
|
|
350
|
+
* way and sent back on the person's later requests, as their browser does. `sse:<event>:<path>` assembles SSE values,
|
|
351
|
+
* and `sse-json:<event>:<path>` decodes the assembled JSON input. */
|
|
352
|
+
export declare function captureAt(body: unknown, spec: string, headers?: Record<string, string>, wireText?: string): unknown;
|
|
353
|
+
export declare function at(body: unknown, path: string): unknown;
|
|
354
|
+
/** Substitute `{name}` placeholders from captured values; throws on an unknown name so a typo
|
|
355
|
+
* in a journey fails loudly rather than requesting a literal `{oops}`. */
|
|
356
|
+
export declare function fill(template: string, captured: Captured, graphqlDocument?: boolean): string;
|
|
357
|
+
/** A step's `form` as the bytes a browser or an SDK's form encoder sends (RFC 7578), behind one fixed boundary so a
|
|
358
|
+
* walk sends the same bytes each time; its fields and text values filled from the captures. */
|
|
359
|
+
export declare function formBody(form: JourneyForm, captured: Captured): {
|
|
360
|
+
contentType: string;
|
|
361
|
+
bytes: Uint8Array<ArrayBuffer>;
|
|
362
|
+
};
|
|
363
|
+
/** A real PNG (8-bit RGB) of `width` × `height`, a gradient over the image, its rows stored uncompressed (zlib's stored
|
|
364
|
+
* blocks, RFC 1950/1951): the bytes an export without compression is, for a step that sends an image of a given weight. */
|
|
365
|
+
export declare function gradientPng(width: number, height: number): Uint8Array;
|
|
366
|
+
/** Build JSON values before serialization: captured text is data, including quotes and braces, not JSON source. */
|
|
367
|
+
export declare function fillJson(value: unknown, captured: Captured, typed?: Record<string, unknown>, graphqlBody?: boolean): unknown;
|
|
368
|
+
/** The ECHO beat, as a pure function so its rule is testable on its own. Every scalar and every
|
|
369
|
+
* array of scalars the request sent must appear in the response. An object-valued field is
|
|
370
|
+
* compared field by field. A field the vendor is KNOWN to transform is named in `echoExcept`;
|
|
371
|
+
* anything else that vanishes is the defect this beat exists to find. */
|
|
372
|
+
export declare function echoFailures(sent: unknown, got: unknown, except?: string[], prefix?: string): string[];
|
|
373
|
+
/** The state-simulation module of a pack, as repo-relative paths. */
|
|
374
|
+
export declare function stateModuleFiles(vendor: string, twinDir: string, fs: {
|
|
375
|
+
readdirSync: (d: string) => string[];
|
|
376
|
+
readFileSync: (p: string, e: 'utf8') => string;
|
|
377
|
+
}): string[];
|
|
378
|
+
export type StateCoverage = {
|
|
379
|
+
file: string;
|
|
380
|
+
lines: {
|
|
381
|
+
found: number;
|
|
382
|
+
hit: number;
|
|
383
|
+
};
|
|
384
|
+
functions: {
|
|
385
|
+
found: number;
|
|
386
|
+
hit: number;
|
|
387
|
+
};
|
|
388
|
+
/** Never-run line ranges, each with the declaration it sits under (nearest preceding
|
|
389
|
+
* function, arrow or method in the source), so a gap reads as "the reopen branch of
|
|
390
|
+
* updateIssue" and not as a line number. */
|
|
391
|
+
never: Array<{
|
|
392
|
+
from: number;
|
|
393
|
+
to: number;
|
|
394
|
+
fn?: string;
|
|
395
|
+
}>;
|
|
396
|
+
/** The declarations that ran: those whose first line ran (a never-run function reads 0 there). With a never-run
|
|
397
|
+
* range inside, such a declaration ran in part. */
|
|
398
|
+
ran?: string[];
|
|
399
|
+
};
|
|
400
|
+
/** Where functions live in a TypeScript source — `function name(`, `const name = (…) =>`,
|
|
401
|
+
* `const name = async (`, ` name(args) {` methods — at any depth, each with the line its
|
|
402
|
+
* block ENDS on: the first later non-blank line indented no deeper than the declaration (that
|
|
403
|
+
* line is the closer when it opens with `}`, `)` or `]`, and is included; a `)` that goes on to
|
|
404
|
+
* open a block is a multi-line parameter list and the scan continues). A one-line arrow
|
|
405
|
+
* spans one line. The declaration a line sits under is the innermost span containing it —
|
|
406
|
+
* which is what makes the names honest in a file whose handlers declare small helpers
|
|
407
|
+
* inline: a never-run branch at the bottom of `applyRepoWrite` is reported under
|
|
408
|
+
* `applyRepoWrite`, not under the one-line `commentExists` declared above it. */
|
|
409
|
+
export declare function functionIndex(source: string): Array<{
|
|
410
|
+
line: number;
|
|
411
|
+
end: number;
|
|
412
|
+
name: string;
|
|
413
|
+
}>;
|
|
414
|
+
/** Read an lcov report (bun's `--coverage-reporter=lcov`) for exactly `files`, matched by
|
|
415
|
+
* path suffix so an absolute or a cwd-relative `SF:` both resolve. Function totals come from
|
|
416
|
+
* the report (`FNF`/`FNH`); names for never-run ranges come from `FN` records when the
|
|
417
|
+
* producer writes them and otherwise from the source (`readSource`), since bun writes none.
|
|
418
|
+
* A file the report does not mention was never loaded by the walk: it is reported at 0 of
|
|
419
|
+
* 0, which is its own finding (the story never reached that module at all). */
|
|
420
|
+
export declare function stateCoverage(lcov: string, files: string[], readSource?: (file: string) => string | undefined): StateCoverage[];
|
|
421
|
+
/** A client asking whether something exists before it makes it (a CLI's `get` answering 404, then its create): the 404
|
|
422
|
+
* is the answer it branches on, not a refusal. A GET answering 404 is that probe when a later successful write of the
|
|
423
|
+
* same sitting makes its subject: at the same path, or at its collection naming the subject exactly (a query value, or
|
|
424
|
+
* a top-level string of its JSON body). */
|
|
425
|
+
export declare function existenceProbe(walked: Array<{
|
|
426
|
+
method: string;
|
|
427
|
+
path: string;
|
|
428
|
+
body: string;
|
|
429
|
+
status: number;
|
|
430
|
+
refused: boolean;
|
|
431
|
+
sitting: number;
|
|
432
|
+
}>, n: number): boolean;
|
|
433
|
+
/** The `Authorization` header an OAuth 1.0a client sends (RFC 5849 §3.5.1), signed over the request (§3.4): its protocol
|
|
434
|
+
* parameters, the URL's query and a form-encoded body. The nonce and timestamp are drawn from the request itself and
|
|
435
|
+
* the instant given, so a walk sends the same bytes each time. */
|
|
436
|
+
export declare function oauth1Authorization(o: NonNullable<JourneyStep['oauth1']>, method: string, url: string, body: string | undefined, contentType: string | undefined, at: number): string;
|
|
437
|
+
/** The `Authorization` header of a step's own JWT (`jwt`): RS256 with the PEM it names, over its claims, captures filled.
|
|
438
|
+
* A PEM is filled as text (its line breaks would not survive a JSON round trip); the claims through JSON, so a claim
|
|
439
|
+
* that is exactly one capture of a number stays a number. */
|
|
440
|
+
export declare function jwtAuthorization(o: NonNullable<JourneyStep['jwt']>, captured: Captured): string;
|
|
441
|
+
/** The headers of a step's SigV4 signature (`sigv4`), captures filled, over the URL, headers and body it sends. */
|
|
442
|
+
export declare function sigv4Headers(o: NonNullable<JourneyStep['sigv4']>, captured: Captured, method: string, url: string, headers: Record<string, string>, body: string | Uint8Array | undefined, at: number): Record<string, string>;
|
|
443
|
+
/** A step's presigned path (`presign`) signed with its key, captures filled: a header the URL names among its signed
|
|
444
|
+
* ones is signed with the value the step sends (its `headers`), as the client that presigned it bound it. */
|
|
445
|
+
export declare function presignedPath(o: NonNullable<JourneyStep['presign']>, captured: Captured, method: string, path: string, host: string, headers?: Record<string, string>): string;
|