@jeffjassky/telemetry 0.5.0 → 0.6.1
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/dist/index.cjs +172 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +172 -43
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/ui/_assets/{index-kvwB9_3A.js → index-BVZYi-43.js} +8 -8
- package/dist/ui/_assets/index-BVZYi-43.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +75 -1
- package/types/test-d.ts +18 -0
- package/dist/ui/_assets/index-kvwB9_3A.js.map +0 -1
package/dist/ui/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Telemetry</title>
|
|
7
7
|
<!--telemetry-config-->
|
|
8
|
-
<script type="module" crossorigin src="./_assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./_assets/index-BVZYi-43.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="./_assets/index-COswHpSX.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffjassky/telemetry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Unified telemetry — product events, errors, traces, state transitions, and billable usage in one Mongo envelope, with typed SDKs and a mountable dashboard.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jeff Jassky <jeff@jeffjassky.com>",
|
package/types/index.d.ts
CHANGED
|
@@ -696,6 +696,59 @@ export interface ForgetResult {
|
|
|
696
696
|
views: number;
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
+
export interface RelinkOptions {
|
|
700
|
+
/**
|
|
701
|
+
* Restrict to these event names. Default: every stored record, whatever its
|
|
702
|
+
* name. A name the registry does not declare THROWS, before any I/O — a typo
|
|
703
|
+
* that silently relinks nothing looks exactly like a clean run.
|
|
704
|
+
*/
|
|
705
|
+
names?: string[];
|
|
706
|
+
/** only records at/after this instant (`occurredAt`) */
|
|
707
|
+
since?: Date;
|
|
708
|
+
/**
|
|
709
|
+
* Stop after this many records are EXAMINED — not linked. A budget for the
|
|
710
|
+
* scan, so a huge collection can be probed cheaply. Default unbounded.
|
|
711
|
+
*/
|
|
712
|
+
limit?: number;
|
|
713
|
+
/**
|
|
714
|
+
* Report what would change and write NOTHING. **Defaults to `true`**, because
|
|
715
|
+
* this rewrites historical aggregates and the short call has to be the safe
|
|
716
|
+
* one. A dry run still asks the host's linker, so the linking counters move;
|
|
717
|
+
* nothing on disk does.
|
|
718
|
+
*/
|
|
719
|
+
dryRun?: boolean;
|
|
720
|
+
/** records fetched per batch, and the `onProgress` cadence. Default 500. */
|
|
721
|
+
batchSize?: number;
|
|
722
|
+
/** cumulative counts after each batch, for progress output. Guarded: a
|
|
723
|
+
* printer that throws does not kill the backfill. */
|
|
724
|
+
onProgress?: (r: RelinkResult) => void;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
export interface RelinkResult {
|
|
728
|
+
/** rows read */
|
|
729
|
+
examined: number;
|
|
730
|
+
/** rows that gained at least one subject */
|
|
731
|
+
linked: number;
|
|
732
|
+
/** subjects added in total — two links on one row count twice */
|
|
733
|
+
subjects: number;
|
|
734
|
+
/** rollup documents written, or under `dryRun` that would have been */
|
|
735
|
+
rollups: number;
|
|
736
|
+
/** rows where the linker answered `[]`. On a backfill this is the expected
|
|
737
|
+
* answer for most rows, and it is not a failure. */
|
|
738
|
+
misses: number;
|
|
739
|
+
/** rows where the linker threw, rejected, timed out or answered garbage —
|
|
740
|
+
* once per ROW however many ways it went wrong. `counters.subjectLinkErrors`
|
|
741
|
+
* and `subjectLinkTimeouts` keep the finer split. */
|
|
742
|
+
errors: number;
|
|
743
|
+
/** rows the run declined to offer the linker (a stored name the registry no
|
|
744
|
+
* longer declares, so its rollup families are unknowable), plus one standing
|
|
745
|
+
* for a call with no `subjectLinker` configured, which reads nothing. */
|
|
746
|
+
skipped: number;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/** records fetched per batch by `relink()`, and how often `onProgress` fires */
|
|
750
|
+
export declare const RELINK_BATCH_SIZE: 500;
|
|
751
|
+
|
|
699
752
|
export interface Scoped {
|
|
700
753
|
find(q?: Record<string, unknown>): Query<any[], any>;
|
|
701
754
|
aggregate(stages: Record<string, unknown>[]): Aggregate<any[]>;
|
|
@@ -760,7 +813,9 @@ export interface SubjectLinker {
|
|
|
760
813
|
* Runs once per record on the write path, so it must answer from a cache.
|
|
761
814
|
* The package bounds it rather than trusting it: past `subjectLinkTimeoutMs`,
|
|
762
815
|
* or on a throw, the record is written unlinked and counted. A linked subject
|
|
763
|
-
* whose `type` the event does not declare is
|
|
816
|
+
* whose `type` the event does not declare is written ANYWAY and counted in
|
|
817
|
+
* `counters.subjectLinkUndeclared` — refusing it could only ever be obeyed by
|
|
818
|
+
* losing rows, because `EventSpec.subjects` is a required list.
|
|
764
819
|
*/
|
|
765
820
|
link(
|
|
766
821
|
subjects: SubjectInput[],
|
|
@@ -802,6 +857,25 @@ export interface Telemetry<R extends Registry = Registry> {
|
|
|
802
857
|
* platform-scoped saved views only when `globalSubjectRefs` is set.
|
|
803
858
|
*/
|
|
804
859
|
forget(tenantId: string, ref: EntityRef): Promise<ForgetResult>;
|
|
860
|
+
/**
|
|
861
|
+
* Backfill for `subjectLinker`: re-ask it about records ALREADY on disk, and
|
|
862
|
+
* replay the rollups the new subjects reach.
|
|
863
|
+
*
|
|
864
|
+
* Linking happens at write time, so configuring the hook fixes the future and
|
|
865
|
+
* nothing else — a lifetime `by:['subject']` family is keyed on the subject
|
|
866
|
+
* the record was written with, permanently, and no read-time join can reach
|
|
867
|
+
* back into it. A host that adopts linking on a Tuesday therefore has a
|
|
868
|
+
* backlog whose rows carry only `machine:<installId>` and whose user-keyed
|
|
869
|
+
* families have no member for any of them. This is how it catches up.
|
|
870
|
+
*
|
|
871
|
+
* **Dry run by default.** It rewrites historical aggregates, so `t.relink()`
|
|
872
|
+
* reports and `t.relink({ dryRun: false })` writes. Idempotent by
|
|
873
|
+
* construction: a row that already carries the linked subject yields nothing
|
|
874
|
+
* new, so a second run writes nothing and replays nothing. Returns
|
|
875
|
+
* `{ skipped: 1 }` rather than throwing when no `subjectLinker` is
|
|
876
|
+
* configured.
|
|
877
|
+
*/
|
|
878
|
+
relink(opts?: RelinkOptions): Promise<RelinkResult>;
|
|
805
879
|
/**
|
|
806
880
|
* Tenant scope is not optional — every read goes through here. Unconditional
|
|
807
881
|
* on purpose: it does not understand PLATFORM_SCOPE, so `scoped('*')` scopes
|
package/types/test-d.ts
CHANGED
|
@@ -33,6 +33,8 @@ import type {
|
|
|
33
33
|
Logger,
|
|
34
34
|
MetricsOf,
|
|
35
35
|
Registry,
|
|
36
|
+
RelinkOptions,
|
|
37
|
+
RelinkResult,
|
|
36
38
|
RollupSpec,
|
|
37
39
|
LinkSubjects,
|
|
38
40
|
Scoped,
|
|
@@ -60,6 +62,7 @@ import {
|
|
|
60
62
|
LogLevel,
|
|
61
63
|
Origin,
|
|
62
64
|
PLATFORM_SCOPE,
|
|
65
|
+
RELINK_BATCH_SIZE,
|
|
63
66
|
RETENTION_DAYS,
|
|
64
67
|
SAMPLE_RATE,
|
|
65
68
|
SCHEMA_VERSION,
|
|
@@ -196,6 +199,21 @@ async function reads() {
|
|
|
196
199
|
const gone: ForgetResult = await t.forget('acc_9', 'user:u_1');
|
|
197
200
|
void (gone.deleted + gone.redacted + gone.rollups + gone.aliases);
|
|
198
201
|
|
|
202
|
+
// relink() — the backfill for subjectLinker. Dry run by DEFAULT, so the
|
|
203
|
+
// no-argument call is the safe one and writing is opted into.
|
|
204
|
+
const preview: RelinkResult = await t.relink();
|
|
205
|
+
const opts: RelinkOptions = {
|
|
206
|
+
names: ['user.signed_up'],
|
|
207
|
+
since: new Date('2026-01-01T00:00:00Z'),
|
|
208
|
+
limit: 10_000,
|
|
209
|
+
dryRun: false,
|
|
210
|
+
batchSize: RELINK_BATCH_SIZE,
|
|
211
|
+
onProgress: (p) => void p.examined,
|
|
212
|
+
};
|
|
213
|
+
const done: RelinkResult = await t.relink(opts);
|
|
214
|
+
void (preview.examined + done.linked + done.subjects + done.rollups
|
|
215
|
+
+ done.misses + done.errors + done.skipped);
|
|
216
|
+
|
|
199
217
|
const cp: Checkpoint = t.checkpoint('mailery-bridge');
|
|
200
218
|
const mark: Date | null = await cp.get();
|
|
201
219
|
await cp.advance(mark ?? new Date());
|