@emulates/firehose 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-TTCJP75R.js → chunk-77CSI3VM.js} +53 -33
- package/dist/chunk-77CSI3VM.js.map +7 -0
- package/dist/{chunk-EYLUAI54.js → chunk-T57YNBTP.js} +3 -3
- package/dist/{chunk-EYLUAI54.js.map → chunk-T57YNBTP.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-TTCJP75R.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# Changelog — @emulates/firehose
|
|
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/firehose
|
|
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
|
Stateful local emulator of Amazon Data Firehose. ESM; Node 22+ or Bun 1.2+.
|
|
6
6
|
|
|
@@ -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
|
|
|
@@ -4220,9 +4240,9 @@ var Collection = class {
|
|
|
4220
4240
|
this.collectionName = collectionName;
|
|
4221
4241
|
}
|
|
4222
4242
|
bumpCollectionSeq() {
|
|
4223
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
4243
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.collectionName);
|
|
4224
4244
|
const next = (row?.value ?? 0) + 1;
|
|
4225
|
-
this.sqlite.prepare(`INSERT INTO
|
|
4245
|
+
this.sqlite.prepare(`INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, 'collection', ?)
|
|
4226
4246
|
ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.collectionName, next);
|
|
4227
4247
|
return next;
|
|
4228
4248
|
}
|
|
@@ -4230,13 +4250,13 @@ var Collection = class {
|
|
|
4230
4250
|
return this.sqlite.transaction(() => this.bumpCollectionSeq());
|
|
4231
4251
|
}
|
|
4232
4252
|
get(id) {
|
|
4233
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
4253
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
4234
4254
|
if (!row)
|
|
4235
4255
|
return void 0;
|
|
4236
4256
|
return JSON.parse(row.value).value;
|
|
4237
4257
|
}
|
|
4238
4258
|
has(id) {
|
|
4239
|
-
const row = this.sqlite.prepare("SELECT 1 AS ok FROM
|
|
4259
|
+
const row = this.sqlite.prepare("SELECT 1 AS ok FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
4240
4260
|
return row !== void 0;
|
|
4241
4261
|
}
|
|
4242
4262
|
/** Insert a new record, assigning it the next sequence number. */
|
|
@@ -4244,7 +4264,7 @@ var Collection = class {
|
|
|
4244
4264
|
return this.sqlite.transaction(() => {
|
|
4245
4265
|
const seq = this.bumpCollectionSeq();
|
|
4246
4266
|
const stored = { seq, value };
|
|
4247
|
-
this.sqlite.prepare(`INSERT INTO
|
|
4267
|
+
this.sqlite.prepare(`INSERT INTO emulates_records (namespace, collection, id, seq, value)
|
|
4248
4268
|
VALUES (?, ?, ?, ?, ?)
|
|
4249
4269
|
ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.collectionName, id, seq, JSON.stringify(stored));
|
|
4250
4270
|
return stored;
|
|
@@ -4253,25 +4273,25 @@ var Collection = class {
|
|
|
4253
4273
|
/** Replace an existing record's value, keeping its position. */
|
|
4254
4274
|
update(id, value) {
|
|
4255
4275
|
return this.sqlite.transaction(() => {
|
|
4256
|
-
const row = this.sqlite.prepare("SELECT seq, value FROM
|
|
4276
|
+
const row = this.sqlite.prepare("SELECT seq, value FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.collectionName, id);
|
|
4257
4277
|
if (!row)
|
|
4258
4278
|
return void 0;
|
|
4259
4279
|
const stored = { seq: row.seq, value };
|
|
4260
|
-
this.sqlite.prepare("UPDATE
|
|
4280
|
+
this.sqlite.prepare("UPDATE emulates_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.collectionName, id);
|
|
4261
4281
|
return stored;
|
|
4262
4282
|
});
|
|
4263
4283
|
}
|
|
4264
4284
|
delete(id) {
|
|
4265
|
-
const result = this.sqlite.prepare("DELETE FROM
|
|
4285
|
+
const result = this.sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.collectionName, id);
|
|
4266
4286
|
return result.changes > 0;
|
|
4267
4287
|
}
|
|
4268
4288
|
/** How many records the collection holds, without reading them. */
|
|
4269
4289
|
count() {
|
|
4270
|
-
const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM
|
|
4290
|
+
const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM emulates_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.collectionName);
|
|
4271
4291
|
return Number(row?.n ?? 0);
|
|
4272
4292
|
}
|
|
4273
4293
|
list(options = {}) {
|
|
4274
|
-
const rows = this.sqlite.prepare("SELECT id, seq, value FROM
|
|
4294
|
+
const rows = this.sqlite.prepare("SELECT id, seq, value FROM emulates_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.collectionName);
|
|
4275
4295
|
const out = [];
|
|
4276
4296
|
for (const row of rows) {
|
|
4277
4297
|
const stored = JSON.parse(row.value);
|
|
@@ -4320,9 +4340,9 @@ var IdSequence = class {
|
|
|
4320
4340
|
}
|
|
4321
4341
|
next(prefix, length = 14) {
|
|
4322
4342
|
return this.sqlite.transaction(() => {
|
|
4323
|
-
const row = this.sqlite.prepare("SELECT value FROM
|
|
4343
|
+
const row = this.sqlite.prepare("SELECT value FROM emulates_sequences WHERE namespace = ? AND name = ? AND kind = 'id'").get(this.namespace, prefix);
|
|
4324
4344
|
const value = (row?.value ?? 0) + 1;
|
|
4325
|
-
this.sqlite.prepare(`INSERT INTO
|
|
4345
|
+
this.sqlite.prepare(`INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, 'id', ?)
|
|
4326
4346
|
ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, prefix, value);
|
|
4327
4347
|
return `${prefix}${opaqueToken(`${this.salt}:${prefix}:${value}`, length)}`;
|
|
4328
4348
|
});
|
|
@@ -5165,7 +5185,7 @@ var json = (status, body) => new Response(JSON.stringify(body), {
|
|
|
5165
5185
|
status,
|
|
5166
5186
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
5167
5187
|
});
|
|
5168
|
-
var adminError = (status, message) => json(status, { error: { type: "
|
|
5188
|
+
var adminError = (status, message) => json(status, { error: { type: "emulates_admin", message } });
|
|
5169
5189
|
var UNITS = {
|
|
5170
5190
|
ms: 1,
|
|
5171
5191
|
s: 1e3,
|
|
@@ -6037,18 +6057,18 @@ var adminUiRoutes = (service, ui, adminPrefix = ADMIN_PREFIX) => {
|
|
|
6037
6057
|
// ../core/dist/snapshot.js
|
|
6038
6058
|
var snapshotNamespace = (sqlite, namespace) => ({
|
|
6039
6059
|
namespace,
|
|
6040
|
-
records: sqlite.prepare("SELECT collection, id, seq, value FROM
|
|
6041
|
-
sequences: sqlite.prepare("SELECT name, kind, value FROM
|
|
6060
|
+
records: sqlite.prepare("SELECT collection, id, seq, value FROM emulates_records WHERE namespace = ? ORDER BY collection, seq").all(namespace),
|
|
6061
|
+
sequences: sqlite.prepare("SELECT name, kind, value FROM emulates_sequences WHERE namespace = ? ORDER BY name, kind").all(namespace)
|
|
6042
6062
|
});
|
|
6043
6063
|
var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
6044
6064
|
sqlite.transaction(() => {
|
|
6045
|
-
sqlite.prepare("DELETE FROM
|
|
6046
|
-
sqlite.prepare("DELETE FROM
|
|
6047
|
-
const record = sqlite.prepare("INSERT INTO
|
|
6065
|
+
sqlite.prepare("DELETE FROM emulates_records WHERE namespace = ?").run(namespace);
|
|
6066
|
+
sqlite.prepare("DELETE FROM emulates_sequences WHERE namespace = ?").run(namespace);
|
|
6067
|
+
const record = sqlite.prepare("INSERT INTO emulates_records (namespace, collection, id, seq, value) VALUES (?, ?, ?, ?, ?)");
|
|
6048
6068
|
for (const row of snapshot.records) {
|
|
6049
6069
|
record.run(namespace, row.collection, row.id, row.seq, row.value);
|
|
6050
6070
|
}
|
|
6051
|
-
const sequence = sqlite.prepare("INSERT INTO
|
|
6071
|
+
const sequence = sqlite.prepare("INSERT INTO emulates_sequences (namespace, name, kind, value) VALUES (?, ?, ?, ?)");
|
|
6052
6072
|
for (const row of snapshot.sequences) {
|
|
6053
6073
|
sequence.run(namespace, row.name, row.kind, row.value);
|
|
6054
6074
|
}
|
|
@@ -6147,8 +6167,8 @@ var discoverCollections = (root) => {
|
|
|
6147
6167
|
return found;
|
|
6148
6168
|
};
|
|
6149
6169
|
var storedNames = (sqlite, namespace) => {
|
|
6150
|
-
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM
|
|
6151
|
-
const sequences = sqlite.prepare("SELECT DISTINCT name FROM
|
|
6170
|
+
const records = sqlite.prepare("SELECT DISTINCT collection AS name FROM emulates_records WHERE namespace = ?").all(namespace);
|
|
6171
|
+
const sequences = sqlite.prepare("SELECT DISTINCT name FROM emulates_sequences WHERE namespace = ? AND kind = 'collection'").all(namespace);
|
|
6152
6172
|
return [...records, ...sequences].map((row) => row.name);
|
|
6153
6173
|
};
|
|
6154
6174
|
var blankAccum = (name) => ({
|
|
@@ -6389,7 +6409,7 @@ var json3 = (status, body) => new Response(JSON.stringify(body), {
|
|
|
6389
6409
|
status,
|
|
6390
6410
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
6391
6411
|
});
|
|
6392
|
-
var adminError2 = (status, message) => json3(status, { error: { type: "
|
|
6412
|
+
var adminError2 = (status, message) => json3(status, { error: { type: "emulates_admin", message } });
|
|
6393
6413
|
var fromResult = (result, status) => result.ok ? json3(status, result.value) : adminError2(result.status, result.message);
|
|
6394
6414
|
var integerParam = (value) => {
|
|
6395
6415
|
if (value === null)
|
|
@@ -6490,14 +6510,14 @@ var stateAdminRoutes = (routes) => {
|
|
|
6490
6510
|
};
|
|
6491
6511
|
|
|
6492
6512
|
// ../core/dist/version.js
|
|
6493
|
-
var PACKAGE_VERSION = true ? "0.
|
|
6513
|
+
var PACKAGE_VERSION = true ? "1.0.0" : UNRELEASED_VERSION;
|
|
6494
6514
|
|
|
6495
6515
|
// ../core/dist/webhooks.js
|
|
6496
6516
|
var json4 = (status, body) => new Response(JSON.stringify(body), {
|
|
6497
6517
|
status,
|
|
6498
6518
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
6499
6519
|
});
|
|
6500
|
-
var adminError3 = (status, message) => json4(status, { error: { type: "
|
|
6520
|
+
var adminError3 = (status, message) => json4(status, { error: { type: "emulates_admin", message } });
|
|
6501
6521
|
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6502
6522
|
var parseEndpoint = (value) => {
|
|
6503
6523
|
if (!isRecord4(value) || typeof value.url !== "string")
|
|
@@ -7099,7 +7119,7 @@ var createRuntime = (options) => {
|
|
|
7099
7119
|
log(400);
|
|
7100
7120
|
return stamp(new Response(JSON.stringify({
|
|
7101
7121
|
error: {
|
|
7102
|
-
type: "
|
|
7122
|
+
type: "emulates_admin",
|
|
7103
7123
|
message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
|
|
7104
7124
|
}
|
|
7105
7125
|
}), { status: 400, headers: { "content-type": "application/json; charset=utf-8" } }));
|
|
@@ -7265,7 +7285,7 @@ var adminJson = (status, body) => new Response(JSON.stringify(body), {
|
|
|
7265
7285
|
status,
|
|
7266
7286
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
7267
7287
|
});
|
|
7268
|
-
var adminFail = (status, message) => adminJson(status, { error: { type: "
|
|
7288
|
+
var adminFail = (status, message) => adminJson(status, { error: { type: "emulates_admin", message } });
|
|
7269
7289
|
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7270
7290
|
var credentialRoutes = (registry) => ({
|
|
7271
7291
|
"GET /credentials": () => adminJson(200, {
|
|
@@ -7468,4 +7488,4 @@ export {
|
|
|
7468
7488
|
createRuntime2 as createRuntime,
|
|
7469
7489
|
FirehoseAPI
|
|
7470
7490
|
};
|
|
7471
|
-
//# sourceMappingURL=chunk-
|
|
7491
|
+
//# sourceMappingURL=chunk-77CSI3VM.js.map
|