@emulates/docker 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -2
- package/DISCOVERY.md +1 -1
- package/README.md +2 -2
- package/dist/{chunk-ZO7PO2NA.js → chunk-PYYL7YKU.js} +2 -2
- package/dist/{chunk-NSXRVOHP.js → chunk-U6P6W4BK.js} +56 -36
- package/dist/chunk-U6P6W4BK.js.map +7 -0
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +5 -5
- package/dist/chunk-NSXRVOHP.js.map +0 -7
- /package/dist/{chunk-ZO7PO2NA.js.map → chunk-PYYL7YKU.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# Changelog — @emulates/docker
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 1.0.0 (2026-10-07)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### ⚠️ Breaking changes
|
|
6
|
+
|
|
7
|
+
- point the repo at crvouga/emulates ([cdb5e53](https://github.com/crvouga/emulates/commit/cdb5e536ed8c1f52345e3984f089001c25fdb08a))
|
|
8
|
+
|
|
9
|
+
### Fixes and improvements
|
|
10
|
+
|
|
11
|
+
- format the emulates records query ([8698872](https://github.com/crvouga/emulates/commit/8698872509d06002be5a8fcdc85d79f8462320c3))
|
|
6
12
|
|
|
7
13
|
### Dependencies
|
|
8
14
|
|
|
9
15
|
- `@emulates/sqlite`
|
|
16
|
+
|
|
17
|
+
## 0.1.1 (2026-10-06)
|
|
18
|
+
|
|
19
|
+
Initial release.
|
package/DISCOVERY.md
CHANGED
|
@@ -46,7 +46,7 @@ designed for assertions and diagnosis by consuming test suites.
|
|
|
46
46
|
|
|
47
47
|
## Report a mismatch or missing capability
|
|
48
48
|
|
|
49
|
-
Follow the [agent reporting contract](https://github.com/crvouga/
|
|
49
|
+
Follow the [agent reporting contract](https://github.com/crvouga/emulates/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
50
50
|
operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
|
|
51
51
|
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
52
52
|
customer data, prompts, PHI, card data, or unredacted recordings.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @emulates/docker
|
|
2
2
|
|
|
3
|
-
> Part of [Emulates](https://github.com/crvouga/
|
|
3
|
+
> Part of [Emulates](https://github.com/crvouga/emulates): high-fidelity, in-process emulators for APIs and databases.
|
|
4
4
|
|
|
5
5
|
Work-in-progress Docker Engine API 1.52 emulator. It implements GET/HEAD `/_ping`,
|
|
6
6
|
GET `/version`, `/info`, `/containers/json`, and `/containers/{id}/json`, plus
|
|
@@ -136,7 +136,7 @@ strings. Request environment keys and labels take precedence. `Entrypoint: [""]`
|
|
|
136
136
|
clears the image entrypoint; provide a replacement command when doing so.
|
|
137
137
|
|
|
138
138
|
Use `?name=...` for a stable name. Conflicting names return 409, including concurrent
|
|
139
|
-
creation requests. Omitted names use `
|
|
139
|
+
creation requests. Omitted names use `emulates_<id-prefix>`. Responses contain
|
|
140
140
|
`Id` and `Warnings`; IDs are deterministic synthetic 64-character hex strings,
|
|
141
141
|
immutable within stored records and restored with the shared ID sequence by
|
|
142
142
|
Timeline checkout. New records inspect as `created` with `Running: false`.
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createRuntime,
|
|
6
6
|
isRunning,
|
|
7
7
|
jsonRes
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-U6P6W4BK.js";
|
|
9
9
|
|
|
10
10
|
// src/attach.ts
|
|
11
11
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -626,4 +626,4 @@ export {
|
|
|
626
626
|
createServer2 as createServer,
|
|
627
627
|
serveTarget
|
|
628
628
|
};
|
|
629
|
-
//# sourceMappingURL=chunk-
|
|
629
|
+
//# sourceMappingURL=chunk-PYYL7YKU.js.map
|
|
@@ -33,7 +33,7 @@ var CORE_MIGRATIONS = [
|
|
|
33
33
|
{
|
|
34
34
|
id: "20260322_core_records_sequences",
|
|
35
35
|
sql: `
|
|
36
|
-
CREATE TABLE IF NOT EXISTS
|
|
36
|
+
CREATE TABLE IF NOT EXISTS emulates_records (
|
|
37
37
|
namespace TEXT NOT NULL,
|
|
38
38
|
collection TEXT NOT NULL,
|
|
39
39
|
id TEXT NOT NULL,
|
|
@@ -41,9 +41,9 @@ var CORE_MIGRATIONS = [
|
|
|
41
41
|
value TEXT NOT NULL,
|
|
42
42
|
PRIMARY KEY (namespace, collection, id)
|
|
43
43
|
);
|
|
44
|
-
CREATE INDEX IF NOT EXISTS
|
|
45
|
-
ON
|
|
46
|
-
CREATE TABLE IF NOT EXISTS
|
|
44
|
+
CREATE INDEX IF NOT EXISTS emulates_records_seq
|
|
45
|
+
ON emulates_records (namespace, collection, seq);
|
|
46
|
+
CREATE TABLE IF NOT EXISTS emulates_sequences (
|
|
47
47
|
namespace TEXT NOT NULL,
|
|
48
48
|
name TEXT NOT NULL,
|
|
49
49
|
kind TEXT NOT NULL,
|
|
@@ -53,13 +53,33 @@ var CORE_MIGRATIONS = [
|
|
|
53
53
|
`
|
|
54
54
|
}
|
|
55
55
|
];
|
|
56
|
+
var LEGACY_TABLES = [
|
|
57
|
+
["emulators_records", "emulates_records"],
|
|
58
|
+
["emulators_sequences", "emulates_sequences"]
|
|
59
|
+
];
|
|
60
|
+
var LEGACY_INDEXES = [["emulators_records_seq", "emulates_records_seq"]];
|
|
61
|
+
var namesOf = (sqlite, type) => new Set(sqlite.prepare("SELECT name FROM sqlite_master WHERE type = ?").all(type).map((row) => row.name));
|
|
56
62
|
var migrateCore = (sqlite) => {
|
|
57
63
|
migrate(sqlite, CORE_MIGRATIONS);
|
|
64
|
+
const tables = namesOf(sqlite, "table");
|
|
65
|
+
const indexes = namesOf(sqlite, "index");
|
|
66
|
+
sqlite.transaction(() => {
|
|
67
|
+
for (const [from, to] of LEGACY_TABLES) {
|
|
68
|
+
if (tables.has(from) && !tables.has(to))
|
|
69
|
+
sqlite.exec(`ALTER TABLE ${from} RENAME TO ${to}`);
|
|
70
|
+
}
|
|
71
|
+
for (const [from, to] of LEGACY_INDEXES) {
|
|
72
|
+
if (!indexes.has(from) || indexes.has(to))
|
|
73
|
+
continue;
|
|
74
|
+
sqlite.exec(`DROP INDEX ${from}`);
|
|
75
|
+
sqlite.exec(`CREATE INDEX ${to} ON emulates_records (namespace, collection, seq)`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
58
78
|
};
|
|
59
79
|
var clearNamespace = (sqlite, namespace) => {
|
|
60
80
|
sqlite.transaction(() => {
|
|
61
|
-
sqlite.prepare("DELETE FROM
|
|
62
|
-
sqlite.prepare("DELETE FROM
|
|
81
|
+
sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ?").run(namespace);
|
|
82
|
+
sqlite.prepare("DELETE FROM emulates_sequences WHERE namespace = ?").run(namespace);
|
|
63
83
|
});
|
|
64
84
|
};
|
|
65
85
|
|
|
@@ -74,9 +94,9 @@ var Collection = class {
|
|
|
74
94
|
this.collectionName = collectionName;
|
|
75
95
|
}
|
|
76
96
|
bumpCollectionSeq() {
|
|
77
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
97
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.collectionName);
|
|
78
98
|
const next = (row?.value ?? 0) + 1;
|
|
79
|
-
this.sqlite.prepare(`INSERT INTO
|
|
99
|
+
this.sqlite.prepare(`INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, 'collection', ?)
|
|
80
100
|
ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.collectionName, next);
|
|
81
101
|
return next;
|
|
82
102
|
}
|
|
@@ -84,13 +104,13 @@ var Collection = class {
|
|
|
84
104
|
return this.sqlite.transaction(() => this.bumpCollectionSeq());
|
|
85
105
|
}
|
|
86
106
|
get(id) {
|
|
87
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
107
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
88
108
|
if (!row)
|
|
89
109
|
return void 0;
|
|
90
110
|
return JSON.parse(row.value).value;
|
|
91
111
|
}
|
|
92
112
|
has(id) {
|
|
93
|
-
const row = this.sqlite.prepare("SELECT 1 AS ok FROM
|
|
113
|
+
const row = this.sqlite.prepare("SELECT 1 AS ok FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
94
114
|
return row !== void 0;
|
|
95
115
|
}
|
|
96
116
|
/** Insert a new record, assigning it the next sequence number. */
|
|
@@ -98,7 +118,7 @@ var Collection = class {
|
|
|
98
118
|
return this.sqlite.transaction(() => {
|
|
99
119
|
const seq = this.bumpCollectionSeq();
|
|
100
120
|
const stored = { seq, value };
|
|
101
|
-
this.sqlite.prepare(`INSERT INTO
|
|
121
|
+
this.sqlite.prepare(`INSERT INTO emulates_records (namespace, collection, id, seq, value)
|
|
102
122
|
VALUES (?, ?, ?, ?, ?)
|
|
103
123
|
ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.collectionName, id, seq, JSON.stringify(stored));
|
|
104
124
|
return stored;
|
|
@@ -107,25 +127,25 @@ var Collection = class {
|
|
|
107
127
|
/** Replace an existing record's value, keeping its position. */
|
|
108
128
|
update(id, value) {
|
|
109
129
|
return this.sqlite.transaction(() => {
|
|
110
|
-
const row = this.sqlite.prepare("SELECT seq, value FROM
|
|
130
|
+
const row = this.sqlite.prepare("SELECT seq, value FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
111
131
|
if (!row)
|
|
112
132
|
return void 0;
|
|
113
133
|
const stored = { seq: row.seq, value };
|
|
114
|
-
this.sqlite.prepare("UPDATE
|
|
134
|
+
this.sqlite.prepare("UPDATE emulates_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.collectionName, id);
|
|
115
135
|
return stored;
|
|
116
136
|
});
|
|
117
137
|
}
|
|
118
138
|
delete(id) {
|
|
119
|
-
const result = this.sqlite.prepare("DELETE FROM
|
|
139
|
+
const result = this.sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.collectionName, id);
|
|
120
140
|
return result.changes > 0;
|
|
121
141
|
}
|
|
122
142
|
/** How many records the collection holds, without reading them. */
|
|
123
143
|
count() {
|
|
124
|
-
const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM
|
|
144
|
+
const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM emulates_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.collectionName);
|
|
125
145
|
return Number(row?.n ?? 0);
|
|
126
146
|
}
|
|
127
147
|
list(options = {}) {
|
|
128
|
-
const rows = this.sqlite.prepare("SELECT id, seq, value FROM
|
|
148
|
+
const rows = this.sqlite.prepare("SELECT id, seq, value FROM emulates_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.collectionName);
|
|
129
149
|
const out = [];
|
|
130
150
|
for (const row of rows) {
|
|
131
151
|
const stored = JSON.parse(row.value);
|
|
@@ -174,9 +194,9 @@ var IdSequence = class {
|
|
|
174
194
|
}
|
|
175
195
|
next(prefix, length = 14) {
|
|
176
196
|
return this.sqlite.transaction(() => {
|
|
177
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
197
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_sequences WHERE namespace = ? AND name = ? AND kind = 'id'").get(this.namespace, prefix);
|
|
178
198
|
const value = (row?.value ?? 0) + 1;
|
|
179
|
-
this.sqlite.prepare(`INSERT INTO
|
|
199
|
+
this.sqlite.prepare(`INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, 'id', ?)
|
|
180
200
|
ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, prefix, value);
|
|
181
201
|
return `${prefix}${opaqueToken(`${this.salt}:${prefix}:${value}`, length)}`;
|
|
182
202
|
});
|
|
@@ -616,7 +636,7 @@ var serviceRoute = (template) => {
|
|
|
616
636
|
offset = match.index + match[0].length;
|
|
617
637
|
}
|
|
618
638
|
expression += escapePattern(segment.slice(offset));
|
|
619
|
-
return `:
|
|
639
|
+
return `:emulatesSegment${index}{${expression}}`;
|
|
620
640
|
}).join("/");
|
|
621
641
|
return {
|
|
622
642
|
path,
|
|
@@ -759,7 +779,7 @@ var json = (status, body) => new Response(JSON.stringify(body), {
|
|
|
759
779
|
status,
|
|
760
780
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
761
781
|
});
|
|
762
|
-
var adminError = (status, message) => json(status, { error: { type: "
|
|
782
|
+
var adminError = (status, message) => json(status, { error: { type: "emulates_admin", message } });
|
|
763
783
|
var UNITS = {
|
|
764
784
|
ms: 1,
|
|
765
785
|
s: 1e3,
|
|
@@ -1629,18 +1649,18 @@ var adminUiRoutes = (service, ui, adminPrefix = ADMIN_PREFIX) => {
|
|
|
1629
1649
|
// ../core/dist/snapshot.js
|
|
1630
1650
|
var snapshotNamespace = (sqlite, namespace) => ({
|
|
1631
1651
|
namespace,
|
|
1632
|
-
records: sqlite.prepare("SELECT collection, id, seq, value FROM
|
|
1633
|
-
sequences: sqlite.prepare("SELECT name, kind, value FROM
|
|
1652
|
+
records: sqlite.prepare("SELECT collection, id, seq, value FROM emulates_records WHERE namespace = ? ORDER BY collection, seq").all(namespace),
|
|
1653
|
+
sequences: sqlite.prepare("SELECT name, kind, value FROM emulates_sequences WHERE namespace = ? ORDER BY name, kind").all(namespace)
|
|
1634
1654
|
});
|
|
1635
1655
|
var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
1636
1656
|
sqlite.transaction(() => {
|
|
1637
|
-
sqlite.prepare("DELETE FROM
|
|
1638
|
-
sqlite.prepare("DELETE FROM
|
|
1639
|
-
const record2 = sqlite.prepare("INSERT INTO
|
|
1657
|
+
sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ?").run(namespace);
|
|
1658
|
+
sqlite.prepare("DELETE FROM emulates_sequences WHERE namespace = ?").run(namespace);
|
|
1659
|
+
const record2 = sqlite.prepare("INSERT INTO emulates_records (namespace, collection, id, seq, value) VALUES (?, ?, ?, ?, ?)");
|
|
1640
1660
|
for (const row of snapshot.records) {
|
|
1641
1661
|
record2.run(namespace, row.collection, row.id, row.seq, row.value);
|
|
1642
1662
|
}
|
|
1643
|
-
const sequence = sqlite.prepare("INSERT INTO
|
|
1663
|
+
const sequence = sqlite.prepare("INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, ?, ?)");
|
|
1644
1664
|
for (const row of snapshot.sequences) {
|
|
1645
1665
|
sequence.run(namespace, row.name, row.kind, row.value);
|
|
1646
1666
|
}
|
|
@@ -1739,8 +1759,8 @@ var discoverCollections = (root) => {
|
|
|
1739
1759
|
return found;
|
|
1740
1760
|
};
|
|
1741
1761
|
var storedNames = (sqlite, namespace) => {
|
|
1742
|
-
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM
|
|
1743
|
-
const sequences = sqlite.prepare("SELECT DISTINCT name FROM
|
|
1762
|
+
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM emulates_records WHERE namespace = ?").all(namespace);
|
|
1763
|
+
const sequences = sqlite.prepare("SELECT DISTINCT name FROM emulates_sequences WHERE namespace = ? AND kind = 'collection'").all(namespace);
|
|
1744
1764
|
return [...records, ...sequences].map((row) => row.name);
|
|
1745
1765
|
};
|
|
1746
1766
|
var blankAccum = (name) => ({
|
|
@@ -1981,7 +2001,7 @@ var json3 = (status, body) => new Response(JSON.stringify(body), {
|
|
|
1981
2001
|
status,
|
|
1982
2002
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
1983
2003
|
});
|
|
1984
|
-
var adminError2 = (status, message) => json3(status, { error: { type: "
|
|
2004
|
+
var adminError2 = (status, message) => json3(status, { error: { type: "emulates_admin", message } });
|
|
1985
2005
|
var fromResult = (result, status) => result.ok ? json3(status, result.value) : adminError2(result.status, result.message);
|
|
1986
2006
|
var integerParam = (value) => {
|
|
1987
2007
|
if (value === null)
|
|
@@ -2082,14 +2102,14 @@ var stateAdminRoutes = (routes) => {
|
|
|
2082
2102
|
};
|
|
2083
2103
|
|
|
2084
2104
|
// ../core/dist/version.js
|
|
2085
|
-
var PACKAGE_VERSION = true ? "0.
|
|
2105
|
+
var PACKAGE_VERSION = true ? "1.0.0" : UNRELEASED_VERSION;
|
|
2086
2106
|
|
|
2087
2107
|
// ../core/dist/webhooks.js
|
|
2088
2108
|
var json4 = (status, body) => new Response(JSON.stringify(body), {
|
|
2089
2109
|
status,
|
|
2090
2110
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
2091
2111
|
});
|
|
2092
|
-
var adminError3 = (status, message) => json4(status, { error: { type: "
|
|
2112
|
+
var adminError3 = (status, message) => json4(status, { error: { type: "emulates_admin", message } });
|
|
2093
2113
|
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2094
2114
|
var parseEndpoint = (value) => {
|
|
2095
2115
|
if (!isRecord4(value) || typeof value.url !== "string")
|
|
@@ -2705,7 +2725,7 @@ var createRuntime = (options) => {
|
|
|
2705
2725
|
log(400);
|
|
2706
2726
|
return stamp(new Response(JSON.stringify({
|
|
2707
2727
|
error: {
|
|
2708
|
-
type: "
|
|
2728
|
+
type: "emulates_admin",
|
|
2709
2729
|
message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
|
|
2710
2730
|
}
|
|
2711
2731
|
}), { status: 400, headers: { "content-type": "application/json; charset=utf-8" } }));
|
|
@@ -2871,7 +2891,7 @@ var adminJson = (status, body) => new Response(JSON.stringify(body), {
|
|
|
2871
2891
|
status,
|
|
2872
2892
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
2873
2893
|
});
|
|
2874
|
-
var adminFail = (status, message) => adminJson(status, { error: { type: "
|
|
2894
|
+
var adminFail = (status, message) => adminJson(status, { error: { type: "emulates_admin", message } });
|
|
2875
2895
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2876
2896
|
var credentialRoutes = (registry) => ({
|
|
2877
2897
|
"GET /credentials": () => adminJson(200, {
|
|
@@ -3103,8 +3123,8 @@ var createContainer = (state, value, url, ids, now) => {
|
|
|
3103
3123
|
let id;
|
|
3104
3124
|
do {
|
|
3105
3125
|
id = [...ids.next("", 32)].map((c) => c.charCodeAt(0).toString(16)).join("");
|
|
3106
|
-
} while (state.containers.has(id) || !name && state.containers.list().some((c) => c.value.name === `
|
|
3107
|
-
name ||= `
|
|
3126
|
+
} while (state.containers.has(id) || !name && state.containers.list().some((c) => c.value.name === `emulates_${id.slice(0, 12)}`));
|
|
3127
|
+
name ||= `emulates_${id.slice(0, 12)}`;
|
|
3108
3128
|
config.Hostname ||= id.slice(0, 12);
|
|
3109
3129
|
state.containers.insert(id, {
|
|
3110
3130
|
id,
|
|
@@ -4086,4 +4106,4 @@ export {
|
|
|
4086
4106
|
DOCKER_NAMESPACE,
|
|
4087
4107
|
DockerAPI
|
|
4088
4108
|
};
|
|
4089
|
-
//# sourceMappingURL=chunk-
|
|
4109
|
+
//# sourceMappingURL=chunk-U6P6W4BK.js.map
|