@unotest/core 0.25.0 → 0.26.1
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/CHANGELOG.md +124 -0
- package/README.md +7 -0
- package/dist/index.d.ts +224 -2
- package/dist/index.js +985 -4
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,129 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.26.1] - 2026-08-30
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `packBundle` accepts `declaredVariables` — the external NAMES a suite
|
|
8
|
+
declares, carried in the manifest so a box can tell a pusher which of
|
|
9
|
+
them it has no value for. Names only; the files that hold the values
|
|
10
|
+
are excluded from every bundle as before.
|
|
11
|
+
|
|
12
|
+
- Updated dependencies [9424f6b]
|
|
13
|
+
- @unotest/protocol@0.26.1
|
|
14
|
+
- @unotest/dsl@0.26.1
|
|
15
|
+
|
|
16
|
+
## [0.26.0] - 2026-08-30
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- 6d1c612: `npx @unotest/web bundle push` — send your suite to a box, instead of
|
|
21
|
+
giving a box access to your repository.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx @unotest/web bundle push --box https://tests.example.com
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
It packs `unotest/`, `unotest.config`, `package.json` and its lockfile
|
|
28
|
+
into one archive and uploads it. That is the whole channel: a box holds
|
|
29
|
+
no deploy key and no token for anybody's code and never pulls anything,
|
|
30
|
+
so nothing is on it that was not pushed to it. (There is no path-scoped
|
|
31
|
+
git credential anywhere — a key that can fetch `unotest/` can fetch the
|
|
32
|
+
product source next to it, which is why this direction is the only one
|
|
33
|
+
we offer.)
|
|
34
|
+
|
|
35
|
+
- **Uncommitted work travels.** The manifest records `dirty: true` and
|
|
36
|
+
the bundle shows up as _wip_, so pushing a fix you have not committed
|
|
37
|
+
yet is a normal loop, not a release step.
|
|
38
|
+
- **What would fail on the box is refused here.** A scenario that reads a
|
|
39
|
+
file outside `unotest/` (`MODULE_NOT_FOUND` on the box, hours later, on
|
|
40
|
+
a run nobody is watching), a missing lockfile (`npm ci` will not run
|
|
41
|
+
without one), no `@unotest/web` in the dependencies, a symlink. Each
|
|
42
|
+
problem names the file, the line and the fix.
|
|
43
|
+
- **Secrets do not travel.** `unotest/.env*` and `.secrets*` are excluded
|
|
44
|
+
even if committed: environment values belong to the environment and are
|
|
45
|
+
injected over the bundle when it runs.
|
|
46
|
+
- **In a git project, git decides what belongs.** The file list comes from
|
|
47
|
+
`git ls-files --cached --others --exclude-standard`, so whatever your
|
|
48
|
+
`.gitignore` keeps out — generated databases, recordings, `.runs` —
|
|
49
|
+
stays out. Content is read from the working tree, not the index.
|
|
50
|
+
- **The id is the content.** `bundleId` is a hash of the packed files, not
|
|
51
|
+
a commit sha (a dirty tree's sha is not unique), so re-pushing an
|
|
52
|
+
unchanged suite is an instant no-op and two people packing the same tree
|
|
53
|
+
get the same bundle.
|
|
54
|
+
- `--dry-run` packs and checks without uploading, `--out <file>` keeps the
|
|
55
|
+
archive (plain `tar.gz` — `tar -tzf` shows what is inside), `--json`
|
|
56
|
+
prints `{ bundleId, status }` for a CI job that runs it afterwards.
|
|
57
|
+
- `UNOTEST_BOX_URL` / `UNOTEST_BOX_TOKEN` supply the address and the
|
|
58
|
+
token, which a CI job usually takes from its secret store. Tokens are
|
|
59
|
+
issued per project on the box.
|
|
60
|
+
|
|
61
|
+
- c43aa00: Runs of one project now take turns instead of colliding.
|
|
62
|
+
|
|
63
|
+
Until now nothing coordinated them: the viewer refused a second run with
|
|
64
|
+
HTTP 409, and that was the whole defence — a `npx @unotest/web e2e` in a
|
|
65
|
+
terminal, an agent's `run_test` and a scheduled suite would all start on
|
|
66
|
+
top of each other, driving the same browser and the same seeded database
|
|
67
|
+
at the same time. The 409 protected the UI, not the machine.
|
|
68
|
+
|
|
69
|
+
- **A filesystem queue**, in `unotest/.queue<target>[.<env>]/` beside the
|
|
70
|
+
runs root it belongs to. A run writes a ticket, waits until it is at
|
|
71
|
+
the head, takes a slot, runs, and gives the slot back. There is no
|
|
72
|
+
daemon: whoever holds the slot executes the run itself, so nothing new
|
|
73
|
+
has to be installed, started or kept alive. Only `open("wx")`, rename,
|
|
74
|
+
unlink and mtime — no `flock`, which lies on network and container
|
|
75
|
+
filesystems. A crashed producer is reaped by whoever comes next.
|
|
76
|
+
- **Every producer is in it**: the CLI (`e2e`, `collection`, `author`),
|
|
77
|
+
the viewer's Run button, and the MCP `run_test` (which spawns the CLI).
|
|
78
|
+
A collection is ONE run — its `workers` still run in parallel inside
|
|
79
|
+
it, because a child process carries its parent's lease and skips the
|
|
80
|
+
queue.
|
|
81
|
+
- **The viewer shows the queue**: the Active panel lists what is waiting,
|
|
82
|
+
including tickets a terminal or an agent wrote, with a button to take
|
|
83
|
+
any of them back out. `GET /api/queue`, `DELETE /api/queue/:ticket`,
|
|
84
|
+
and a `queue:changed` websocket message.
|
|
85
|
+
- **Config**: `queue.concurrency` (default 1) and `queue.enabled`
|
|
86
|
+
(default true) in `unotest.config`. Both the CLI and the viewer read
|
|
87
|
+
the same file, so they cannot disagree about how many slots exist.
|
|
88
|
+
- **Escape hatches**: `UNOTEST_NO_QUEUE=1` runs without queueing;
|
|
89
|
+
Ctrl-C while waiting gives up your place in line and runs nothing.
|
|
90
|
+
- **For boxes**: `UNOTEST_QUEUE_GLOBAL_DIR` + `UNOTEST_QUEUE_GLOBAL_SLOTS`
|
|
91
|
+
add a host-wide budget several environments share (a collection weighs
|
|
92
|
+
its worker count), and `UNOTEST_ARTIFACTS_ROOT` puts `.runs` / `.queue`
|
|
93
|
+
somewhere other than the directory the tests live in — so the history
|
|
94
|
+
outlives a throwaway copy of the tests.
|
|
95
|
+
|
|
96
|
+
### Migration notes
|
|
97
|
+
|
|
98
|
+
**`POST /api/run` no longer answers 409.** Ordering a run while another
|
|
99
|
+
one is active used to fail with `ActiveRunConflictError` (HTTP 409); it
|
|
100
|
+
now succeeds and the run waits. The response changed shape with it:
|
|
101
|
+
|
|
102
|
+
```diff
|
|
103
|
+
- { runId, kind, ref, pid, startedAt }
|
|
104
|
+
+ { runId, kind, ref, status: "queued" | "running", ticket, acceptedAt }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`runId` is still final and immediately usable — open the tab on it as
|
|
108
|
+
before — but the run may not have started yet, and `pid` is not known at
|
|
109
|
+
that point. `POST /api/runs/:runId/abort` covers both states: it
|
|
110
|
+
withdraws a waiting ticket or SIGTERMs a running child.
|
|
111
|
+
|
|
112
|
+
Anything that treated 409 as "busy, try later" should now simply order
|
|
113
|
+
the run. Anything that relied on "only one run can exist" should read
|
|
114
|
+
`GET /api/queue`. To keep the old immediate-start behaviour (without the
|
|
115
|
+
protection), set `queue.enabled: false`.
|
|
116
|
+
|
|
117
|
+
### Patch Changes
|
|
118
|
+
|
|
119
|
+
- Updated dependencies [3eede7c]
|
|
120
|
+
- Updated dependencies [6d1c612]
|
|
121
|
+
- Updated dependencies [dbfa36f]
|
|
122
|
+
- Updated dependencies [c43aa00]
|
|
123
|
+
- Updated dependencies [8ec4177]
|
|
124
|
+
- @unotest/protocol@0.26.0
|
|
125
|
+
- @unotest/dsl@0.26.0
|
|
126
|
+
|
|
3
127
|
## [0.25.0] - 2026-08-29
|
|
4
128
|
|
|
5
129
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -13,6 +13,13 @@ filesystem layer both `@unotest/web` and `@unotest/mobile` need:
|
|
|
13
13
|
`process.env` always winning.
|
|
14
14
|
- **`appendUniqueLines`** — idempotent `.gitignore` (or any line-based
|
|
15
15
|
file) updater used by the runners' `init`.
|
|
16
|
+
- **`FsRunQueue`** — the filesystem run queue (`unotest/.queue…`) that
|
|
17
|
+
serialises RUNS of one project + environment across every producer:
|
|
18
|
+
a CLI in a terminal, the viewer's Run button, an agent. Ticket +
|
|
19
|
+
slot-lease model, with the crash recovery that implies (mtime
|
|
20
|
+
liveness, rename-quarantine reaping). Uses only `open("wx")`, rename,
|
|
21
|
+
unlink and mtime — never `flock`, which lies on the network and
|
|
22
|
+
container filesystems this has to work on.
|
|
16
23
|
|
|
17
24
|
Depends only on `@unotest/protocol`. Unlike protocol (pure data, no fs),
|
|
18
25
|
this package deliberately owns filesystem side effects.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { RunArtifact, RunManifest, RunSources, RuntimeStateFile, RuntimeState, DbgCommand, PickerMode, DbgCommandRecord, DbgCommandProbe } from '@unotest/protocol';
|
|
1
|
+
import { RunArtifact, RunManifest, RunSources, RuntimeStateFile, RuntimeState, DbgCommand, PickerMode, DbgCommandRecord, DbgCommandProbe, IRunQueue, EnqueueRunInput, RunQueueTicketHandle, QueueSnapshot, QueueTicket, RunQueueLease, BundleGitInfo, BundleManifest } from '@unotest/protocol';
|
|
2
|
+
export { isRecord } from '@unotest/protocol';
|
|
2
3
|
import { ExecutionEvent } from '@unotest/dsl/executor';
|
|
3
4
|
|
|
4
5
|
interface IRunArtifactWriter {
|
|
@@ -259,4 +260,225 @@ declare class UnotestError extends Error {
|
|
|
259
260
|
constructor(message: string, context?: Record<string, unknown>);
|
|
260
261
|
}
|
|
261
262
|
|
|
262
|
-
|
|
263
|
+
interface GlobalPoolOptions {
|
|
264
|
+
/** Absolute directory shared by every environment on this host
|
|
265
|
+
* (`UNOTEST_QUEUE_GLOBAL_DIR`; in compose, a shared volume). */
|
|
266
|
+
dir: string;
|
|
267
|
+
/** Total WEIGHT the host can run at once
|
|
268
|
+
* (`UNOTEST_QUEUE_GLOBAL_SLOTS`). */
|
|
269
|
+
slots: number;
|
|
270
|
+
}
|
|
271
|
+
/** Read the optional host-wide weight pool from the environment. Present
|
|
272
|
+
* on a box where several environments share one machine's CPU and RAM;
|
|
273
|
+
* absent for an npm user, who then only has the per-environment queue.
|
|
274
|
+
* The ONE parser for every producer — the CLI and the viewer counting
|
|
275
|
+
* different pools over the same directory would defeat the pool. */
|
|
276
|
+
declare function globalPoolFromEnv(env?: NodeJS.ProcessEnv): GlobalPoolOptions | null;
|
|
277
|
+
interface FsRunQueueOptions {
|
|
278
|
+
/** Absolute queue root — `<artifacts root>/unotest/.queue[…]`. */
|
|
279
|
+
root: string;
|
|
280
|
+
/** Environment concurrency: how many RUNS may be in flight here. */
|
|
281
|
+
slots: number;
|
|
282
|
+
global?: GlobalPoolOptions;
|
|
283
|
+
staleAfterMs?: number;
|
|
284
|
+
touchIntervalMs?: number;
|
|
285
|
+
pollIntervalMs?: number;
|
|
286
|
+
/** Injected clock + entropy + timer, so the tests can drive staleness
|
|
287
|
+
* and races deterministically instead of sleeping through them. */
|
|
288
|
+
now?: () => number;
|
|
289
|
+
random?: () => string;
|
|
290
|
+
sleep?: (ms: number) => Promise<void>;
|
|
291
|
+
}
|
|
292
|
+
declare class FsRunQueue implements IRunQueue {
|
|
293
|
+
private readonly root;
|
|
294
|
+
private readonly ticketsDir;
|
|
295
|
+
private readonly runningDir;
|
|
296
|
+
private readonly staleAfterMs;
|
|
297
|
+
private readonly touchIntervalMs;
|
|
298
|
+
private readonly pollIntervalMs;
|
|
299
|
+
private readonly now;
|
|
300
|
+
private readonly random;
|
|
301
|
+
private readonly sleep;
|
|
302
|
+
private readonly envPool;
|
|
303
|
+
private readonly globalPool;
|
|
304
|
+
constructor(opts: FsRunQueueOptions);
|
|
305
|
+
enqueue(input: EnqueueRunInput): Promise<RunQueueTicketHandle>;
|
|
306
|
+
snapshot(): Promise<QueueSnapshot>;
|
|
307
|
+
cancel(ticketName: string): Promise<boolean>;
|
|
308
|
+
/** Reap what died, then drop `running/` entries with no lease behind
|
|
309
|
+
* them. Every producer calls this on its way past — a queue with any
|
|
310
|
+
* traffic needs no janitor process. */
|
|
311
|
+
sweep(): Promise<void>;
|
|
312
|
+
/** Wait until `handle` owns a slot. Public only for the handle it
|
|
313
|
+
* belongs to. */
|
|
314
|
+
acquire(handle: FsTicketHandle, signal?: AbortSignal): Promise<RunQueueLease>;
|
|
315
|
+
/** One claim attempt for the ticket sitting at `index` of the live
|
|
316
|
+
* queue. Null means "not now" — the caller waits and retries.
|
|
317
|
+
*
|
|
318
|
+
* Position rule: a ticket may attempt a claim when its index is below
|
|
319
|
+
* the environment's slot count. With the default concurrency of 1
|
|
320
|
+
* that is strict FIFO; above it, two eligible tickets may take a
|
|
321
|
+
* freed slot out of order, which is the price of not serialising
|
|
322
|
+
* every waiter behind the head of the queue.
|
|
323
|
+
*
|
|
324
|
+
* A maintenance ticket is exclusive: it waits for the head position
|
|
325
|
+
* and then takes EVERY slot, so nothing else runs while it mutates
|
|
326
|
+
* the environment. While one is at the head, NOBODY else may claim —
|
|
327
|
+
* without that drain rule a steady stream of later tickets would keep
|
|
328
|
+
* grabbing each freed slot and the head's all-or-nothing claim would
|
|
329
|
+
* starve forever. Detected from ticket names (the maintenance rank is
|
|
330
|
+
* the first character), so waiters never read each other's files. */
|
|
331
|
+
private tryClaim;
|
|
332
|
+
/** Live waiting tickets, in service order. */
|
|
333
|
+
private waitingOrder;
|
|
334
|
+
private readEntries;
|
|
335
|
+
private clampWeight;
|
|
336
|
+
/** Heartbeat + teardown for a lease — kept here so `FsLease` stays a
|
|
337
|
+
* handle and the filesystem knowledge stays in one class. */
|
|
338
|
+
releaseLease(handle: FsTicketHandle, envSlots: readonly string[], globalSlots: readonly string[] | null): Promise<void>;
|
|
339
|
+
releaseLeaseSync(handle: FsTicketHandle, envSlots: readonly string[], globalSlots: readonly string[] | null): void;
|
|
340
|
+
touchLease(handle: FsTicketHandle, envSlots: readonly string[], globalSlots: readonly string[] | null): Promise<void>;
|
|
341
|
+
get heartbeatIntervalMs(): number;
|
|
342
|
+
}
|
|
343
|
+
declare class FsTicketHandle implements RunQueueTicketHandle {
|
|
344
|
+
private readonly queue;
|
|
345
|
+
readonly name: string;
|
|
346
|
+
readonly ticket: QueueTicket;
|
|
347
|
+
constructor(queue: FsRunQueue, name: string, ticket: QueueTicket);
|
|
348
|
+
acquire(opts?: {
|
|
349
|
+
signal?: AbortSignal;
|
|
350
|
+
}): Promise<RunQueueLease>;
|
|
351
|
+
cancel(): Promise<void>;
|
|
352
|
+
}
|
|
353
|
+
/** Env every child of a lease holder inherits. The boundary the queue
|
|
354
|
+
* draws is "top-level run": a collection's scenario subprocesses carry
|
|
355
|
+
* this and skip the queue, so `workers` keeps meaning what it always
|
|
356
|
+
* meant instead of deadlocking behind its own parent. */
|
|
357
|
+
declare function leaseEnv(runId: string): Readonly<Record<string, string>>;
|
|
358
|
+
|
|
359
|
+
/** The ticket disappeared while its owner was waiting for a slot: it was
|
|
360
|
+
* cancelled (viewer `DELETE /api/queue/:ticket`) or reaped as stale
|
|
361
|
+
* (the owner was suspended long enough to look dead). Callers treat
|
|
362
|
+
* this as "this run will never start" — it is not a failure of the run,
|
|
363
|
+
* and there is nothing to retry automatically. */
|
|
364
|
+
declare class QueueTicketLostError extends UnotestError {
|
|
365
|
+
constructor(ticketName: string);
|
|
366
|
+
}
|
|
367
|
+
/** The caller's abort signal fired while waiting (Ctrl-C in a terminal,
|
|
368
|
+
* viewer shutdown). The ticket has already been withdrawn. */
|
|
369
|
+
declare class QueueWaitAbortedError extends UnotestError {
|
|
370
|
+
constructor(ticketName: string);
|
|
371
|
+
}
|
|
372
|
+
/** A queue directory that cannot be created / written. Distinct from a
|
|
373
|
+
* full queue: this one never resolves by waiting. */
|
|
374
|
+
declare class QueueUnavailableError extends UnotestError {
|
|
375
|
+
constructor(root: string, cause: string);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
interface TarEntry {
|
|
379
|
+
/** Relative posix path inside the archive. */
|
|
380
|
+
path: string;
|
|
381
|
+
content: Uint8Array;
|
|
382
|
+
/** Only the exec bit travels. The rest of a file's mode is the packing
|
|
383
|
+
* machine's umask, and a suite that hashes differently because of the
|
|
384
|
+
* umask it was packed under would break the "same tree, same id"
|
|
385
|
+
* promise the whole upload path rests on. */
|
|
386
|
+
executable?: boolean;
|
|
387
|
+
}
|
|
388
|
+
/** Everything a member path must be before it is written or restored.
|
|
389
|
+
*
|
|
390
|
+
* Rejected on BOTH sides on purpose: the packing check is a courtesy to
|
|
391
|
+
* whoever is packing, the unpacking check is the one that stops an
|
|
392
|
+
* uploaded archive from writing outside the directory it was given. */
|
|
393
|
+
declare function assertSafeBundlePath(path: string): void;
|
|
394
|
+
/** Serialise entries into a tar archive. Entries are sorted by path and
|
|
395
|
+
* duplicates are refused: an archive with two members of the same name
|
|
396
|
+
* restores to whichever came last, which makes the id of a bundle depend
|
|
397
|
+
* on the order somebody happened to walk a directory. */
|
|
398
|
+
declare function writeTar(entries: readonly TarEntry[]): Buffer;
|
|
399
|
+
/** Restore entries from a tar archive.
|
|
400
|
+
*
|
|
401
|
+
* Strict by design: only plain files, only the ustar magic, only paths
|
|
402
|
+
* that stay inside the bundle. A member this refuses is not a member we
|
|
403
|
+
* would know what to do with — a symlink in an uploaded archive is a way
|
|
404
|
+
* to make the next reader follow a path off the box. */
|
|
405
|
+
declare function readTar(data: Uint8Array): TarEntry[];
|
|
406
|
+
|
|
407
|
+
type BundleFile = TarEntry;
|
|
408
|
+
/** FULL content hash of a payload: sha256 over the sorted list of
|
|
409
|
+
* (path, exec bit, content hash). Two trees with the same files have
|
|
410
|
+
* the same hash no matter how they were walked, compressed or
|
|
411
|
+
* transported. This is the integrity anchor — the manifest carries it
|
|
412
|
+
* whole, and the box verifies the WHOLE of it after unpacking. */
|
|
413
|
+
declare function computeTreeSha256(files: readonly BundleFile[]): string;
|
|
414
|
+
/** The short HANDLE derived from the full hash: a directory name, a URL
|
|
415
|
+
* segment, a run-manifest field. Identity and integrity are the full
|
|
416
|
+
* sha256; this is only how humans and paths refer to it. */
|
|
417
|
+
declare function computeBundleId(files: readonly BundleFile[]): string;
|
|
418
|
+
/** What the manifest carries that the files themselves cannot say. */
|
|
419
|
+
interface BundleProvenance {
|
|
420
|
+
createdAt: number;
|
|
421
|
+
webVersion: string;
|
|
422
|
+
author?: string;
|
|
423
|
+
git?: BundleGitInfo;
|
|
424
|
+
/** Names of the externals the suite declares. The files that hold them
|
|
425
|
+
* never travel (their values belong to an environment), so the names
|
|
426
|
+
* ride in the manifest — that is what lets a box say at upload which
|
|
427
|
+
* of them it cannot supply. */
|
|
428
|
+
declaredVariables?: readonly string[];
|
|
429
|
+
}
|
|
430
|
+
interface PackedBundle {
|
|
431
|
+
bundleId: string;
|
|
432
|
+
manifest: BundleManifest;
|
|
433
|
+
/** gzipped tar, ready to be written to a file or POSTed. */
|
|
434
|
+
archive: Buffer;
|
|
435
|
+
}
|
|
436
|
+
declare function packBundle(files: readonly BundleFile[], provenance: BundleProvenance): PackedBundle;
|
|
437
|
+
interface UnpackedBundle {
|
|
438
|
+
manifest: BundleManifest;
|
|
439
|
+
/** The suite, without the manifest. */
|
|
440
|
+
files: BundleFile[];
|
|
441
|
+
}
|
|
442
|
+
interface UnpackOptions {
|
|
443
|
+
/** Cap on the UNPACKED size, enforced while decompressing. Without one
|
|
444
|
+
* a 1 MB upload can expand into a full disk. */
|
|
445
|
+
maxBytes?: number;
|
|
446
|
+
}
|
|
447
|
+
declare function unpackBundle(archive: Uint8Array, options?: UnpackOptions): UnpackedBundle;
|
|
448
|
+
|
|
449
|
+
/** The archive is not a readable bundle: broken gzip, a truncated tar, a
|
|
450
|
+
* header whose checksum does not add up, a member that is not a plain
|
|
451
|
+
* file. Everything a box refuses BEFORE writing anything to disk. */
|
|
452
|
+
declare class BundleFormatError extends UnotestError {
|
|
453
|
+
constructor(reason: string, context?: Record<string, unknown>);
|
|
454
|
+
}
|
|
455
|
+
/** A member path a bundle may not carry.
|
|
456
|
+
*
|
|
457
|
+
* This is a security boundary, not tidiness: an archive is written by
|
|
458
|
+
* whoever uploaded it, and `../../etc/cron.d/x` or an absolute path in a
|
|
459
|
+
* tar header is the oldest way to make an unpacker write outside the
|
|
460
|
+
* directory it was told to. Paths are checked when PACKING and again
|
|
461
|
+
* when UNPACKING — the second check is the one that matters, because the
|
|
462
|
+
* first ran on the uploader's machine. */
|
|
463
|
+
declare class BundlePathError extends UnotestError {
|
|
464
|
+
constructor(path: string, reason: string);
|
|
465
|
+
}
|
|
466
|
+
/** The archive unpacks to more than the caller allowed.
|
|
467
|
+
*
|
|
468
|
+
* A separate error because the answer is different: a malformed archive
|
|
469
|
+
* is never worth retrying, an oversized one means "split the suite or
|
|
470
|
+
* raise the box's limit". It is also the defence against a small upload
|
|
471
|
+
* that expands to fill a disk — the limit is enforced DURING
|
|
472
|
+
* decompression, not after. */
|
|
473
|
+
declare class BundleTooLargeError extends UnotestError {
|
|
474
|
+
constructor(limitBytes: number);
|
|
475
|
+
}
|
|
476
|
+
/** The archive's content does not hash to the id its manifest claims.
|
|
477
|
+
* Either the upload was truncated or something rewrote it in flight; in
|
|
478
|
+
* both cases the box must not store it under an id that means something
|
|
479
|
+
* else — the id is what every ticket, symlink and cache key refers to. */
|
|
480
|
+
declare class BundleIdMismatchError extends UnotestError {
|
|
481
|
+
constructor(claimed: string, computed: string);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export { type ArtifactRedactor, type BundleFile, BundleFormatError, BundleIdMismatchError, BundlePathError, type BundleProvenance, BundleTooLargeError, type DebugCommandsWatcherDeps, type DebugControlTarget, type DebugWatcherLogger, type E2EFlags, FsRunQueue, type FsRunQueueOptions, type GitignoreUpdate, type GlobalPoolOptions, type IRunArtifactWriter, JsonlRunArtifactWriter, type JsonlRunArtifactWriterOptions, type PackedBundle, QueueTicketLostError, QueueUnavailableError, QueueWaitAbortedError, type RunHeartbeat, type RuntimeExecState, type RuntimeInspection, type RuntimeInspectionInput, type RuntimeStateExtra, type RuntimeStateWriter, type RuntimeStateWriterDeps, type TarEntry, UnotestError, type UnpackOptions, type UnpackedBundle, type WriteManifestInput, type WriteRunSourcesInput, appendUniqueLines, applyEnvLayers, assertSafeBundlePath, buildRuntimeInspection, computeBundleId, computeTreeSha256, createRunArtifactWriter, createRuntimeStateWriter, currentLocation, extractCallStack, globalPoolFromEnv, leaseEnv, levenshteinDistance, packBundle, parseE2EFlags, readDebuggerBreakpoints, readEnvFile, readEnvLayers, readTar, startDebugCommandsWatcher, startRunHeartbeat, suggestClosest, toProtocolRuntimeState, unpackBundle, writeRunManifest, writeRunSources, writeTar };
|