@observertc/observer-js 1.0.0-beta.4 → 1.0.0-beta.5

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/README.md CHANGED
@@ -15,8 +15,9 @@ and emits a single, unified stream of typed events the application can react to.
15
15
  > agent) should be able to integrate the library, or develop it further, from this file alone.
16
16
  > A companion doc, [`docs/logging.md`](./docs/logging.md), covers logging integration in depth.
17
17
 
18
- > **Packaging:** the package is **ESM-only** and **server-side** (Node.js ≥ 16). Everything —
19
- > including the built-in file sink — is exported from the single `@observertc/observer-js` entry.
18
+ > **Packaging:** server-side, **Node.js ≥ 22**, shipped as a **dual ESM + CommonJS** build — so it
19
+ > works whether your project uses `import` (ESM) or `require()` (CommonJS). Everything — including
20
+ > the built-in file sink — is exported from the single `@observertc/observer-js` entry.
20
21
 
21
22
  ---
22
23
 
@@ -51,22 +52,20 @@ npm install @observertc/observer-js
51
52
  yarn add @observertc/observer-js
52
53
  ```
53
54
 
54
- **ESM-only, server-side.** The package ships ES Modules (`import`, not `require()`) and targets
55
- **Node.js ≥ 16**. Use it from ESM code (`"type": "module"`, or `.mjs`), or from TypeScript
56
- compiled to ESM. Everything is exported from the single `@observertc/observer-js` entry:
55
+ **Server-side, Node.js ≥ 22, dual ESM + CommonJS.** The package ships both module formats, so it
56
+ works the same whether your project is ESM or CommonJS — your import line is unchanged either way:
57
57
 
58
58
  ```ts
59
59
  import { Observer, ClientSample, createJsonlFileSinkFactory } from '@observertc/observer-js';
60
60
  ```
61
61
 
62
- Written in TypeScript; ships type declarations alongside the build (`dist/index.d.mts`). Runtime
62
+ In an ESM project this resolves to the `.mjs` build; in a CommonJS project (where TypeScript
63
+ compiles your `import` down to `require()`) it resolves to the `.js` build. Everything is exported
64
+ from the single `@observertc/observer-js` entry. Written in TypeScript; ships type declarations for
65
+ both formats (`dist/index.d.ts` for `require`, `dist/index.d.mts` for `import`). Runtime
63
66
  dependencies: `@bufbuild/protobuf`, `events`, `uuid`. The library does **not** bundle a logger or
64
67
  any transport — see [Logging](#logging).
65
68
 
66
- > **Note on `require()`.** Being ESM-only, the package can't be loaded with CommonJS
67
- > `require('@observertc/observer-js')`; consume it with `import` (or `await import()` from a CJS
68
- > module). If you need a CommonJS build, a dual ESM+CJS output is a small change — ask.
69
-
70
69
  `ClientSample` and friends are re-exported from this package, and are also published as the
71
70
  shared schema in [`@observertc/schemas`](https://github.com/observertc/schemas); samples
72
71
  produced on the client (e.g. by `@observertc/client-monitor-js`) conform to the same shape.
@@ -733,12 +732,32 @@ observer.on('client-sink-created', ({ observedClient, sink }) => {
733
732
  |--------|-----------|-------|
734
733
  | `createJsonlFileSinkFactory` | `({ directory, flags?, getFileName?, serializeSample? }) => ClientSampleSinkFactory` | per-client JSONL files; path defaults to `${callId}__${clientId}.jsonl` under `directory` (which **must exist**) |
735
734
  | `createJsonlFileSink` | `({ path, flags?, serializeSample? }) => ClientSampleSink` | a single JSONL file; wraps `fs.WriteStream` and re-emits its `close`/`finish`/`drain`/`error` |
736
- | `JsonlFileSink` | `class extends ClientSampleSink` | the underlying class, if you want to construct it directly |
735
+ | `JsonlFileSink` | `class extends ClientSampleSink` | the underlying class; exposes `readonly path` so a `close` handler knows which file is ready |
737
736
  | `createInMemorySink` / `InMemorySink` | `(samples?: ClientSample[]) => InMemorySink` | collects the accepted **sample objects** into `.samples: ClientSample[]`; emits `close` on `end()` |
738
737
 
739
738
  `serializeSample?: (sample: ClientSample) => string` overrides the default `JSON.stringify` for
740
739
  the JSONL sinks (e.g. to redact or reshape before writing).
741
740
 
741
+ ### Reading sink-specific info (e.g. the file path)
742
+
743
+ The bus hands you the sink as the base `ClientSampleSink`. To read information specific to a sink
744
+ type — for a file sink, where it was written — **narrow with `instanceof`** and read the sink's
745
+ public fields. `JsonlFileSink` exposes `path`:
746
+
747
+ ```ts
748
+ import { JsonlFileSink } from '@observertc/observer-js';
749
+
750
+ observer.on('client-sink-created', ({ observedClient, sink }) => {
751
+ if (sink instanceof JsonlFileSink) {
752
+ const { path } = sink; // the file this client's samples go to
753
+ sink.once('close', () => uploadFile(path)); // close = flushed & fd closed → ready
754
+ }
755
+ });
756
+ ```
757
+
758
+ The general pattern: each concrete sink exposes whatever it wants as `public readonly` fields, and
759
+ consumers narrow (`instanceof YourSink`) to read them. Your own sinks do the same.
760
+
742
761
  ### Writing your own sink
743
762
 
744
763
  Subclass `ClientSampleSink` and emit the lifecycle events yourself — for any non-file
@@ -853,23 +872,23 @@ export type { TrackReport, ClientReport } from './Reports';
853
872
 
854
873
  ```bash
855
874
  yarn install
856
- yarn build # tsup → dist/ (ESM: index + sinks, with .d.mts types & sourcemaps)
875
+ yarn build # tsup → dist/ (dual ESM .mjs + CJS .js, single entry, .d.ts/.d.mts + sourcemaps)
857
876
  yarn lint # eslint -c .eslintrc.json "src/**/*.ts"
858
877
  yarn typecheck # tsc --noEmit
859
878
  yarn test # jest
860
879
  ```
861
880
 
862
881
  The build is driven by [`tsup`](https://tsup.egoist.dev) (config in `tsup.config.ts`): a single
863
- entry (`src/index.ts`), ESM output to `dist/` with `.d.mts` types and sourcemaps. CI
864
- (`.github/workflows/ci.yml`) runs lint + typecheck + **build** + test on every push/PR.
882
+ entry (`src/index.ts`), dual ESM + CommonJS output to `dist/` (`index.mjs` / `index.js`) with
883
+ `.d.mts` / `.d.ts` types and sourcemaps, targeting Node 22. CI (`.github/workflows/ci.yml`) runs
884
+ lint + typecheck + **build** + test on every push/PR.
865
885
 
866
886
  **Project layout** (`src/`): `Observer.ts`, `ObservedCall.ts`, `ObservedClient.ts`,
867
887
  `ObservedPeerConnection.ts`, the `Observed*` sub-stat classes, `ObserverEvents.ts` (the typed
868
888
  event map + scope types), `detectors/` (`Detector`, `Detectors`), `scores/`, `updaters/`
869
889
  (update-policy strategies), `utils/` (remote-track resolvers), `common/` (`logger`, `utils`,
870
- `Middleware`), `schema/` (sample/event/meta types), and `sinks/` (the import-safe
871
- `ClientSampleSink` base + the Node-only `JsonlFileSink` / `InMemorySink`, re-exported via the
872
- `@observertc/observer-js/sinks` subpath).
890
+ `Middleware`), `schema/` (sample/event/meta types), and `sinks/` (the `ClientSampleSink` base +
891
+ `JsonlFileSink` / `InMemorySink`, re-exported from the package root).
873
892
 
874
893
  **Conventions to follow when developing further:**
875
894
 
package/dist/index.d.mts CHANGED
@@ -2751,6 +2751,8 @@ type JsonlFileSinkOptions = {
2751
2751
  * and its descriptor is closed (file ready) and `error` surfaces file errors.
2752
2752
  */
2753
2753
  declare class JsonlFileSink extends ClientSampleSink {
2754
+ /** The file this sink writes to. Read it (e.g. in a `close` handler) to upload/move the file. */
2755
+ readonly path: string;
2754
2756
  private readonly _stream;
2755
2757
  private readonly _serializeSample;
2756
2758
  constructor(options: JsonlFileSinkOptions);