@promigence/sdk 0.1.0-beta.14 → 0.1.0-beta.16
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/caller.js +1 -1
- package/dist/cjs/caller.js +1 -1
- package/dist/cjs/client.d.ts +134 -23
- package/dist/cjs/client.js +202 -80
- package/dist/cjs/command-errors.d.ts +1 -0
- package/dist/cjs/command-errors.js +2 -0
- package/dist/cjs/context.d.ts +19 -4
- package/dist/cjs/context.js +77 -5
- package/dist/cjs/core.d.ts +32 -4
- package/dist/cjs/core.js +30 -5
- package/dist/cjs/http2.d.ts +8 -0
- package/dist/cjs/http2.js +22 -1
- package/dist/cjs/models.gen.d.ts +663 -43
- package/dist/cjs/pty.d.ts +11 -14
- package/dist/cjs/pty.js +15 -12
- package/dist/cjs/sandbox.d.ts +26 -8
- package/dist/cjs/sandbox.js +28 -4
- package/dist/cjs/transport-worker.js +10 -1
- package/dist/client.d.ts +134 -23
- package/dist/client.js +201 -77
- package/dist/command-errors.d.ts +1 -0
- package/dist/command-errors.js +3 -1
- package/dist/context.d.ts +19 -4
- package/dist/context.js +74 -4
- package/dist/core.d.ts +32 -4
- package/dist/core.js +26 -4
- package/dist/http2.d.ts +8 -0
- package/dist/http2.js +22 -1
- package/dist/models.gen.d.ts +663 -43
- package/dist/pty.d.ts +11 -14
- package/dist/pty.js +13 -11
- package/dist/sandbox.d.ts +26 -8
- package/dist/sandbox.js +28 -4
- package/dist/transport-worker.js +10 -1
- package/package.json +1 -1
package/dist/cjs/context.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CREDENTIAL_CHECK_NOTE = exports.CONTEXT_SKIP = exports.CONTEXT_MAX_BYTES = exports.CONTEXT_TAR_PREFIX = void 0;
|
|
3
|
+
exports.CREDENTIAL_CHECK_NOTE = exports.CONTENT_CHECK_MAX_BYTES = exports.CONTEXT_SKIP = exports.CONTEXT_MAX_BYTES = exports.CONTEXT_TAR_PREFIX = void 0;
|
|
4
4
|
exports.parseDockerignore = parseDockerignore;
|
|
5
5
|
exports.dockerignoreMatcher = dockerignoreMatcher;
|
|
6
6
|
exports.secretLike = secretLike;
|
|
7
|
+
exports.clearedByContent = clearedByContent;
|
|
8
|
+
exports.secretLikeFile = secretLikeFile;
|
|
7
9
|
exports.credentialRefusalMessage = credentialRefusalMessage;
|
|
8
10
|
exports.listContext = listContext;
|
|
9
11
|
exports.packContext = packContext;
|
|
@@ -397,6 +399,74 @@ function secretLike(path) {
|
|
|
397
399
|
return false;
|
|
398
400
|
return SECRET_NAMES.some((re) => re.test(name));
|
|
399
401
|
}
|
|
402
|
+
exports.CONTENT_CHECK_MAX_BYTES = 1024 * 1024;
|
|
403
|
+
const PUBLIC_PEM = new Set([
|
|
404
|
+
"CERTIFICATE", "TRUSTED CERTIFICATE", "X509 CERTIFICATE", "X509 CRL", "CERTIFICATE REQUEST",
|
|
405
|
+
"NEW CERTIFICATE REQUEST", "PUBLIC KEY", "RSA PUBLIC KEY", "PKCS7", "CMS", "DH PARAMETERS",
|
|
406
|
+
"X9.42 DH PARAMETERS", "EC PARAMETERS",
|
|
407
|
+
]);
|
|
408
|
+
function publicPem(text) {
|
|
409
|
+
if (/private[\s_-]*key/i.test(text))
|
|
410
|
+
return false;
|
|
411
|
+
const types = [...text.matchAll(/-----BEGIN ([^\r\n-]+)-----/g)].map((m) => m[1].trim().toUpperCase());
|
|
412
|
+
if (types.length === 0 || !types.every((t) => PUBLIC_PEM.has(t)))
|
|
413
|
+
return false;
|
|
414
|
+
const outside = text.replace(/-----BEGIN ([^\r\n-]+)-----[\s\S]*?-----END \1-----/g, "");
|
|
415
|
+
if (/-----(BEGIN|END) /.test(outside))
|
|
416
|
+
return false;
|
|
417
|
+
return !outside.split(/\r?\n/).some((l) => /^(?=.*[A-Za-z0-9+/])[A-Za-z0-9+/=]{40,}$/.test(l.trim()));
|
|
418
|
+
}
|
|
419
|
+
const NPMRC_CREDENTIAL = /_auth|token|passw|secret|credential|certfile|keyfile|otp|username/i;
|
|
420
|
+
function npmrcWithoutCredentials(text) {
|
|
421
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
422
|
+
const line = raw.trim().replace(/^[;#]+\s*/, "");
|
|
423
|
+
if (!line)
|
|
424
|
+
continue;
|
|
425
|
+
if (NPMRC_CREDENTIAL.test(line) || /:\/\/[^\s/]*@/.test(line))
|
|
426
|
+
return false;
|
|
427
|
+
const eq = line.indexOf("=");
|
|
428
|
+
const key = (eq < 0 ? line : line.slice(0, eq)).trim();
|
|
429
|
+
const own = key.startsWith("//") ? key.slice(key.lastIndexOf(":") + 1) : key;
|
|
430
|
+
if (/^(key|cert|email)$/i.test(own.trim()))
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
function clearedByContent(path, content) {
|
|
436
|
+
const name = path.split("/").filter(Boolean).pop() ?? "";
|
|
437
|
+
let text;
|
|
438
|
+
if (typeof content === "string") {
|
|
439
|
+
text = content;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
try {
|
|
443
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (text.includes("\u0000"))
|
|
450
|
+
return false;
|
|
451
|
+
if (/\.pem$/i.test(name))
|
|
452
|
+
return publicPem(text);
|
|
453
|
+
if (/^\.npmrc$/i.test(name))
|
|
454
|
+
return npmrcWithoutCredentials(text);
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
function secretLikeFile(rel, abs) {
|
|
458
|
+
if (!secretLike(rel))
|
|
459
|
+
return false;
|
|
460
|
+
try {
|
|
461
|
+
const st = (0, node_fs_1.lstatSync)(abs);
|
|
462
|
+
if (!st.isFile() || st.size > exports.CONTENT_CHECK_MAX_BYTES)
|
|
463
|
+
return true;
|
|
464
|
+
return !clearedByContent(rel, (0, node_fs_1.readFileSync)(abs));
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
400
470
|
function namedList(items) {
|
|
401
471
|
const more = items.length > NAMED ? ` and ${items.length - NAMED} more` : "";
|
|
402
472
|
return items.slice(0, NAMED).join(", ") + more;
|
|
@@ -405,7 +475,7 @@ function credentialRefusalMessage(hits) {
|
|
|
405
475
|
const one = hits.length === 1;
|
|
406
476
|
return `${hits.length} file${one ? " looks" : "s look"} like ${one ? "a credential" : "credentials"} and ${one ? "was" : "were"} not uploaded: ${namedList(hits)}; nothing was created`;
|
|
407
477
|
}
|
|
408
|
-
exports.CREDENTIAL_CHECK_NOTE = "The check goes by file name and is best-effort: it
|
|
478
|
+
exports.CREDENTIAL_CHECK_NOTE = "The check goes by file name and is best-effort: it reads only a .pem (one with no private key passes) and an .npmrc (one with no token or password passes), so look over what you send.";
|
|
409
479
|
function insidePath(p) {
|
|
410
480
|
return goClean(p.split(node_path_1.sep).join("/").replace(/^\.\/+/, ""));
|
|
411
481
|
}
|
|
@@ -505,7 +575,7 @@ function collect(dir, opts) {
|
|
|
505
575
|
if (special.length > 0) {
|
|
506
576
|
notes.push(`Left out ${namedList(special)}: a socket, device or pipe; a build context holds only files, directories and links.`);
|
|
507
577
|
}
|
|
508
|
-
const secrets = entries.filter((e) => e.kind === "file" &&
|
|
578
|
+
const secrets = entries.filter((e) => e.kind === "file" && secretLikeFile(e.rel, e.abs)).map((e) => e.rel);
|
|
509
579
|
return { entries, bytes, notes, secrets, ignore };
|
|
510
580
|
}
|
|
511
581
|
function listContext(dir, opts = {}) {
|
|
@@ -528,8 +598,10 @@ function packContext(dir, opts = {}) {
|
|
|
528
598
|
const ig = c.ignore?.file;
|
|
529
599
|
throw new core_js_1.InvalidArgumentError(`the build context is over ${max} bytes${ig ? ` even without what its ${ig} excludes` : ""}`, `${ig ? `Exclude more in ${ig}` : "Add a .dockerignore"}, point --context at a smaller directory, or pass a git URL pinned to a full 40-character sha.`);
|
|
530
600
|
}
|
|
531
|
-
|
|
532
|
-
|
|
601
|
+
const allowed = new Set((opts.allowSecretFiles ?? []).map(insidePath));
|
|
602
|
+
const refused = c.secrets.filter((s) => !allowed.has(s));
|
|
603
|
+
if (refused.length > 0 && !opts.allowSecrets) {
|
|
604
|
+
throw new core_js_1.InvalidArgumentError(credentialRefusalMessage(refused), `A snapshot keeps every file it holds, and every sandbox started from it has them. Exclude them in ${c.ignore?.file ?? ".dockerignore"}, pass the values as secrets, or name each that is not a real credential in { allowSecretFiles: [...] } ({ allowSecrets: true } for all of them). ${exports.CREDENTIAL_CHECK_NOTE}`);
|
|
533
605
|
}
|
|
534
606
|
if (c.entries.length === 0) {
|
|
535
607
|
throw new core_js_1.InvalidArgumentError(`the build context ${JSON.stringify(dir)} is empty`, "Point --context at the directory holding your Dockerfile.");
|
package/dist/cjs/core.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/** Promigence SDK — constants, error hierarchy and shared types. */
|
|
2
|
-
export declare const VERSION = "0.1.0-beta.
|
|
2
|
+
export declare const VERSION = "0.1.0-beta.16";
|
|
3
3
|
export declare const DOCS_URL = "https://promigence.ai";
|
|
4
4
|
/** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
|
|
5
5
|
export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
6
|
+
/** The row of `code` in the error reference. */
|
|
7
|
+
export declare function errorsDocsFor(code: string): string;
|
|
6
8
|
export declare const DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
|
7
9
|
/** Where to write about a key. A key is self-serve: `promigence login` signs up or in and stores one. */
|
|
8
10
|
export declare const KEY_CONTACT = "support@promigence.ai";
|
|
@@ -43,8 +45,8 @@ export interface PromigenceErrorInit {
|
|
|
43
45
|
/** The API error's `retryable`: nothing ran and the same request, sent again as is, can succeed. */
|
|
44
46
|
retryable?: boolean;
|
|
45
47
|
}
|
|
46
|
-
/** What a caller reads instead: a fact about
|
|
47
|
-
export declare const PREVIEWS_UNAVAILABLE = "preview URLs are not
|
|
48
|
+
/** What a caller reads instead: a fact about their account, not a mistake of theirs. */
|
|
49
|
+
export declare const PREVIEWS_UNAVAILABLE = "preview URLs are not turned on for this account";
|
|
48
50
|
export declare function previewsFix(sandboxId?: string, port?: string | number): string;
|
|
49
51
|
/**
|
|
50
52
|
* A `fix` that opens by repeating the message says only what it adds: the API's `usage` fix is
|
|
@@ -97,7 +99,13 @@ export declare class PromigenceError extends Error {
|
|
|
97
99
|
guide: string;
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Bad arguments (e.g. an unknown template, a missing spend cap). exit 2
|
|
104
|
+
*
|
|
105
|
+
* `code` says who refused: `invalid_argument` is the SDK, before anything was sent; `usage` is the
|
|
106
|
+
* API, refusing what it was sent. Nothing ran and nothing was billed either way, so catch this
|
|
107
|
+
* class (or exit 2) to handle both.
|
|
108
|
+
*/
|
|
101
109
|
export declare class InvalidArgumentError extends PromigenceError {
|
|
102
110
|
/**
|
|
103
111
|
* `code` defaults to `invalid_argument` — what the SDK raises for a bad argument it catches
|
|
@@ -122,6 +130,26 @@ export declare function previewsOff(message: string): boolean;
|
|
|
122
130
|
export declare class PreviewsUnavailableError extends InvalidArgumentError {
|
|
123
131
|
constructor(sandboxId?: string, port?: string | number);
|
|
124
132
|
}
|
|
133
|
+
/** The sandbox and its state when an API message is the "has ended" refusal, else `undefined`. */
|
|
134
|
+
export declare function sandboxEndedIn(message: string): {
|
|
135
|
+
sandboxId: string;
|
|
136
|
+
state: string;
|
|
137
|
+
} | undefined;
|
|
138
|
+
/** What a caller of this SDK does about an ended sandbox. */
|
|
139
|
+
export declare const SANDBOX_ENDED_FIX = "Start a new sandbox: Sandbox.create(). This one's getInfo() says how it ended (`outcome`).";
|
|
140
|
+
/**
|
|
141
|
+
* A call to a sandbox that has ended (`state` `ended` or `failed`): it does not come back. Still an
|
|
142
|
+
* `InvalidArgumentError` with code `usage` and exit 2, as the API answers it, so a catch written
|
|
143
|
+
* against earlier releases still catches it; catch this class to tell an ended sandbox from a bad
|
|
144
|
+
* argument. `sandboxId` and `state` name the sandbox and how it stands.
|
|
145
|
+
*/
|
|
146
|
+
export declare class SandboxEndedError extends InvalidArgumentError {
|
|
147
|
+
/** The sandbox that has ended. */
|
|
148
|
+
readonly sandboxId: string;
|
|
149
|
+
/** Its state: `ended`, or `failed` for one that ended on a failure. */
|
|
150
|
+
readonly state: string;
|
|
151
|
+
constructor(message: string, sandboxId: string, state: string, fix?: string);
|
|
152
|
+
}
|
|
125
153
|
/** The environment failed (our fault, never the task's). exit 3 */
|
|
126
154
|
export declare class EnvFailedError extends PromigenceError {
|
|
127
155
|
constructor(code: string, message: string, fix: string);
|
package/dist/cjs/core.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EXIT_BY_CODE = exports.NotReleasedError = exports.ApiError = exports.ServiceRestartError = exports.TimeoutError = exports.RateLimitError = exports.UnreachableError = exports.NotFoundError = exports.AuthError = exports.NotVerifiedError = exports.SpendCapError = exports.CommandExitError = exports.EnvFailedError = exports.PreviewsUnavailableError = exports.UnsupportedError = exports.InvalidArgumentError = exports.PromigenceError = exports.PREVIEWS_UNAVAILABLE = exports.FILE_PUT_CHUNK_BYTES = exports.MAX_OUTPUT_BYTES_DEFAULT = exports.IMAGE_BUILD_LIST_PRICE = exports.IMAGE_BUILD_TIMEOUT_MS = exports.WAIT_TIMEOUT_MS = exports.KEY_CONTACT = exports.DEFAULT_API_URL = exports.ERRORS_DOCS_URL = exports.DOCS_URL = exports.VERSION = void 0;
|
|
3
|
+
exports.EXIT_BY_CODE = exports.NotReleasedError = exports.ApiError = exports.ServiceRestartError = exports.TimeoutError = exports.RateLimitError = exports.UnreachableError = exports.NotFoundError = exports.AuthError = exports.NotVerifiedError = exports.SpendCapError = exports.CommandExitError = exports.EnvFailedError = exports.SandboxEndedError = exports.SANDBOX_ENDED_FIX = exports.PreviewsUnavailableError = exports.UnsupportedError = exports.InvalidArgumentError = exports.PromigenceError = exports.PREVIEWS_UNAVAILABLE = exports.FILE_PUT_CHUNK_BYTES = exports.MAX_OUTPUT_BYTES_DEFAULT = exports.IMAGE_BUILD_LIST_PRICE = exports.IMAGE_BUILD_TIMEOUT_MS = exports.WAIT_TIMEOUT_MS = exports.KEY_CONTACT = exports.DEFAULT_API_URL = exports.ERRORS_DOCS_URL = exports.DOCS_URL = exports.VERSION = void 0;
|
|
4
|
+
exports.errorsDocsFor = errorsDocsFor;
|
|
4
5
|
exports.previewsFix = previewsFix;
|
|
5
6
|
exports.withPreviewsFix = withPreviewsFix;
|
|
6
7
|
exports.fixBeyondMessage = fixBeyondMessage;
|
|
7
8
|
exports.previewsOff = previewsOff;
|
|
9
|
+
exports.sandboxEndedIn = sandboxEndedIn;
|
|
8
10
|
exports.exitForCode = exitForCode;
|
|
9
11
|
const hint_js_1 = require("./hint.js");
|
|
10
|
-
exports.VERSION = "0.1.0-beta.
|
|
12
|
+
exports.VERSION = "0.1.0-beta.16";
|
|
11
13
|
exports.DOCS_URL = "https://promigence.ai";
|
|
12
14
|
exports.ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
|
|
15
|
+
function errorsDocsFor(code) {
|
|
16
|
+
return `${exports.ERRORS_DOCS_URL}#${code}`;
|
|
17
|
+
}
|
|
13
18
|
exports.DEFAULT_API_URL = "https://api.promigence.ai/v1";
|
|
14
19
|
exports.KEY_CONTACT = "support@promigence.ai";
|
|
15
20
|
exports.WAIT_TIMEOUT_MS = 100_000;
|
|
@@ -18,10 +23,11 @@ exports.IMAGE_BUILD_LIST_PRICE = 0.1656;
|
|
|
18
23
|
exports.MAX_OUTPUT_BYTES_DEFAULT = 64 * 1024;
|
|
19
24
|
exports.FILE_PUT_CHUNK_BYTES = 16 * 1024 * 1024;
|
|
20
25
|
const PREVIEWS_OFF = /^previews are not enabled on this deployment\.?$/i;
|
|
21
|
-
exports.PREVIEWS_UNAVAILABLE = "preview URLs are not
|
|
26
|
+
exports.PREVIEWS_UNAVAILABLE = "preview URLs are not turned on for this account";
|
|
22
27
|
function previewsFix(sandboxId = "SID", port = 8000) {
|
|
23
28
|
return `Reach the port over SSH instead: \`promigence sandbox ssh ${(0, hint_js_1.hintArg)(sandboxId)} -- -N -L ${(0, hint_js_1.hintArg)(`${port}:localhost:${port}`)}\` ` +
|
|
24
|
-
`forwards http://localhost:${port} here to port ${port} in the sandbox until Ctrl-C
|
|
29
|
+
`forwards http://localhost:${port} here to port ${port} in the sandbox until Ctrl-C. ` +
|
|
30
|
+
"Write to support@promigence.ai to have preview URLs turned on.";
|
|
25
31
|
}
|
|
26
32
|
async function withPreviewsFix(call, sandboxId, port = "PORT") {
|
|
27
33
|
try {
|
|
@@ -60,7 +66,7 @@ class PromigenceError extends Error {
|
|
|
60
66
|
this.name = new.target.name;
|
|
61
67
|
this.code = code;
|
|
62
68
|
this.fix = previewsOff ? previewsFix() : fixBeyondMessage(init.message, init.fix);
|
|
63
|
-
this.docs = init.docs ??
|
|
69
|
+
this.docs = init.docs ?? errorsDocsFor(code);
|
|
64
70
|
this.exitCode = init.exitCode ?? 1;
|
|
65
71
|
this.cliExit = init.exitCode ?? 1;
|
|
66
72
|
this.guide = `promigence agent-guide --section errors --code ${(0, hint_js_1.hintArg)(code)}`;
|
|
@@ -111,6 +117,22 @@ class PreviewsUnavailableError extends InvalidArgumentError {
|
|
|
111
117
|
}
|
|
112
118
|
}
|
|
113
119
|
exports.PreviewsUnavailableError = PreviewsUnavailableError;
|
|
120
|
+
const SANDBOX_ENDED = /^sandbox (\S+) has ended \(`([a-z_]+)`\)/;
|
|
121
|
+
function sandboxEndedIn(message) {
|
|
122
|
+
const m = SANDBOX_ENDED.exec(message.trim());
|
|
123
|
+
return m ? { sandboxId: m[1], state: m[2] } : undefined;
|
|
124
|
+
}
|
|
125
|
+
exports.SANDBOX_ENDED_FIX = "Start a new sandbox: Sandbox.create(). This one's getInfo() says how it ended (`outcome`).";
|
|
126
|
+
class SandboxEndedError extends InvalidArgumentError {
|
|
127
|
+
sandboxId;
|
|
128
|
+
state;
|
|
129
|
+
constructor(message, sandboxId, state, fix = exports.SANDBOX_ENDED_FIX) {
|
|
130
|
+
super(message, fix, "usage");
|
|
131
|
+
this.sandboxId = sandboxId;
|
|
132
|
+
this.state = state;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.SandboxEndedError = SandboxEndedError;
|
|
114
136
|
class EnvFailedError extends PromigenceError {
|
|
115
137
|
constructor(code, message, fix) {
|
|
116
138
|
super({ code, message, fix, exitCode: 3 });
|
|
@@ -240,6 +262,7 @@ exports.EXIT_BY_CODE = {
|
|
|
240
262
|
env_failed: 3, snapshot_rejected: 6, build_failed: 3, snapshot_failed: 4,
|
|
241
263
|
not_connected: 7,
|
|
242
264
|
service_restart: 3,
|
|
265
|
+
digest_mismatch: 4,
|
|
243
266
|
fork_failed: 3, start_timeout: 3, readiness_failed: 3, verify_too_slow: 3,
|
|
244
267
|
verify_each_failed: 3, toolchain_mismatch: 3, sandbox_unresponsive: 3, oom_within_tier: 3,
|
|
245
268
|
disk_full_within_tier: 3, sandbox_interrupted: 3, integrity_mismatch: 3, egress_unavailable: 3,
|
|
@@ -271,6 +294,8 @@ exports.EXIT_BY_CODE = {
|
|
|
271
294
|
project_cap_reached: 5,
|
|
272
295
|
spend_limit_reached: 5,
|
|
273
296
|
billing_hold: 5,
|
|
297
|
+
credits_exhausted: 5, purchase_limit: 5, purchases_blocked: 5,
|
|
298
|
+
card_needed: 5, auto_reload_unavailable: 5,
|
|
274
299
|
tier_over_plan: 2,
|
|
275
300
|
network_host_refused: 9, network_policy_unsupported: 3,
|
|
276
301
|
secrets_in_snapshot: 2, secrets_unavailable: 3,
|
package/dist/cjs/http2.d.ts
CHANGED
|
@@ -197,10 +197,18 @@ export declare const __http2Internals: {
|
|
|
197
197
|
gapUs: number;
|
|
198
198
|
pace: string;
|
|
199
199
|
};
|
|
200
|
+
/** The offset that turns the thread's stamps into trace times (`clockOffset`), read from the two clocks given. */
|
|
201
|
+
clockOffset(hr: () => number, now: () => number): number;
|
|
200
202
|
/** Tests: make the transport thread stop answering (it reads nothing more), as a stuck one would. */
|
|
201
203
|
wedgeThreadForTest(): void;
|
|
202
204
|
/** Tests: make the transport thread fail as a crash would. */
|
|
203
205
|
crashThreadForTest(): void;
|
|
206
|
+
/**
|
|
207
|
+
* Tests: the transport thread gives no spaced request its turn until `releaseTurnsForTest`. Both go as messages, in order
|
|
208
|
+
* with the requests and their cancels, so what is sent between them reaches the thread while every spaced request waits.
|
|
209
|
+
*/
|
|
210
|
+
holdTurnsForTest(): void;
|
|
211
|
+
releaseTurnsForTest(): void;
|
|
204
212
|
/** The early lookup, for tests: start one with a platform, DNS servers and hosts file of the test's own. */
|
|
205
213
|
early: {
|
|
206
214
|
start(host: string): boolean;
|
package/dist/cjs/http2.js
CHANGED
|
@@ -1351,6 +1351,12 @@ const THREAD_GRACE_MS = 1_000;
|
|
|
1351
1351
|
const THREAD_SWEEP_MS = 250;
|
|
1352
1352
|
const THREAD_MARKS = 4096;
|
|
1353
1353
|
const THREAD_PHASES = ["w.got", "w.sent", "w.head", "w.end"];
|
|
1354
|
+
function clockOffset(hr = () => Number(process.hrtime.bigint()) / 1e6, now = () => performance.now(), n = 5) {
|
|
1355
|
+
let best = -Infinity;
|
|
1356
|
+
for (let i = 0; i < n; i++)
|
|
1357
|
+
best = Math.max(best, hr() - now());
|
|
1358
|
+
return best;
|
|
1359
|
+
}
|
|
1354
1360
|
class TransportThread {
|
|
1355
1361
|
origin;
|
|
1356
1362
|
worker;
|
|
@@ -1376,7 +1382,7 @@ class TransportThread {
|
|
|
1376
1382
|
posts = 0;
|
|
1377
1383
|
answered = 0;
|
|
1378
1384
|
pace = THREAD_DEFAULTS.pace;
|
|
1379
|
-
hrOffset =
|
|
1385
|
+
hrOffset = clockOffset();
|
|
1380
1386
|
constructor(origin, worker, port, acks) {
|
|
1381
1387
|
this.origin = origin;
|
|
1382
1388
|
this.worker = worker;
|
|
@@ -2066,6 +2072,9 @@ exports.__http2Internals = {
|
|
|
2066
2072
|
threadKnobs(env) {
|
|
2067
2073
|
return threadKnobs(env);
|
|
2068
2074
|
},
|
|
2075
|
+
clockOffset(hr, now) {
|
|
2076
|
+
return clockOffset(hr, now);
|
|
2077
|
+
},
|
|
2069
2078
|
wedgeThreadForTest() {
|
|
2070
2079
|
try {
|
|
2071
2080
|
transportThread?.port.postMessage({ t: "wedge-for-test" });
|
|
@@ -2078,6 +2087,18 @@ exports.__http2Internals = {
|
|
|
2078
2087
|
}
|
|
2079
2088
|
catch { }
|
|
2080
2089
|
},
|
|
2090
|
+
holdTurnsForTest() {
|
|
2091
|
+
try {
|
|
2092
|
+
transportThread?.port.postMessage({ t: "hold-turns-for-test" });
|
|
2093
|
+
}
|
|
2094
|
+
catch { }
|
|
2095
|
+
},
|
|
2096
|
+
releaseTurnsForTest() {
|
|
2097
|
+
try {
|
|
2098
|
+
transportThread?.port.postMessage({ t: "release-turns-for-test" });
|
|
2099
|
+
}
|
|
2100
|
+
catch { }
|
|
2101
|
+
},
|
|
2081
2102
|
early: {
|
|
2082
2103
|
start(host) {
|
|
2083
2104
|
return earlyLookupFor(host.toLowerCase()) !== undefined;
|