@emulates/ecs 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 +1 -1
- package/dist/{chunk-3ZZ7W2QG.js → chunk-C2SUTQ6J.js} +53 -33
- package/dist/chunk-C2SUTQ6J.js.map +7 -0
- package/dist/{chunk-D366PMWW.js → chunk-FRH5LGFP.js} +3 -3
- package/dist/{chunk-D366PMWW.js.map → chunk-FRH5LGFP.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +5 -5
- package/dist/chunk-3ZZ7W2QG.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# Changelog — @emulates/ecs
|
|
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
|
@@ -47,7 +47,7 @@ designed for assertions and diagnosis by consuming test suites.
|
|
|
47
47
|
|
|
48
48
|
## Report a mismatch or missing capability
|
|
49
49
|
|
|
50
|
-
Follow the [agent reporting contract](https://github.com/crvouga/
|
|
50
|
+
Follow the [agent reporting contract](https://github.com/crvouga/emulates/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
51
51
|
operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
|
|
52
52
|
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
53
53
|
customer data, prompts, PHI, card data, or unredacted recordings.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @emulates/ecs
|
|
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
|
WIP AWS ECS Fargate RunTask control-plane emulator. It records task acceptance; it never starts
|
|
6
6
|
containers. Wire contract follows [RunTask](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html).
|
|
@@ -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(prefix2, 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, prefix2);
|
|
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, prefix2, value);
|
|
181
201
|
return `${prefix2}${opaqueToken(`${this.salt}:${prefix2}:${value}`, length)}`;
|
|
182
202
|
});
|
|
@@ -884,7 +904,7 @@ var json = (status, body) => new Response(JSON.stringify(body), {
|
|
|
884
904
|
status,
|
|
885
905
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
886
906
|
});
|
|
887
|
-
var adminError = (status, message) => json(status, { error: { type: "
|
|
907
|
+
var adminError = (status, message) => json(status, { error: { type: "emulates_admin", message } });
|
|
888
908
|
var UNITS = {
|
|
889
909
|
ms: 1,
|
|
890
910
|
s: 1e3,
|
|
@@ -1756,18 +1776,18 @@ var adminUiRoutes = (service, ui, adminPrefix = ADMIN_PREFIX) => {
|
|
|
1756
1776
|
// ../core/dist/snapshot.js
|
|
1757
1777
|
var snapshotNamespace = (sqlite, namespace) => ({
|
|
1758
1778
|
namespace,
|
|
1759
|
-
records: sqlite.prepare("SELECT collection, id, seq, value FROM
|
|
1760
|
-
sequences: sqlite.prepare("SELECT name, kind, value FROM
|
|
1779
|
+
records: sqlite.prepare("SELECT collection, id, seq, value FROM emulates_records WHERE namespace = ? ORDER BY collection, seq").all(namespace),
|
|
1780
|
+
sequences: sqlite.prepare("SELECT name, kind, value FROM emulates_sequences WHERE namespace = ? ORDER BY name, kind").all(namespace)
|
|
1761
1781
|
});
|
|
1762
1782
|
var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
1763
1783
|
sqlite.transaction(() => {
|
|
1764
|
-
sqlite.prepare("DELETE FROM
|
|
1765
|
-
sqlite.prepare("DELETE FROM
|
|
1766
|
-
const record = sqlite.prepare("INSERT INTO
|
|
1784
|
+
sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ?").run(namespace);
|
|
1785
|
+
sqlite.prepare("DELETE FROM emulates_sequences WHERE namespace = ?").run(namespace);
|
|
1786
|
+
const record = sqlite.prepare("INSERT INTO emulates_records (namespace, collection, id, seq, value) VALUES (?, ?, ?, ?, ?)");
|
|
1767
1787
|
for (const row of snapshot.records) {
|
|
1768
1788
|
record.run(namespace, row.collection, row.id, row.seq, row.value);
|
|
1769
1789
|
}
|
|
1770
|
-
const sequence = sqlite.prepare("INSERT INTO
|
|
1790
|
+
const sequence = sqlite.prepare("INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, ?, ?)");
|
|
1771
1791
|
for (const row of snapshot.sequences) {
|
|
1772
1792
|
sequence.run(namespace, row.name, row.kind, row.value);
|
|
1773
1793
|
}
|
|
@@ -1866,8 +1886,8 @@ var discoverCollections = (root) => {
|
|
|
1866
1886
|
return found;
|
|
1867
1887
|
};
|
|
1868
1888
|
var storedNames = (sqlite, namespace) => {
|
|
1869
|
-
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM
|
|
1870
|
-
const sequences = sqlite.prepare("SELECT DISTINCT name FROM
|
|
1889
|
+
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM emulates_records WHERE namespace = ?").all(namespace);
|
|
1890
|
+
const sequences = sqlite.prepare("SELECT DISTINCT name FROM emulates_sequences WHERE namespace = ? AND kind = 'collection'").all(namespace);
|
|
1871
1891
|
return [...records, ...sequences].map((row) => row.name);
|
|
1872
1892
|
};
|
|
1873
1893
|
var blankAccum = (name) => ({
|
|
@@ -2108,7 +2128,7 @@ var json3 = (status, body) => new Response(JSON.stringify(body), {
|
|
|
2108
2128
|
status,
|
|
2109
2129
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
2110
2130
|
});
|
|
2111
|
-
var adminError2 = (status, message) => json3(status, { error: { type: "
|
|
2131
|
+
var adminError2 = (status, message) => json3(status, { error: { type: "emulates_admin", message } });
|
|
2112
2132
|
var fromResult = (result, status) => result.ok ? json3(status, result.value) : adminError2(result.status, result.message);
|
|
2113
2133
|
var integerParam = (value) => {
|
|
2114
2134
|
if (value === null)
|
|
@@ -2209,14 +2229,14 @@ var stateAdminRoutes = (routes) => {
|
|
|
2209
2229
|
};
|
|
2210
2230
|
|
|
2211
2231
|
// ../core/dist/version.js
|
|
2212
|
-
var PACKAGE_VERSION = true ? "0.
|
|
2232
|
+
var PACKAGE_VERSION = true ? "1.0.0" : UNRELEASED_VERSION;
|
|
2213
2233
|
|
|
2214
2234
|
// ../core/dist/webhooks.js
|
|
2215
2235
|
var json4 = (status, body) => new Response(JSON.stringify(body), {
|
|
2216
2236
|
status,
|
|
2217
2237
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
2218
2238
|
});
|
|
2219
|
-
var adminError3 = (status, message) => json4(status, { error: { type: "
|
|
2239
|
+
var adminError3 = (status, message) => json4(status, { error: { type: "emulates_admin", message } });
|
|
2220
2240
|
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2221
2241
|
var parseEndpoint = (value) => {
|
|
2222
2242
|
if (!isRecord4(value) || typeof value.url !== "string")
|
|
@@ -2818,7 +2838,7 @@ var createRuntime = (options) => {
|
|
|
2818
2838
|
log(400);
|
|
2819
2839
|
return stamp(new Response(JSON.stringify({
|
|
2820
2840
|
error: {
|
|
2821
|
-
type: "
|
|
2841
|
+
type: "emulates_admin",
|
|
2822
2842
|
message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
|
|
2823
2843
|
}
|
|
2824
2844
|
}), { status: 400, headers: { "content-type": "application/json; charset=utf-8" } }));
|
|
@@ -2984,7 +3004,7 @@ var adminJson = (status, body) => new Response(JSON.stringify(body), {
|
|
|
2984
3004
|
status,
|
|
2985
3005
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
2986
3006
|
});
|
|
2987
|
-
var adminFail = (status, message) => adminJson(status, { error: { type: "
|
|
3007
|
+
var adminFail = (status, message) => adminJson(status, { error: { type: "emulates_admin", message } });
|
|
2988
3008
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2989
3009
|
var credentialRoutes = (registry) => ({
|
|
2990
3010
|
"GET /credentials": () => adminJson(200, {
|
|
@@ -3272,4 +3292,4 @@ export {
|
|
|
3272
3292
|
DEFAULT_TASK_DEFINITION,
|
|
3273
3293
|
ECSAPI
|
|
3274
3294
|
};
|
|
3275
|
-
//# sourceMappingURL=chunk-
|
|
3295
|
+
//# sourceMappingURL=chunk-C2SUTQ6J.js.map
|