@earendil-works/chord 1.2.0 → 1.4.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 +71 -0
- package/README.md +69 -14
- package/dist/cell.d.ts +30 -9
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js +52 -12
- package/dist/cell.js.map +1 -1
- package/dist/cluster.d.ts +4 -3
- package/dist/cluster.d.ts.map +1 -1
- package/dist/cluster.js +58 -42
- package/dist/cluster.js.map +1 -1
- package/dist/connection.d.ts +171 -58
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +676 -217
- package/dist/connection.js.map +1 -1
- package/dist/context/index.d.ts +8 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +15 -0
- package/dist/context/index.js.map +1 -1
- package/dist/contract.d.ts +120 -45
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +104 -15
- package/dist/contract.js.map +1 -1
- package/dist/declare.d.ts +11 -48
- package/dist/declare.d.ts.map +1 -1
- package/dist/declare.js +1 -21
- package/dist/declare.js.map +1 -1
- package/dist/delta/index.d.ts +2 -1
- package/dist/delta/index.d.ts.map +1 -1
- package/dist/delta/index.js +4 -0
- package/dist/delta/index.js.map +1 -1
- package/dist/errors.d.ts +4 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +1 -1
- package/dist/host.d.ts +214 -120
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +446 -241
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/link.d.ts +47 -12
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +200 -14
- package/dist/link.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +7 -2
- package/dist/loader.js.map +1 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +35 -9
- package/dist/manager.js.map +1 -1
- package/dist/object.d.ts +438 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/{record.js → object.js} +614 -335
- package/dist/object.js.map +1 -0
- package/dist/output.d.ts +91 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +360 -0
- package/dist/output.js.map +1 -0
- package/dist/registry.d.ts +3 -3
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/reload.d.ts +35 -27
- package/dist/reload.d.ts.map +1 -1
- package/dist/reload.js +256 -192
- package/dist/reload.js.map +1 -1
- package/dist/remote.d.ts +130 -35
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +493 -189
- package/dist/remote.js.map +1 -1
- package/dist/sqlite/cloudflare.d.ts +52 -0
- package/dist/sqlite/cloudflare.d.ts.map +1 -0
- package/dist/sqlite/cloudflare.js +134 -0
- package/dist/sqlite/cloudflare.js.map +1 -0
- package/dist/sqlite/database.d.ts +37 -0
- package/dist/sqlite/database.d.ts.map +1 -0
- package/dist/sqlite/database.js +2 -0
- package/dist/sqlite/database.js.map +1 -0
- package/dist/sqlite/index.d.ts +4 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +3 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/migrations.d.ts +10 -0
- package/dist/sqlite/migrations.d.ts.map +1 -0
- package/dist/sqlite/migrations.js +33 -0
- package/dist/sqlite/migrations.js.map +1 -0
- package/dist/sqlite/node.d.ts +38 -0
- package/dist/sqlite/node.d.ts.map +1 -0
- package/dist/sqlite/node.js +174 -0
- package/dist/sqlite/node.js.map +1 -0
- package/dist/sqlite/schema.d.ts +3 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +10 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/telemetry-sqlite/index.d.ts +24 -0
- package/dist/telemetry-sqlite/index.d.ts.map +1 -0
- package/dist/telemetry-sqlite/index.js +273 -0
- package/dist/telemetry-sqlite/index.js.map +1 -0
- package/dist/telemetry-sqlite/store.d.ts +9 -0
- package/dist/telemetry-sqlite/store.d.ts.map +1 -0
- package/dist/telemetry-sqlite/store.js +110 -0
- package/dist/telemetry-sqlite/store.js.map +1 -0
- package/dist/types.d.ts +39 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/websocket.d.ts +4 -5
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +3 -2
- package/dist/websocket.js.map +1 -1
- package/dist/wire.d.ts +210 -129
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +108 -55
- package/dist/wire.js.map +1 -1
- package/docs/guide.md +267 -203
- package/docs/spec.md +141 -114
- package/docs/tutorial.md +257 -235
- package/package.json +27 -1
- package/dist/record.d.ts +0 -320
- package/dist/record.d.ts.map +0 -1
- package/dist/record.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
function storedSpan(row) {
|
|
2
|
+
return {
|
|
3
|
+
id: row.id,
|
|
4
|
+
traceId: row.trace_id,
|
|
5
|
+
...(row.parent_id === null ? {} : { parentId: row.parent_id }),
|
|
6
|
+
name: row.name,
|
|
7
|
+
startedMs: row.started_ms,
|
|
8
|
+
endedMs: row.ended_ms,
|
|
9
|
+
attributes: JSON.parse(row.attributes),
|
|
10
|
+
events: JSON.parse(row.events).map((event) => ({ name: event.name, timeMs: event.time_ms, attributes: event.attributes })),
|
|
11
|
+
links: JSON.parse(row.links).map((link) => ({
|
|
12
|
+
traceId: link.trace_id,
|
|
13
|
+
spanId: link.span_id,
|
|
14
|
+
...(link.attributes === undefined ? {} : { attributes: link.attributes }),
|
|
15
|
+
})),
|
|
16
|
+
status: JSON.parse(row.status),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// JSON equality must distinguish boolean true from numeric 1. json_each also handles arbitrary attribute keys.
|
|
20
|
+
function attributesSql(attributes, column, params) {
|
|
21
|
+
return (Object.entries(attributes ?? {})
|
|
22
|
+
.map(([key, value]) => {
|
|
23
|
+
const type = typeof value === "number"
|
|
24
|
+
? "entry.type IN ('integer', 'real')"
|
|
25
|
+
: typeof value === "boolean"
|
|
26
|
+
? `entry.type = '${value ? "true" : "false"}'`
|
|
27
|
+
: "entry.type = 'text'";
|
|
28
|
+
params.push(key, typeof value === "boolean" ? Number(value) : value);
|
|
29
|
+
return `EXISTS (SELECT 1 FROM json_each(${column}) entry WHERE entry.key = ? AND ${type} AND entry.value = ?)`;
|
|
30
|
+
})
|
|
31
|
+
.join(" AND ") || "1");
|
|
32
|
+
}
|
|
33
|
+
export async function getTrace(db, traceId) {
|
|
34
|
+
const rows = await db.all("SELECT * FROM telemetry_spans WHERE trace_id = ? ORDER BY started_ms, id", traceId);
|
|
35
|
+
return rows.length ? { traceId, spans: rows.map(storedSpan) } : undefined;
|
|
36
|
+
}
|
|
37
|
+
export async function findTraces(db, query = {}) {
|
|
38
|
+
const limit = query.limit ?? 20;
|
|
39
|
+
const conditions = [];
|
|
40
|
+
const params = [];
|
|
41
|
+
if (query.name !== undefined) {
|
|
42
|
+
conditions.push("(SELECT name FROM telemetry_spans WHERE trace_id = heads.trace_id ORDER BY started_ms, id LIMIT 1) = ?");
|
|
43
|
+
params.push(query.name);
|
|
44
|
+
}
|
|
45
|
+
if (query.startMin !== undefined) {
|
|
46
|
+
conditions.push("started_ms >= ?");
|
|
47
|
+
params.push(query.startMin);
|
|
48
|
+
}
|
|
49
|
+
if (query.startMax !== undefined) {
|
|
50
|
+
conditions.push("started_ms <= ?");
|
|
51
|
+
params.push(query.startMax);
|
|
52
|
+
}
|
|
53
|
+
if (query.errors)
|
|
54
|
+
conditions.push("EXISTS (SELECT 1 FROM telemetry_spans WHERE trace_id = heads.trace_id AND json_extract(status, '$.status') = 'error')");
|
|
55
|
+
if (query.attributes)
|
|
56
|
+
conditions.push(`EXISTS (SELECT 1 FROM telemetry_spans candidate WHERE candidate.trace_id = heads.trace_id AND ${attributesSql(query.attributes, "candidate.attributes", params)})`);
|
|
57
|
+
if (query.cursor !== undefined) {
|
|
58
|
+
const cursor = JSON.parse(query.cursor);
|
|
59
|
+
conditions.push("(started_ms < ? OR (started_ms = ? AND trace_id > ?))");
|
|
60
|
+
params.push(cursor[0], cursor[0], cursor[1]);
|
|
61
|
+
}
|
|
62
|
+
return db.transaction(async (tx) => {
|
|
63
|
+
const heads = await tx.all(`WITH heads AS (
|
|
64
|
+
SELECT trace_id, min(started_ms) started_ms FROM telemetry_spans GROUP BY trace_id
|
|
65
|
+
) SELECT * FROM heads WHERE ${conditions.join(" AND ") || "1"} ORDER BY started_ms DESC, trace_id LIMIT ?`, ...params, limit + 1);
|
|
66
|
+
const more = heads.length > limit;
|
|
67
|
+
if (more)
|
|
68
|
+
heads.pop();
|
|
69
|
+
if (!heads.length)
|
|
70
|
+
return { traces: [] };
|
|
71
|
+
const rows = await tx.all("SELECT * FROM telemetry_spans WHERE trace_id IN (SELECT value FROM json_each(?)) ORDER BY started_ms, id", JSON.stringify(heads.map((head) => head.trace_id)));
|
|
72
|
+
const grouped = new Map();
|
|
73
|
+
for (const row of rows) {
|
|
74
|
+
const spans = grouped.get(row.trace_id) ?? [];
|
|
75
|
+
spans.push(storedSpan(row));
|
|
76
|
+
grouped.set(row.trace_id, spans);
|
|
77
|
+
}
|
|
78
|
+
const last = heads.at(-1);
|
|
79
|
+
return {
|
|
80
|
+
traces: heads.map((head) => ({ traceId: head.trace_id, spans: grouped.get(head.trace_id) })),
|
|
81
|
+
...(more ? { cursor: JSON.stringify([last.started_ms, last.trace_id]) } : {}),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export async function findMetrics(db, query) {
|
|
86
|
+
const params = [query.name];
|
|
87
|
+
const conditions = ["name = ?"];
|
|
88
|
+
if (query.startMin !== undefined) {
|
|
89
|
+
conditions.push("interval_ms >= ?");
|
|
90
|
+
params.push(query.startMin);
|
|
91
|
+
}
|
|
92
|
+
if (query.startMax !== undefined) {
|
|
93
|
+
conditions.push("interval_ms <= ?");
|
|
94
|
+
params.push(query.startMax);
|
|
95
|
+
}
|
|
96
|
+
conditions.push(attributesSql(query.attributes, "attributes", params));
|
|
97
|
+
const rows = await db.all(`SELECT * FROM telemetry_metrics WHERE ${conditions.join(" AND ")} ORDER BY interval_ms, series_key`, ...params);
|
|
98
|
+
return rows.map((row) => ({
|
|
99
|
+
intervalMs: row.interval_ms,
|
|
100
|
+
name: row.name,
|
|
101
|
+
kind: row.kind,
|
|
102
|
+
...(row.unit === null ? {} : { unit: row.unit }),
|
|
103
|
+
attributes: JSON.parse(row.attributes),
|
|
104
|
+
count: row.count,
|
|
105
|
+
sum: row.sum,
|
|
106
|
+
min: row.min,
|
|
107
|
+
max: row.max,
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/telemetry-sqlite/store.ts"],"names":[],"mappings":"AAwBA,SAAS,UAAU,CAAC,GAAY;IAC/B,OAAO;QACN,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC;QAC9D,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,OAAO,EAAE,GAAG,CAAC,QAAQ;QACrB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAmB;QACxD,MAAM,EAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAqE,CAAC,GAAG,CACtG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CACtF;QACD,KAAK,EAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAA0E,CAAC,GAAG,CACzG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACV,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;SACzE,CAAC,CACF;QACD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAe;KAC5C,CAAC;AACH,CAAC;AAED,+GAA+G;AAC/G,SAAS,aAAa,CAAC,UAAoC,EAAE,MAAc,EAAE,MAAqB;IACjG,OAAO,CACN,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACrB,MAAM,IAAI,GACT,OAAO,KAAK,KAAK,QAAQ;YACxB,CAAC,CAAC,mCAAmC;YACrC,CAAC,CAAC,OAAO,KAAK,KAAK,SAAS;gBAC3B,CAAC,CAAC,iBAAiB,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GAAG;gBAC9C,CAAC,CAAC,qBAAqB,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrE,OAAO,mCAAmC,MAAM,mCAAmC,IAAI,uBAAuB,CAAC;IAChH,CAAC,CAAC;SACD,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CACtB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAkB,EAAE,OAAe;IACjE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CACxB,0EAA0E,EAC1E,OAAO,CACP,CAAC;IACF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,EAAkB,EAClB,KAAK,GAAe,EAAE;IAEtB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAChC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,UAAU,CAAC,IAAI,CACd,wGAAwG,CACxG,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,MAAM;QACf,UAAU,CAAC,IAAI,CACd,uHAAuH,CACvH,CAAC;IACH,IAAI,KAAK,CAAC,UAAU;QACnB,UAAU,CAAC,IAAI,CACd,iGAAiG,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,sBAAsB,EAAE,MAAM,CAAC,GAAG,CACnK,CAAC;IACH,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAqB,CAAC;QAC5D,UAAU,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACzE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CACzB;;gCAE6B,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,6CAA6C,EACzG,GAAG,MAAM,EACT,KAAK,GAAG,CAAC,CACT,CAAC;QACF,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QAClC,IAAI,IAAI;YAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CACxB,0GAA0G,EAC1G,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAClD,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;QAC3B,OAAO;YACN,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAE,EAAE,CAAC,CAAC;YAC7F,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7E,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAkB,EAAE,KAAkB;IACvE,MAAM,MAAM,GAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAUtB,yCAAyC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,mCAAmC,EAAE,GAAG,MAAM,CAAC,CAAC;IACpH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzB,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAChD,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAmB;QACxD,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,GAAG,EAAE,GAAG,CAAC,GAAG;KACZ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n\tMetricQuery,\n\tSpanAttributes,\n\tSpanStatus,\n\tStoredMetric,\n\tStoredSpan,\n\tStoredTrace,\n\tTraceQuery,\n} from \"@earendil-works/pi-telemetry\";\nimport type { SqliteDatabase, SqliteValue } from \"../sqlite/index.ts\";\n\ntype SpanRow = {\n\tid: string;\n\ttrace_id: string;\n\tparent_id: string | null;\n\tname: string;\n\tstarted_ms: number;\n\tended_ms: number;\n\tattributes: string;\n\tevents: string;\n\tlinks: string;\n\tstatus: string;\n};\n\nfunction storedSpan(row: SpanRow): StoredSpan {\n\treturn {\n\t\tid: row.id,\n\t\ttraceId: row.trace_id,\n\t\t...(row.parent_id === null ? {} : { parentId: row.parent_id }),\n\t\tname: row.name,\n\t\tstartedMs: row.started_ms,\n\t\tendedMs: row.ended_ms,\n\t\tattributes: JSON.parse(row.attributes) as SpanAttributes,\n\t\tevents: (JSON.parse(row.events) as { name: string; time_ms: number; attributes: SpanAttributes }[]).map(\n\t\t\t(event) => ({ name: event.name, timeMs: event.time_ms, attributes: event.attributes }),\n\t\t),\n\t\tlinks: (JSON.parse(row.links) as { trace_id: string; span_id: string; attributes?: SpanAttributes }[]).map(\n\t\t\t(link) => ({\n\t\t\t\ttraceId: link.trace_id,\n\t\t\t\tspanId: link.span_id,\n\t\t\t\t...(link.attributes === undefined ? {} : { attributes: link.attributes }),\n\t\t\t}),\n\t\t),\n\t\tstatus: JSON.parse(row.status) as SpanStatus,\n\t};\n}\n\n// JSON equality must distinguish boolean true from numeric 1. json_each also handles arbitrary attribute keys.\nfunction attributesSql(attributes: TraceQuery[\"attributes\"], column: string, params: SqliteValue[]): string {\n\treturn (\n\t\tObject.entries(attributes ?? {})\n\t\t\t.map(([key, value]) => {\n\t\t\t\tconst type =\n\t\t\t\t\ttypeof value === \"number\"\n\t\t\t\t\t\t? \"entry.type IN ('integer', 'real')\"\n\t\t\t\t\t\t: typeof value === \"boolean\"\n\t\t\t\t\t\t\t? `entry.type = '${value ? \"true\" : \"false\"}'`\n\t\t\t\t\t\t\t: \"entry.type = 'text'\";\n\t\t\t\tparams.push(key, typeof value === \"boolean\" ? Number(value) : value);\n\t\t\t\treturn `EXISTS (SELECT 1 FROM json_each(${column}) entry WHERE entry.key = ? AND ${type} AND entry.value = ?)`;\n\t\t\t})\n\t\t\t.join(\" AND \") || \"1\"\n\t);\n}\n\nexport async function getTrace(db: SqliteDatabase, traceId: string): Promise<StoredTrace | undefined> {\n\tconst rows = await db.all<SpanRow>(\n\t\t\"SELECT * FROM telemetry_spans WHERE trace_id = ? ORDER BY started_ms, id\",\n\t\ttraceId,\n\t);\n\treturn rows.length ? { traceId, spans: rows.map(storedSpan) } : undefined;\n}\n\nexport async function findTraces(\n\tdb: SqliteDatabase,\n\tquery: TraceQuery = {},\n): Promise<{ traces: readonly StoredTrace[]; cursor?: string }> {\n\tconst limit = query.limit ?? 20;\n\tconst conditions: string[] = [];\n\tconst params: SqliteValue[] = [];\n\tif (query.name !== undefined) {\n\t\tconditions.push(\n\t\t\t\"(SELECT name FROM telemetry_spans WHERE trace_id = heads.trace_id ORDER BY started_ms, id LIMIT 1) = ?\",\n\t\t);\n\t\tparams.push(query.name);\n\t}\n\tif (query.startMin !== undefined) {\n\t\tconditions.push(\"started_ms >= ?\");\n\t\tparams.push(query.startMin);\n\t}\n\tif (query.startMax !== undefined) {\n\t\tconditions.push(\"started_ms <= ?\");\n\t\tparams.push(query.startMax);\n\t}\n\tif (query.errors)\n\t\tconditions.push(\n\t\t\t\"EXISTS (SELECT 1 FROM telemetry_spans WHERE trace_id = heads.trace_id AND json_extract(status, '$.status') = 'error')\",\n\t\t);\n\tif (query.attributes)\n\t\tconditions.push(\n\t\t\t`EXISTS (SELECT 1 FROM telemetry_spans candidate WHERE candidate.trace_id = heads.trace_id AND ${attributesSql(query.attributes, \"candidate.attributes\", params)})`,\n\t\t);\n\tif (query.cursor !== undefined) {\n\t\tconst cursor = JSON.parse(query.cursor) as [number, string];\n\t\tconditions.push(\"(started_ms < ? OR (started_ms = ? AND trace_id > ?))\");\n\t\tparams.push(cursor[0], cursor[0], cursor[1]);\n\t}\n\treturn db.transaction(async (tx) => {\n\t\tconst heads = await tx.all<{ trace_id: string; started_ms: number }>(\n\t\t\t`WITH heads AS (\n\t\t\tSELECT trace_id, min(started_ms) started_ms FROM telemetry_spans GROUP BY trace_id\n\t\t) SELECT * FROM heads WHERE ${conditions.join(\" AND \") || \"1\"} ORDER BY started_ms DESC, trace_id LIMIT ?`,\n\t\t\t...params,\n\t\t\tlimit + 1,\n\t\t);\n\t\tconst more = heads.length > limit;\n\t\tif (more) heads.pop();\n\t\tif (!heads.length) return { traces: [] };\n\t\tconst rows = await tx.all<SpanRow>(\n\t\t\t\"SELECT * FROM telemetry_spans WHERE trace_id IN (SELECT value FROM json_each(?)) ORDER BY started_ms, id\",\n\t\t\tJSON.stringify(heads.map((head) => head.trace_id)),\n\t\t);\n\t\tconst grouped = new Map<string, StoredSpan[]>();\n\t\tfor (const row of rows) {\n\t\t\tconst spans = grouped.get(row.trace_id) ?? [];\n\t\t\tspans.push(storedSpan(row));\n\t\t\tgrouped.set(row.trace_id, spans);\n\t\t}\n\t\tconst last = heads.at(-1)!;\n\t\treturn {\n\t\t\ttraces: heads.map((head) => ({ traceId: head.trace_id, spans: grouped.get(head.trace_id)! })),\n\t\t\t...(more ? { cursor: JSON.stringify([last.started_ms, last.trace_id]) } : {}),\n\t\t};\n\t});\n}\n\nexport async function findMetrics(db: SqliteDatabase, query: MetricQuery): Promise<readonly StoredMetric[]> {\n\tconst params: SqliteValue[] = [query.name];\n\tconst conditions = [\"name = ?\"];\n\tif (query.startMin !== undefined) {\n\t\tconditions.push(\"interval_ms >= ?\");\n\t\tparams.push(query.startMin);\n\t}\n\tif (query.startMax !== undefined) {\n\t\tconditions.push(\"interval_ms <= ?\");\n\t\tparams.push(query.startMax);\n\t}\n\tconditions.push(attributesSql(query.attributes, \"attributes\", params));\n\tconst rows = await db.all<{\n\t\tinterval_ms: number;\n\t\tname: string;\n\t\tkind: StoredMetric[\"kind\"];\n\t\tunit: string | null;\n\t\tattributes: string;\n\t\tcount: number;\n\t\tsum: number;\n\t\tmin: number;\n\t\tmax: number;\n\t}>(`SELECT * FROM telemetry_metrics WHERE ${conditions.join(\" AND \")} ORDER BY interval_ms, series_key`, ...params);\n\treturn rows.map((row) => ({\n\t\tintervalMs: row.interval_ms,\n\t\tname: row.name,\n\t\tkind: row.kind,\n\t\t...(row.unit === null ? {} : { unit: row.unit }),\n\t\tattributes: JSON.parse(row.attributes) as SpanAttributes,\n\t\tcount: row.count,\n\t\tsum: row.sum,\n\t\tmin: row.min,\n\t\tmax: row.max,\n\t}));\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
|
-
import type { AnyContract,
|
|
1
|
+
import type { AnyContract, InputOf, Ref } from "./contract.ts";
|
|
2
2
|
import type { Op } from "./delta/index.ts";
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
readonly
|
|
3
|
+
/**
|
|
4
|
+
* Options of `get`. `P` is the app's principal type: a JSON-serializable identity of a caller, mapped to an object key
|
|
5
|
+
* by `createHost({ principalKey })`.
|
|
6
|
+
*/
|
|
7
|
+
export interface GetOptions<P = unknown> {
|
|
8
|
+
/** Principal the object is created for. Defaults to the host's own principal (`createHost({ self })`). */
|
|
9
|
+
readonly principal?: P;
|
|
10
|
+
/**
|
|
11
|
+
* False: a method-only reference. It receives no state (unless another reference to the same object wants it), and
|
|
12
|
+
* is ready once its object is live.
|
|
13
|
+
*/
|
|
14
|
+
readonly state?: boolean;
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
|
-
* The explicit call context of one root (a short Chord call tree). Calls made with it carry its root id and
|
|
13
|
-
* `get`
|
|
17
|
+
* The explicit call context of one root (a short Chord call tree). Calls made with it carry its root id and code
|
|
18
|
+
* version; `get` references are released when the call that received this ctx settles.
|
|
14
19
|
*/
|
|
15
|
-
export interface Ctx {
|
|
20
|
+
export interface Ctx<P = unknown> {
|
|
16
21
|
readonly signal: AbortSignal;
|
|
17
22
|
readonly rootId: string;
|
|
18
|
-
readonly
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
readonly codeVersion: number;
|
|
24
|
+
/** Who the root acts as: the caller at ingress, the host's own principal inside objects. */
|
|
25
|
+
readonly principal: P;
|
|
26
|
+
/**
|
|
27
|
+
* A reference for this call, released when the call settles. Resolves once it is ready: live, with every state
|
|
28
|
+
* member of its variant (unless `state: false`). Rejects with the activation error, `aborted` when the ctx is
|
|
29
|
+
* aborted or the call settles first, or `unavailable/ready_timeout` after the host's `readyTimeout`; the reference
|
|
30
|
+
* is released then.
|
|
31
|
+
*/
|
|
32
|
+
get<C extends AnyContract>(type: C, input: InputOf<C>, options?: GetOptions<P>): Promise<Ref<C>>;
|
|
21
33
|
}
|
|
22
34
|
export type StateEvent<T> = {
|
|
23
35
|
readonly type: "snap";
|
|
@@ -30,7 +42,7 @@ export type StateEvent<T> = {
|
|
|
30
42
|
readonly type: "reset";
|
|
31
43
|
readonly value: T;
|
|
32
44
|
};
|
|
33
|
-
/** A readable state source: a cell, a derivation, or a
|
|
45
|
+
/** A readable state source: a cell, a derivation, or a reference's state member. Values are immutable by contract. */
|
|
34
46
|
export interface Readable<T> {
|
|
35
47
|
readonly value: T;
|
|
36
48
|
/** Called with the current value immediately, then with every new value. */
|
|
@@ -39,38 +51,37 @@ export interface Readable<T> {
|
|
|
39
51
|
events(listener: (event: StateEvent<T>) => void): () => void;
|
|
40
52
|
}
|
|
41
53
|
/**
|
|
42
|
-
* What
|
|
43
|
-
*
|
|
54
|
+
* What an implementation may expose as a state member: a cell, a derivation, or a held object's state member (an
|
|
55
|
+
* object goes live only once the members it exposes have their values).
|
|
44
56
|
*/
|
|
45
|
-
export type StateSource<T> = Readable<T
|
|
57
|
+
export type StateSource<T> = Readable<T>;
|
|
46
58
|
declare const EXPOSED: unique symbol;
|
|
47
|
-
/** A local member backed by a resource (`self.expose(res.x)`):
|
|
59
|
+
/** A local member backed by a resource (`self.expose(res.x)`): references read the resource's value once live. */
|
|
48
60
|
export interface Exposed<T> {
|
|
49
61
|
readonly [EXPOSED]: T;
|
|
50
62
|
}
|
|
51
|
-
export type
|
|
63
|
+
export type RefStatus = "loading" | "live" | "released" | "gone";
|
|
52
64
|
export interface Shape {
|
|
53
|
-
readonly
|
|
65
|
+
readonly variant: string | undefined;
|
|
54
66
|
readonly members: readonly string[];
|
|
55
67
|
/** Fingerprint of the provider contract. */
|
|
56
68
|
readonly contract: string;
|
|
57
|
-
readonly epoch: number;
|
|
58
69
|
}
|
|
59
|
-
export interface
|
|
60
|
-
/** The consumer contract token this
|
|
70
|
+
export interface RefBase<C extends AnyContract> {
|
|
71
|
+
/** The consumer contract token this reference was bound with. */
|
|
61
72
|
readonly contract: C;
|
|
62
|
-
readonly status:
|
|
73
|
+
readonly status: RefStatus;
|
|
63
74
|
readonly ready: Promise<void>;
|
|
64
|
-
readonly
|
|
75
|
+
readonly variant: string | undefined;
|
|
65
76
|
readonly shape: Shape | undefined;
|
|
66
|
-
/** Why the
|
|
77
|
+
/** Why the reference is gone, if it is. */
|
|
67
78
|
readonly error: unknown;
|
|
68
79
|
onShape(listener: (shape: Shape) => void): () => void;
|
|
69
80
|
/**
|
|
70
81
|
* Called after every status change (`loading` → `live`, `live` → `loading` while a link reconnects, `released`,
|
|
71
82
|
* `gone` with `error`). A UI watches for `gone` to `get` again.
|
|
72
83
|
*/
|
|
73
|
-
onStatus(listener: (status:
|
|
84
|
+
onStatus(listener: (status: RefStatus, error: unknown) => void): () => void;
|
|
74
85
|
release(): void;
|
|
75
86
|
}
|
|
76
87
|
export {};
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AnyContract,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AnyContract, InputOf, Ref } from \"./contract.ts\";\nimport type { Op } from \"./delta/index.ts\";\n\n/**\n * Options of `get`. `P` is the app's principal type: a JSON-serializable identity of a caller, mapped to an object key\n * by `createHost({ principalKey })`.\n */\nexport interface GetOptions<P = unknown> {\n\t/** Principal the object is created for. Defaults to the host's own principal (`createHost({ self })`). */\n\treadonly principal?: P;\n\t/**\n\t * False: a method-only reference. It receives no state (unless another reference to the same object wants it), and\n\t * is ready once its object is live.\n\t */\n\treadonly state?: boolean;\n}\n\n/**\n * The explicit call context of one root (a short Chord call tree). Calls made with it carry its root id and code\n * version; `get` references are released when the call that received this ctx settles.\n */\nexport interface Ctx<P = unknown> {\n\treadonly signal: AbortSignal;\n\treadonly rootId: string;\n\treadonly codeVersion: number;\n\t/** Who the root acts as: the caller at ingress, the host's own principal inside objects. */\n\treadonly principal: P;\n\t/**\n\t * A reference for this call, released when the call settles. Resolves once it is ready: live, with every state\n\t * member of its variant (unless `state: false`). Rejects with the activation error, `aborted` when the ctx is\n\t * aborted or the call settles first, or `unavailable/ready_timeout` after the host's `readyTimeout`; the reference\n\t * is released then.\n\t */\n\tget<C extends AnyContract>(type: C, input: InputOf<C>, options?: GetOptions<P>): Promise<Ref<C>>;\n}\n\nexport type StateEvent<T> =\n\t| { readonly type: \"snap\"; readonly value: T }\n\t| { readonly type: \"ops\"; readonly ops: readonly Op[]; readonly value: T }\n\t| { readonly type: \"reset\"; readonly value: T };\n\n/** A readable state source: a cell, a derivation, or a reference's state member. Values are immutable by contract. */\nexport interface Readable<T> {\n\treadonly value: T;\n\t/** Called with the current value immediately, then with every new value. */\n\tsubscribe(listener: (value: T) => void): () => void;\n\t/** Delta stream: a snapshot immediately, then ops, or a reset after a migration or rebind. */\n\tevents(listener: (event: StateEvent<T>) => void): () => void;\n}\n\n/**\n * What an implementation may expose as a state member: a cell, a derivation, or a held object's state member (an\n * object goes live only once the members it exposes have their values).\n */\nexport type StateSource<T> = Readable<T>;\n\ndeclare const EXPOSED: unique symbol;\n\n/** A local member backed by a resource (`self.expose(res.x)`): references read the resource's value once live. */\nexport interface Exposed<T> {\n\treadonly [EXPOSED]: T;\n}\n\nexport type RefStatus = \"loading\" | \"live\" | \"released\" | \"gone\";\n\nexport interface Shape {\n\treadonly variant: string | undefined;\n\treadonly members: readonly string[];\n\t/** Fingerprint of the provider contract. */\n\treadonly contract: string;\n}\n\nexport interface RefBase<C extends AnyContract> {\n\t/** The consumer contract token this reference was bound with. */\n\treadonly contract: C;\n\treadonly status: RefStatus;\n\treadonly ready: Promise<void>;\n\treadonly variant: string | undefined;\n\treadonly shape: Shape | undefined;\n\t/** Why the reference is gone, if it is. */\n\treadonly error: unknown;\n\tonShape(listener: (shape: Shape) => void): () => void;\n\t/**\n\t * Called after every status change (`loading` → `live`, `live` → `loading` while a link reconnects, `released`,\n\t * `gone` with `error`). A UI watches for `gone` to `get` again.\n\t */\n\tonStatus(listener: (status: RefStatus, error: unknown) => void): () => void;\n\trelease(): void;\n}\n"]}
|
package/dist/websocket.d.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { AcceptOptions } from "./connection.ts";
|
|
2
2
|
import type { Host } from "./host.ts";
|
|
3
3
|
import { type WebSocketLike } from "./link.ts";
|
|
4
|
-
import type { Principal } from "./types.ts";
|
|
5
4
|
/** A WebSocket server (e.g. `ws`'s `WebSocketServer`), structurally: Chord does not depend on `ws`. */
|
|
6
5
|
export interface WebSocketServerLike<Request> {
|
|
7
6
|
on(event: "connection", listener: (socket: WebSocketLike, request: Request) => void): unknown;
|
|
8
7
|
}
|
|
9
|
-
/** WebSocket peers are always accepted as clients: no roots, no
|
|
10
|
-
export interface WebSocketAcceptOptions<Request> extends Omit<AcceptOptions
|
|
8
|
+
/** WebSocket peers are always accepted as clients: no roots, no code versions, no reload votes, not addressable. */
|
|
9
|
+
export interface WebSocketAcceptOptions<Request, P = unknown> extends Omit<AcceptOptions<P>, "principal" | "kind" | "name"> {
|
|
11
10
|
/** The authenticator: the principal of the connecting client, or undefined to refuse it. */
|
|
12
|
-
principal(request: Request):
|
|
11
|
+
principal(request: Request): P | undefined | Promise<P | undefined>;
|
|
13
12
|
}
|
|
14
13
|
/** Accepts every connection of `server` on `host` as a client link stamped with the authenticated principal. */
|
|
15
|
-
export declare function acceptWebSockets<Request>(server: WebSocketServerLike<Request>, host: Host
|
|
14
|
+
export declare function acceptWebSockets<Request, P = unknown>(server: WebSocketServerLike<Request>, host: Host<P>, options: WebSocketAcceptOptions<Request, P>): void;
|
|
16
15
|
//# sourceMappingURL=websocket.d.ts.map
|
package/dist/websocket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,WAAW,CAAC;AAE9D,uGAAuG;AACvG,MAAM,WAAW,mBAAmB,CAAC,OAAO;IAC3C,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC;CAC9F;AAED,oHAAoH;AACpH,MAAM,WAAW,sBAAsB,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAC3D,SAAQ,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,4FAA4F;IAC5F,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;CACpE;AAED,gHAAgH;AAChH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,EACpD,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC,EACpC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACb,OAAO,EAAE,sBAAsB,CAAC,OAAO,EAAE,CAAC,CAAC,GACzC,IAAI,CAaN"}
|
package/dist/websocket.js
CHANGED
|
@@ -2,8 +2,9 @@ import { webSocketLink } from "./link.js";
|
|
|
2
2
|
/** Accepts every connection of `server` on `host` as a client link stamped with the authenticated principal. */
|
|
3
3
|
export function acceptWebSockets(server, host, options) {
|
|
4
4
|
server.on("connection", (socket, request) => {
|
|
5
|
-
// Wrap at once so frames sent while authenticating are queued, not lost.
|
|
6
|
-
|
|
5
|
+
// Wrap at once so frames sent while authenticating are queued, not lost. Large payloads go out deflated; clients
|
|
6
|
+
// may not send packed frames (a small message could inflate to anything).
|
|
7
|
+
const link = webSocketLink(socket, { compress: true, acceptPacked: false });
|
|
7
8
|
Promise.resolve(options.principal(request)).then((principal) => {
|
|
8
9
|
if (principal === undefined)
|
|
9
10
|
link.close("unauthorized");
|
package/dist/websocket.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,aAAa,EAAE,MAAM,WAAW,CAAC;AAc9D,gHAAgH;AAChH,MAAM,UAAU,gBAAgB,CAC/B,MAAoC,EACpC,
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,aAAa,EAAE,MAAM,WAAW,CAAC;AAc9D,gHAAgH;AAChH,MAAM,UAAU,gBAAgB,CAC/B,MAAoC,EACpC,IAAa,EACb,OAA2C;IAE3C,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;QAC3C,iHAAiH;QACjH,0EAA0E;QAC1E,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5E,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC/C,CAAC,SAAS,EAAE,EAAE;YACb,IAAI,SAAS,KAAK,SAAS;gBAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;;gBACnD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnE,CAAC,EACD,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CACzC,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { AcceptOptions } from \"./connection.ts\";\nimport type { Host } from \"./host.ts\";\nimport { type WebSocketLike, webSocketLink } from \"./link.ts\";\n\n/** A WebSocket server (e.g. `ws`'s `WebSocketServer`), structurally: Chord does not depend on `ws`. */\nexport interface WebSocketServerLike<Request> {\n\ton(event: \"connection\", listener: (socket: WebSocketLike, request: Request) => void): unknown;\n}\n\n/** WebSocket peers are always accepted as clients: no roots, no code versions, no reload votes, not addressable. */\nexport interface WebSocketAcceptOptions<Request, P = unknown>\n\textends Omit<AcceptOptions<P>, \"principal\" | \"kind\" | \"name\"> {\n\t/** The authenticator: the principal of the connecting client, or undefined to refuse it. */\n\tprincipal(request: Request): P | undefined | Promise<P | undefined>;\n}\n\n/** Accepts every connection of `server` on `host` as a client link stamped with the authenticated principal. */\nexport function acceptWebSockets<Request, P = unknown>(\n\tserver: WebSocketServerLike<Request>,\n\thost: Host<P>,\n\toptions: WebSocketAcceptOptions<Request, P>,\n): void {\n\tserver.on(\"connection\", (socket, request) => {\n\t\t// Wrap at once so frames sent while authenticating are queued, not lost. Large payloads go out deflated; clients\n\t\t// may not send packed frames (a small message could inflate to anything).\n\t\tconst link = webSocketLink(socket, { compress: true, acceptPacked: false });\n\t\tPromise.resolve(options.principal(request)).then(\n\t\t\t(principal) => {\n\t\t\t\tif (principal === undefined) link.close(\"unauthorized\");\n\t\t\t\telse host.accept(link, { ...options, principal, kind: \"client\" });\n\t\t\t},\n\t\t\t() => link.close(\"authentication failed\"),\n\t\t);\n\t});\n}\n"]}
|