@executablemd/runtime 0.6.0 → 0.8.0
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/esm/apis.js +287 -57
- package/esm/config.js +46 -17
- package/esm/duration.js +44 -0
- package/esm/files.js +558 -0
- package/esm/host-files.js +496 -0
- package/esm/mod.js +16 -8
- package/esm/service.js +126 -0
- package/esm/test/mod.js +2 -1
- package/esm/test/stubs.js +41 -6
- package/package.json +10 -4
- package/types/apis.d.ts +142 -24
- package/types/config.d.ts +30 -12
- package/types/duration.d.ts +18 -0
- package/types/files.d.ts +299 -0
- package/types/host-files.d.ts +80 -0
- package/types/mod.d.ts +21 -9
- package/types/service.d.ts +77 -0
- package/types/test/mod.d.ts +2 -1
- package/types/test/stubs.d.ts +12 -0
- package/esm/find-free-port.js +0 -33
- package/types/find-free-port.d.ts +0 -9
package/esm/test/stubs.js
CHANGED
|
@@ -19,13 +19,34 @@
|
|
|
19
19
|
* });
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
+
import { Stdio } from "@effectionx/process";
|
|
22
23
|
import { API } from "../apis.js";
|
|
24
|
+
import { SERVICE_HOSTNAME } from "../service.js";
|
|
25
|
+
/** Install a provider-neutral scoped service attachment stub. */
|
|
26
|
+
export function* useStubService(endpoint) {
|
|
27
|
+
if (endpoint.hostname !== SERVICE_HOSTNAME) {
|
|
28
|
+
throw new Error("stub service endpoint must use 127.0.0.1");
|
|
29
|
+
}
|
|
30
|
+
if (!Number.isInteger(endpoint.port) || endpoint.port < 1 || endpoint.port > 65_535) {
|
|
31
|
+
throw new Error("stub service endpoint port must be an integer from 1 through 65535");
|
|
32
|
+
}
|
|
33
|
+
const exact = Object.freeze({ hostname: SERVICE_HOSTNAME, port: endpoint.port });
|
|
34
|
+
yield* API.Service.around({
|
|
35
|
+
// deno-lint-ignore require-yield
|
|
36
|
+
*start() {
|
|
37
|
+
return { endpoint: exact };
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
23
41
|
/**
|
|
24
42
|
* Install an in-memory filesystem stub.
|
|
25
43
|
*
|
|
26
44
|
* - `readTextFile` returns content from the `files` map; throws ENOENT for missing keys.
|
|
27
45
|
* - `stat` returns `{ exists: true, isFile: true }` for keys in the map.
|
|
28
46
|
* - `glob` throws (not stubbed). Install `API.Fs.around()` directly if needed.
|
|
47
|
+
* - the writing half — `writeTextFile`, `ensureDir`, `rename`, `remove`, and
|
|
48
|
+
* `realpath` — is not stubbed and reaches the real filesystem. A test that
|
|
49
|
+
* exercises a document writing files wants a real temporary directory.
|
|
29
50
|
*
|
|
30
51
|
* The `files` object is captured **by reference** — mutating it between
|
|
31
52
|
* operations changes what `readTextFile`/`stat` see. This is useful for
|
|
@@ -54,15 +75,20 @@ export function* useStubFs(files) {
|
|
|
54
75
|
*
|
|
55
76
|
* Recognizes `bash -c "echo ..."` and returns the echo'd text as stdout.
|
|
56
77
|
* All other commands return the script text as stdout with exit code 0.
|
|
78
|
+
*
|
|
79
|
+
* It answers like a real child: the text is written to the stdio chain as it
|
|
80
|
+
* "arrives", so whatever encloses the call sees it the way it would see a
|
|
81
|
+
* child's, and it is retained in the outcome only when the caller asked
|
|
82
|
+
* for retention. A stub that always returned strings would let a document
|
|
83
|
+
* render output the contract says was already displayed.
|
|
57
84
|
*/
|
|
58
85
|
export function* useEchoExec() {
|
|
59
86
|
yield* API.Process.around({
|
|
60
87
|
*exec([options], _next) {
|
|
61
88
|
const script = (options.command[2] ?? "").trim();
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
return { exitCode: 0, stdout: script + "\n", stderr: "" };
|
|
89
|
+
const text = script.startsWith("echo ") ? script.slice(5) + "\n" : script + "\n";
|
|
90
|
+
yield* Stdio.operations.stdout(encoder.encode(text));
|
|
91
|
+
return retained(options, { exitCode: 0, stdout: text, stderr: "" });
|
|
66
92
|
},
|
|
67
93
|
});
|
|
68
94
|
}
|
|
@@ -73,8 +99,17 @@ export function* useEchoExec() {
|
|
|
73
99
|
*/
|
|
74
100
|
export function* useFailingExec(exitCode, stderr = "command failed") {
|
|
75
101
|
yield* API.Process.around({
|
|
76
|
-
*exec(
|
|
77
|
-
|
|
102
|
+
*exec([options], _next) {
|
|
103
|
+
yield* Stdio.operations.stderr(encoder.encode(stderr));
|
|
104
|
+
return retained(options, { exitCode, stdout: "", stderr });
|
|
78
105
|
},
|
|
79
106
|
});
|
|
80
107
|
}
|
|
108
|
+
const encoder = new TextEncoder();
|
|
109
|
+
/** What the caller asked to keep, so a stub cannot retain more than a call would. */
|
|
110
|
+
function retained(options, outcome) {
|
|
111
|
+
if (options.retain === false) {
|
|
112
|
+
return { exitCode: outcome.exitCode, stdout: undefined, stderr: undefined };
|
|
113
|
+
}
|
|
114
|
+
return outcome;
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@executablemd/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Runtime host APIs for executable.md documents.",
|
|
5
5
|
"homepage": "https://executable.md",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"default": "./esm/mod.js"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
|
+
"./files": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./types/files.d.ts",
|
|
26
|
+
"default": "./esm/files.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
23
29
|
"./test": {
|
|
24
30
|
"import": {
|
|
25
31
|
"types": "./types/test/mod.d.ts",
|
|
@@ -30,11 +36,11 @@
|
|
|
30
36
|
"scripts": {},
|
|
31
37
|
"dependencies": {
|
|
32
38
|
"@effectionx/context-api": "0.6.0",
|
|
33
|
-
"@effectionx/fetch": "0.2.
|
|
39
|
+
"@effectionx/fetch": "0.2.1",
|
|
34
40
|
"@effectionx/fs": "0.3.0",
|
|
35
|
-
"@effectionx/node": "0.2.4",
|
|
36
41
|
"@effectionx/process": "0.8.1",
|
|
37
|
-
"effection": "4.1.0
|
|
42
|
+
"effection": "4.1.0",
|
|
43
|
+
"@effectionx/node": "0.2.4"
|
|
38
44
|
},
|
|
39
45
|
"_generatedBy": "dnt@dev"
|
|
40
46
|
}
|
package/types/apis.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime Context APIs — platform I/O operations with pluggable middleware.
|
|
3
3
|
*
|
|
4
|
-
* Five
|
|
4
|
+
* Five host-backed domain APIs plus the provider-neutral Service Api, built on
|
|
5
|
+
* `@effectionx/context-api`.
|
|
5
6
|
* Each API provides default Node.js implementations. Use `.around()` to
|
|
6
7
|
* install middleware (mocking, instrumentation, sandboxing) scoped to the
|
|
7
8
|
* current Effection scope.
|
|
@@ -25,17 +26,29 @@
|
|
|
25
26
|
* });
|
|
26
27
|
* ```
|
|
27
28
|
*
|
|
28
|
-
* ## Why
|
|
29
|
+
* ## Why separate APIs?
|
|
29
30
|
*
|
|
30
31
|
* - **Process** — subprocess lifecycle has its own cancellation semantics
|
|
31
32
|
* (killing processes on scope teardown). Middleware targets exec only.
|
|
32
|
-
* - **Fs** —
|
|
33
|
-
*
|
|
33
|
+
* - **Fs** — the low-level host file surface: reading, writing, and inspecting
|
|
34
|
+
* paths the engine itself resolves, for component lookup, replay guards, and
|
|
35
|
+
* the root document. It is the host adapter's own dependency, not the
|
|
36
|
+
* boundary a document's paths cross.
|
|
37
|
+
* - **Files** — document filesystem access, in whole semantic operations
|
|
38
|
+
* (`files.ts`). `<File>`, `<Glob>`, and `<TempDir>` speak only this Api, so
|
|
39
|
+
* the same document means the same thing whether its paths resolve in the
|
|
40
|
+
* caller's filesystem or in a run-owned logical one. Its terminal handler
|
|
41
|
+
* throws: an uninstalled provider must not silently reach the host.
|
|
34
42
|
* - **Fetch** — HTTP has distinct timeout/body/abort semantics. Merging
|
|
35
43
|
* with Fs or Process would blur cancellation boundaries.
|
|
36
|
-
* - **Env** —
|
|
37
|
-
*
|
|
38
|
-
*
|
|
44
|
+
* - **Env** — the host itself: metadata (env vars, platform) plus the two
|
|
45
|
+
* capabilities only the entrypoint can supply, `command` (how to re-invoke
|
|
46
|
+
* this xmd) and `compile` (how this host loads a generated module). Tests
|
|
47
|
+
* use `.around()` to mock platform/env for deterministic replay; an
|
|
48
|
+
* entrypoint installs its `command` and `compile` with `{ at: "min" }` so
|
|
49
|
+
* ordinary middleware can wrap them.
|
|
50
|
+
* - **Service** — scoped service attachment. Its terminal handler requires an
|
|
51
|
+
* explicit host provider and never detects or imports a runtime.
|
|
39
52
|
*
|
|
40
53
|
* ## Middleware
|
|
41
54
|
*
|
|
@@ -54,10 +67,13 @@
|
|
|
54
67
|
* ## Test stubs
|
|
55
68
|
*
|
|
56
69
|
* Common stubs are provided by `@executablemd/runtime/test`:
|
|
57
|
-
* `useStubFs(files)`, `useEchoExec()`, `useFailingExec(code, stderr)
|
|
70
|
+
* `useStubFs(files)`, `useEchoExec()`, `useFailingExec(code, stderr)`,
|
|
71
|
+
* `useStubService(endpoint)`.
|
|
58
72
|
*/
|
|
59
73
|
import { type Api } from "@effectionx/context-api";
|
|
60
74
|
import type { Operation } from "effection";
|
|
75
|
+
import { Files } from "./files.js";
|
|
76
|
+
import { Service } from "./service.js";
|
|
61
77
|
/**
|
|
62
78
|
* Result of a `stat` call.
|
|
63
79
|
*
|
|
@@ -90,21 +106,54 @@ export interface RuntimeFetchResponse {
|
|
|
90
106
|
/** Read the response body as text. */
|
|
91
107
|
text(): Operation<string>;
|
|
92
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* What a finished child process reports.
|
|
111
|
+
*
|
|
112
|
+
* `stdout` and `stderr` are the text a caller asked to be retained. A caller
|
|
113
|
+
* that asked for none gets `undefined` rather than an empty string: nothing was
|
|
114
|
+
* accumulated, and "no output" and "not retained" are different answers.
|
|
115
|
+
*/
|
|
116
|
+
export interface ProcessOutcome {
|
|
117
|
+
exitCode: number;
|
|
118
|
+
stdout: string | undefined;
|
|
119
|
+
stderr: string | undefined;
|
|
120
|
+
}
|
|
121
|
+
export interface ProcessExecOptions {
|
|
122
|
+
command: string[];
|
|
123
|
+
cwd?: string;
|
|
124
|
+
env?: Record<string, string>;
|
|
125
|
+
timeout?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Retain the child's output in the outcome. Default `true`.
|
|
128
|
+
*
|
|
129
|
+
* Retention is the caller's explicit choice, never an inference: a run that
|
|
130
|
+
* keeps no diagnostic record asks for `false` and the text is forwarded to
|
|
131
|
+
* whatever is displaying it without ever being accumulated here.
|
|
132
|
+
*/
|
|
133
|
+
retain?: boolean;
|
|
134
|
+
}
|
|
93
135
|
interface ProcessHandler {
|
|
94
|
-
exec(options:
|
|
95
|
-
command: string[];
|
|
96
|
-
cwd?: string;
|
|
97
|
-
env?: Record<string, string>;
|
|
98
|
-
timeout?: number;
|
|
99
|
-
}): Operation<{
|
|
100
|
-
exitCode: number;
|
|
101
|
-
stdout: string;
|
|
102
|
-
stderr: string;
|
|
103
|
-
}>;
|
|
136
|
+
exec(options: ProcessExecOptions): Operation<ProcessOutcome>;
|
|
104
137
|
}
|
|
105
138
|
interface FsHandler {
|
|
106
139
|
readTextFile(path: string): Operation<string>;
|
|
107
140
|
stat(path: string): Operation<StatResult>;
|
|
141
|
+
/**
|
|
142
|
+
* Files and symbolic links beneath `root` whose path relative to it matches
|
|
143
|
+
* `patterns` and matches none of `exclude`. Paths come back relative and
|
|
144
|
+
* POSIX-separated, which is what both pattern lists are matched against, so a
|
|
145
|
+
* caller's patterns mean the same thing on every platform.
|
|
146
|
+
*
|
|
147
|
+
* Exclusion is per candidate: an entry is dropped when its own relative path
|
|
148
|
+
* matches. Directories are not candidates and are not reported, so an
|
|
149
|
+
* exclusion matching a directory does not remove what is beneath it — only a
|
|
150
|
+
* pattern ending in `/**`, which provably covers every descendant, lets the
|
|
151
|
+
* subtree be skipped rather than walked and filtered.
|
|
152
|
+
*
|
|
153
|
+
* Symbolic links are reported but never followed: a link's own path can
|
|
154
|
+
* match, and a link to a directory is not descended into. Traversal
|
|
155
|
+
* therefore stays inside `root` and cannot cycle.
|
|
156
|
+
*/
|
|
108
157
|
glob(options: {
|
|
109
158
|
patterns: string[];
|
|
110
159
|
root: string;
|
|
@@ -113,6 +162,20 @@ interface FsHandler {
|
|
|
113
162
|
path: string;
|
|
114
163
|
isFile: boolean;
|
|
115
164
|
}>>;
|
|
165
|
+
writeTextFile(path: string, content: string): Operation<void>;
|
|
166
|
+
ensureDir(path: string): Operation<void>;
|
|
167
|
+
rename(from: string, to: string): Operation<void>;
|
|
168
|
+
remove(path: string, options?: {
|
|
169
|
+
recursive?: boolean;
|
|
170
|
+
force?: boolean;
|
|
171
|
+
}): Operation<void>;
|
|
172
|
+
/**
|
|
173
|
+
* The canonical path, with every symlink resolved, or `undefined` when the
|
|
174
|
+
* path does not exist. Like `stat`, "it isn't there" is an answer rather
|
|
175
|
+
* than a failure — a caller resolving a path it is about to create asks
|
|
176
|
+
* about ancestors that may legitimately be missing.
|
|
177
|
+
*/
|
|
178
|
+
realpath(path: string): Operation<string | undefined>;
|
|
116
179
|
}
|
|
117
180
|
interface FetchHandler {
|
|
118
181
|
fetch(input: string, init?: {
|
|
@@ -122,6 +185,12 @@ interface FetchHandler {
|
|
|
122
185
|
timeout?: number;
|
|
123
186
|
}): Operation<RuntimeFetchResponse>;
|
|
124
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* A compiled eval block accepts the document binding environment and returns
|
|
190
|
+
* an Operation. Current compilers implement it with generated `function*`
|
|
191
|
+
* modules, but callers do not depend on that representation.
|
|
192
|
+
*/
|
|
193
|
+
export type EvalBlock = (env: Record<string, unknown>) => Operation<unknown>;
|
|
125
194
|
interface EnvHandler {
|
|
126
195
|
cwd(): Operation<string>;
|
|
127
196
|
env(name: string): Operation<string | undefined>;
|
|
@@ -129,26 +198,75 @@ interface EnvHandler {
|
|
|
129
198
|
os: string;
|
|
130
199
|
arch: string;
|
|
131
200
|
}>;
|
|
132
|
-
|
|
133
|
-
interface CompilerHandler {
|
|
201
|
+
command(args?: string[]): Operation<string[]>;
|
|
134
202
|
compile(source: string, options?: {
|
|
135
203
|
imports: string[];
|
|
136
|
-
}): Operation<
|
|
204
|
+
}): Operation<EvalBlock>;
|
|
137
205
|
}
|
|
138
206
|
export declare const API: {
|
|
139
207
|
Process: Api<ProcessHandler>;
|
|
140
208
|
Fs: Api<FsHandler>;
|
|
209
|
+
Files: typeof Files;
|
|
141
210
|
Fetch: Api<FetchHandler>;
|
|
142
211
|
Env: Api<EnvHandler>;
|
|
143
|
-
|
|
212
|
+
Service: typeof Service;
|
|
144
213
|
};
|
|
145
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Run a child process.
|
|
216
|
+
*
|
|
217
|
+
* A caller that says nothing about retention keeps the output, which is what
|
|
218
|
+
* every caller with something to read wants and what callers have always had.
|
|
219
|
+
* Asking for `retain: false` keeps the exit status alone, and the overloads say
|
|
220
|
+
* so: there is no string to read on that path, and the type refuses to pretend
|
|
221
|
+
* otherwise. Core, which decides retention per block, calls the Api operation
|
|
222
|
+
* directly and handles both.
|
|
223
|
+
*/
|
|
224
|
+
export declare function exec(options: ProcessExecOptions & {
|
|
225
|
+
retain: false;
|
|
226
|
+
}): Operation<{
|
|
227
|
+
exitCode: number;
|
|
228
|
+
stdout: undefined;
|
|
229
|
+
stderr: undefined;
|
|
230
|
+
}>;
|
|
231
|
+
export declare function exec(options: ProcessExecOptions & {
|
|
232
|
+
retain?: true;
|
|
233
|
+
}): Operation<{
|
|
234
|
+
exitCode: number;
|
|
235
|
+
stdout: string;
|
|
236
|
+
stderr: string;
|
|
237
|
+
}>;
|
|
146
238
|
export declare const readTextFile: typeof API.Fs.operations.readTextFile;
|
|
147
239
|
export declare const stat: typeof API.Fs.operations.stat;
|
|
148
240
|
export declare const glob: typeof API.Fs.operations.glob;
|
|
241
|
+
export declare const writeTextFile: typeof API.Fs.operations.writeTextFile;
|
|
242
|
+
export declare const ensureDir: typeof API.Fs.operations.ensureDir;
|
|
243
|
+
export declare const rename: typeof API.Fs.operations.rename;
|
|
244
|
+
export declare const remove: typeof API.Fs.operations.remove;
|
|
245
|
+
export declare const realpath: typeof API.Fs.operations.realpath;
|
|
149
246
|
export declare const fetch: typeof API.Fetch.operations.fetch;
|
|
150
247
|
export declare const env: typeof API.Env.operations.env;
|
|
151
248
|
export declare const cwd: typeof API.Env.operations.cwd;
|
|
152
249
|
export declare const platform: typeof API.Env.operations.platform;
|
|
153
|
-
export declare const
|
|
250
|
+
export declare const command: typeof API.Env.operations.command;
|
|
251
|
+
export declare const compile: typeof API.Env.operations.compile;
|
|
252
|
+
/**
|
|
253
|
+
* Discard the standard output of subprocesses started in this scope.
|
|
254
|
+
*
|
|
255
|
+
* For a caller whose subprocess output is an *answer* rather than something to
|
|
256
|
+
* show: a command whose stdout is parsed and returned would otherwise also
|
|
257
|
+
* print itself into whatever the process was rendering. `stderr` is left alone,
|
|
258
|
+
* because that is where a failing command explains itself and a diagnostic is
|
|
259
|
+
* worth seeing.
|
|
260
|
+
*
|
|
261
|
+
* It lives here because reaching the process Api's stdio directly is host
|
|
262
|
+
* behavior, and modules held to the runtime-neutral boundary may not import a
|
|
263
|
+
* host process module of their own.
|
|
264
|
+
*
|
|
265
|
+
* Installed at the display boundary, where the host's own writer sits: not
|
|
266
|
+
* showing something and not knowing it are different, and a caller that asked
|
|
267
|
+
* for the answer must still be given it. Anything upstream — this adapter's
|
|
268
|
+
* retention, a document's capture, a run's record — reads the bytes first and
|
|
269
|
+
* only the host is left out.
|
|
270
|
+
*/
|
|
271
|
+
export declare function useQuietProcessOutput(): Operation<void>;
|
|
154
272
|
export {};
|
package/types/config.d.ts
CHANGED
|
@@ -1,24 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config Api — shared execution configuration with pluggable middleware.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* timeout
|
|
4
|
+
* Three timeouts, three owners, no defaults:
|
|
5
|
+
*
|
|
6
|
+
* - `timeout` is the deadline for the whole run — preparation and execution
|
|
7
|
+
* together — and only the outer run boundary consumes it.
|
|
8
|
+
* - `timeoutExec` is what each exec block gets, and only exec blocks and the
|
|
9
|
+
* built-in `timeout` modifier consume it.
|
|
10
|
+
* - `timeoutFetch` is what each Fetch gets, and only Fetch consumes it.
|
|
11
|
+
*
|
|
12
|
+
* `undefined` means no timeout, and it is what every field starts as. An
|
|
13
|
+
* operation nobody bounded runs until it finishes or the run's own deadline
|
|
14
|
+
* cancels it; a general "shared timeout" that quietly bounded processes,
|
|
15
|
+
* requests, prompts, and services alike is what this replaces. Override a
|
|
16
|
+
* field for a scope with:
|
|
7
17
|
*
|
|
8
18
|
* ```typescript
|
|
9
|
-
* yield* Config.around({
|
|
19
|
+
* yield* Config.around({ timeoutExec: () => 30_000 }, { at: "min" });
|
|
10
20
|
* ```
|
|
21
|
+
*
|
|
22
|
+
* Installing at `min` is what lets a nested override win: a block's own
|
|
23
|
+
* `timeout=` outranks the value the command line established for the run.
|
|
24
|
+
* Omitting a field inherits the enclosing value rather than clearing it.
|
|
11
25
|
*/
|
|
12
26
|
import { type Api } from "@effectionx/context-api";
|
|
13
27
|
import type { Operation } from "effection";
|
|
14
28
|
export interface ConfigApi {
|
|
15
|
-
/**
|
|
16
|
-
timeout: number;
|
|
29
|
+
/** Deadline for the entire run, in milliseconds; undefined for none. */
|
|
30
|
+
timeout: number | undefined;
|
|
31
|
+
/** Default timeout for each exec block, in milliseconds; undefined for none. */
|
|
32
|
+
timeoutExec: number | undefined;
|
|
33
|
+
/** Default timeout for each Fetch, in milliseconds; undefined for none. */
|
|
34
|
+
timeoutFetch: number | undefined;
|
|
17
35
|
}
|
|
18
36
|
export declare const Config: Api<ConfigApi>;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
24
|
-
export declare const
|
|
37
|
+
/** The validated run deadline. Read by the run boundary and nothing else. */
|
|
38
|
+
export declare const timeout: Operation<number | undefined>;
|
|
39
|
+
/** The validated default timeout for an exec block. */
|
|
40
|
+
export declare const timeoutExec: Operation<number | undefined>;
|
|
41
|
+
/** The validated default timeout for a Fetch. */
|
|
42
|
+
export declare const timeoutFetch: Operation<number | undefined>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The duration grammar, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Every timeout a caller or a document writes is spelled the same way: the
|
|
5
|
+
* three CLI options, and the `timeout=` modifier a block declares. A duration
|
|
6
|
+
* is a positive whole number with a unit — `500ms`, `30s`, `5min`, `20min` —
|
|
7
|
+
* or bare digits, which are milliseconds.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here substitutes a value. An empty, zero, negative, or malformed
|
|
10
|
+
* duration is refused where it was written, because the alternative is a run
|
|
11
|
+
* bounded by a number nobody asked for.
|
|
12
|
+
*/
|
|
13
|
+
/** Milliseconds, or `undefined` when `text` is not a duration. */
|
|
14
|
+
export declare function asDuration(text: string): number | undefined;
|
|
15
|
+
/** What a rejected duration says, with `label` naming where it was written. */
|
|
16
|
+
export declare function durationError(label: string, text: string): Error;
|
|
17
|
+
/** Milliseconds. Throws when `text` is not a duration this grammar accepts. */
|
|
18
|
+
export declare function parseDuration(text: string, label: string): number;
|