@docx-editor.dev/telemetry 0.0.1 → 2.29.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.
Files changed (47) hide show
  1. package/README.md +85 -7
  2. package/THIRD_PARTY_NOTICES.md +22 -0
  3. package/dist/chunk-73YEPWY5.js +1 -0
  4. package/dist/chunk-7ZXJ6HP2.cjs +1 -0
  5. package/dist/chunk-DT2KMPUJ.js +1 -0
  6. package/dist/chunk-DTEVX2XS.js +1 -0
  7. package/dist/chunk-LFQSY2TD.cjs +1 -0
  8. package/dist/chunk-LFX65QJG.js +4 -0
  9. package/dist/chunk-LGNT4I5W.cjs +1 -0
  10. package/dist/chunk-MJX3ECH3.cjs +4 -0
  11. package/dist/chunk-NUN46EXH.js +2 -0
  12. package/dist/chunk-RRYW7EAV.js +1 -0
  13. package/dist/chunk-S7KBMROA.js +1 -0
  14. package/dist/chunk-U6ERFNUM.cjs +1 -0
  15. package/dist/chunk-UK2MNJCG.cjs +2 -0
  16. package/dist/chunk-WGWKNKUT.cjs +1 -0
  17. package/dist/hocuspocus/index.cjs +1 -0
  18. package/dist/hocuspocus/index.d.cts +118 -0
  19. package/dist/hocuspocus/index.d.ts +118 -0
  20. package/dist/hocuspocus/index.js +1 -0
  21. package/dist/index.cjs +1 -0
  22. package/dist/index.d.cts +332 -0
  23. package/dist/index.d.ts +332 -0
  24. package/dist/index.js +1 -0
  25. package/dist/ingest/index.cjs +1 -0
  26. package/dist/ingest/index.d.cts +107 -0
  27. package/dist/ingest/index.d.ts +107 -0
  28. package/dist/ingest/index.js +1 -0
  29. package/dist/otel/index.cjs +1 -0
  30. package/dist/otel/index.d.cts +57 -0
  31. package/dist/otel/index.d.ts +57 -0
  32. package/dist/otel/index.js +1 -0
  33. package/dist/pipeline-DbVW7S-K.d.cts +223 -0
  34. package/dist/pipeline-DbVW7S-K.d.ts +223 -0
  35. package/dist/prometheus/index.cjs +1 -0
  36. package/dist/prometheus/index.d.cts +88 -0
  37. package/dist/prometheus/index.d.ts +88 -0
  38. package/dist/prometheus/index.js +1 -0
  39. package/dist/sentry/index.cjs +1 -0
  40. package/dist/sentry/index.d.cts +134 -0
  41. package/dist/sentry/index.d.ts +134 -0
  42. package/dist/sentry/index.js +1 -0
  43. package/dist/server/index.cjs +1 -0
  44. package/dist/server/index.d.cts +156 -0
  45. package/dist/server/index.d.ts +156 -0
  46. package/dist/server/index.js +1 -0
  47. package/package.json +100 -16
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Anonymous diagnostics, metrics, and error reporting for editors and collaboration servers.
3
+ *
4
+ * - `@docx-editor.dev/telemetry`: {@link createTelemetry} for editors, the console, callback,
5
+ * memory, and HTTP exporters, and {@link browserPrivacySignals}.
6
+ * - `@docx-editor.dev/telemetry/ingest`: a route that receives telemetry from `httpExporter`
7
+ * and forwards it to a log service.
8
+ * - `@docx-editor.dev/telemetry/otel`: OpenTelemetry metrics and traces through your SDK.
9
+ * - `@docx-editor.dev/telemetry/sentry`: Sentry issues and breadcrumbs.
10
+ * - `@docx-editor.dev/telemetry/prometheus`: a `/metrics` endpoint.
11
+ * - `@docx-editor.dev/telemetry/server`: room telemetry for any Yjs server.
12
+ * - `@docx-editor.dev/telemetry/hocuspocus`: room telemetry for Hocuspocus in one call.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ import { EditorModule, EditorDiagnostic } from '@docx-editor.dev/core';
17
+ export { ByteSizeBucket, CollaborationDiagnosticCode, CollaborationParticipantsDiagnostic, CollaborationStatusDiagnostic, CountBucket, CustomNodeDiagnosticCode, CustomNodeDiagnosticEvent, DiagnosticBase, DiagnosticCode, DiagnosticErrorName, DiagnosticExplanation, DiagnosticFailureDetail, DocumentOpenDiagnostic, DocumentOpenDiagnosticCode, DocumentPartKind, DocumentShape, EditDiagnosticCode, EditRejectedDiagnostic, EditRejectionOrigin, EditorDiagnostic, EditorDiagnosticKind, FailureStage, FontDiagnosticCode, FontFailedDiagnostic, FormattingChangeKind, HistoryDiagnosticCode, HistoryDiagnosticEvent, PerformanceDiagnostic, PerformanceDiagnosticCode, ReviewChangeKind, ReviewDiagnosticCode, ReviewOperation, ReviewSkippedDiagnostic, TimingSummary, explainDiagnostic } from '@docx-editor.dev/core';
18
+ import { A as Attributes, T as TelemetryPipelineOptions, a as TelemetryEvent, b as TelemetryExporter, M as MetricRecord } from './pipeline-DbVW7S-K.cjs';
19
+ export { c as AttributeValue, E as ENGINE_VERSION, d as MAX_METRIC_LABEL_VALUES, e as METRICS, f as MetricDefinition, g as MetricId, h as MetricKind, i as TelemetryEventContext, j as TelemetryExporterContext, p as prometheusName } from './pipeline-DbVW7S-K.cjs';
20
+
21
+ /**
22
+ * The areas you can switch on. Each one selects diagnostic kinds:
23
+ *
24
+ * - `documentOpen`: documents opened and refused (`document.open`).
25
+ * - `editing`: edits and commands refused while changes are not tracked (`edit.rejected`).
26
+ * - `trackedChanges`: refusals while suggesting, refusals about tracking itself, and skipped
27
+ * tracked changes
28
+ * (`edit.rejected` with `tracked: true`, `review.skipped`).
29
+ * - `fonts`: fonts that failed and were substituted (`font.failed`).
30
+ * - `history`: undo steps that did not group as the gesture intended (`history`).
31
+ * - `customNodes`: custom node payloads that could not be read (`custom-node`).
32
+ * - `collaborationClient`: collaboration status changes and room size (`collab.*`).
33
+ * - `performance`: how fast the editor responds: key presses and clicks to the next frame,
34
+ * and engine layout and paint times, as one summary per minute of use (`performance`).
35
+ *
36
+ * @public
37
+ */
38
+ type TelemetryArea = 'documentOpen' | 'editing' | 'trackedChanges' | 'fonts' | 'history' | 'customNodes' | 'collaborationClient' | 'performance';
39
+ /** Options for one area. @public */
40
+ interface TelemetryAreaOptions {
41
+ /**
42
+ * Share of events to send, from 0 to 1. Metrics count every event, except that a
43
+ * `performance` rate of 0 turns performance off, metrics included. Defaults to 1.
44
+ * Each event is sampled on its own, except in `performance`: there the choice is made once
45
+ * for each editor, which sends all of its summaries or none.
46
+ */
47
+ readonly sampleRate?: number;
48
+ }
49
+ /** Which areas to record. An area that is absent or `false` records nothing. @public */
50
+ type TelemetryInclude = {
51
+ readonly [A in TelemetryArea]?: boolean | TelemetryAreaOptions;
52
+ };
53
+ /** Options for {@link createTelemetry}. @public */
54
+ interface TelemetryOptions extends TelemetryPipelineOptions {
55
+ /**
56
+ * The areas to record. Omitted, every area is on.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * include: { documentOpen: true, trackedChanges: true, editing: { sampleRate: 0.2 } }
61
+ * ```
62
+ */
63
+ readonly include?: TelemetryInclude;
64
+ /**
65
+ * Identical refusals inside this window are sent once, then summarized with `repeated`.
66
+ * Holding Backspace on a locked drawing is one event, not hundreds. Defaults to 10 000 ms.
67
+ * Set 0 to send every event.
68
+ */
69
+ readonly dedupeWindowMs?: number;
70
+ /**
71
+ * How much of an opened document's shape to send. `'basic'`, the default, sends the size and
72
+ * page buckets. `'detailed'` also sends part, paragraph, review item, and embedded font
73
+ * buckets: useful to group open times, but several counts together describe a document more
74
+ * closely, so ask for it only when you need it.
75
+ */
76
+ readonly documentShape?: 'basic' | 'detailed';
77
+ /**
78
+ * Your identifiers on every event, such as a tenant or a pseudonymous user id. A function is
79
+ * read at each event, so it can follow sign-in and tenant switches. Events only: to label
80
+ * metrics with a key, list it in `metricLabels`. For values that belong to one editor, such as
81
+ * its room or document, use {@link Telemetry.moduleFor}.
82
+ *
83
+ * These values are yours and go only to your exporters. Keys follow attribute rules: letters,
84
+ * digits, `_`, `.`, and `-`, up to 64 characters. Values are strings, numbers, or booleans;
85
+ * strings are cut at 256 characters.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * attributes: () => ({ 'tenant.id': session.tenantId, 'user.id': session.pseudonymousId })
90
+ * ```
91
+ */
92
+ readonly attributes?: Attributes | (() => Attributes);
93
+ /**
94
+ * Which events go to your exporters, after the privacy check and sampling. Metrics still count
95
+ * every event. A filter that throws drops the event.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * // Failures and performance summaries only: no event for an open that succeeded.
100
+ * filter: (event) => isFailure(event) || event.kind === 'performance'
101
+ * ```
102
+ */
103
+ readonly filter?: (event: TelemetryEvent) => boolean;
104
+ }
105
+ /** A configured telemetry client. Create it once, outside any component. @public */
106
+ interface Telemetry {
107
+ /**
108
+ * Register this in the editor's `modules`. It observes every editor created with it, from
109
+ * the first open on.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * const MODULES = [reviewModule(), telemetry.module];
114
+ * <DocxEditor document={bytes} modules={MODULES} />;
115
+ * ```
116
+ */
117
+ readonly module: EditorModule;
118
+ /**
119
+ * A module for one editor, whose events also carry `attributes`, such as its collaboration
120
+ * room, document, or workspace. Create one for each editor, and keep it stable across renders.
121
+ * Use it in place of `module`, not next to it.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * const modules = useMemo(
126
+ * () => [reviewModule(), telemetry.moduleFor({ 'room.id': roomId, 'document.id': documentId })],
127
+ * [roomId, documentId]
128
+ * );
129
+ * ```
130
+ */
131
+ moduleFor(attributes: Attributes): EditorModule;
132
+ /**
133
+ * Record one diagnostic yourself, for example from a runtime you created without `module`.
134
+ * Use `module` or `record`, not both, or every event is recorded twice. `attributes` are added
135
+ * to this event, as `moduleFor` adds them.
136
+ */
137
+ record(event: EditorDiagnostic, attributes?: Attributes): void;
138
+ /** Send summaries of open deduplication windows, then flush every exporter. */
139
+ flush(): Promise<void>;
140
+ /** Flush, then stop. Later events are dropped. */
141
+ shutdown(): Promise<void>;
142
+ }
143
+ /**
144
+ * Create a telemetry client for editors.
145
+ *
146
+ * Nothing is sent unless you pass an exporter. With `consent`, nothing is sent while it is
147
+ * false; without it, events are sent. Every event is anonymous: codes, enums, and bucketed
148
+ * numbers only.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import { createTelemetry, consoleExporter } from '@docx-editor.dev/telemetry';
153
+ *
154
+ * export const telemetry = createTelemetry({
155
+ * exporters: [consoleExporter()],
156
+ * include: { documentOpen: true, trackedChanges: true },
157
+ * });
158
+ * ```
159
+ *
160
+ * @public
161
+ */
162
+ declare function createTelemetry(options: TelemetryOptions): Telemetry;
163
+
164
+ /**
165
+ * Sending telemetry to your own HTTP endpoint from a browser, and reading the browser's privacy
166
+ * signals. No dependency: `fetch`, `sendBeacon`, and `localStorage` only.
167
+ */
168
+
169
+ /** Options for {@link httpExporter}. @public */
170
+ interface HttpExporterOptions {
171
+ /**
172
+ * Where to POST. Use a route on your own origin, which then forwards to your log service, so
173
+ * the service's token never reaches the browser. See `@docx-editor.dev/telemetry/ingest`.
174
+ */
175
+ readonly url: string;
176
+ /**
177
+ * Events per request, from 1 to 50. Defaults to 20. A full batch is sent at once. The limit
178
+ * matches the default `maxEvents` of `telemetryRoute`, which refuses a longer list.
179
+ */
180
+ readonly batchSize?: number;
181
+ /** How long an event waits for its batch to fill, in milliseconds. Defaults to 10 000. */
182
+ readonly flushIntervalMs?: number;
183
+ /**
184
+ * Extra request headers. A browser cannot set headers on `sendBeacon`, so with headers, the
185
+ * batches sent while the page closes use `fetch` with `keepalive` instead.
186
+ */
187
+ readonly headers?: Readonly<Record<string, string>>;
188
+ /**
189
+ * Called when a request fails, or when batches are dropped because the page closed with more
190
+ * than the browser lets it send. Telemetry never throws into your app.
191
+ */
192
+ readonly onError?: (error: unknown) => void;
193
+ }
194
+ /**
195
+ * Batch events and POST them as a JSON array to your own endpoint.
196
+ *
197
+ * While the page is open, events wait up to `flushIntervalMs` for a batch of `batchSize`, and
198
+ * each batch goes out with an ordinary `fetch`; so does a `telemetry.flush()` you call. When the
199
+ * page is hidden or closes, everything waiting goes out with `sendBeacon`. A closing page may
200
+ * send only about 64 KiB in all, so delivery while closing is best effort: batches beyond that
201
+ * are dropped and reported to `onError`. If consent is withdrawn, events still waiting are
202
+ * discarded.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * import { createTelemetry, httpExporter } from '@docx-editor.dev/telemetry';
207
+ *
208
+ * export const telemetry = createTelemetry({
209
+ * exporters: [httpExporter({ url: '/api/telemetry' })],
210
+ * include: { documentOpen: true, editing: true, trackedChanges: true, performance: true },
211
+ * });
212
+ * ```
213
+ *
214
+ * @public
215
+ */
216
+ declare function httpExporter(options: HttpExporterOptions): TelemetryExporter;
217
+ /** Options for {@link browserPrivacySignals}. @public */
218
+ interface BrowserPrivacySignalsOptions {
219
+ /**
220
+ * A `localStorage` key that opts the visitor out when its value is `off`, such as one your
221
+ * privacy settings page writes. Omit it to read only the browser signals.
222
+ */
223
+ readonly optOutKey?: string;
224
+ }
225
+ /**
226
+ * A `consent` function that follows the browser's privacy signals: it returns `false` when the
227
+ * visitor sends Global Privacy Control or Do Not Track, or when `optOutKey` is set to `off`.
228
+ * Outside a browser page, such as during server rendering, it returns `false`. Combine it with
229
+ * your consent manager when you have one.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * import { browserPrivacySignals, createTelemetry } from '@docx-editor.dev/telemetry';
234
+ *
235
+ * const privacy = browserPrivacySignals({ optOutKey: 'my-app:telemetry' });
236
+ * export const telemetry = createTelemetry({
237
+ * exporters: [],
238
+ * consent: () => privacy() && consentManager.analytics,
239
+ * });
240
+ * ```
241
+ *
242
+ * @public
243
+ */
244
+ declare function browserPrivacySignals(options?: BrowserPrivacySignalsOptions): () => boolean;
245
+
246
+ /**
247
+ * Send each event to your own function: Datadog RUM, PostHog, a logger, or an API route.
248
+ *
249
+ * `onMetric` receives every measurement. A performance summary arrives as one histogram sample
250
+ * for each timing in it, at the middle of the timing's band, so the bucket counts are exact.
251
+ * To chart percentiles, read `p50Ms` and `p95Ms` from the `performance` event instead.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * callbackExporter((event) =>
256
+ * isFailure(event)
257
+ * ? datadogRum.addError(new Error(event.kind), event)
258
+ * : datadogRum.addAction(event.kind, event)
259
+ * );
260
+ * ```
261
+ *
262
+ * @public
263
+ */
264
+ declare function callbackExporter(onEvent: (event: TelemetryEvent) => void, onMetric?: (record: MetricRecord) => void): TelemetryExporter;
265
+ /** Options for {@link consoleExporter}. @public */
266
+ interface ConsoleExporterOptions {
267
+ /** Also print every metric measurement. Defaults to `false`. */
268
+ readonly metrics?: boolean;
269
+ /** Where to print. Defaults to the global `console`. */
270
+ readonly console?: Pick<Console, 'info' | 'warn'>;
271
+ }
272
+ /**
273
+ * Whether an event is a failure: a refused open, a refused edit other than an expected one
274
+ * (a stale range, a read-only document, protected content, a session or refresh not ready
275
+ * yet), an unreadable payload, a font that did not load, or a collaboration error. A missing
276
+ * font that was substituted and a tracked change someone else already reviewed are not. Use it
277
+ * in a callback to send failures as errors and the rest as actions or logs.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * callbackExporter((event) => {
282
+ * const code = 'code' in event ? event.code : undefined;
283
+ * if (isFailure(event)) myLogger.error(event.kind, { code, hint: event.hint });
284
+ * else myLogger.info(event.kind);
285
+ * });
286
+ * ```
287
+ *
288
+ * @public
289
+ */
290
+ declare function isFailure(event: TelemetryEvent): boolean;
291
+ /**
292
+ * Print each event with its hint and docs link. Use it during development to see why a
293
+ * document did not open or why a tracked change was refused.
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * createTelemetry({ exporters: [consoleExporter()] });
298
+ * // An agent deletes a picture while changes are tracked:
299
+ * // [docx-editor] edit refused: edit.tracked-operation-unsupported (automation, tracked)
300
+ * // This editing API operation cannot be recorded as a tracked change, so nothing was ...
301
+ * // https://docx-editor.dev/docs/2.x/guides/diagnostics#edit-tracked-operation-unsupported
302
+ * ```
303
+ *
304
+ * @public
305
+ */
306
+ declare function consoleExporter(options?: ConsoleExporterOptions): TelemetryExporter;
307
+ /** An exporter that keeps what it receives. Returned by {@link memoryExporter}. @public */
308
+ interface MemoryExporter extends TelemetryExporter {
309
+ readonly events: readonly TelemetryEvent[];
310
+ readonly metrics: readonly MetricRecord[];
311
+ /** Forget everything received so far. */
312
+ clear(): void;
313
+ }
314
+ /**
315
+ * Keep events and metrics in memory, for tests in your own suite.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const memory = memoryExporter();
320
+ * const telemetry = createTelemetry({ exporters: [memory] });
321
+ * // ...open a file that fails...
322
+ * expect(memory.events).toContainEqual(
323
+ * expect.objectContaining({ code: 'open.missing-main-document' })
324
+ * );
325
+ * ```
326
+ *
327
+ * @public
328
+ */
329
+ declare function memoryExporter(): MemoryExporter;
330
+
331
+ export { Attributes, MetricRecord, TelemetryEvent, TelemetryExporter, TelemetryPipelineOptions, browserPrivacySignals, callbackExporter, consoleExporter, createTelemetry, httpExporter, isFailure, memoryExporter };
332
+ export type { BrowserPrivacySignalsOptions, ConsoleExporterOptions, HttpExporterOptions, MemoryExporter, Telemetry, TelemetryArea, TelemetryAreaOptions, TelemetryInclude, TelemetryOptions };
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Anonymous diagnostics, metrics, and error reporting for editors and collaboration servers.
3
+ *
4
+ * - `@docx-editor.dev/telemetry`: {@link createTelemetry} for editors, the console, callback,
5
+ * memory, and HTTP exporters, and {@link browserPrivacySignals}.
6
+ * - `@docx-editor.dev/telemetry/ingest`: a route that receives telemetry from `httpExporter`
7
+ * and forwards it to a log service.
8
+ * - `@docx-editor.dev/telemetry/otel`: OpenTelemetry metrics and traces through your SDK.
9
+ * - `@docx-editor.dev/telemetry/sentry`: Sentry issues and breadcrumbs.
10
+ * - `@docx-editor.dev/telemetry/prometheus`: a `/metrics` endpoint.
11
+ * - `@docx-editor.dev/telemetry/server`: room telemetry for any Yjs server.
12
+ * - `@docx-editor.dev/telemetry/hocuspocus`: room telemetry for Hocuspocus in one call.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ import { EditorModule, EditorDiagnostic } from '@docx-editor.dev/core';
17
+ export { ByteSizeBucket, CollaborationDiagnosticCode, CollaborationParticipantsDiagnostic, CollaborationStatusDiagnostic, CountBucket, CustomNodeDiagnosticCode, CustomNodeDiagnosticEvent, DiagnosticBase, DiagnosticCode, DiagnosticErrorName, DiagnosticExplanation, DiagnosticFailureDetail, DocumentOpenDiagnostic, DocumentOpenDiagnosticCode, DocumentPartKind, DocumentShape, EditDiagnosticCode, EditRejectedDiagnostic, EditRejectionOrigin, EditorDiagnostic, EditorDiagnosticKind, FailureStage, FontDiagnosticCode, FontFailedDiagnostic, FormattingChangeKind, HistoryDiagnosticCode, HistoryDiagnosticEvent, PerformanceDiagnostic, PerformanceDiagnosticCode, ReviewChangeKind, ReviewDiagnosticCode, ReviewOperation, ReviewSkippedDiagnostic, TimingSummary, explainDiagnostic } from '@docx-editor.dev/core';
18
+ import { A as Attributes, T as TelemetryPipelineOptions, a as TelemetryEvent, b as TelemetryExporter, M as MetricRecord } from './pipeline-DbVW7S-K.js';
19
+ export { c as AttributeValue, E as ENGINE_VERSION, d as MAX_METRIC_LABEL_VALUES, e as METRICS, f as MetricDefinition, g as MetricId, h as MetricKind, i as TelemetryEventContext, j as TelemetryExporterContext, p as prometheusName } from './pipeline-DbVW7S-K.js';
20
+
21
+ /**
22
+ * The areas you can switch on. Each one selects diagnostic kinds:
23
+ *
24
+ * - `documentOpen`: documents opened and refused (`document.open`).
25
+ * - `editing`: edits and commands refused while changes are not tracked (`edit.rejected`).
26
+ * - `trackedChanges`: refusals while suggesting, refusals about tracking itself, and skipped
27
+ * tracked changes
28
+ * (`edit.rejected` with `tracked: true`, `review.skipped`).
29
+ * - `fonts`: fonts that failed and were substituted (`font.failed`).
30
+ * - `history`: undo steps that did not group as the gesture intended (`history`).
31
+ * - `customNodes`: custom node payloads that could not be read (`custom-node`).
32
+ * - `collaborationClient`: collaboration status changes and room size (`collab.*`).
33
+ * - `performance`: how fast the editor responds: key presses and clicks to the next frame,
34
+ * and engine layout and paint times, as one summary per minute of use (`performance`).
35
+ *
36
+ * @public
37
+ */
38
+ type TelemetryArea = 'documentOpen' | 'editing' | 'trackedChanges' | 'fonts' | 'history' | 'customNodes' | 'collaborationClient' | 'performance';
39
+ /** Options for one area. @public */
40
+ interface TelemetryAreaOptions {
41
+ /**
42
+ * Share of events to send, from 0 to 1. Metrics count every event, except that a
43
+ * `performance` rate of 0 turns performance off, metrics included. Defaults to 1.
44
+ * Each event is sampled on its own, except in `performance`: there the choice is made once
45
+ * for each editor, which sends all of its summaries or none.
46
+ */
47
+ readonly sampleRate?: number;
48
+ }
49
+ /** Which areas to record. An area that is absent or `false` records nothing. @public */
50
+ type TelemetryInclude = {
51
+ readonly [A in TelemetryArea]?: boolean | TelemetryAreaOptions;
52
+ };
53
+ /** Options for {@link createTelemetry}. @public */
54
+ interface TelemetryOptions extends TelemetryPipelineOptions {
55
+ /**
56
+ * The areas to record. Omitted, every area is on.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * include: { documentOpen: true, trackedChanges: true, editing: { sampleRate: 0.2 } }
61
+ * ```
62
+ */
63
+ readonly include?: TelemetryInclude;
64
+ /**
65
+ * Identical refusals inside this window are sent once, then summarized with `repeated`.
66
+ * Holding Backspace on a locked drawing is one event, not hundreds. Defaults to 10 000 ms.
67
+ * Set 0 to send every event.
68
+ */
69
+ readonly dedupeWindowMs?: number;
70
+ /**
71
+ * How much of an opened document's shape to send. `'basic'`, the default, sends the size and
72
+ * page buckets. `'detailed'` also sends part, paragraph, review item, and embedded font
73
+ * buckets: useful to group open times, but several counts together describe a document more
74
+ * closely, so ask for it only when you need it.
75
+ */
76
+ readonly documentShape?: 'basic' | 'detailed';
77
+ /**
78
+ * Your identifiers on every event, such as a tenant or a pseudonymous user id. A function is
79
+ * read at each event, so it can follow sign-in and tenant switches. Events only: to label
80
+ * metrics with a key, list it in `metricLabels`. For values that belong to one editor, such as
81
+ * its room or document, use {@link Telemetry.moduleFor}.
82
+ *
83
+ * These values are yours and go only to your exporters. Keys follow attribute rules: letters,
84
+ * digits, `_`, `.`, and `-`, up to 64 characters. Values are strings, numbers, or booleans;
85
+ * strings are cut at 256 characters.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * attributes: () => ({ 'tenant.id': session.tenantId, 'user.id': session.pseudonymousId })
90
+ * ```
91
+ */
92
+ readonly attributes?: Attributes | (() => Attributes);
93
+ /**
94
+ * Which events go to your exporters, after the privacy check and sampling. Metrics still count
95
+ * every event. A filter that throws drops the event.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * // Failures and performance summaries only: no event for an open that succeeded.
100
+ * filter: (event) => isFailure(event) || event.kind === 'performance'
101
+ * ```
102
+ */
103
+ readonly filter?: (event: TelemetryEvent) => boolean;
104
+ }
105
+ /** A configured telemetry client. Create it once, outside any component. @public */
106
+ interface Telemetry {
107
+ /**
108
+ * Register this in the editor's `modules`. It observes every editor created with it, from
109
+ * the first open on.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * const MODULES = [reviewModule(), telemetry.module];
114
+ * <DocxEditor document={bytes} modules={MODULES} />;
115
+ * ```
116
+ */
117
+ readonly module: EditorModule;
118
+ /**
119
+ * A module for one editor, whose events also carry `attributes`, such as its collaboration
120
+ * room, document, or workspace. Create one for each editor, and keep it stable across renders.
121
+ * Use it in place of `module`, not next to it.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * const modules = useMemo(
126
+ * () => [reviewModule(), telemetry.moduleFor({ 'room.id': roomId, 'document.id': documentId })],
127
+ * [roomId, documentId]
128
+ * );
129
+ * ```
130
+ */
131
+ moduleFor(attributes: Attributes): EditorModule;
132
+ /**
133
+ * Record one diagnostic yourself, for example from a runtime you created without `module`.
134
+ * Use `module` or `record`, not both, or every event is recorded twice. `attributes` are added
135
+ * to this event, as `moduleFor` adds them.
136
+ */
137
+ record(event: EditorDiagnostic, attributes?: Attributes): void;
138
+ /** Send summaries of open deduplication windows, then flush every exporter. */
139
+ flush(): Promise<void>;
140
+ /** Flush, then stop. Later events are dropped. */
141
+ shutdown(): Promise<void>;
142
+ }
143
+ /**
144
+ * Create a telemetry client for editors.
145
+ *
146
+ * Nothing is sent unless you pass an exporter. With `consent`, nothing is sent while it is
147
+ * false; without it, events are sent. Every event is anonymous: codes, enums, and bucketed
148
+ * numbers only.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import { createTelemetry, consoleExporter } from '@docx-editor.dev/telemetry';
153
+ *
154
+ * export const telemetry = createTelemetry({
155
+ * exporters: [consoleExporter()],
156
+ * include: { documentOpen: true, trackedChanges: true },
157
+ * });
158
+ * ```
159
+ *
160
+ * @public
161
+ */
162
+ declare function createTelemetry(options: TelemetryOptions): Telemetry;
163
+
164
+ /**
165
+ * Sending telemetry to your own HTTP endpoint from a browser, and reading the browser's privacy
166
+ * signals. No dependency: `fetch`, `sendBeacon`, and `localStorage` only.
167
+ */
168
+
169
+ /** Options for {@link httpExporter}. @public */
170
+ interface HttpExporterOptions {
171
+ /**
172
+ * Where to POST. Use a route on your own origin, which then forwards to your log service, so
173
+ * the service's token never reaches the browser. See `@docx-editor.dev/telemetry/ingest`.
174
+ */
175
+ readonly url: string;
176
+ /**
177
+ * Events per request, from 1 to 50. Defaults to 20. A full batch is sent at once. The limit
178
+ * matches the default `maxEvents` of `telemetryRoute`, which refuses a longer list.
179
+ */
180
+ readonly batchSize?: number;
181
+ /** How long an event waits for its batch to fill, in milliseconds. Defaults to 10 000. */
182
+ readonly flushIntervalMs?: number;
183
+ /**
184
+ * Extra request headers. A browser cannot set headers on `sendBeacon`, so with headers, the
185
+ * batches sent while the page closes use `fetch` with `keepalive` instead.
186
+ */
187
+ readonly headers?: Readonly<Record<string, string>>;
188
+ /**
189
+ * Called when a request fails, or when batches are dropped because the page closed with more
190
+ * than the browser lets it send. Telemetry never throws into your app.
191
+ */
192
+ readonly onError?: (error: unknown) => void;
193
+ }
194
+ /**
195
+ * Batch events and POST them as a JSON array to your own endpoint.
196
+ *
197
+ * While the page is open, events wait up to `flushIntervalMs` for a batch of `batchSize`, and
198
+ * each batch goes out with an ordinary `fetch`; so does a `telemetry.flush()` you call. When the
199
+ * page is hidden or closes, everything waiting goes out with `sendBeacon`. A closing page may
200
+ * send only about 64 KiB in all, so delivery while closing is best effort: batches beyond that
201
+ * are dropped and reported to `onError`. If consent is withdrawn, events still waiting are
202
+ * discarded.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * import { createTelemetry, httpExporter } from '@docx-editor.dev/telemetry';
207
+ *
208
+ * export const telemetry = createTelemetry({
209
+ * exporters: [httpExporter({ url: '/api/telemetry' })],
210
+ * include: { documentOpen: true, editing: true, trackedChanges: true, performance: true },
211
+ * });
212
+ * ```
213
+ *
214
+ * @public
215
+ */
216
+ declare function httpExporter(options: HttpExporterOptions): TelemetryExporter;
217
+ /** Options for {@link browserPrivacySignals}. @public */
218
+ interface BrowserPrivacySignalsOptions {
219
+ /**
220
+ * A `localStorage` key that opts the visitor out when its value is `off`, such as one your
221
+ * privacy settings page writes. Omit it to read only the browser signals.
222
+ */
223
+ readonly optOutKey?: string;
224
+ }
225
+ /**
226
+ * A `consent` function that follows the browser's privacy signals: it returns `false` when the
227
+ * visitor sends Global Privacy Control or Do Not Track, or when `optOutKey` is set to `off`.
228
+ * Outside a browser page, such as during server rendering, it returns `false`. Combine it with
229
+ * your consent manager when you have one.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * import { browserPrivacySignals, createTelemetry } from '@docx-editor.dev/telemetry';
234
+ *
235
+ * const privacy = browserPrivacySignals({ optOutKey: 'my-app:telemetry' });
236
+ * export const telemetry = createTelemetry({
237
+ * exporters: [],
238
+ * consent: () => privacy() && consentManager.analytics,
239
+ * });
240
+ * ```
241
+ *
242
+ * @public
243
+ */
244
+ declare function browserPrivacySignals(options?: BrowserPrivacySignalsOptions): () => boolean;
245
+
246
+ /**
247
+ * Send each event to your own function: Datadog RUM, PostHog, a logger, or an API route.
248
+ *
249
+ * `onMetric` receives every measurement. A performance summary arrives as one histogram sample
250
+ * for each timing in it, at the middle of the timing's band, so the bucket counts are exact.
251
+ * To chart percentiles, read `p50Ms` and `p95Ms` from the `performance` event instead.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * callbackExporter((event) =>
256
+ * isFailure(event)
257
+ * ? datadogRum.addError(new Error(event.kind), event)
258
+ * : datadogRum.addAction(event.kind, event)
259
+ * );
260
+ * ```
261
+ *
262
+ * @public
263
+ */
264
+ declare function callbackExporter(onEvent: (event: TelemetryEvent) => void, onMetric?: (record: MetricRecord) => void): TelemetryExporter;
265
+ /** Options for {@link consoleExporter}. @public */
266
+ interface ConsoleExporterOptions {
267
+ /** Also print every metric measurement. Defaults to `false`. */
268
+ readonly metrics?: boolean;
269
+ /** Where to print. Defaults to the global `console`. */
270
+ readonly console?: Pick<Console, 'info' | 'warn'>;
271
+ }
272
+ /**
273
+ * Whether an event is a failure: a refused open, a refused edit other than an expected one
274
+ * (a stale range, a read-only document, protected content, a session or refresh not ready
275
+ * yet), an unreadable payload, a font that did not load, or a collaboration error. A missing
276
+ * font that was substituted and a tracked change someone else already reviewed are not. Use it
277
+ * in a callback to send failures as errors and the rest as actions or logs.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * callbackExporter((event) => {
282
+ * const code = 'code' in event ? event.code : undefined;
283
+ * if (isFailure(event)) myLogger.error(event.kind, { code, hint: event.hint });
284
+ * else myLogger.info(event.kind);
285
+ * });
286
+ * ```
287
+ *
288
+ * @public
289
+ */
290
+ declare function isFailure(event: TelemetryEvent): boolean;
291
+ /**
292
+ * Print each event with its hint and docs link. Use it during development to see why a
293
+ * document did not open or why a tracked change was refused.
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * createTelemetry({ exporters: [consoleExporter()] });
298
+ * // An agent deletes a picture while changes are tracked:
299
+ * // [docx-editor] edit refused: edit.tracked-operation-unsupported (automation, tracked)
300
+ * // This editing API operation cannot be recorded as a tracked change, so nothing was ...
301
+ * // https://docx-editor.dev/docs/2.x/guides/diagnostics#edit-tracked-operation-unsupported
302
+ * ```
303
+ *
304
+ * @public
305
+ */
306
+ declare function consoleExporter(options?: ConsoleExporterOptions): TelemetryExporter;
307
+ /** An exporter that keeps what it receives. Returned by {@link memoryExporter}. @public */
308
+ interface MemoryExporter extends TelemetryExporter {
309
+ readonly events: readonly TelemetryEvent[];
310
+ readonly metrics: readonly MetricRecord[];
311
+ /** Forget everything received so far. */
312
+ clear(): void;
313
+ }
314
+ /**
315
+ * Keep events and metrics in memory, for tests in your own suite.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const memory = memoryExporter();
320
+ * const telemetry = createTelemetry({ exporters: [memory] });
321
+ * // ...open a file that fails...
322
+ * expect(memory.events).toContainEqual(
323
+ * expect.objectContaining({ code: 'open.missing-main-document' })
324
+ * );
325
+ * ```
326
+ *
327
+ * @public
328
+ */
329
+ declare function memoryExporter(): MemoryExporter;
330
+
331
+ export { Attributes, MetricRecord, TelemetryEvent, TelemetryExporter, TelemetryPipelineOptions, browserPrivacySignals, callbackExporter, consoleExporter, createTelemetry, httpExporter, isFailure, memoryExporter };
332
+ export type { BrowserPrivacySignalsOptions, ConsoleExporterOptions, HttpExporterOptions, MemoryExporter, Telemetry, TelemetryArea, TelemetryAreaOptions, TelemetryInclude, TelemetryOptions };