@docx-editor.dev/telemetry 0.0.0-stage → 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 (48) hide show
  1. package/LICENSE.md +121 -0
  2. package/README.md +94 -2
  3. package/THIRD_PARTY_NOTICES.md +22 -0
  4. package/dist/chunk-73YEPWY5.js +1 -0
  5. package/dist/chunk-7ZXJ6HP2.cjs +1 -0
  6. package/dist/chunk-DT2KMPUJ.js +1 -0
  7. package/dist/chunk-DTEVX2XS.js +1 -0
  8. package/dist/chunk-LFQSY2TD.cjs +1 -0
  9. package/dist/chunk-LFX65QJG.js +4 -0
  10. package/dist/chunk-LGNT4I5W.cjs +1 -0
  11. package/dist/chunk-MJX3ECH3.cjs +4 -0
  12. package/dist/chunk-NUN46EXH.js +2 -0
  13. package/dist/chunk-RRYW7EAV.js +1 -0
  14. package/dist/chunk-S7KBMROA.js +1 -0
  15. package/dist/chunk-U6ERFNUM.cjs +1 -0
  16. package/dist/chunk-UK2MNJCG.cjs +2 -0
  17. package/dist/chunk-WGWKNKUT.cjs +1 -0
  18. package/dist/hocuspocus/index.cjs +1 -0
  19. package/dist/hocuspocus/index.d.cts +118 -0
  20. package/dist/hocuspocus/index.d.ts +118 -0
  21. package/dist/hocuspocus/index.js +1 -0
  22. package/dist/index.cjs +1 -0
  23. package/dist/index.d.cts +332 -0
  24. package/dist/index.d.ts +332 -0
  25. package/dist/index.js +1 -0
  26. package/dist/ingest/index.cjs +1 -0
  27. package/dist/ingest/index.d.cts +107 -0
  28. package/dist/ingest/index.d.ts +107 -0
  29. package/dist/ingest/index.js +1 -0
  30. package/dist/otel/index.cjs +1 -0
  31. package/dist/otel/index.d.cts +57 -0
  32. package/dist/otel/index.d.ts +57 -0
  33. package/dist/otel/index.js +1 -0
  34. package/dist/pipeline-DbVW7S-K.d.cts +223 -0
  35. package/dist/pipeline-DbVW7S-K.d.ts +223 -0
  36. package/dist/prometheus/index.cjs +1 -0
  37. package/dist/prometheus/index.d.cts +88 -0
  38. package/dist/prometheus/index.d.ts +88 -0
  39. package/dist/prometheus/index.js +1 -0
  40. package/dist/sentry/index.cjs +1 -0
  41. package/dist/sentry/index.d.cts +134 -0
  42. package/dist/sentry/index.d.ts +134 -0
  43. package/dist/sentry/index.js +1 -0
  44. package/dist/server/index.cjs +1 -0
  45. package/dist/server/index.d.cts +156 -0
  46. package/dist/server/index.d.ts +156 -0
  47. package/dist/server/index.js +1 -0
  48. package/package.json +114 -4
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Prometheus: an in-process registry in the text exposition format, and `/metrics` handlers for
3
+ * Node-style servers ({@link metricsHandler}) and Fetch-style servers
4
+ * ({@link fetchMetricsHandler}). No dependency on `prom-client`; a `prom-client` registry
5
+ * merges into the same endpoint.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { b as TelemetryExporter } from '../pipeline-DbVW7S-K.js';
10
+ import '@docx-editor.dev/core';
11
+
12
+ /** The Prometheus text exposition content type. @public */
13
+ declare const PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
14
+ /** Anything that renders Prometheus text, such as a `prom-client` `Registry`. @public */
15
+ interface PrometheusTextSource {
16
+ metrics(): string | Promise<string>;
17
+ }
18
+ /** The exporter returned by {@link prometheusRegistry}. @public */
19
+ interface PrometheusRegistry extends TelemetryExporter, PrometheusTextSource {
20
+ /** Every recorded metric in the Prometheus text format. */
21
+ metrics(): string;
22
+ /** Forget every recorded value. */
23
+ reset(): void;
24
+ }
25
+ /**
26
+ * Collect metrics in memory and render them for Prometheus to scrape.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { prometheusRegistry, metricsHandler } from '@docx-editor.dev/telemetry/prometheus';
31
+ *
32
+ * const metrics = prometheusRegistry();
33
+ * // Pass `metrics` as an exporter, then serve it:
34
+ * app.get('/metrics', metricsHandler(metrics));
35
+ * ```
36
+ *
37
+ * @public
38
+ */
39
+ declare function prometheusRegistry(): PrometheusRegistry;
40
+ /** The part of a Node `ServerResponse` the handler writes to. @public */
41
+ interface NodeResponseLike {
42
+ setHeader(name: string, value: string): unknown;
43
+ end(body: string): unknown;
44
+ statusCode?: number;
45
+ }
46
+ /** Options for {@link metricsHandler} and {@link fetchMetricsHandler}. @public */
47
+ interface MetricsHandlerOptions {
48
+ /**
49
+ * More sources rendered into the same response, such as your `prom-client` registry:
50
+ * `merge: [register]`.
51
+ */
52
+ readonly merge?: readonly PrometheusTextSource[];
53
+ }
54
+ /**
55
+ * A Node-style handler, `(request, response)`, for Node `http`, Express, and Koa
56
+ * (`ctx.res`). For Fastify, hijack the reply and pass `reply.raw`. For Fetch-style servers,
57
+ * use {@link fetchMetricsHandler}. Protect the route as you protect other operational
58
+ * endpoints.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * app.get('/metrics', metricsHandler(metrics, { merge: [promClient.register] })); // Express
63
+ *
64
+ * fastify.get('/metrics', (request, reply) => {
65
+ * reply.hijack();
66
+ * return metricsHandler(metrics)(request.raw, reply.raw);
67
+ * });
68
+ * ```
69
+ *
70
+ * @public
71
+ */
72
+ declare function metricsHandler(registry: PrometheusTextSource, options?: MetricsHandlerOptions): (request: unknown, response: NodeResponseLike) => Promise<void>;
73
+ /**
74
+ * A Fetch-style handler that returns a `Response`, for Next.js route handlers, Hono, Bun,
75
+ * Deno, and workers. It ignores its argument, so a Hono context works as well as a `Request`.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * // app/metrics/route.ts
80
+ * export const GET = fetchMetricsHandler(metrics);
81
+ * ```
82
+ *
83
+ * @public
84
+ */
85
+ declare function fetchMetricsHandler(registry: PrometheusTextSource, options?: MetricsHandlerOptions): (request?: unknown) => Promise<Response>;
86
+
87
+ export { PROMETHEUS_CONTENT_TYPE, TelemetryExporter, fetchMetricsHandler, metricsHandler, prometheusRegistry };
88
+ export type { MetricsHandlerOptions, NodeResponseLike, PrometheusRegistry, PrometheusTextSource };
@@ -0,0 +1 @@
1
+ export{a as PROMETHEUS_CONTENT_TYPE,d as fetchMetricsHandler,c as metricsHandler,b as prometheusRegistry}from'../chunk-LFX65QJG.js';import'../chunk-73YEPWY5.js';
@@ -0,0 +1 @@
1
+ 'use strict';var chunkUK2MNJCG_cjs=require('../chunk-UK2MNJCG.cjs');var i=class extends Error{name="DocxEditorDiagnosticError";code;constructor(r,t){super(t?`${r}: ${t}`:r),this.code=r;}},u=new Set(["collab.authentication-failed","collab.room-generation-changed"]),l=new Set(["open.failed","open.unsupported-source","open.invalid-limits"]),f=Object.freeze([".docx-editor",".docx-pages"]);function x(e,r){return e.category?.startsWith("ui.")&&g(r?.event?.target)?null:e}function g(e){try{let r=e;return r&&typeof r.closest!="function"&&(r=r.parentElement),!r||typeof r.closest!="function"?!0:r.closest(f.join(", "))!=null}catch{return true}}function p(e){switch(e.kind){case "document.open":return e.outcome==="rejected"&&l.has(e.code);case "edit.rejected":return e.code==="edit.internal-invariant";case "review.skipped":return e.code==="review.revision-unsupported"||e.code==="review.retained-structure";case "font.failed":return e.code==="font.shaper-unavailable";case "collab.status":return e.status==="error"&&!e.recovering&&!u.has(e.code??"");default:return false}}function m(e){return e.kind==="document.open"?e.outcome==="opened":e.kind==="performance"&&e.code===void 0}function c(e){return "code"in e&&e.code?e.code:e.kind}function y(e){let r=e,t={"docx_editor.kind":e.kind,"docx_editor.code":c(e),"docx_editor.version":e.engineVersion,"docx_editor.editor_session":e.editorSessionId};for(let[o,n]of [["origin","origin"],["tracked","tracked"],["status","status"],["outcome","outcome"],["command","command"],["cause","cause"],["part","part"],["errorName","error_name"],["stage","stage"],["operation","operation"],["changeKind","change_kind"],["formattingKind","formatting_kind"]])r[o]!==void 0&&(t[`docx_editor.${n}`]=String(r[o]));for(let[o,n]of Object.entries(e.attributes??{}))t[o]=String(n);return t}function k(e,r){let t=e,o=["cause","errorName","stage","changeKind","formattingKind"].map(n=>t[n]).filter(n=>typeof n=="string");return ["docx-editor",e.kind,r,...o]}function d(e){let{resource:r,...t}=e;return {...t,...Object.keys(r).length?{resource:r}:{}}}function b(e,r={}){let t=r.issue??p,o=r.breadcrumbs??true;return {name:"sentry",event(n){let s=c(n);if(n.repeated===void 0&&t(n)){e.captureException(new i(s,n.hint),{fingerprint:k(n,s),level:n.kind==="font.failed"||n.kind==="review.skipped"?"warning":"error",tags:y(n),contexts:{docx_editor:d(n)}});return}o&&e.addBreadcrumb({category:"docx-editor",message:chunkUK2MNJCG_cjs.b(n),level:m(n)?"info":"warning",data:d(n)});},async flush(){await e.flush?.(2e3);}}}exports.DocxEditorDiagnosticError=i;exports.EDITOR_SELECTORS=f;exports.dropEditorBreadcrumbs=x;exports.isSentryIssue=p;exports.sentryExporter=b;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Sentry: actionable failures become issues grouped by code; everything else becomes a
3
+ * breadcrumb, so it appears beside your own errors without using your event quota.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import { a as TelemetryEvent, b as TelemetryExporter } from '../pipeline-DbVW7S-K.cjs';
8
+ import '@docx-editor.dev/core';
9
+
10
+ /** Sentry severity, as the Sentry SDKs spell it. @public */
11
+ type SentrySeverity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
12
+ /**
13
+ * The part of a Sentry SDK the exporter uses. `@sentry/browser`, `@sentry/react`,
14
+ * `@sentry/vue`, `@sentry/nextjs`, and `@sentry/node` all fit: pass the module itself.
15
+ *
16
+ * @public
17
+ */
18
+ interface SentryLike {
19
+ captureException(exception: unknown, context?: {
20
+ readonly fingerprint?: string[];
21
+ readonly level?: SentrySeverity;
22
+ readonly tags?: Record<string, string>;
23
+ readonly contexts?: Record<string, Record<string, unknown>>;
24
+ }): unknown;
25
+ addBreadcrumb(breadcrumb: {
26
+ readonly category?: string;
27
+ readonly message?: string;
28
+ readonly level?: SentrySeverity;
29
+ readonly data?: Record<string, unknown>;
30
+ }): void;
31
+ /** Send queued events. Called when the page is hidden. */
32
+ flush?(timeout?: number): PromiseLike<boolean>;
33
+ }
34
+ /** Options for {@link sentryExporter}. @public */
35
+ interface SentryExporterOptions {
36
+ /**
37
+ * Which events become Sentry issues. The rest become breadcrumbs. The default sends an issue
38
+ * when the app can act: an open refused because the app passed something wrong or for an
39
+ * unknown reason, a collaboration error that will not heal, a text shaper that did not load,
40
+ * and an engine invariant. A corrupt or oversized file and a refused keystroke are
41
+ * breadcrumbs.
42
+ */
43
+ readonly issue?: (event: TelemetryEvent) => boolean;
44
+ /** Record breadcrumbs for the other events. Defaults to `true`. */
45
+ readonly breadcrumbs?: boolean;
46
+ }
47
+ /**
48
+ * The error a Sentry issue is made from. The message is the code, so issues group by cause,
49
+ * and the stack trace contains no document data.
50
+ *
51
+ * @public
52
+ */
53
+ declare class DocxEditorDiagnosticError extends Error {
54
+ readonly name = "DocxEditorDiagnosticError";
55
+ readonly code: string;
56
+ constructor(code: string, hint?: string);
57
+ }
58
+ /**
59
+ * CSS selectors for everything that shows the document: the editor shell and the painted pages.
60
+ * Pass them to Session Replay's `block` option, which replaces the editor with a placeholder, so
61
+ * `mask` is not needed as well.
62
+ *
63
+ * @public
64
+ */
65
+ declare const EDITOR_SELECTORS: readonly string[];
66
+ /** A Sentry breadcrumb, as `beforeBreadcrumb` receives it. @public */
67
+ interface SentryBreadcrumbLike {
68
+ readonly category?: string;
69
+ }
70
+ /** The hint Sentry passes with a breadcrumb. For a click or key press, `event` is the DOM event. @public */
71
+ interface SentryBreadcrumbHintLike {
72
+ readonly event?: unknown;
73
+ }
74
+ /**
75
+ * A `beforeBreadcrumb` filter that drops Sentry's click and key press breadcrumbs from inside the
76
+ * editor. Sentry describes the element a person clicked with its `alt`, `title`, and
77
+ * `aria-label` values. Inside the editor, those values come from the document: picture
78
+ * descriptions, link tooltips, and content control names. A `ui` breadcrumb whose element is
79
+ * unknown is dropped too. Other breadcrumbs pass unchanged.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * Sentry.init({ dsn, beforeBreadcrumb: dropEditorBreadcrumbs });
84
+ * // With a filter of your own:
85
+ * Sentry.init({
86
+ * dsn,
87
+ * beforeBreadcrumb: (crumb, hint) => {
88
+ * const kept = dropEditorBreadcrumbs(crumb, hint);
89
+ * return kept && myFilter(kept, hint); // dropped breadcrumbs never reach your filter
90
+ * },
91
+ * });
92
+ * ```
93
+ *
94
+ * @public
95
+ */
96
+ declare function dropEditorBreadcrumbs<B extends SentryBreadcrumbLike>(breadcrumb: B, hint?: SentryBreadcrumbHintLike): B | null;
97
+ /**
98
+ * The default issue rule: an open the app caused, an internal error, a shaper that never
99
+ * loaded, and a collaboration error that will not heal become issues. Wrap it to add your own.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * sentryExporter(Sentry, {
104
+ * issue: (event) =>
105
+ * isSentryIssue(event) || ('code' in event && event.code === 'font.load-failed'),
106
+ * });
107
+ * ```
108
+ *
109
+ * @public
110
+ */
111
+ declare function isSentryIssue(event: TelemetryEvent): boolean;
112
+ /**
113
+ * Send diagnostics to Sentry.
114
+ *
115
+ * Sentry Session Replay records the page, and the page shows the document. Block the editor in
116
+ * Replay, so neither its text nor its pictures are recorded, and drop the click breadcrumbs that
117
+ * describe its elements: `Sentry.replayIntegration({ block: [...EDITOR_SELECTORS] })` and
118
+ * `beforeBreadcrumb: dropEditorBreadcrumbs`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import * as Sentry from '@sentry/react';
123
+ * import { createTelemetry } from '@docx-editor.dev/telemetry';
124
+ * import { sentryExporter } from '@docx-editor.dev/telemetry/sentry';
125
+ *
126
+ * export const telemetry = createTelemetry({ exporters: [sentryExporter(Sentry)] });
127
+ * ```
128
+ *
129
+ * @public
130
+ */
131
+ declare function sentryExporter(sentry: SentryLike, options?: SentryExporterOptions): TelemetryExporter;
132
+
133
+ export { DocxEditorDiagnosticError, EDITOR_SELECTORS, TelemetryEvent, TelemetryExporter, dropEditorBreadcrumbs, isSentryIssue, sentryExporter };
134
+ export type { SentryBreadcrumbHintLike, SentryBreadcrumbLike, SentryExporterOptions, SentryLike, SentrySeverity };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Sentry: actionable failures become issues grouped by code; everything else becomes a
3
+ * breadcrumb, so it appears beside your own errors without using your event quota.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import { a as TelemetryEvent, b as TelemetryExporter } from '../pipeline-DbVW7S-K.js';
8
+ import '@docx-editor.dev/core';
9
+
10
+ /** Sentry severity, as the Sentry SDKs spell it. @public */
11
+ type SentrySeverity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
12
+ /**
13
+ * The part of a Sentry SDK the exporter uses. `@sentry/browser`, `@sentry/react`,
14
+ * `@sentry/vue`, `@sentry/nextjs`, and `@sentry/node` all fit: pass the module itself.
15
+ *
16
+ * @public
17
+ */
18
+ interface SentryLike {
19
+ captureException(exception: unknown, context?: {
20
+ readonly fingerprint?: string[];
21
+ readonly level?: SentrySeverity;
22
+ readonly tags?: Record<string, string>;
23
+ readonly contexts?: Record<string, Record<string, unknown>>;
24
+ }): unknown;
25
+ addBreadcrumb(breadcrumb: {
26
+ readonly category?: string;
27
+ readonly message?: string;
28
+ readonly level?: SentrySeverity;
29
+ readonly data?: Record<string, unknown>;
30
+ }): void;
31
+ /** Send queued events. Called when the page is hidden. */
32
+ flush?(timeout?: number): PromiseLike<boolean>;
33
+ }
34
+ /** Options for {@link sentryExporter}. @public */
35
+ interface SentryExporterOptions {
36
+ /**
37
+ * Which events become Sentry issues. The rest become breadcrumbs. The default sends an issue
38
+ * when the app can act: an open refused because the app passed something wrong or for an
39
+ * unknown reason, a collaboration error that will not heal, a text shaper that did not load,
40
+ * and an engine invariant. A corrupt or oversized file and a refused keystroke are
41
+ * breadcrumbs.
42
+ */
43
+ readonly issue?: (event: TelemetryEvent) => boolean;
44
+ /** Record breadcrumbs for the other events. Defaults to `true`. */
45
+ readonly breadcrumbs?: boolean;
46
+ }
47
+ /**
48
+ * The error a Sentry issue is made from. The message is the code, so issues group by cause,
49
+ * and the stack trace contains no document data.
50
+ *
51
+ * @public
52
+ */
53
+ declare class DocxEditorDiagnosticError extends Error {
54
+ readonly name = "DocxEditorDiagnosticError";
55
+ readonly code: string;
56
+ constructor(code: string, hint?: string);
57
+ }
58
+ /**
59
+ * CSS selectors for everything that shows the document: the editor shell and the painted pages.
60
+ * Pass them to Session Replay's `block` option, which replaces the editor with a placeholder, so
61
+ * `mask` is not needed as well.
62
+ *
63
+ * @public
64
+ */
65
+ declare const EDITOR_SELECTORS: readonly string[];
66
+ /** A Sentry breadcrumb, as `beforeBreadcrumb` receives it. @public */
67
+ interface SentryBreadcrumbLike {
68
+ readonly category?: string;
69
+ }
70
+ /** The hint Sentry passes with a breadcrumb. For a click or key press, `event` is the DOM event. @public */
71
+ interface SentryBreadcrumbHintLike {
72
+ readonly event?: unknown;
73
+ }
74
+ /**
75
+ * A `beforeBreadcrumb` filter that drops Sentry's click and key press breadcrumbs from inside the
76
+ * editor. Sentry describes the element a person clicked with its `alt`, `title`, and
77
+ * `aria-label` values. Inside the editor, those values come from the document: picture
78
+ * descriptions, link tooltips, and content control names. A `ui` breadcrumb whose element is
79
+ * unknown is dropped too. Other breadcrumbs pass unchanged.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * Sentry.init({ dsn, beforeBreadcrumb: dropEditorBreadcrumbs });
84
+ * // With a filter of your own:
85
+ * Sentry.init({
86
+ * dsn,
87
+ * beforeBreadcrumb: (crumb, hint) => {
88
+ * const kept = dropEditorBreadcrumbs(crumb, hint);
89
+ * return kept && myFilter(kept, hint); // dropped breadcrumbs never reach your filter
90
+ * },
91
+ * });
92
+ * ```
93
+ *
94
+ * @public
95
+ */
96
+ declare function dropEditorBreadcrumbs<B extends SentryBreadcrumbLike>(breadcrumb: B, hint?: SentryBreadcrumbHintLike): B | null;
97
+ /**
98
+ * The default issue rule: an open the app caused, an internal error, a shaper that never
99
+ * loaded, and a collaboration error that will not heal become issues. Wrap it to add your own.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * sentryExporter(Sentry, {
104
+ * issue: (event) =>
105
+ * isSentryIssue(event) || ('code' in event && event.code === 'font.load-failed'),
106
+ * });
107
+ * ```
108
+ *
109
+ * @public
110
+ */
111
+ declare function isSentryIssue(event: TelemetryEvent): boolean;
112
+ /**
113
+ * Send diagnostics to Sentry.
114
+ *
115
+ * Sentry Session Replay records the page, and the page shows the document. Block the editor in
116
+ * Replay, so neither its text nor its pictures are recorded, and drop the click breadcrumbs that
117
+ * describe its elements: `Sentry.replayIntegration({ block: [...EDITOR_SELECTORS] })` and
118
+ * `beforeBreadcrumb: dropEditorBreadcrumbs`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import * as Sentry from '@sentry/react';
123
+ * import { createTelemetry } from '@docx-editor.dev/telemetry';
124
+ * import { sentryExporter } from '@docx-editor.dev/telemetry/sentry';
125
+ *
126
+ * export const telemetry = createTelemetry({ exporters: [sentryExporter(Sentry)] });
127
+ * ```
128
+ *
129
+ * @public
130
+ */
131
+ declare function sentryExporter(sentry: SentryLike, options?: SentryExporterOptions): TelemetryExporter;
132
+
133
+ export { DocxEditorDiagnosticError, EDITOR_SELECTORS, TelemetryEvent, TelemetryExporter, dropEditorBreadcrumbs, isSentryIssue, sentryExporter };
134
+ export type { SentryBreadcrumbHintLike, SentryBreadcrumbLike, SentryExporterOptions, SentryLike, SentrySeverity };
@@ -0,0 +1 @@
1
+ import {b as b$1}from'../chunk-NUN46EXH.js';var i=class extends Error{name="DocxEditorDiagnosticError";code;constructor(r,t){super(t?`${r}: ${t}`:r),this.code=r;}},u=new Set(["collab.authentication-failed","collab.room-generation-changed"]),l=new Set(["open.failed","open.unsupported-source","open.invalid-limits"]),f=Object.freeze([".docx-editor",".docx-pages"]);function x(e,r){return e.category?.startsWith("ui.")&&g(r?.event?.target)?null:e}function g(e){try{let r=e;return r&&typeof r.closest!="function"&&(r=r.parentElement),!r||typeof r.closest!="function"?!0:r.closest(f.join(", "))!=null}catch{return true}}function p(e){switch(e.kind){case "document.open":return e.outcome==="rejected"&&l.has(e.code);case "edit.rejected":return e.code==="edit.internal-invariant";case "review.skipped":return e.code==="review.revision-unsupported"||e.code==="review.retained-structure";case "font.failed":return e.code==="font.shaper-unavailable";case "collab.status":return e.status==="error"&&!e.recovering&&!u.has(e.code??"");default:return false}}function m(e){return e.kind==="document.open"?e.outcome==="opened":e.kind==="performance"&&e.code===void 0}function c(e){return "code"in e&&e.code?e.code:e.kind}function y(e){let r=e,t={"docx_editor.kind":e.kind,"docx_editor.code":c(e),"docx_editor.version":e.engineVersion,"docx_editor.editor_session":e.editorSessionId};for(let[o,n]of [["origin","origin"],["tracked","tracked"],["status","status"],["outcome","outcome"],["command","command"],["cause","cause"],["part","part"],["errorName","error_name"],["stage","stage"],["operation","operation"],["changeKind","change_kind"],["formattingKind","formatting_kind"]])r[o]!==void 0&&(t[`docx_editor.${n}`]=String(r[o]));for(let[o,n]of Object.entries(e.attributes??{}))t[o]=String(n);return t}function k(e,r){let t=e,o=["cause","errorName","stage","changeKind","formattingKind"].map(n=>t[n]).filter(n=>typeof n=="string");return ["docx-editor",e.kind,r,...o]}function d(e){let{resource:r,...t}=e;return {...t,...Object.keys(r).length?{resource:r}:{}}}function b(e,r={}){let t=r.issue??p,o=r.breadcrumbs??true;return {name:"sentry",event(n){let s=c(n);if(n.repeated===void 0&&t(n)){e.captureException(new i(s,n.hint),{fingerprint:k(n,s),level:n.kind==="font.failed"||n.kind==="review.skipped"?"warning":"error",tags:y(n),contexts:{docx_editor:d(n)}});return}o&&e.addBreadcrumb({category:"docx-editor",message:b$1(n),level:m(n)?"info":"warning",data:d(n)});},async flush(){await e.flush?.(2e3);}}}export{i as DocxEditorDiagnosticError,f as EDITOR_SELECTORS,x as dropEditorBreadcrumbs,p as isSentryIssue,b as sentryExporter};
@@ -0,0 +1 @@
1
+ 'use strict';var chunk7ZXJ6HP2_cjs=require('../chunk-7ZXJ6HP2.cjs');require('../chunk-U6ERFNUM.cjs'),require('../chunk-LGNT4I5W.cjs'),require('../chunk-LFQSY2TD.cjs'),require('../chunk-WGWKNKUT.cjs');Object.defineProperty(exports,"hmacRoomLabel",{enumerable:true,get:function(){return chunk7ZXJ6HP2_cjs.a}});Object.defineProperty(exports,"roomTelemetry",{enumerable:true,get:function(){return chunk7ZXJ6HP2_cjs.b}});
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Room telemetry for any Yjs collaboration server: rooms, connections, the people in each
3
+ * room, the memory each room holds, and why connections and messages were refused.
4
+ *
5
+ * No metric carries a room name. Per-room detail is in {@link RoomTelemetry.list}, for an
6
+ * admin endpoint you protect.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ import * as Y from 'yjs';
11
+ import { T as TelemetryPipelineOptions } from '../pipeline-DbVW7S-K.cjs';
12
+ export { b as TelemetryExporter } from '../pipeline-DbVW7S-K.cjs';
13
+ import '@docx-editor.dev/core';
14
+
15
+ /** The part of a `y-protocols` `Awareness` the telemetry reads. @public */
16
+ interface AwarenessLike {
17
+ getStates(): Map<number, unknown>;
18
+ on(event: 'change', listener: (...args: unknown[]) => void): void;
19
+ off(event: 'change', listener: (...args: unknown[]) => void): void;
20
+ }
21
+ /** Options for {@link roomTelemetry}. @public */
22
+ interface RoomTelemetryOptions extends TelemetryPipelineOptions {
23
+ /**
24
+ * How often to sample people, memory, and room size, in milliseconds. Defaults to 60 000.
25
+ * `false` turns the timer off; call `sample()` yourself, for example before each scrape.
26
+ */
27
+ readonly sampleInterval?: number | false;
28
+ /**
29
+ * Measure each room's encoded Yjs state. This encodes the room once per sample, and only when
30
+ * it changed since the last one. Defaults to `true`.
31
+ */
32
+ readonly measureState?: boolean;
33
+ /**
34
+ * How a room appears in {@link RoomTelemetry.list}. Defaults to an index that is valid only
35
+ * inside this process, such as `room-3`. Use `(name) => name` to see real names, or
36
+ * {@link hmacRoomLabel} for a stable label that does not reveal the name.
37
+ */
38
+ readonly roomLabel?: (name: string) => string;
39
+ /**
40
+ * Also record each room's people and memory as gauges labeled `room`. Off by default: a label
41
+ * per room makes one series per room, and a large server has many rooms. A closed room's
42
+ * series is dropped. Only the default label or `hmacRoomLabel` is accepted, so no room name
43
+ * can become a label; any other `roomLabel` throws.
44
+ */
45
+ readonly perRoomSeries?: boolean;
46
+ /**
47
+ * Your own reading of a refusal, for errors whose `reason` or `code` is free text. Return a
48
+ * common reason such as `unauthorized`, a collaboration failure code, or one of `reasons`;
49
+ * anything else is counted as `other`. Return undefined to use the default reading.
50
+ */
51
+ readonly reasonOf?: (error: unknown) => string | undefined;
52
+ /**
53
+ * Refusal reasons to keep as metric labels. Compared without case and recorded in lowercase. Collaboration failure codes are always kept.
54
+ * Any other reason is counted as `other`, so a free-text error never becomes a time series.
55
+ */
56
+ readonly reasons?: readonly string[];
57
+ }
58
+ /** One room in {@link RoomTelemetry.list}. @public */
59
+ interface RoomStats {
60
+ readonly room: string;
61
+ /** People present, from awareness states. A person with two tabs counts twice. */
62
+ readonly participants: number;
63
+ readonly connections: number;
64
+ /** Encoded Yjs state at the last sample, or `null` before the first one. */
65
+ readonly stateBytes: number | null;
66
+ readonly blobBytes: number | null;
67
+ /** Bytes of updates applied since the room was last stored. */
68
+ readonly pendingBytes: number;
69
+ readonly nodes: number | null;
70
+ /** The largest share of any room limit in use, from 0 to 1. */
71
+ readonly limitRatio: number | null;
72
+ readonly ageSeconds: number;
73
+ readonly idleSeconds: number;
74
+ }
75
+ /** What a timed room operation is. @public */
76
+ type RoomOperation = 'load' | 'store';
77
+ /** Records a started operation's duration with its outcome. Later calls do nothing. @public */
78
+ type SettleOperation = (outcome: 'success' | 'failure') => void;
79
+ /** One open room. Returned by {@link RoomTelemetry.open}. @public */
80
+ interface RoomHandle {
81
+ /** The room's label, as {@link RoomTelemetryOptions.roomLabel} made it. */
82
+ readonly label: string;
83
+ /** Run `operation` and record its duration and outcome. Rethrows its error. */
84
+ time<T>(operation: RoomOperation, run: () => T | Promise<T>): Promise<T>;
85
+ /** Start timing `operation` where `time` cannot wrap it; call the result once it settles. */
86
+ begin(operation: RoomOperation): SettleOperation;
87
+ connected(): void;
88
+ disconnected(): void;
89
+ /** Stop tracking the room, for example when the server unloads it. */
90
+ close(): void;
91
+ }
92
+ /** Room telemetry for one server process. @public */
93
+ interface RoomTelemetry {
94
+ /**
95
+ * Start tracking a loaded room. Pass the room's awareness to count people; without it the
96
+ * number of connections stands in.
97
+ */
98
+ open(doc: Y.Doc, options?: {
99
+ readonly name?: string;
100
+ readonly awareness?: AwarenessLike;
101
+ }): RoomHandle;
102
+ /** A connection refused at admission. `reason` can be a string or an error with `reason`. */
103
+ rejected(reason: unknown): void;
104
+ /** A message refused before it applied, such as `room-generation-changed`. */
105
+ refused(reason: unknown): void;
106
+ /** A room migration finished. */
107
+ migrated(outcome: 'migrated' | 'unchanged' | 'failed'): void;
108
+ /** Time an operation that happens before the room is open, such as a load that fails. */
109
+ time<T>(operation: RoomOperation, run: () => T | Promise<T>): Promise<T>;
110
+ /** Start timing an operation that has no room yet; call the result once it settles. */
111
+ begin(operation: RoomOperation): SettleOperation;
112
+ /** Every open room, for an admin endpoint you protect. */
113
+ list(): readonly RoomStats[];
114
+ /** Sample people, memory, and size now. */
115
+ sample(): void;
116
+ flush(): Promise<void>;
117
+ /** Stop the timer, close every room, and shut the exporters down. */
118
+ shutdown(): Promise<void>;
119
+ }
120
+ /**
121
+ * A stable room label that does not reveal the name: an HMAC of the name with your secret.
122
+ * The same name gives the same label across processes and restarts.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const rooms = roomTelemetry({
127
+ * exporters: [metrics],
128
+ * roomLabel: hmacRoomLabel(process.env.ROOM_LABEL_SECRET),
129
+ * });
130
+ * ```
131
+ *
132
+ * @public
133
+ */
134
+ declare function hmacRoomLabel(secret: string | undefined): (name: string) => string;
135
+ /**
136
+ * Track rooms on a Yjs server.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * import { roomTelemetry } from '@docx-editor.dev/telemetry/server';
141
+ * import { prometheusRegistry, metricsHandler } from '@docx-editor.dev/telemetry/prometheus';
142
+ *
143
+ * const metrics = prometheusRegistry();
144
+ * const rooms = roomTelemetry({ exporters: [metrics] });
145
+ *
146
+ * const room = rooms.open(ydoc, { name, awareness });
147
+ * await room.time('load', () => loadRoom(ydoc)); // the loaded state is not counted as edits
148
+ * room.connected();
149
+ * ```
150
+ *
151
+ * @public
152
+ */
153
+ declare function roomTelemetry(options: RoomTelemetryOptions): RoomTelemetry;
154
+
155
+ export { TelemetryPipelineOptions, hmacRoomLabel, roomTelemetry };
156
+ export type { AwarenessLike, RoomHandle, RoomOperation, RoomStats, RoomTelemetry, RoomTelemetryOptions, SettleOperation };