@zukhruf/docker 0.3.33 → 0.3.35

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.
Files changed (72) hide show
  1. package/README.md +26 -12
  2. package/dist/container-process.d.ts +35 -0
  3. package/dist/container-process.d.ts.map +1 -0
  4. package/dist/container-process.js +91 -0
  5. package/dist/container-process.js.map +1 -0
  6. package/dist/containers.d.ts +160 -0
  7. package/dist/containers.d.ts.map +1 -0
  8. package/dist/containers.js +700 -0
  9. package/dist/containers.js.map +1 -0
  10. package/dist/docker.d.ts +25 -2
  11. package/dist/docker.d.ts.map +1 -1
  12. package/dist/docker.js +43 -7
  13. package/dist/docker.js.map +1 -1
  14. package/dist/engine-api.d.ts +369 -0
  15. package/dist/engine-api.d.ts.map +1 -1
  16. package/dist/engine-client.d.ts +52 -4
  17. package/dist/engine-client.d.ts.map +1 -1
  18. package/dist/engine-client.js +167 -31
  19. package/dist/engine-client.js.map +1 -1
  20. package/dist/errors.d.ts +95 -5
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +156 -6
  23. package/dist/errors.js.map +1 -1
  24. package/dist/images.d.ts +40 -0
  25. package/dist/images.d.ts.map +1 -0
  26. package/dist/images.js +173 -0
  27. package/dist/images.js.map +1 -0
  28. package/dist/index.d.ts +8 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/multiplexed-stream.d.ts +11 -0
  33. package/dist/multiplexed-stream.d.ts.map +1 -0
  34. package/dist/multiplexed-stream.js +81 -0
  35. package/dist/multiplexed-stream.js.map +1 -0
  36. package/dist/ndjson.d.ts +7 -0
  37. package/dist/ndjson.d.ts.map +1 -0
  38. package/dist/ndjson.js +21 -0
  39. package/dist/ndjson.js.map +1 -0
  40. package/dist/networks.d.ts +46 -0
  41. package/dist/networks.d.ts.map +1 -0
  42. package/dist/networks.js +170 -0
  43. package/dist/networks.js.map +1 -0
  44. package/dist/output.d.ts +18 -0
  45. package/dist/output.d.ts.map +1 -0
  46. package/dist/output.js +21 -0
  47. package/dist/output.js.map +1 -0
  48. package/dist/progress.d.ts +9 -0
  49. package/dist/progress.d.ts.map +1 -0
  50. package/dist/progress.js +20 -0
  51. package/dist/progress.js.map +1 -0
  52. package/dist/ssh-connection.d.ts +3 -0
  53. package/dist/ssh-connection.d.ts.map +1 -1
  54. package/dist/ssh-connection.js +46 -10
  55. package/dist/ssh-connection.js.map +1 -1
  56. package/dist/tar-reader.d.ts +22 -0
  57. package/dist/tar-reader.d.ts.map +1 -0
  58. package/dist/tar-reader.js +163 -0
  59. package/dist/tar-reader.js.map +1 -0
  60. package/dist/tar-writer.d.ts +16 -0
  61. package/dist/tar-writer.d.ts.map +1 -0
  62. package/dist/tar-writer.js +114 -0
  63. package/dist/tar-writer.js.map +1 -0
  64. package/dist/transport.d.ts +11 -1
  65. package/dist/transport.d.ts.map +1 -1
  66. package/dist/transport.js +34 -2
  67. package/dist/transport.js.map +1 -1
  68. package/dist/volumes.d.ts +41 -0
  69. package/dist/volumes.d.ts.map +1 -0
  70. package/dist/volumes.js +119 -0
  71. package/dist/volumes.js.map +1 -0
  72. package/package.json +5 -2
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @zukhruf/docker
2
2
 
3
- > **Status: design for review.** This README is the API on paper. No code exists yet. The decisions are in the [ADRs](./docs/adr/). The package leads, and the repositories that use Docker change to fit it ([ADR 0003](./docs/adr/0003-the-package-leads-and-the-repositories-change.md)).
3
+ > **Status: implemented.** Each call of the `## API` block works, and the tests run it on Docker Desktop (API 1.56) and on an SSH engine (API 1.51). A Windows named pipe is not proved yet (backlog #2753). The decisions are in the [ADRs](./docs/adr/). The package leads, and the repositories that use Docker change to fit it ([ADR 0003](./docs/adr/0003-the-package-leads-and-the-repositories-change.md)).
4
4
 
5
- A client for the Docker Engine API on Node.js. It selects the engine that the Docker CLI selects, and it sends HTTP requests to that engine with `node:http`: through a Unix socket, a Windows named pipe, or SSH. It needs no Docker CLI on the `PATH`, and it has no dependencies. Thus a desktop app that starts from the macOS Dock can use it, and so can a test, a sandbox or a deploy tool.
5
+ A client for the Docker Engine API on Node.js. It selects the engine that the Docker CLI selects, and it sends HTTP requests to that engine with `node:http`: through a Unix socket, a Windows named pipe, or SSH. It needs no Docker CLI on the `PATH`, and it has no third-party dependencies; it uses `@zukhruf/async`. Thus a desktop app that starts from the macOS Dock can use it, and so can a test, a sandbox or a deploy tool.
6
6
 
7
7
  The package covers the containers of one engine and the resources around them: their images, volumes, networks, files, processes, output and events.
8
8
 
@@ -19,7 +19,7 @@ The words in these documents have one meaning each. See the glossary in [CONTEXT
19
19
 
20
20
  The package does not read `process.env`. The host program gives its environment as `environment`, and the package reads `DOCKER_HOST`, `DOCKER_CONTEXT` and `DOCKER_CONFIG` from it. Without `environment`, no variable applies, and the selection reads only the context store.
21
21
 
22
- Then `connect()` sends `GET /_ping` and negotiates the API version. Dispose the client to close its connections. An idle connection does not keep the process running.
22
+ Then `connect()` sends `GET /_ping` and negotiates the API version. Dispose the client to close its connections. After the disposal starts, a request in flight and each new request reject with a `DockerError` that says that the client was disposed. An idle connection does not keep the process running.
23
23
 
24
24
  ```ts
25
25
  import { Docker } from '@zukhruf/docker';
@@ -72,19 +72,20 @@ An error has its own class when a caller does something different after it: star
72
72
  | `ContextNotFoundError` | `Docker.connect({ context })` names a context that the context store does not have | Pick another context from `Docker.contexts()` |
73
73
  | `ContainerNotFoundError` | A request about a container gets 404 | Create the container again |
74
74
  | `ContainerNotRunningError` | An exec or a kill gets 409, or an attach finds the container exited or paused. `state` comes from an inspect: `created`, `exited`, `paused` … | Start or unpause it, then try again |
75
+ | `ContainerRunningError` | A removal without `force` gets 409, and an inspect shows that the container runs | Stop it first, or remove it with `force` |
75
76
  | `NameConflictError` | A create of a container, network or volume finds the name taken | Use `createOrGet()`, or another name |
76
- | `ImageNotFoundError` | A create gets 404 and the image is not on the engine; a pull or a push gets 404 | Pull or build the image; check the name or `auth` |
77
- | `NetworkNotFoundError` | A create gets 404 and the image is on the engine; a request about a network gets 404 | Create the network first |
77
+ | `ImageNotFoundError` | A create, a pull or a tag gets 404, or a push reports an error in its progress and an inspect does not find the image | Pull or build the image; check the name or `auth` |
78
+ | `NetworkNotFoundError` | A start or a connect gets 404 (500 on an engine in swarm mode), and a lookup finds the network missing; a request about a network gets 404 | Create the network first |
78
79
  | `VolumeNotFoundError` | A request about a volume gets 404 | Create the volume again |
79
80
  | `BindSourceNotFoundError` | A create or a start refuses a bind mount whose `source` does not exist on the engine's host | Tell the user which host path is missing |
80
81
  | `VolumeInUseError` | A volume removal gets 409 | Remove its containers first, or wait and try again |
81
- | `NetworkInUseError` | A network removal gets 403 while containers are connected | Disconnect them first |
82
+ | `NetworkInUseError` | A network removal gets 403 while running containers are connected | Disconnect them first |
82
83
  | `ImageInUseError` | An image removal gets 409 | Remove its containers first, or use `force` |
83
84
  | `PathNotFoundError` | A file request gets 404 and the container exists | Use a default, or create the file |
84
85
  | `NotAFileError` | `readFile()` finds a directory, a link or another kind of entry. `kind` names it | Use `getArchive()` for the tree |
85
86
  | `ReadOnlyPathError` | The engine refuses an extraction, and an inspect shows that the directory is on a read-only root filesystem or in a read-only mount | Write under a writable mount, or use `exec()` |
86
87
 
87
- Each class extends a base class, so a broad catch still works. `DockerError` is the base of every error of the package. `DockerEngineError` is the base of every answer of the engine that is an error. It has `status`, `method` and `path`, and its `message` is the message of the engine. An error with no class of its own, such as an error in the progress of a pull, is a `DockerEngineError`.
88
+ Each class extends a base class, so a broad catch still works. `DockerError` is the base of every error of the package. `DockerEngineError` is the base of every answer of the engine that is an error. It has `status` and `request` (the `method` and `path` of the request), and its `message` is the message of the engine. The base keeps the request apart in `request`, so `path` on `PathNotFoundError`, `NotAFileError` and `ReadOnlyPathError` is the path in the container. An error with no class of its own, such as an error in the progress of a pull, is a `DockerEngineError`.
88
89
 
89
90
  ```ts
90
91
  import {
@@ -129,7 +130,7 @@ Some answers of the engine are not errors here:
129
130
  | `container.start()` of a running container, `container.stop()` of a stopped one | 304 | resolves |
130
131
  | `remove()` of a missing container, volume, network or image | 404 | resolves |
131
132
 
132
- A removal of a container whose removal is already in progress (409) waits until the container is gone, and then resolves.
133
+ A removal of a container whose removal is already in progress (409) inspects the container until it is gone, and then resolves. When the other removal fails and the container stays, it sends a forced removal again. The signal stops the wait.
133
134
 
134
135
  ## Create a container that you own
135
136
 
@@ -245,6 +246,8 @@ await postgres.exec(
245
246
  );
246
247
  ```
247
248
 
249
+ A command can end before it reads all of its input, such as `head -c1`. The result of the command then stands: the input that the command did not read is not an error.
250
+
248
251
  `container.spawn()` gives the container process while the command runs. With `stdin: true`, the container process has a writable `stdin`. Only `stdin.close()` ends the input. The package drops an empty write, so an empty write never ends the input.
249
252
 
250
253
  ```ts
@@ -358,6 +361,11 @@ await network.connect(postgres, { Aliases: ['db'] });
358
361
  await network.disconnect(postgres);
359
362
  ```
360
363
 
364
+ - From API 1.44, the engine refuses a create with a taken name. Thus `networks.create()` rejects with `NameConflictError`, and `networks.createOrGet()` gives `{ created: false, network }` with the network that exists.
365
+ - Only a running container has an endpoint on a network. Thus only a running container keeps a network from its removal, and `NetworkInUseError` gives the IDs of the running containers.
366
+ - The engine keeps the network of a created container for its start. Thus a connect of a created container to a missing network resolves, and `start()` then rejects with `NetworkNotFoundError`.
367
+ - `remove()` of `bridge`, `host` or `none` rejects with `DockerEngineError` and 403: the engine never removes a pre-defined network.
368
+
361
369
  ## Images
362
370
 
363
371
  Images have no owner: an image is a cache that each container on the engine shares, so no handle removes one on disposal.
@@ -384,7 +392,9 @@ await docker.images.load(Readable.toWeb(createReadStream('alpine.tar')));
384
392
  await docker.images.remove('alpine:3');
385
393
  ```
386
394
 
387
- `pull()`, `push()` and `load()` read the progress of the engine until the end. They reject with `DockerEngineError` when the engine reports an error in the progress. Then `status` is `undefined`, because the HTTP status was 200. `auth` holds explicit credentials. The package runs no credential helper, because a helper is a program on the `PATH`.
395
+ `pull()`, `push()` and `load()` read the progress of the engine until the end. They reject with `DockerEngineError` when the engine reports an error in the progress. Then `status` is `undefined`, because the HTTP status was 200. The engine answers a push of an image that it does not have with 200 too, and reports the error in the progress. Thus `push()` inspects the image after an error in the progress: when the engine does not have the image, `push()` rejects with `ImageNotFoundError`.
396
+
397
+ `images.remove()` of one name of an image that has more names removes only that name. A removal of an image that a container uses, or a removal by ID of an image with more than one name, rejects with `ImageInUseError`. `auth` holds explicit credentials. The package runs no credential helper, because a helper is a program on the `PATH`.
388
398
 
389
399
  ## Events
390
400
 
@@ -788,8 +798,8 @@ export class UnsupportedEngineError extends DockerError {
788
798
  export class DockerEngineError extends DockerError {
789
799
  /** The HTTP status; undefined for an error in the progress of a pull, a push or a load. */
790
800
  readonly status: number | undefined;
791
- readonly method: string;
792
- readonly path: string;
801
+ /** The request that got the error. */
802
+ readonly request: { readonly method: string; readonly path: string };
793
803
  }
794
804
 
795
805
  export class ContainerNotFoundError extends DockerEngineError {
@@ -800,9 +810,13 @@ export class ContainerNotRunningError extends DockerEngineError {
800
810
  /** From an inspect after the 409: 'created', 'exited', 'paused', 'restarting', 'removing' or 'dead'. */
801
811
  readonly state: string;
802
812
  }
813
+ export class ContainerRunningError extends DockerEngineError {
814
+ readonly container: string;
815
+ }
803
816
  export class NameConflictError extends DockerEngineError {
804
817
  readonly kind: 'container' | 'network' | 'volume';
805
- readonly name: string;
818
+ /** The name that another resource has. Not `name`: that is the name of the error class. */
819
+ readonly takenName: string;
806
820
  }
807
821
  export class ImageNotFoundError extends DockerEngineError {
808
822
  readonly image: string;
@@ -0,0 +1,35 @@
1
+ import type { HijackedConnection } from './engine-client.ts';
2
+ import { type OutputChunk } from './multiplexed-stream.ts';
3
+ import { type ExecResult } from './output.ts';
4
+ export interface ContainerProcessOptions {
5
+ /** Gives the process a writable `stdin`. */
6
+ stdin: boolean;
7
+ /** An abort rejects the iteration with the reason of the signal. It does not stop the command. */
8
+ signal?: AbortSignal | undefined;
9
+ /**
10
+ * Gives the exit code. `outputEnded` resolves when the output ends, and
11
+ * rejects when the iteration ends first.
12
+ */
13
+ exitCode: (outputEnded: Promise<void>) => Promise<number>;
14
+ }
15
+ /**
16
+ * An exec or an attached container: one stream of output chunks in the order
17
+ * of the engine, given once, a writable `stdin` when it has one, and the exit
18
+ * code when it ends.
19
+ */
20
+ export declare class ContainerProcess implements AsyncIterable<OutputChunk> {
21
+ #private;
22
+ /** Present with { stdin: true }. Only close() ends the input; an empty write is dropped. */
23
+ readonly stdin: WritableStream<Uint8Array> | undefined;
24
+ constructor(connection: HijackedConnection, options: ContainerProcessOptions);
25
+ /**
26
+ * The exit code. For an exec, it comes after the output ends. It rejects
27
+ * when the iteration ends before the output ends.
28
+ */
29
+ get exitCode(): Promise<number>;
30
+ /** The chunks, once. A second iteration rejects. */
31
+ [Symbol.asyncIterator](): AsyncIterator<OutputChunk>;
32
+ /** Reads each chunk and gives the result. */
33
+ collect(): Promise<ExecResult>;
34
+ }
35
+ //# sourceMappingURL=container-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-process.d.ts","sourceRoot":"","sources":["../src/container-process.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,uBAAuB;IACtC,4CAA4C;IAC5C,KAAK,EAAE,OAAO,CAAC;IACf,kGAAkG;IAClG,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC;;;OAGG;IACH,QAAQ,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAmBD;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,aAAa,CAAC,WAAW,CAAC;;IACjE,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;gBAMrD,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,uBAAuB;IA6ClC;;;OAGG;IACH,IAAI,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9B;IAED,oDAAoD;IACpD,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,WAAW,CAAC;IASpD,6CAA6C;IACvC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;CAIrC"}
@@ -0,0 +1,91 @@
1
+ import { DockerError } from "./errors.js";
2
+ import { outputChunks } from "./multiplexed-stream.js";
3
+ import { readOutput } from "./output.js";
4
+ async function* withHead(head, socket) {
5
+ if (head.byteLength > 0)
6
+ yield head;
7
+ for await (const chunk of socket) {
8
+ if (chunk instanceof Uint8Array)
9
+ yield chunk;
10
+ }
11
+ }
12
+ /** Writes to the socket and resolves when the socket took the bytes. */
13
+ async function write(socket, chunk) {
14
+ const { promise, resolve, reject } = Promise.withResolvers();
15
+ socket.write(chunk, (error) => (error ? reject(error) : resolve()));
16
+ return promise;
17
+ }
18
+ /**
19
+ * An exec or an attached container: one stream of output chunks in the order
20
+ * of the engine, given once, a writable `stdin` when it has one, and the exit
21
+ * code when it ends.
22
+ */
23
+ export class ContainerProcess {
24
+ /** Present with { stdin: true }. Only close() ends the input; an empty write is dropped. */
25
+ stdin;
26
+ /** Holds the chunks until the first iteration takes them. */
27
+ #unread;
28
+ #exitCode;
29
+ constructor(connection, options) {
30
+ const { socket, head, contentType } = connection;
31
+ const { signal } = options;
32
+ // An error before the first read waits for the iteration, which rejects with it.
33
+ socket.on('error', () => undefined);
34
+ const abort = () => socket.destroy(signal?.reason);
35
+ signal?.addEventListener('abort', abort, { once: true });
36
+ socket.once('close', () => signal?.removeEventListener('abort', abort));
37
+ const outputEnded = Promise.withResolvers();
38
+ outputEnded.promise.catch(() => undefined);
39
+ async function* read() {
40
+ try {
41
+ signal?.throwIfAborted();
42
+ yield* outputChunks(withHead(head, socket), contentType);
43
+ outputEnded.resolve();
44
+ }
45
+ catch (error) {
46
+ outputEnded.reject(error);
47
+ throw error;
48
+ }
49
+ finally {
50
+ outputEnded.reject(new DockerError('The iteration ended before the output ended.'));
51
+ socket.destroy();
52
+ }
53
+ }
54
+ this.#unread = [read()];
55
+ const exitCode = options.exitCode(outputEnded.promise);
56
+ // A caller that never reads the exit code gets no unhandled rejection.
57
+ exitCode.catch(() => undefined);
58
+ this.#exitCode = () => exitCode;
59
+ this.stdin = options.stdin
60
+ ? new WritableStream({
61
+ write: async (chunk) => {
62
+ if (chunk.byteLength > 0)
63
+ await write(socket, chunk);
64
+ },
65
+ close: () => connection.endInput(),
66
+ abort: () => connection.endInput(),
67
+ })
68
+ : undefined;
69
+ }
70
+ /**
71
+ * The exit code. For an exec, it comes after the output ends. It rejects
72
+ * when the iteration ends before the output ends.
73
+ */
74
+ get exitCode() {
75
+ return this.#exitCode();
76
+ }
77
+ /** The chunks, once. A second iteration rejects. */
78
+ [Symbol.asyncIterator]() {
79
+ const chunks = this.#unread.pop();
80
+ if (chunks !== undefined)
81
+ return chunks;
82
+ const error = new DockerError('A container process gives its output once, and an iteration already read it.');
83
+ return { next: async () => Promise.reject(error) };
84
+ }
85
+ /** Reads each chunk and gives the result. */
86
+ async collect() {
87
+ const output = await readOutput(this);
88
+ return { exitCode: await this.exitCode, ...output };
89
+ }
90
+ }
91
+ //# sourceMappingURL=container-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-process.js","sourceRoot":"","sources":["../src/container-process.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAoB,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAmB,UAAU,EAAE,MAAM,aAAa,CAAC;AAc1D,KAAK,SAAS,CAAC,CAAC,QAAQ,CACtB,IAAY,EACZ,MAAc;IAEd,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC;QAAE,MAAM,IAAI,CAAC;IACpC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,YAAY,UAAU;YAAE,MAAM,KAAK,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,KAAK,UAAU,KAAK,CAAC,MAAc,EAAE,KAAiB;IACpD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAC;IACnE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,gBAAgB;IAC3B,4FAA4F;IACnF,KAAK,CAAyC;IACvD,6DAA6D;IACpD,OAAO,CAAgC;IACvC,SAAS,CAAwB;IAE1C,YACE,UAA8B,EAC9B,OAAgC;QAEhC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC;QACjD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,iFAAiF;QACjF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAExE,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,EAAQ,CAAC;QAClD,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3C,KAAK,SAAS,CAAC,CAAC,IAAI;YAClB,IAAI,CAAC;gBACH,MAAM,EAAE,cAAc,EAAE,CAAC;gBACzB,KAAK,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;gBACzD,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,MAAM,KAAK,CAAC;YACd,CAAC;oBAAS,CAAC;gBACT,WAAW,CAAC,MAAM,CAChB,IAAI,WAAW,CAAC,8CAA8C,CAAC,CAChE,CAAC;gBACF,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAExB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACvD,uEAAuE;QACvE,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC;QAEhC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK;YACxB,CAAC,CAAC,IAAI,cAAc,CAAa;gBAC7B,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;oBACrB,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC;wBAAE,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACvD,CAAC;gBACD,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;gBAClC,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;aACnC,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1B,CAAC;IAED,oDAAoD;IACpD,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,WAAW,CAC3B,8EAA8E,CAC/E,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,OAAO;QACX,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;IACtD,CAAC;CACF"}
@@ -0,0 +1,160 @@
1
+ import { ContainerProcess } from './container-process.ts';
2
+ import type * as EngineApi from './engine-api.ts';
3
+ import { type EngineClient } from './engine-client.ts';
4
+ import type { Images } from './images.ts';
5
+ import { type OutputChunk } from './multiplexed-stream.ts';
6
+ import type { CancelOptions, Filters } from './options.ts';
7
+ import { type ExecResult, type Output } from './output.ts';
8
+ /** A file or a directory for writeFiles(). It belongs to uid:gid, or to 0:0. */
9
+ export type ContainerFile = {
10
+ path: string;
11
+ data: string | Uint8Array;
12
+ mode?: number;
13
+ uid?: number;
14
+ gid?: number;
15
+ } | {
16
+ path: string;
17
+ directory: true;
18
+ mode?: number;
19
+ uid?: number;
20
+ gid?: number;
21
+ };
22
+ /** The query of the logs, as the Engine API takes it. */
23
+ export interface LogQuery {
24
+ tail?: number | 'all';
25
+ /** Seconds since the Unix epoch. */
26
+ since?: number;
27
+ timestamps?: boolean;
28
+ }
29
+ /** The result of createOrGet(): an owned handle when this call created the resource. */
30
+ export type Created<Key extends string, Owned, Found> = ({
31
+ created: true;
32
+ } & Record<Key, Owned>) | ({
33
+ created: false;
34
+ } & Record<Key, Found>);
35
+ /** A container, by the ID that the engine gave it. It has no disposal. */
36
+ export declare class Container {
37
+ #private;
38
+ readonly id: string;
39
+ constructor(client: EngineClient, id: string);
40
+ inspect(options?: CancelOptions): Promise<EngineApi.ContainerInspectResponse>;
41
+ /**
42
+ * Resolves for 304 (already running). A 404 is a missing container or a
43
+ * missing network: an inspect of the container tells which, and a lookup of
44
+ * each of its networks names the missing one. An engine in swarm mode
45
+ * answers a missing network with 500 ("Could not attach to network", probed
46
+ * at API 1.51), so a 500 gets the same lookup.
47
+ */
48
+ start(options?: CancelOptions): Promise<void>;
49
+ /** Resolves for 304 (already stopped). t: seconds before the engine kills the main process. */
50
+ stop(options?: CancelOptions & {
51
+ t?: number;
52
+ }): Promise<void>;
53
+ restart(options?: CancelOptions & {
54
+ t?: number;
55
+ }): Promise<void>;
56
+ /** A 409 means that the container does not run: an inspect gives its state. */
57
+ kill(processSignal?: string, options?: CancelOptions): Promise<void>;
58
+ pause(options?: CancelOptions): Promise<void>;
59
+ unpause(options?: CancelOptions): Promise<void>;
60
+ wait(options?: CancelOptions & {
61
+ condition?: 'not-running' | 'next-exit' | 'removed';
62
+ }): Promise<EngineApi.ContainerWaitResponse>;
63
+ /**
64
+ * Resolves for 404. A 409 is a removal that is already in progress, or,
65
+ * without `force`, a container that runs: an inspect tells which. For a
66
+ * removal in progress, it inspects the container until it is gone, and
67
+ * when the other removal ends and leaves the container, it sends a forced
68
+ * removal again. A container
69
+ * that runs or is paused rejects with ContainerRunningError. The engine
70
+ * gives the state 'running' while it kills the container for a forced
71
+ * removal, so with `force` the state alone does not tell: it waits.
72
+ */
73
+ remove(options?: CancelOptions & {
74
+ force?: boolean;
75
+ v?: boolean;
76
+ }): Promise<void>;
77
+ /** The published binding of a port of the container; undefined when it is not published. The IPv4 binding comes first. */
78
+ port(containerPort: number, protocol?: 'tcp' | 'udp', options?: CancelOptions): Promise<{
79
+ host: string;
80
+ port: number;
81
+ } | undefined>;
82
+ /** The logs until now. */
83
+ logs(options?: CancelOptions & LogQuery): Promise<Output>;
84
+ /** The logs as the container writes them. The iteration ends when the container stops. */
85
+ followLogs(options?: CancelOptions & LogQuery): AsyncIterable<OutputChunk>;
86
+ /** Runs a command in the container and reads its output to the end. stdin is sent, and then the input ends. */
87
+ exec(body: EngineApi.ExecConfig, options?: CancelOptions & {
88
+ stdin?: string | Uint8Array;
89
+ }): Promise<ExecResult>;
90
+ /** Runs a command in the container and gives its container process while it runs. */
91
+ spawn(body: EngineApi.ExecConfig, options?: CancelOptions & {
92
+ stdin?: boolean;
93
+ }): Promise<ContainerProcess>;
94
+ /**
95
+ * Attaches to the output of the container. For a created container, the
96
+ * exit code is the code of its next exit, so attach before start(). For a
97
+ * running container, it is the code when the container stops. An exited or
98
+ * a paused container rejects with ContainerNotRunningError: Moby gives no
99
+ * 409 for an attach to an exited container, so the state of an inspect
100
+ * decides.
101
+ */
102
+ attach(options?: CancelOptions & {
103
+ stdin?: boolean;
104
+ }): Promise<ContainerProcess>;
105
+ /** The path stat of a path in the container: HEAD of the archive. undefined for 404 when the container exists. */
106
+ stat(path: string, options?: CancelOptions): Promise<EngineApi.ContainerPathStat | undefined>;
107
+ /** The bytes of one regular file. A directory, a link and each other kind of entry reject with NotAFileError. */
108
+ readFile(path: string, options?: CancelOptions): Promise<Uint8Array>;
109
+ /**
110
+ * Sends one archive of the files. It extracts the archive in the deepest
111
+ * directory that holds each entry and that exists, which stat() finds. The
112
+ * engine creates each parent directory that an entry implies below it.
113
+ */
114
+ writeFiles(files: ContainerFile[], options?: CancelOptions): Promise<void>;
115
+ /** The tar stream of a path in the container: a file, or a directory with its tree. */
116
+ getArchive(path: string, options?: CancelOptions): Promise<ReadableStream<Uint8Array>>;
117
+ /**
118
+ * Extracts a tar archive in a directory of the container. With copyUIDGID,
119
+ * each entry belongs to the user of the container (Config.User), not to
120
+ * the owner in the archive.
121
+ */
122
+ putArchive(directory: string, archive: ReadableStream<Uint8Array> | Uint8Array, options?: CancelOptions & {
123
+ copyUIDGID?: boolean;
124
+ }): Promise<void>;
125
+ }
126
+ /** A container that this handle created. Disposal removes it with its anonymous volumes. */
127
+ export declare class OwnedContainer extends Container implements AsyncDisposable {
128
+ /** remove({ force: true, v: true }) */
129
+ [Symbol.asyncDispose](): Promise<void>;
130
+ }
131
+ /** The containers of one engine. */
132
+ export declare class Containers {
133
+ #private;
134
+ constructor(client: EngineClient, images: Images);
135
+ create(body: EngineApi.ContainerCreateRequest, options?: CancelOptions & {
136
+ name?: string;
137
+ platform?: string;
138
+ }): Promise<OwnedContainer>;
139
+ /**
140
+ * Creates the container, attaches to its output, starts it, waits for its
141
+ * end, and removes it. A non-zero exit code is data. A failure and a cancel
142
+ * also remove the container.
143
+ */
144
+ run(body: EngineApi.ContainerCreateRequest, options?: CancelOptions & {
145
+ name?: string;
146
+ platform?: string;
147
+ }): Promise<ExecResult>;
148
+ /** Creates the container, or gives the one that has the name. The container that exists wins. */
149
+ createOrGet(body: EngineApi.ContainerCreateRequest, options: CancelOptions & {
150
+ name: string;
151
+ platform?: string;
152
+ }): Promise<Created<'container', OwnedContainer, Container>>;
153
+ /** One inspect: a handle with the ID of the engine, or undefined for 404. */
154
+ find(nameOrId: string, options?: CancelOptions): Promise<Container | undefined>;
155
+ list(options?: CancelOptions & {
156
+ all?: boolean;
157
+ filters?: Filters;
158
+ }): Promise<EngineApi.ContainerSummary[]>;
159
+ }
160
+ //# sourceMappingURL=containers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containers.d.ts","sourceRoot":"","sources":["../src/containers.ts"],"names":[],"mappings":"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"}