@zukhruf/testing 0.0.0-stage → 0.3.3
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/LICENSE +21 -0
- package/README.md +143 -2
- package/dist/async/index.d.ts +3 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/async/index.js +3 -0
- package/dist/async/index.js.map +1 -0
- package/dist/async/settle-within.d.ts +2 -0
- package/dist/async/settle-within.d.ts.map +1 -0
- package/dist/async/settle-within.js +6 -0
- package/dist/async/settle-within.js.map +1 -0
- package/dist/async/timebox.d.ts +41 -0
- package/dist/async/timebox.d.ts.map +1 -0
- package/dist/async/timebox.js +42 -0
- package/dist/async/timebox.js.map +1 -0
- package/dist/databases/bigquery.d.ts +16 -0
- package/dist/databases/bigquery.d.ts.map +1 -0
- package/dist/databases/bigquery.js +21 -0
- package/dist/databases/bigquery.js.map +1 -0
- package/dist/databases/clickhouse.d.ts +12 -0
- package/dist/databases/clickhouse.d.ts.map +1 -0
- package/dist/databases/clickhouse.js +22 -0
- package/dist/databases/clickhouse.js.map +1 -0
- package/dist/databases/database.d.ts +24 -0
- package/dist/databases/database.d.ts.map +1 -0
- package/dist/databases/database.js +2 -0
- package/dist/databases/database.js.map +1 -0
- package/dist/databases/duckdb.d.ts +11 -0
- package/dist/databases/duckdb.d.ts.map +1 -0
- package/dist/databases/duckdb.js +13 -0
- package/dist/databases/duckdb.js.map +1 -0
- package/dist/databases/mysql.d.ts +17 -0
- package/dist/databases/mysql.d.ts.map +1 -0
- package/dist/databases/mysql.js +116 -0
- package/dist/databases/mysql.js.map +1 -0
- package/dist/databases/postgres.d.ts +15 -0
- package/dist/databases/postgres.d.ts.map +1 -0
- package/dist/databases/postgres.js +96 -0
- package/dist/databases/postgres.js.map +1 -0
- package/dist/databases/sqlite.d.ts +14 -0
- package/dist/databases/sqlite.d.ts.map +1 -0
- package/dist/databases/sqlite.js +66 -0
- package/dist/databases/sqlite.js.map +1 -0
- package/dist/databases/sqlserver.d.ts +14 -0
- package/dist/databases/sqlserver.d.ts.map +1 -0
- package/dist/databases/sqlserver.js +134 -0
- package/dist/databases/sqlserver.js.map +1 -0
- package/dist/docker/container.d.ts +29 -0
- package/dist/docker/container.d.ts.map +1 -0
- package/dist/docker/container.js +55 -0
- package/dist/docker/container.js.map +1 -0
- package/dist/docker/directory.d.ts +18 -0
- package/dist/docker/directory.d.ts.map +1 -0
- package/dist/docker/directory.js +77 -0
- package/dist/docker/directory.js.map +1 -0
- package/dist/docker/docker.d.ts +74 -0
- package/dist/docker/docker.d.ts.map +1 -0
- package/dist/docker/docker.js +257 -0
- package/dist/docker/docker.js.map +1 -0
- package/dist/docker/host.d.ts +17 -0
- package/dist/docker/host.d.ts.map +1 -0
- package/dist/docker/host.js +172 -0
- package/dist/docker/host.js.map +1 -0
- package/dist/docker/index.d.ts +7 -0
- package/dist/docker/index.d.ts.map +1 -0
- package/dist/docker/index.js +7 -0
- package/dist/docker/index.js.map +1 -0
- package/dist/docker/skip-without-docker.d.ts +12 -0
- package/dist/docker/skip-without-docker.d.ts.map +1 -0
- package/dist/docker/skip-without-docker.js +31 -0
- package/dist/docker/skip-without-docker.js.map +1 -0
- package/dist/docker/test-run.d.ts +32 -0
- package/dist/docker/test-run.d.ts.map +1 -0
- package/dist/docker/test-run.js +52 -0
- package/dist/docker/test-run.js.map +1 -0
- package/dist/docker/volume.d.ts +9 -0
- package/dist/docker/volume.d.ts.map +1 -0
- package/dist/docker/volume.js +13 -0
- package/dist/docker/volume.js.map +1 -0
- package/dist/http/http-server.d.ts +12 -0
- package/dist/http/http-server.d.ts.map +1 -0
- package/dist/http/http-server.js +29 -0
- package/dist/http/http-server.js.map +1 -0
- package/dist/scripts/run-docker-tests.d.ts +3 -0
- package/dist/scripts/run-docker-tests.d.ts.map +1 -0
- package/dist/scripts/run-docker-tests.js +227 -0
- package/dist/scripts/run-docker-tests.js.map +1 -0
- package/dist/streams/stream-harness.d.ts +29 -0
- package/dist/streams/stream-harness.d.ts.map +1 -0
- package/dist/streams/stream-harness.js +75 -0
- package/dist/streams/stream-harness.js.map +1 -0
- package/package.json +123 -4
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Container, type DockerCommand, ServiceContainer } from './container.ts';
|
|
2
|
+
import { DockerDirectory } from './directory.ts';
|
|
3
|
+
import type { TestRun } from './test-run.ts';
|
|
4
|
+
import { DockerVolume } from './volume.ts';
|
|
5
|
+
/** Marks a server that `reuse()` shares across processes and runs. */
|
|
6
|
+
export declare const SHARED_SERVER_LABEL = "dev.zukhruf.testing.shared";
|
|
7
|
+
/** A volume or a path on the engine's host, mounted into a container. */
|
|
8
|
+
export interface Mount {
|
|
9
|
+
/** A volume's name, or an absolute path on the engine's host. */
|
|
10
|
+
source: string;
|
|
11
|
+
target: string;
|
|
12
|
+
readOnly?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ContainerOptions {
|
|
15
|
+
image: string;
|
|
16
|
+
/** Replaces the image's command. */
|
|
17
|
+
command?: string[];
|
|
18
|
+
/** A dedicated container gets a generated name; a reused server's name comes from its configuration. */
|
|
19
|
+
name?: string;
|
|
20
|
+
hostname?: string;
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
labels?: Record<string, string> | undefined;
|
|
23
|
+
mounts?: Mount[];
|
|
24
|
+
tmpfs?: string[];
|
|
25
|
+
ipcHost?: boolean;
|
|
26
|
+
memory?: string;
|
|
27
|
+
cpus?: number;
|
|
28
|
+
memorySwappiness?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ServiceOptions extends ContainerOptions {
|
|
31
|
+
/** The container's port that answers on 127.0.0.1 of this machine. */
|
|
32
|
+
internalPort: number;
|
|
33
|
+
/** Runs on every acquisition. Throw until ready, using timebox for polling. */
|
|
34
|
+
healthy?: (container: ServiceContainer) => unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface DockerOptions {
|
|
37
|
+
/** The supervised run that owns what this process creates: `TestRun.fromEnvironment(process.env)`. */
|
|
38
|
+
testRun?: TestRun | undefined;
|
|
39
|
+
}
|
|
40
|
+
/** The engine the Docker CLI selects. Docker owns cross-process coordination; this instance holds no server cache. */
|
|
41
|
+
export declare class Docker {
|
|
42
|
+
#private;
|
|
43
|
+
constructor({ testRun }?: DockerOptions);
|
|
44
|
+
/** Defaults for disposable test containers, including external launchers. */
|
|
45
|
+
get defaults(): {
|
|
46
|
+
resources: {
|
|
47
|
+
memory: string;
|
|
48
|
+
cpus: number;
|
|
49
|
+
};
|
|
50
|
+
labels: Record<string, string>;
|
|
51
|
+
};
|
|
52
|
+
info(): Promise<{
|
|
53
|
+
architecture: string;
|
|
54
|
+
endpoint: string;
|
|
55
|
+
}>;
|
|
56
|
+
directory(): Promise<DockerDirectory>;
|
|
57
|
+
/** Creates a named volume. The supervisor removes it with its run if this process dies first. */
|
|
58
|
+
volume(): Promise<DockerVolume>;
|
|
59
|
+
readonly command: DockerCommand;
|
|
60
|
+
isAvailable(): Promise<boolean>;
|
|
61
|
+
/** Starts a container this process owns. It stays after it stops, until disposal removes it. */
|
|
62
|
+
start(options: ContainerOptions): Promise<Container>;
|
|
63
|
+
/** Starts a dedicated server. Failure or disposal stops it, and a stopped server is removed. */
|
|
64
|
+
serve(options: ServiceOptions): Promise<ServiceContainer>;
|
|
65
|
+
/**
|
|
66
|
+
* Reuse one server per configuration on this daemon, across processes/runs.
|
|
67
|
+
* Docker reserves the name atomically. Readiness failure leaves it running.
|
|
68
|
+
* Only explicitly dispose the returned container after all its users finish.
|
|
69
|
+
*/
|
|
70
|
+
reuse(options: ServiceOptions): Promise<ServiceContainer>;
|
|
71
|
+
/** Runs a container to completion and returns its standard output. The container is removed afterwards. */
|
|
72
|
+
run(options: ContainerOptions): Promise<string>;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/docker/docker.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,SAAS,EACT,KAAK,aAAa,EAClB,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAGhE,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wGAAwG;IACxG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC5C,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,gBAAgB,KAAK,OAAO,CAAC;CACpD;AAED,MAAM,WAAW,aAAa;IAC5B,sGAAsG;IACtG,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B;AA2BD,sHAAsH;AACtH,qBAAa,MAAM;;gBAaL,EAAE,OAAO,EAAE,GAAE,aAAkB;IAI3C,6EAA6E;IAC7E,IAAI,QAAQ,IAAI;QACd,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAKA;IAEK,IAAI,IAAI,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAU3D,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAI3C,iGAAiG;IAC3F,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAYrC,QAAQ,CAAC,OAAO,EAAE,aAAa,CACY;IAErC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAarC,gGAAgG;IAC1F,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IAK1D,gGAAgG;IAC1F,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAe/D;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqB/D,2GAA2G;IACrG,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;CAgLtD"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { SubprocessError } from 'nano-spawn';
|
|
3
|
+
import pMemoize from 'p-memoize';
|
|
4
|
+
import { timebox } from "../async/timebox.js";
|
|
5
|
+
import { Container, ServiceContainer, } from "./container.js";
|
|
6
|
+
import { DockerDirectory } from "./directory.js";
|
|
7
|
+
import { DockerHost } from "./host.js";
|
|
8
|
+
import { DockerVolume } from "./volume.js";
|
|
9
|
+
/** Marks a server that `reuse()` shares across processes and runs. */
|
|
10
|
+
export const SHARED_SERVER_LABEL = 'dev.zukhruf.testing.shared';
|
|
11
|
+
const CONFIG_LABEL = 'dev.zukhruf.testing.config';
|
|
12
|
+
const labelArgs = (labels) => Object.entries(labels).flatMap(([key, value]) => [
|
|
13
|
+
'--label',
|
|
14
|
+
`${key}=${value}`,
|
|
15
|
+
]);
|
|
16
|
+
const publish = (internalPort) => [
|
|
17
|
+
'-p',
|
|
18
|
+
`127.0.0.1::${internalPort}`,
|
|
19
|
+
];
|
|
20
|
+
const byKey = ([a], [b]) => {
|
|
21
|
+
if (a < b)
|
|
22
|
+
return -1;
|
|
23
|
+
if (a > b)
|
|
24
|
+
return 1;
|
|
25
|
+
return 0;
|
|
26
|
+
};
|
|
27
|
+
/** The engine the Docker CLI selects. Docker owns cross-process coordination; this instance holds no server cache. */
|
|
28
|
+
export class Docker {
|
|
29
|
+
#testRun;
|
|
30
|
+
// The first resolution pins the engine: handles keep the engine they were made on.
|
|
31
|
+
#connection = pMemoize(() => DockerHost.resolve());
|
|
32
|
+
#probe = pMemoize(async () => {
|
|
33
|
+
try {
|
|
34
|
+
await this.command(['info']);
|
|
35
|
+
return { available: true };
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
return { available: false, cause };
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
constructor({ testRun } = {}) {
|
|
42
|
+
this.#testRun = testRun;
|
|
43
|
+
}
|
|
44
|
+
/** Defaults for disposable test containers, including external launchers. */
|
|
45
|
+
get defaults() {
|
|
46
|
+
return {
|
|
47
|
+
resources: { memory: '1g', cpus: 1 },
|
|
48
|
+
labels: this.#testRun?.labels ?? {},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async info() {
|
|
52
|
+
const host = await this.#connection();
|
|
53
|
+
const { stdout } = await host.command([
|
|
54
|
+
'info',
|
|
55
|
+
'--format',
|
|
56
|
+
'{{.Architecture}}',
|
|
57
|
+
]);
|
|
58
|
+
return { architecture: stdout.trim(), endpoint: host.endpoint };
|
|
59
|
+
}
|
|
60
|
+
async directory() {
|
|
61
|
+
return DockerDirectory.create(await this.#connection(), this.#testRun);
|
|
62
|
+
}
|
|
63
|
+
/** Creates a named volume. The supervisor removes it with its run if this process dies first. */
|
|
64
|
+
async volume() {
|
|
65
|
+
const host = await this.#require();
|
|
66
|
+
const name = `zukhruf-testing-${randomUUID()}`;
|
|
67
|
+
await host.command([
|
|
68
|
+
'volume',
|
|
69
|
+
'create',
|
|
70
|
+
...labelArgs(this.defaults.labels),
|
|
71
|
+
name,
|
|
72
|
+
]);
|
|
73
|
+
return new DockerVolume(name, host.command);
|
|
74
|
+
}
|
|
75
|
+
command = async (args) => (await this.#connection()).command(args);
|
|
76
|
+
async isAvailable() {
|
|
77
|
+
return (await this.#probe()).available;
|
|
78
|
+
}
|
|
79
|
+
async #require() {
|
|
80
|
+
const probe = await this.#probe();
|
|
81
|
+
if (!probe.available)
|
|
82
|
+
throw new Error('Docker is required for container-backed tests', {
|
|
83
|
+
cause: probe.cause,
|
|
84
|
+
});
|
|
85
|
+
return this.#connection();
|
|
86
|
+
}
|
|
87
|
+
/** Starts a container this process owns. It stays after it stops, until disposal removes it. */
|
|
88
|
+
async start(options) {
|
|
89
|
+
const host = await this.#require();
|
|
90
|
+
return new Container(await this.#detach(host, [], options), host.command);
|
|
91
|
+
}
|
|
92
|
+
/** Starts a dedicated server. Failure or disposal stops it, and a stopped server is removed. */
|
|
93
|
+
async serve(options) {
|
|
94
|
+
const host = await this.#require();
|
|
95
|
+
const id = await this.#detach(host, ['--rm', ...publish(options.internalPort)], options);
|
|
96
|
+
try {
|
|
97
|
+
return await this.#ready(host, id, options);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
await host.command(['stop', id]).catch(() => { });
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reuse one server per configuration on this daemon, across processes/runs.
|
|
106
|
+
* Docker reserves the name atomically. Readiness failure leaves it running.
|
|
107
|
+
* Only explicitly dispose the returned container after all its users finish.
|
|
108
|
+
*/
|
|
109
|
+
async reuse(options) {
|
|
110
|
+
const host = await this.#require();
|
|
111
|
+
const fingerprint = this.#fingerprint(options);
|
|
112
|
+
const name = options.name ?? `zukhruf-testing-${fingerprint}`;
|
|
113
|
+
const inspected = (await this.#inspect(host, name)) ??
|
|
114
|
+
(await this.#create(host, name, fingerprint, options));
|
|
115
|
+
if (inspected.Config.Labels?.[SHARED_SERVER_LABEL] !== '1' ||
|
|
116
|
+
inspected.Config.Labels[CONFIG_LABEL] !== fingerprint) {
|
|
117
|
+
throw new Error(`Container ${name} does not match the shared test configuration`);
|
|
118
|
+
}
|
|
119
|
+
if (inspected.State.Status !== 'running') {
|
|
120
|
+
await host.command(['start', inspected.Id]);
|
|
121
|
+
}
|
|
122
|
+
return this.#ready(host, inspected.Id, options);
|
|
123
|
+
}
|
|
124
|
+
/** Runs a container to completion and returns its standard output. The container is removed afterwards. */
|
|
125
|
+
async run(options) {
|
|
126
|
+
const host = await this.#require();
|
|
127
|
+
return this.#launch(host, ['run', '--rm'], options);
|
|
128
|
+
}
|
|
129
|
+
async #create(host, name, fingerprint, options) {
|
|
130
|
+
try {
|
|
131
|
+
await host.command([
|
|
132
|
+
'create',
|
|
133
|
+
'--rm',
|
|
134
|
+
...publish(options.internalPort),
|
|
135
|
+
...this.#args({
|
|
136
|
+
...options,
|
|
137
|
+
name,
|
|
138
|
+
labels: {
|
|
139
|
+
...options.labels,
|
|
140
|
+
[SHARED_SERVER_LABEL]: '1',
|
|
141
|
+
[CONFIG_LABEL]: fingerprint,
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
options.image,
|
|
145
|
+
...(options.command ?? []),
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (!(error instanceof SubprocessError) ||
|
|
150
|
+
!error.stderr.includes('is already in use by container'))
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
// A competing create reserves the name before Docker registers it for
|
|
154
|
+
// inspection. Retry only that absence, not daemon or permission errors.
|
|
155
|
+
const pending = new Error(`Container ${name} is not visible yet`);
|
|
156
|
+
return timebox(async () => {
|
|
157
|
+
const container = await this.#inspect(host, name);
|
|
158
|
+
if (!container)
|
|
159
|
+
throw pending;
|
|
160
|
+
return container;
|
|
161
|
+
}, { shouldRetry: ({ error }) => error === pending });
|
|
162
|
+
}
|
|
163
|
+
async #detach(host, flags, options) {
|
|
164
|
+
const id = (await this.#launch(host, ['run', '--detach', ...flags], options)).trim();
|
|
165
|
+
if (!id)
|
|
166
|
+
throw new Error(`Docker started no container from ${options.image}`);
|
|
167
|
+
return id;
|
|
168
|
+
}
|
|
169
|
+
async #launch(host, verb, options) {
|
|
170
|
+
const name = options.name ??
|
|
171
|
+
`test-${options.image.replace(/[^a-zA-Z0-9_.-]/g, '-')}-${randomUUID()}`;
|
|
172
|
+
const labels = { ...options.labels, ...this.defaults.labels };
|
|
173
|
+
const { stdout } = await host.command([
|
|
174
|
+
...verb,
|
|
175
|
+
...this.#args({ ...options, name, labels }),
|
|
176
|
+
options.image,
|
|
177
|
+
...(options.command ?? []),
|
|
178
|
+
]);
|
|
179
|
+
return stdout;
|
|
180
|
+
}
|
|
181
|
+
#fingerprint(options) {
|
|
182
|
+
const { resources } = this.defaults;
|
|
183
|
+
return createHash('sha256')
|
|
184
|
+
.update(JSON.stringify({
|
|
185
|
+
transportVersion: 2,
|
|
186
|
+
memory: options.memory ?? resources.memory,
|
|
187
|
+
cpus: options.cpus ?? resources.cpus,
|
|
188
|
+
image: options.image,
|
|
189
|
+
command: options.command ?? [],
|
|
190
|
+
hostname: options.hostname,
|
|
191
|
+
internalPort: options.internalPort,
|
|
192
|
+
env: Object.entries(options.env ?? {}).sort(byKey),
|
|
193
|
+
labels: Object.entries(options.labels ?? {}).sort(byKey),
|
|
194
|
+
mounts: options.mounts ?? [],
|
|
195
|
+
tmpfs: options.tmpfs ?? [],
|
|
196
|
+
ipcHost: Boolean(options.ipcHost),
|
|
197
|
+
memorySwappiness: options.memorySwappiness,
|
|
198
|
+
}))
|
|
199
|
+
.digest('hex');
|
|
200
|
+
}
|
|
201
|
+
#args(options) {
|
|
202
|
+
const { resources } = this.defaults;
|
|
203
|
+
return [
|
|
204
|
+
'--name',
|
|
205
|
+
options.name,
|
|
206
|
+
...(options.hostname ? ['--hostname', options.hostname] : []),
|
|
207
|
+
...labelArgs(options.labels ?? {}),
|
|
208
|
+
...Object.entries(options.env ?? {}).flatMap(([key, value]) => [
|
|
209
|
+
'-e',
|
|
210
|
+
`${key}=${value}`,
|
|
211
|
+
]),
|
|
212
|
+
...(options.mounts ?? []).flatMap(({ source, target, readOnly }) => [
|
|
213
|
+
'--volume',
|
|
214
|
+
`${source}:${target}${readOnly ? ':ro' : ''}`,
|
|
215
|
+
]),
|
|
216
|
+
...(options.tmpfs ?? []).flatMap((spec) => ['--tmpfs', spec]),
|
|
217
|
+
...(options.ipcHost ? ['--ipc=host'] : []),
|
|
218
|
+
...(options.memorySwappiness === undefined
|
|
219
|
+
? []
|
|
220
|
+
: [`--memory-swappiness=${options.memorySwappiness}`]),
|
|
221
|
+
'--memory',
|
|
222
|
+
options.memory ?? resources.memory,
|
|
223
|
+
'--cpus',
|
|
224
|
+
String(options.cpus ?? resources.cpus),
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
async #inspect(host, name) {
|
|
228
|
+
try {
|
|
229
|
+
const { stdout } = await host.command(['container', 'inspect', name]);
|
|
230
|
+
const [container] = JSON.parse(stdout);
|
|
231
|
+
return container;
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
if (error instanceof SubprocessError &&
|
|
235
|
+
/No such (object|container):/i.test(error.stderr))
|
|
236
|
+
return undefined;
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async #ready(host, id, { internalPort, healthy }) {
|
|
241
|
+
const { stdout } = await host.command(['port', id, String(internalPort)]);
|
|
242
|
+
const port = /:(\d+)$/.exec(stdout.trim())?.[1];
|
|
243
|
+
if (!port)
|
|
244
|
+
throw new Error(`Failed to get mapped port for container ${id}: ${stdout}`);
|
|
245
|
+
const connection = await host.forward(Number.parseInt(port, 10), this.#testRun);
|
|
246
|
+
const container = new ServiceContainer(id, connection.port, host.command, connection.close);
|
|
247
|
+
try {
|
|
248
|
+
await healthy?.(container);
|
|
249
|
+
return container;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
await container.disconnect();
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/docker/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type TestRun } from './test-run.ts';
|
|
2
|
+
export declare const quote: (value: string) => string;
|
|
3
|
+
/** Resolve with the Docker CLI so its native context/environment precedence wins. */
|
|
4
|
+
export declare class DockerHost {
|
|
5
|
+
readonly endpoint: string;
|
|
6
|
+
private constructor();
|
|
7
|
+
static resolve(): Promise<DockerHost>;
|
|
8
|
+
get remote(): boolean;
|
|
9
|
+
readonly command: (args: string[]) => import("nano-spawn").Subprocess;
|
|
10
|
+
sshArgs(options?: string[]): string[];
|
|
11
|
+
shell(script: string, input?: string): import("nano-spawn").Subprocess;
|
|
12
|
+
forward(port: number, run: TestRun | undefined): Promise<{
|
|
13
|
+
port: number;
|
|
14
|
+
close: () => Promise<void>;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/docker/host.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,eAAe,CAAC;AAE5D,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,MACC,CAAC;AAKxC,qFAAqF;AACrF,qBAAa,UAAU;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO;WAIM,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IAgB3C,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,QAAQ,CAAC,OAAO,GAAI,MAAM,MAAM,EAAE,qCAI7B;IAEL,OAAO,CAAC,OAAO,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAqBzC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAO9B,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,OAAO,GAAG,SAAS,GACvB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CAuHzD"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import { mkdtempDisposable, stat } from 'node:fs/promises';
|
|
4
|
+
import { connect, createServer } from 'node:net';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import command from 'nano-spawn';
|
|
7
|
+
import { timebox } from "../async/timebox.js";
|
|
8
|
+
import { forwardPrefix } from "./test-run.js";
|
|
9
|
+
export const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
10
|
+
/** Local Unix sockets, Docker Desktop's named pipe on Windows, and SSH engines. A `tcp://` engine may be on another machine. */
|
|
11
|
+
const supportedEndpoints = ['unix://', 'npipe://', 'ssh://'];
|
|
12
|
+
/** Resolve with the Docker CLI so its native context/environment precedence wins. */
|
|
13
|
+
export class DockerHost {
|
|
14
|
+
endpoint;
|
|
15
|
+
constructor(endpoint) {
|
|
16
|
+
this.endpoint = endpoint;
|
|
17
|
+
}
|
|
18
|
+
static async resolve() {
|
|
19
|
+
const { stdout } = await command('docker', [
|
|
20
|
+
'context',
|
|
21
|
+
'inspect',
|
|
22
|
+
'--format',
|
|
23
|
+
'{{.Endpoints.docker.Host}}',
|
|
24
|
+
]);
|
|
25
|
+
const endpoint = stdout.trim();
|
|
26
|
+
if (!supportedEndpoints.some((scheme) => endpoint.startsWith(scheme))) {
|
|
27
|
+
throw new Error(`Unsupported Docker endpoint ${endpoint}; tests support ${supportedEndpoints.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
return new DockerHost(endpoint);
|
|
30
|
+
}
|
|
31
|
+
get remote() {
|
|
32
|
+
return this.endpoint.startsWith('ssh://');
|
|
33
|
+
}
|
|
34
|
+
command = (args) => command('docker', args, {
|
|
35
|
+
env: { DOCKER_CONTEXT: undefined, DOCKER_HOST: this.endpoint },
|
|
36
|
+
timeout: 240_000,
|
|
37
|
+
});
|
|
38
|
+
sshArgs(options = []) {
|
|
39
|
+
const url = new URL(this.endpoint);
|
|
40
|
+
// Pass the original hostname to OpenSSH: ~/.ssh/config, jump hosts, keys,
|
|
41
|
+
// agents and host-key policy continue to work exactly as for Docker.
|
|
42
|
+
return [
|
|
43
|
+
'-T',
|
|
44
|
+
'-o',
|
|
45
|
+
'BatchMode=yes',
|
|
46
|
+
'-o',
|
|
47
|
+
'ConnectTimeout=10',
|
|
48
|
+
'-o',
|
|
49
|
+
'ServerAliveInterval=10',
|
|
50
|
+
'-o',
|
|
51
|
+
'ServerAliveCountMax=2',
|
|
52
|
+
...(url.port ? ['-p', url.port] : []),
|
|
53
|
+
...(url.username ? ['-l', decodeURIComponent(url.username)] : []),
|
|
54
|
+
...options,
|
|
55
|
+
url.hostname,
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
shell(script, input) {
|
|
59
|
+
return command('ssh', [...this.sshArgs(), script], {
|
|
60
|
+
stdin: input === undefined ? 'ignore' : { string: input },
|
|
61
|
+
timeout: 30_000,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async forward(port, run) {
|
|
65
|
+
if (!this.remote)
|
|
66
|
+
return { port, close: async () => { } };
|
|
67
|
+
const resources = new AsyncDisposableStack();
|
|
68
|
+
const close = () => resources.disposeAsync();
|
|
69
|
+
try {
|
|
70
|
+
// Keep the Unix socket path below macOS's 104-byte limit. The private
|
|
71
|
+
// directory and ownership record also cover workers killed mid-test.
|
|
72
|
+
const directory = resources.use(await mkdtempDisposable(forwardPrefix(run)));
|
|
73
|
+
if (run)
|
|
74
|
+
resources.use(await run.record('forward', directory.path));
|
|
75
|
+
const path = join(directory.path, 's');
|
|
76
|
+
const sockets = new Set();
|
|
77
|
+
const server = createServer((socket) => {
|
|
78
|
+
const channel = connect(path);
|
|
79
|
+
const pairs = [
|
|
80
|
+
[socket, channel],
|
|
81
|
+
[channel, socket],
|
|
82
|
+
];
|
|
83
|
+
for (const [stream, peer] of pairs) {
|
|
84
|
+
sockets.add(stream);
|
|
85
|
+
stream.on('error', () => peer.destroy());
|
|
86
|
+
stream.once('close', () => {
|
|
87
|
+
sockets.delete(stream);
|
|
88
|
+
peer.destroy();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
socket.pipe(channel).pipe(socket);
|
|
92
|
+
});
|
|
93
|
+
// OpenSSH multiplexes client streams over one authenticated connection.
|
|
94
|
+
// Node owns the ephemeral TCP listener, so no port-reservation race or
|
|
95
|
+
// fresh SSH login is added to each database client's connect timeout.
|
|
96
|
+
const child = spawn('ssh', this.sshArgs([
|
|
97
|
+
'-N',
|
|
98
|
+
'-S',
|
|
99
|
+
'none',
|
|
100
|
+
'-o',
|
|
101
|
+
'ControlMaster=no',
|
|
102
|
+
'-o',
|
|
103
|
+
'ControlPersist=no',
|
|
104
|
+
'-o',
|
|
105
|
+
'ExitOnForwardFailure=yes',
|
|
106
|
+
'-L',
|
|
107
|
+
`${path}:127.0.0.1:${port}`,
|
|
108
|
+
]), { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
109
|
+
let errorOutput = '';
|
|
110
|
+
let spawnError;
|
|
111
|
+
child.stderr.on('data', (data) => {
|
|
112
|
+
errorOutput += data;
|
|
113
|
+
});
|
|
114
|
+
child.once('error', (error) => {
|
|
115
|
+
spawnError = error;
|
|
116
|
+
});
|
|
117
|
+
const exited = new Promise((resolve) => child.once('close', (code) => {
|
|
118
|
+
if (code && errorOutput)
|
|
119
|
+
process.stderr.write(`Docker SSH forwarding: ${errorOutput}`);
|
|
120
|
+
for (const socket of sockets)
|
|
121
|
+
socket.destroy();
|
|
122
|
+
if (server.listening)
|
|
123
|
+
server.close();
|
|
124
|
+
resolve();
|
|
125
|
+
}));
|
|
126
|
+
resources.defer(async () => {
|
|
127
|
+
child.kill('SIGTERM');
|
|
128
|
+
const deadline = setTimeout(() => child.kill('SIGKILL'), 1_000);
|
|
129
|
+
try {
|
|
130
|
+
await exited;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
clearTimeout(deadline);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
resources.defer(async () => {
|
|
137
|
+
const closed = new Promise((resolve, reject) => server.close((error) => error &&
|
|
138
|
+
!('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING')
|
|
139
|
+
? reject(error)
|
|
140
|
+
: resolve()));
|
|
141
|
+
for (const socket of sockets)
|
|
142
|
+
socket.destroy();
|
|
143
|
+
await closed;
|
|
144
|
+
});
|
|
145
|
+
// OpenSSH binds local forwards after authentication. Wait for that
|
|
146
|
+
// listener before publishing the TCP port to a database client.
|
|
147
|
+
await timebox(async () => {
|
|
148
|
+
if (spawnError)
|
|
149
|
+
throw spawnError;
|
|
150
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
151
|
+
throw new Error(`Docker SSH forwarding exited: ${errorOutput.trim()}`);
|
|
152
|
+
if (!(await stat(path)).isSocket())
|
|
153
|
+
throw new Error('Docker SSH forwarding did not create a Unix socket');
|
|
154
|
+
}, {
|
|
155
|
+
maxRetryTime: 15_000,
|
|
156
|
+
shouldRetry: ({ error }) => !spawnError && 'code' in error && error.code === 'ENOENT',
|
|
157
|
+
});
|
|
158
|
+
server.listen(0, '127.0.0.1');
|
|
159
|
+
await once(server, 'listening');
|
|
160
|
+
server.unref();
|
|
161
|
+
const address = server.address();
|
|
162
|
+
if (!address || typeof address === 'string')
|
|
163
|
+
throw new Error('Docker forwarding did not acquire a TCP port');
|
|
164
|
+
return { port: address.port, close };
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
await close();
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/docker/host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Container, type DockerCommand, ServiceContainer, } from './container.ts';
|
|
2
|
+
export { DockerDirectory } from './directory.ts';
|
|
3
|
+
export { type ContainerOptions, Docker, type DockerOptions, type Mount, type ServiceOptions, } from './docker.ts';
|
|
4
|
+
export { DOCKER_TESTS, skipWithoutDocker } from './skip-without-docker.ts';
|
|
5
|
+
export { TestRun } from './test-run.ts';
|
|
6
|
+
export { DockerVolume } from './volume.ts';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/docker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,aAAa,EAClB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,KAAK,gBAAgB,EACrB,MAAM,EACN,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Container, ServiceContainer, } from "./container.js";
|
|
2
|
+
export { DockerDirectory } from "./directory.js";
|
|
3
|
+
export { Docker, } from "./docker.js";
|
|
4
|
+
export { DOCKER_TESTS, skipWithoutDocker } from "./skip-without-docker.js";
|
|
5
|
+
export { TestRun } from "./test-run.js";
|
|
6
|
+
export { DockerVolume } from "./volume.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/docker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAET,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAEL,MAAM,GAIP,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Docker } from './docker.ts';
|
|
2
|
+
/** How tests treat Docker: `required`, `skip`, or unset to run when Docker answers. */
|
|
3
|
+
export declare const DOCKER_TESTS = "ZUKHRUF_TESTING_DOCKER";
|
|
4
|
+
/**
|
|
5
|
+
* The `skip` option of a test that needs Docker, read from the environment the
|
|
6
|
+
* host passes (`process.env`). With `ZUKHRUF_TESTING_DOCKER=required` it
|
|
7
|
+
* throws when Docker does not answer, so a machine that must run the tests
|
|
8
|
+
* fails instead of skipping them. With `skip` it always skips. Unset, it skips
|
|
9
|
+
* only when Docker does not answer.
|
|
10
|
+
*/
|
|
11
|
+
export declare function skipWithoutDocker(docker: Docker, environment: Readonly<Record<string, string | undefined>>): Promise<string | false>;
|
|
12
|
+
//# sourceMappingURL=skip-without-docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skip-without-docker.d.ts","sourceRoot":"","sources":["../../src/docker/skip-without-docker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,uFAAuF;AACvF,eAAO,MAAM,YAAY,2BAA2B,CAAC;AAErD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GACxD,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,CAuBzB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** How tests treat Docker: `required`, `skip`, or unset to run when Docker answers. */
|
|
2
|
+
export const DOCKER_TESTS = 'ZUKHRUF_TESTING_DOCKER';
|
|
3
|
+
/**
|
|
4
|
+
* The `skip` option of a test that needs Docker, read from the environment the
|
|
5
|
+
* host passes (`process.env`). With `ZUKHRUF_TESTING_DOCKER=required` it
|
|
6
|
+
* throws when Docker does not answer, so a machine that must run the tests
|
|
7
|
+
* fails instead of skipping them. With `skip` it always skips. Unset, it skips
|
|
8
|
+
* only when Docker does not answer.
|
|
9
|
+
*/
|
|
10
|
+
export async function skipWithoutDocker(docker, environment) {
|
|
11
|
+
const mode = environment[DOCKER_TESTS];
|
|
12
|
+
switch (mode) {
|
|
13
|
+
case 'skip':
|
|
14
|
+
return `${DOCKER_TESTS}=skip`;
|
|
15
|
+
case 'required':
|
|
16
|
+
await docker.command(['info']).catch((cause) => {
|
|
17
|
+
throw new Error(`${DOCKER_TESTS}=required, but Docker does not answer`, {
|
|
18
|
+
cause,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
return false;
|
|
22
|
+
case undefined:
|
|
23
|
+
case '':
|
|
24
|
+
return (await docker.isAvailable())
|
|
25
|
+
? false
|
|
26
|
+
: `needs Docker; set ${DOCKER_TESTS}=required to fail instead of skipping`;
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`${DOCKER_TESTS} is "required" or "skip", not "${mode}"`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=skip-without-docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skip-without-docker.js","sourceRoot":"","sources":["../../src/docker/skip-without-docker.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,MAAM,CAAC,MAAM,YAAY,GAAG,wBAAwB,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,WAAyD;IAEzD,MAAM,IAAI,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IACvC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,GAAG,YAAY,OAAO,CAAC;QAChC,KAAK,UAAU;YACb,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACtD,MAAM,IAAI,KAAK,CACb,GAAG,YAAY,uCAAuC,EACtD;oBACE,KAAK;iBACN,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,KAAK,SAAS,CAAC;QACf,KAAK,EAAE;YACL,OAAO,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;gBACjC,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,qBAAqB,YAAY,uCAAuC,CAAC;QAC/E;YACE,MAAM,IAAI,KAAK,CAAC,GAAG,YAAY,kCAAkC,IAAI,GAAG,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC"}
|