cursedbelt-server 4.17.1 → 4.18.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.
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/telemetry` — the telemetry seam, from a WORKER.
|
|
3
|
+
*
|
|
4
|
+
* `requestLogger` and `createTelemetrySink` are built for exactly one host that most needs them:
|
|
5
|
+
* a Worker, where a row per request in D1 is the one line on Cloudflare's price list with a
|
|
6
|
+
* dollar-scale overage and Workers Analytics Engine is the purpose-built sink
|
|
7
|
+
* (`./metrics/telemetrySink.ts`). Until 2026-09-23 the only door to them was
|
|
8
|
+
* `cursedbelt-server/middleware`, whose export map sends every non-Bun importer to
|
|
9
|
+
* `_bunOnly.js` because the barrel carries Bun-only middleware beside them — so `wrangler`
|
|
10
|
+
* refused the import (`No matching export … for import "requestLogger"`, measured on `music`'s
|
|
11
|
+
* first attempt to mount it) and the seam could reach every host except the one it was for.
|
|
12
|
+
*
|
|
13
|
+
* Everything here is runtime-safe on workerd: `bun:sqlite` appears only as a TYPE (a Mac host
|
|
14
|
+
* passes its `Database`; a Worker passes `null` and an `analytics` binding).
|
|
15
|
+
* `telemetryIsWorkerSafe.spec.ts` walks this file's runtime import graph and fails on any
|
|
16
|
+
* `bun:` import, so it stays that way.
|
|
17
|
+
*/
|
|
18
|
+
export { type RequestLoggerOpts, requestLogger } from './middleware/requestLogger.js';
|
|
19
|
+
export { type AnalyticsEngineDataset, createTelemetrySink, TELEMETRY_POINT_VERSION, type TelemetryEvent, type TelemetrySink, type TelemetrySinkOptions, toDataPoint, } from './metrics/telemetrySink.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/telemetry` — the telemetry seam, from a WORKER.
|
|
3
|
+
*
|
|
4
|
+
* `requestLogger` and `createTelemetrySink` are built for exactly one host that most needs them:
|
|
5
|
+
* a Worker, where a row per request in D1 is the one line on Cloudflare's price list with a
|
|
6
|
+
* dollar-scale overage and Workers Analytics Engine is the purpose-built sink
|
|
7
|
+
* (`./metrics/telemetrySink.ts`). Until 2026-09-23 the only door to them was
|
|
8
|
+
* `cursedbelt-server/middleware`, whose export map sends every non-Bun importer to
|
|
9
|
+
* `_bunOnly.js` because the barrel carries Bun-only middleware beside them — so `wrangler`
|
|
10
|
+
* refused the import (`No matching export … for import "requestLogger"`, measured on `music`'s
|
|
11
|
+
* first attempt to mount it) and the seam could reach every host except the one it was for.
|
|
12
|
+
*
|
|
13
|
+
* Everything here is runtime-safe on workerd: `bun:sqlite` appears only as a TYPE (a Mac host
|
|
14
|
+
* passes its `Database`; a Worker passes `null` and an `analytics` binding).
|
|
15
|
+
* `telemetryIsWorkerSafe.spec.ts` walks this file's runtime import graph and fails on any
|
|
16
|
+
* `bun:` import, so it stays that way.
|
|
17
|
+
*/
|
|
18
|
+
export { requestLogger } from './middleware/requestLogger.js';
|
|
19
|
+
export { createTelemetrySink, TELEMETRY_POINT_VERSION, toDataPoint, } from './metrics/telemetrySink.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt-server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.18.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The app-facing Bun/Hono server tier of the cursedbelt split — storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
@@ -163,6 +163,12 @@
|
|
|
163
163
|
"source": "./src/server/master-lock/index.ts",
|
|
164
164
|
"import": "./dist/server/master-lock/index.js"
|
|
165
165
|
},
|
|
166
|
+
"./telemetry": {
|
|
167
|
+
"types": "./dist/server/telemetry.d.ts",
|
|
168
|
+
"bun": "./src/server/telemetry.ts",
|
|
169
|
+
"source": "./src/server/telemetry.ts",
|
|
170
|
+
"import": "./dist/server/telemetry.js"
|
|
171
|
+
},
|
|
166
172
|
"./middleware": {
|
|
167
173
|
"types": "./dist/server/middleware/index.d.ts",
|
|
168
174
|
"bun": "./src/server/middleware/index.ts",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/telemetry` — the telemetry seam, from a WORKER.
|
|
3
|
+
*
|
|
4
|
+
* `requestLogger` and `createTelemetrySink` are built for exactly one host that most needs them:
|
|
5
|
+
* a Worker, where a row per request in D1 is the one line on Cloudflare's price list with a
|
|
6
|
+
* dollar-scale overage and Workers Analytics Engine is the purpose-built sink
|
|
7
|
+
* (`./metrics/telemetrySink.ts`). Until 2026-09-23 the only door to them was
|
|
8
|
+
* `cursedbelt-server/middleware`, whose export map sends every non-Bun importer to
|
|
9
|
+
* `_bunOnly.js` because the barrel carries Bun-only middleware beside them — so `wrangler`
|
|
10
|
+
* refused the import (`No matching export … for import "requestLogger"`, measured on `music`'s
|
|
11
|
+
* first attempt to mount it) and the seam could reach every host except the one it was for.
|
|
12
|
+
*
|
|
13
|
+
* Everything here is runtime-safe on workerd: `bun:sqlite` appears only as a TYPE (a Mac host
|
|
14
|
+
* passes its `Database`; a Worker passes `null` and an `analytics` binding).
|
|
15
|
+
* `telemetryIsWorkerSafe.spec.ts` walks this file's runtime import graph and fails on any
|
|
16
|
+
* `bun:` import, so it stays that way.
|
|
17
|
+
*/
|
|
18
|
+
export { type RequestLoggerOpts, requestLogger } from './middleware/requestLogger.js';
|
|
19
|
+
export {
|
|
20
|
+
type AnalyticsEngineDataset,
|
|
21
|
+
createTelemetrySink,
|
|
22
|
+
TELEMETRY_POINT_VERSION,
|
|
23
|
+
type TelemetryEvent,
|
|
24
|
+
type TelemetrySink,
|
|
25
|
+
type TelemetrySinkOptions,
|
|
26
|
+
toDataPoint,
|
|
27
|
+
} from './metrics/telemetrySink.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `cursedbelt-server/telemetry` must be importable by a Worker: nothing reachable from it at
|
|
7
|
+
* RUNTIME may import `bun:*`. A `import type` / `export type` is erased and does not count.
|
|
8
|
+
*/
|
|
9
|
+
const RUNTIME_IMPORT = /^\s*(?:import|export)\s+(?!type\b)(?:[^'"]*?from\s+)?['"]([^'"]+)['"]/gm;
|
|
10
|
+
|
|
11
|
+
function runtimeGraph(entry: string): { files: string[]; bunImports: string[] } {
|
|
12
|
+
const seen = new Set<string>();
|
|
13
|
+
const bunImports: string[] = [];
|
|
14
|
+
const walk = (file: string): void => {
|
|
15
|
+
if (seen.has(file)) return;
|
|
16
|
+
seen.add(file);
|
|
17
|
+
const source = readFileSync(file, 'utf8');
|
|
18
|
+
for (const match of source.matchAll(RUNTIME_IMPORT)) {
|
|
19
|
+
const spec = match[1] as string;
|
|
20
|
+
if (spec.startsWith('bun:')) bunImports.push(`${file.split('/src/')[1]} → ${spec}`);
|
|
21
|
+
if (spec.startsWith('.')) walk(resolve(dirname(file), spec.replace(/\.js$/, '.ts')));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
walk(entry);
|
|
25
|
+
return { files: [...seen], bunImports };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('cursedbelt-server/telemetry', () => {
|
|
29
|
+
test('🔴 reaches no `bun:` module at runtime — a Worker can import it', () => {
|
|
30
|
+
const { files, bunImports } = runtimeGraph(join(import.meta.dir, 'telemetry.ts'));
|
|
31
|
+
expect(bunImports).toEqual([]);
|
|
32
|
+
// Not vacuous: the logger and the sink are both in the graph.
|
|
33
|
+
expect(files.some((f) => f.endsWith('requestLogger.ts'))).toBe(true);
|
|
34
|
+
expect(files.some((f) => f.endsWith('telemetrySink.ts'))).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('the walk goes red on the shape it exists for', () => {
|
|
38
|
+
const regex = new RegExp(RUNTIME_IMPORT.source, RUNTIME_IMPORT.flags);
|
|
39
|
+
const found = [..."import { Database } from 'bun:sqlite';\nimport type { X } from 'bun:sqlite';".matchAll(regex)].map((m) => m[1]);
|
|
40
|
+
expect(found).toEqual(['bun:sqlite']);
|
|
41
|
+
});
|
|
42
|
+
});
|