@ooopsstudio/performance 0.9.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/budget-engine-KDA4FRYU.js +160 -0
  4. package/dist/budget-engine-KDA4FRYU.js.map +1 -0
  5. package/dist/chunk-2XXIOHOO.js +58 -0
  6. package/dist/chunk-2XXIOHOO.js.map +1 -0
  7. package/dist/chunk-3GMKPRQP.js +197 -0
  8. package/dist/chunk-3GMKPRQP.js.map +1 -0
  9. package/dist/chunk-J6KBSLYO.js +118 -0
  10. package/dist/chunk-J6KBSLYO.js.map +1 -0
  11. package/dist/chunk-MMO5UEUU.js +20 -0
  12. package/dist/chunk-MMO5UEUU.js.map +1 -0
  13. package/dist/chunk-RL2752FW.js +31 -0
  14. package/dist/chunk-RL2752FW.js.map +1 -0
  15. package/dist/chunk-RY65GERM.js +159 -0
  16. package/dist/chunk-RY65GERM.js.map +1 -0
  17. package/dist/chunk-W72QW2AD.js +29 -0
  18. package/dist/chunk-W72QW2AD.js.map +1 -0
  19. package/dist/chunk-ZAXGEDZW.js +1184 -0
  20. package/dist/chunk-ZAXGEDZW.js.map +1 -0
  21. package/dist/custom/exporters/http.d.ts +11 -0
  22. package/dist/custom/exporters/http.js +364 -0
  23. package/dist/custom/exporters/http.js.map +1 -0
  24. package/dist/custom/exporters/raw.d.ts +8 -0
  25. package/dist/custom/exporters/raw.js +6 -0
  26. package/dist/custom/exporters/raw.js.map +1 -0
  27. package/dist/custom.d.ts +58 -0
  28. package/dist/custom.js +299 -0
  29. package/dist/custom.js.map +1 -0
  30. package/dist/development.d.ts +19 -0
  31. package/dist/development.js +55 -0
  32. package/dist/development.js.map +1 -0
  33. package/dist/event-export-manager-DEB64LWZ.js +632 -0
  34. package/dist/event-export-manager-DEB64LWZ.js.map +1 -0
  35. package/dist/index.d.ts +26 -0
  36. package/dist/index.js +205 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/monitors-VNNUIKAK.js +753 -0
  39. package/dist/monitors-VNNUIKAK.js.map +1 -0
  40. package/dist/n1-detector-6IQ2DINP.js +247 -0
  41. package/dist/n1-detector-6IQ2DINP.js.map +1 -0
  42. package/dist/observability.d.ts +32 -0
  43. package/dist/observability.js +76 -0
  44. package/dist/observability.js.map +1 -0
  45. package/dist/ports-DwUYdKj-.d.ts +23 -0
  46. package/dist/production.d.ts +19 -0
  47. package/dist/production.js +42 -0
  48. package/dist/production.js.map +1 -0
  49. package/dist/public/types.d.ts +11 -0
  50. package/dist/public/types.js +3 -0
  51. package/dist/public/types.js.map +1 -0
  52. package/package.json +74 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ooops Design Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @ooopsstudio/performance
2
+
3
+ Bounded server performance measurements, budgets, runtime monitoring, N+1 detection, and optional event delivery for Ooops System applications.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @ooopsstudio/performance @ooopsstudio/core
9
+ ```
10
+
11
+ ## Presets
12
+
13
+ ```ts
14
+ import {createProductionPerformance} from '@ooopsstudio/performance/production'
15
+
16
+ const performance = await createProductionPerformance()
17
+ await performance.measureAsync('checkout', async () => processCheckout())
18
+ await performance.flush()
19
+ await performance.shutdown()
20
+ ```
21
+
22
+ Development and production enable bounded event-loop, GC, and resource monitors. Custom composition can add immutable budgets, trace-scoped N+1 detection, selected runtime monitors, and at most two event destinations.
23
+
24
+ ## Container registration
25
+
26
+ ```ts
27
+ import {registerPerformance} from '@ooopsstudio/performance'
28
+
29
+ await registerPerformance(container, {preset: 'production'})
30
+ ```
31
+
32
+ Errors, tracing, and lifecycle are optional core ports. Their failure does not change measurement, delivery, or shutdown behavior.
33
+
34
+ ## Explicit entrypoints
35
+
36
+ - `@ooopsstudio/performance/development`
37
+ - `@ooopsstudio/performance/production`
38
+ - `@ooopsstudio/performance/custom`
39
+ - `@ooopsstudio/performance/custom/exporters/raw`
40
+ - `@ooopsstudio/performance/custom/exporters/http`
41
+ - `@ooopsstudio/performance/observability`
42
+ - `@ooopsstudio/performance/public/types`
43
+
44
+ The observability entrypoint emits bounded domain events. Cross-domain mapping to logging, errors, metrics, and tracing belongs in an integration or bridges package.
45
+
46
+ Browser RUM and Web Vitals are intentionally outside this server package.
47
+
48
+ ## License
49
+
50
+ MIT
@@ -0,0 +1,160 @@
1
+ import { hasControlCharacters } from './chunk-2XXIOHOO.js';
2
+ import './chunk-RL2752FW.js';
3
+
4
+ // src/performance/features/core/budget-engine.ts
5
+ var matches = (config, eventName) => {
6
+ const pattern = config.pattern ?? config.name;
7
+ if (!pattern.includes("*")) return eventName === pattern;
8
+ let patternIndex = 0;
9
+ let nameIndex = 0;
10
+ let starIndex = -1;
11
+ let retryIndex = 0;
12
+ while (nameIndex < eventName.length) {
13
+ if (pattern[patternIndex] === eventName[nameIndex]) {
14
+ patternIndex += 1;
15
+ nameIndex += 1;
16
+ } else if (pattern[patternIndex] === "*") {
17
+ starIndex = patternIndex++;
18
+ retryIndex = nameIndex;
19
+ } else if (starIndex >= 0) {
20
+ patternIndex = starIndex + 1;
21
+ nameIndex = ++retryIndex;
22
+ } else return false;
23
+ }
24
+ while (pattern[patternIndex] === "*") patternIndex += 1;
25
+ return patternIndex === pattern.length;
26
+ };
27
+ function createBudgetEngine(options = {}) {
28
+ const now = options.now ?? Date.now;
29
+ const maxSamples = options.maxSamplesPerBudget ?? 2e3;
30
+ if (!Number.isInteger(maxSamples) || maxSamples <= 0 || maxSamples > 1e5) {
31
+ throw new Error("maxSamplesPerBudget must be 1..100000");
32
+ }
33
+ const budgets = /* @__PURE__ */ new Map();
34
+ const samples = /* @__PURE__ */ new Map();
35
+ const violationTimestamps = /* @__PURE__ */ new Map();
36
+ const activeViolations = /* @__PURE__ */ new Set();
37
+ const lastTimestamps = /* @__PURE__ */ new Map();
38
+ const discardOldest = (state, config) => {
39
+ const discarded = state.items[state.start];
40
+ if (discarded && discarded.value > config.target) state.aboveTarget -= 1;
41
+ state.start += 1;
42
+ };
43
+ const compact = (state) => {
44
+ if (state.start === state.items.length) {
45
+ state.items = [];
46
+ state.start = 0;
47
+ } else if (state.start >= Math.min(maxSamples, 1024)) {
48
+ state.items = state.items.slice(state.start);
49
+ state.start = 0;
50
+ }
51
+ };
52
+ const prune = (name, config, timestamp) => {
53
+ const state = samples.get(name) ?? { items: [], start: 0, aboveTarget: 0 };
54
+ while ((state.items[state.start]?.timestamp ?? Number.POSITIVE_INFINITY) < timestamp - config.window) {
55
+ discardOldest(state, config);
56
+ }
57
+ compact(state);
58
+ samples.set(name, state);
59
+ return state;
60
+ };
61
+ const percentileValue = (state, config) => {
62
+ const values = state.items.slice(state.start).map(({ value }) => value).sort((left, right) => left - right);
63
+ return values[Math.max(0, Math.ceil((config.percentile ?? 0.95) * values.length) - 1)] ?? 0;
64
+ };
65
+ const isViolated = (state, config) => {
66
+ const count = state.items.length - state.start;
67
+ if (count === 0) return false;
68
+ const percentileIndex = Math.max(0, Math.ceil((config.percentile ?? 0.95) * count) - 1);
69
+ return state.aboveTarget >= count - percentileIndex;
70
+ };
71
+ const pruneViolations = (name, config, timestamp) => {
72
+ const retained = (violationTimestamps.get(name) ?? []).filter((entry) => entry >= timestamp - config.window).slice(-maxSamples);
73
+ violationTimestamps.set(name, retained);
74
+ return retained;
75
+ };
76
+ return {
77
+ registerBudget(config) {
78
+ if (typeof config.name !== "string" || config.name.length > 128 || !/^[a-z_][a-z0-9_.*?-]{0,127}$/i.test(config.name)) {
79
+ throw new Error("Budget name must be a safe identifier of at most 128 characters");
80
+ }
81
+ if (config.pattern !== void 0 && (typeof config.pattern !== "string" || config.pattern.length > 256 || !config.pattern.trim() || hasControlCharacters(config.pattern))) {
82
+ throw new Error(`Budget pattern for "${config.name}" must be a safe pattern of at most 256 characters`);
83
+ }
84
+ if (!Number.isFinite(config.target) || config.target < 0) throw new Error(`Budget target for "${config.name}" must be a non-negative finite number`);
85
+ if (!Number.isFinite(config.window) || config.window <= 0) throw new Error(`Budget window for "${config.name}" must be a positive finite number`);
86
+ if (config.percentile !== void 0 && (!Number.isFinite(config.percentile) || config.percentile <= 0 || config.percentile > 1)) {
87
+ throw new Error(`Budget percentile for "${config.name}" must be in (0, 1]`);
88
+ }
89
+ budgets.set(config.name, { ...config });
90
+ samples.delete(config.name);
91
+ violationTimestamps.delete(config.name);
92
+ activeViolations.delete(config.name);
93
+ lastTimestamps.delete(config.name);
94
+ },
95
+ checkEvent(event) {
96
+ const found = [];
97
+ for (const config of budgets.values()) {
98
+ if (!matches(config, event.name)) continue;
99
+ const timestamp = Math.max(event.end, lastTimestamps.get(config.name) ?? event.end);
100
+ lastTimestamps.set(config.name, timestamp);
101
+ const state = prune(config.name, config, timestamp);
102
+ state.items.push({ value: event.duration, timestamp });
103
+ if (event.duration > config.target) state.aboveTarget += 1;
104
+ if (state.items.length - state.start > maxSamples) discardOldest(state, config);
105
+ compact(state);
106
+ if (!isViolated(state, config)) {
107
+ activeViolations.delete(config.name);
108
+ continue;
109
+ }
110
+ if (activeViolations.has(config.name)) continue;
111
+ const actual = percentileValue(state, config);
112
+ const violation = {
113
+ name: config.name,
114
+ target: config.target,
115
+ actual,
116
+ window: config.window,
117
+ diff: actual - config.target
118
+ };
119
+ activeViolations.add(config.name);
120
+ found.push({ ...violation });
121
+ const history = violationTimestamps.get(config.name) ?? [];
122
+ history.push(timestamp);
123
+ violationTimestamps.set(config.name, history.slice(-maxSamples));
124
+ try {
125
+ options.onViolation?.({ ...violation });
126
+ } catch {
127
+ }
128
+ }
129
+ return found;
130
+ },
131
+ getStatus(name) {
132
+ const config = budgets.get(name);
133
+ if (!config) return void 0;
134
+ const observedAt = now();
135
+ const timestamp = Math.max(observedAt, lastTimestamps.get(name) ?? observedAt);
136
+ const state = prune(name, config, timestamp);
137
+ const current = percentileValue(state, config);
138
+ const violated = current > config.target;
139
+ if (!violated) activeViolations.delete(name);
140
+ return Object.freeze({
141
+ name,
142
+ target: config.target,
143
+ current,
144
+ violated,
145
+ violationCount: pruneViolations(name, config, timestamp).length,
146
+ window: config.window
147
+ });
148
+ },
149
+ reset() {
150
+ samples.clear();
151
+ violationTimestamps.clear();
152
+ activeViolations.clear();
153
+ lastTimestamps.clear();
154
+ }
155
+ };
156
+ }
157
+
158
+ export { createBudgetEngine };
159
+ //# sourceMappingURL=budget-engine-KDA4FRYU.js.map
160
+ //# sourceMappingURL=budget-engine-KDA4FRYU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/performance/features/core/budget-engine.ts"],"names":[],"mappings":"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","file":"budget-engine-KDA4FRYU.js","sourcesContent":["import type {BudgetStatus, BudgetViolation, PerfEvent} from '@ooopsstudio/core/contracts/performance'\n\nimport {hasControlCharacters} from '../../utils/safe-identifiers'\n\nexport interface BudgetConfig {\n\tname: string\n\tpattern?: string\n\tpercentile?: number\n\ttarget: number\n\twindow: number\n}\n\nexport interface BudgetEngine {\n\tregisterBudget(config: BudgetConfig): void\n\tcheckEvent(event: PerfEvent): BudgetViolation[]\n\tgetStatus(name: string): BudgetStatus | undefined\n\treset(): void\n}\n\nexport interface BudgetEngineOptions {\n\tonViolation?: (violation: BudgetViolation) => void\n\tnow?: () => number\n\tmaxSamplesPerBudget?: number\n}\n\ntype Sample = {value: number; timestamp: number}\ntype SampleState = {items: Sample[]; start: number; aboveTarget: number}\n\nconst matches = (config: BudgetConfig, eventName: string): boolean => {\n\tconst pattern = config.pattern ?? config.name\n\tif (!pattern.includes('*')) return eventName === pattern\n\tlet patternIndex = 0\n\tlet nameIndex = 0\n\tlet starIndex = -1\n\tlet retryIndex = 0\n\twhile (nameIndex < eventName.length) {\n\t\tif (pattern[patternIndex] === eventName[nameIndex]) {\n\t\t\tpatternIndex += 1\n\t\t\tnameIndex += 1\n\t\t} else if (pattern[patternIndex] === '*') {\n\t\t\tstarIndex = patternIndex++\n\t\t\tretryIndex = nameIndex\n\t\t} else if (starIndex >= 0) {\n\t\t\tpatternIndex = starIndex + 1\n\t\t\tnameIndex = ++retryIndex\n\t\t} else return false\n\t}\n\twhile (pattern[patternIndex] === '*') patternIndex += 1\n\treturn patternIndex === pattern.length\n}\n\nexport function createBudgetEngine(options: BudgetEngineOptions = {}): BudgetEngine {\n\tconst now = options.now ?? Date.now\n\tconst maxSamples = options.maxSamplesPerBudget ?? 2_000\n\tif (!Number.isInteger(maxSamples) || maxSamples <= 0 || maxSamples > 100_000) {\n\t\tthrow new Error('maxSamplesPerBudget must be 1..100000')\n\t}\n\tconst budgets = new Map<string, BudgetConfig>()\n\tconst samples = new Map<string, SampleState>()\n\tconst violationTimestamps = new Map<string, number[]>()\n\tconst activeViolations = new Set<string>()\n\tconst lastTimestamps = new Map<string, number>()\n\n\tconst discardOldest = (state: SampleState, config: BudgetConfig): void => {\n\t\tconst discarded = state.items[state.start]\n\t\tif (discarded && discarded.value > config.target) state.aboveTarget -= 1\n\t\tstate.start += 1\n\t}\n\tconst compact = (state: SampleState): void => {\n\t\tif (state.start === state.items.length) {\n\t\t\tstate.items = []\n\t\t\tstate.start = 0\n\t\t} else if (state.start >= Math.min(maxSamples, 1_024)) {\n\t\t\tstate.items = state.items.slice(state.start)\n\t\t\tstate.start = 0\n\t\t}\n\t}\n\tconst prune = (name: string, config: BudgetConfig, timestamp: number): SampleState => {\n\t\tconst state = samples.get(name) ?? {items: [], start: 0, aboveTarget: 0}\n\t\twhile ((state.items[state.start]?.timestamp ?? Number.POSITIVE_INFINITY) < timestamp - config.window) {\n\t\t\tdiscardOldest(state, config)\n\t\t}\n\t\tcompact(state)\n\t\tsamples.set(name, state)\n\t\treturn state\n\t}\n\tconst percentileValue = (state: SampleState, config: BudgetConfig): number => {\n\t\tconst values = state.items.slice(state.start).map(({value}) => value).sort((left, right) => left - right)\n\t\treturn values[Math.max(0, Math.ceil((config.percentile ?? 0.95) * values.length) - 1)] ?? 0\n\t}\n\tconst isViolated = (state: SampleState, config: BudgetConfig): boolean => {\n\t\tconst count = state.items.length - state.start\n\t\tif (count === 0) return false\n\t\tconst percentileIndex = Math.max(0, Math.ceil((config.percentile ?? 0.95) * count) - 1)\n\t\treturn state.aboveTarget >= count - percentileIndex\n\t}\n\tconst pruneViolations = (name: string, config: BudgetConfig, timestamp: number) => {\n\t\tconst retained = (violationTimestamps.get(name) ?? [])\n\t\t\t.filter((entry) => entry >= timestamp - config.window)\n\t\t\t.slice(-maxSamples)\n\t\tviolationTimestamps.set(name, retained)\n\t\treturn retained\n\t}\n\n\treturn {\n\t\tregisterBudget(config) {\n\t\t\tif (typeof config.name !== 'string' || config.name.length > 128 ||\n\t\t\t\t!/^[a-z_][a-z0-9_.*?-]{0,127}$/i.test(config.name)) {\n\t\t\t\tthrow new Error('Budget name must be a safe identifier of at most 128 characters')\n\t\t\t}\n\t\t\tif (config.pattern !== undefined && (typeof config.pattern !== 'string' || config.pattern.length > 256 ||\n\t\t\t\t!config.pattern.trim() || hasControlCharacters(config.pattern))) {\n\t\t\t\tthrow new Error(`Budget pattern for \"${config.name}\" must be a safe pattern of at most 256 characters`)\n\t\t\t}\n\t\t\tif (!Number.isFinite(config.target) || config.target < 0) throw new Error(`Budget target for \"${config.name}\" must be a non-negative finite number`)\n\t\t\tif (!Number.isFinite(config.window) || config.window <= 0) throw new Error(`Budget window for \"${config.name}\" must be a positive finite number`)\n\t\t\tif (config.percentile !== undefined && (!Number.isFinite(config.percentile) || config.percentile <= 0 || config.percentile > 1)) {\n\t\t\t\tthrow new Error(`Budget percentile for \"${config.name}\" must be in (0, 1]`)\n\t\t\t}\n\t\t\tbudgets.set(config.name, {...config})\n\t\t\tsamples.delete(config.name)\n\t\t\tviolationTimestamps.delete(config.name)\n\t\t\tactiveViolations.delete(config.name)\n\t\t\tlastTimestamps.delete(config.name)\n\t\t},\n\t\tcheckEvent(event) {\n\t\t\tconst found: BudgetViolation[] = []\n\t\t\tfor (const config of budgets.values()) {\n\t\t\t\tif (!matches(config, event.name)) continue\n\t\t\t\tconst timestamp = Math.max(event.end, lastTimestamps.get(config.name) ?? event.end)\n\t\t\t\tlastTimestamps.set(config.name, timestamp)\n\t\t\t\tconst state = prune(config.name, config, timestamp)\n\t\t\t\tstate.items.push({value: event.duration, timestamp})\n\t\t\t\tif (event.duration > config.target) state.aboveTarget += 1\n\t\t\t\tif (state.items.length - state.start > maxSamples) discardOldest(state, config)\n\t\t\t\tcompact(state)\n\t\t\t\tif (!isViolated(state, config)) {\n\t\t\t\t\tactiveViolations.delete(config.name)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (activeViolations.has(config.name)) continue\n\t\t\t\tconst actual = percentileValue(state, config)\n\t\t\t\tconst violation = {\n\t\t\t\t\tname: config.name,\n\t\t\t\t\ttarget: config.target,\n\t\t\t\t\tactual,\n\t\t\t\t\twindow: config.window,\n\t\t\t\t\tdiff: actual - config.target\n\t\t\t\t}\n\t\t\t\tactiveViolations.add(config.name)\n\t\t\t\tfound.push({...violation})\n\t\t\t\tconst history = violationTimestamps.get(config.name) ?? []\n\t\t\t\thistory.push(timestamp)\n\t\t\t\tviolationTimestamps.set(config.name, history.slice(-maxSamples))\n\t\t\t\ttry { options.onViolation?.({...violation}) } catch { /* observers are isolated */ }\n\t\t\t}\n\t\t\treturn found\n\t\t},\n\t\tgetStatus(name) {\n\t\t\tconst config = budgets.get(name)\n\t\t\tif (!config) return undefined\n\t\t\tconst observedAt = now()\n\t\t\tconst timestamp = Math.max(observedAt, lastTimestamps.get(name) ?? observedAt)\n\t\t\tconst state = prune(name, config, timestamp)\n\t\t\tconst current = percentileValue(state, config)\n\t\t\tconst violated = current > config.target\n\t\t\tif (!violated) activeViolations.delete(name)\n\t\t\treturn Object.freeze({\n\t\t\t\tname,\n\t\t\t\ttarget: config.target,\n\t\t\t\tcurrent,\n\t\t\t\tviolated,\n\t\t\t\tviolationCount: pruneViolations(name, config, timestamp).length,\n\t\t\t\twindow: config.window\n\t\t\t})\n\t\t},\n\t\treset() { samples.clear(); violationTimestamps.clear(); activeViolations.clear(); lastTimestamps.clear() }\n\t}\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { isRuntimeProxy } from './chunk-RL2752FW.js';
2
+
3
+ // src/performance/utils/safe-identifiers.ts
4
+ function hasControlCharacters(value) {
5
+ for (let index = 0; index < value.length; index += 1) {
6
+ const code = value.charCodeAt(index);
7
+ if (code <= 31 || code === 127) return true;
8
+ }
9
+ return false;
10
+ }
11
+ var SAFE_IDENTIFIER = /^[a-z][a-z0-9_.-]{0,63}$/i;
12
+ var EMAIL_PATTERN = /@/u;
13
+ var URL_PATTERN = /(?:https?:\/\/|\/|\?)/iu;
14
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
15
+ var NUMERIC_ID_PATTERN = /^\d{5,}$/u;
16
+ var OPAQUE_TOKEN_PATTERN = /^[a-z0-9+/_=-]{24,}$/iu;
17
+ var SENSITIVE_KEY_PATTERN = /(?:authorization|authentication|cookie|credential|password|secret|token|bearer|session[_.-]?id|(?:api|access|private)[_.-]?key)/iu;
18
+ var isSensitivePerformanceKey = (value) => SENSITIVE_KEY_PATTERN.test(value);
19
+ function sanitizePerformanceEventName(value) {
20
+ return SAFE_IDENTIFIER.test(value) && !hasControlCharacters(value) ? value : "custom_event";
21
+ }
22
+ function sanitizePerformanceLabelValue(value) {
23
+ const trimmed = value.trim();
24
+ if (trimmed.length === 0) return "";
25
+ if (EMAIL_PATTERN.test(trimmed)) return "[email]";
26
+ if (URL_PATTERN.test(trimmed)) return "[url]";
27
+ if (UUID_PATTERN.test(trimmed)) return "[uuid]";
28
+ if (NUMERIC_ID_PATTERN.test(trimmed)) return "[numeric-id]";
29
+ if (OPAQUE_TOKEN_PATTERN.test(trimmed)) return "[opaque]";
30
+ return trimmed.length <= 64 && !hasControlCharacters(trimmed) ? trimmed : "[redacted]";
31
+ }
32
+ function snapshotPerformanceLabels(labels) {
33
+ if (labels === void 0) return void 0;
34
+ try {
35
+ if (!labels || typeof labels !== "object" || isRuntimeProxy(labels) || Array.isArray(labels)) throw new TypeError();
36
+ const prototype = Object.getPrototypeOf(labels);
37
+ if (prototype !== Object.prototype && prototype !== null) throw new TypeError();
38
+ const snapshot = /* @__PURE__ */ Object.create(null);
39
+ let inspected = 0;
40
+ for (const key in labels) {
41
+ if (inspected >= 32) throw new TypeError();
42
+ const descriptor = Object.getOwnPropertyDescriptor(labels, key);
43
+ if (!descriptor) throw new TypeError();
44
+ inspected += 1;
45
+ if (!descriptor.enumerable || !("value" in descriptor) || !/^[a-z_][a-z0-9_.-]{0,63}$/i.test(key) || typeof descriptor.value !== "string" || descriptor.value.length > 256) {
46
+ throw new TypeError();
47
+ }
48
+ snapshot[key] = descriptor.value;
49
+ }
50
+ return snapshot;
51
+ } catch {
52
+ throw new Error("Performance event labels exceed safe key/value limits");
53
+ }
54
+ }
55
+
56
+ export { hasControlCharacters, isSensitivePerformanceKey, sanitizePerformanceEventName, sanitizePerformanceLabelValue, snapshotPerformanceLabels };
57
+ //# sourceMappingURL=chunk-2XXIOHOO.js.map
58
+ //# sourceMappingURL=chunk-2XXIOHOO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/performance/utils/safe-identifiers.ts"],"names":[],"mappings":";;;AAAO,SAAS,qBAAqB,KAAA,EAAwB;AAC5D,EAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,KAAA,CAAM,MAAA,EAAQ,SAAS,CAAA,EAAG;AACrD,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,KAAK,CAAA;AACnC,IAAA,IAAI,IAAA,IAAQ,EAAA,IAAM,IAAA,KAAS,GAAA,EAAK,OAAO,IAAA;AAAA,EACxC;AACA,EAAA,OAAO,KAAA;AACR;AAEA,IAAM,eAAA,GAAkB,2BAAA;AACxB,IAAM,aAAA,GAAgB,IAAA;AACtB,IAAM,WAAA,GAAc,yBAAA;AACpB,IAAM,YAAA,GAAe,6EAAA;AACrB,IAAM,kBAAA,GAAqB,WAAA;AAC3B,IAAM,oBAAA,GAAuB,wBAAA;AAC7B,IAAM,qBAAA,GAAwB,mIAAA;AAEvB,IAAM,yBAAA,GAA4B,CAAC,KAAA,KAA2B,qBAAA,CAAsB,KAAK,KAAK;AAE9F,SAAS,6BAA6B,KAAA,EAAuB;AACnE,EAAA,OAAO,eAAA,CAAgB,KAAK,KAAK,CAAA,IAAK,CAAC,oBAAA,CAAqB,KAAK,IAC9D,KAAA,GACA,cAAA;AACJ;AAEO,SAAS,8BAA8B,KAAA,EAAuB;AACpE,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACjC,EAAA,IAAI,aAAA,CAAc,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,SAAA;AACxC,EAAA,IAAI,WAAA,CAAY,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,OAAA;AACtC,EAAA,IAAI,YAAA,CAAa,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,QAAA;AACvC,EAAA,IAAI,kBAAA,CAAmB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,cAAA;AAC7C,EAAA,IAAI,oBAAA,CAAqB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,UAAA;AAC/C,EAAA,OAAO,QAAQ,MAAA,IAAU,EAAA,IAAM,CAAC,oBAAA,CAAqB,OAAO,IAAI,OAAA,GAAU,YAAA;AAC3E;AAGO,SAAS,0BACf,MAAA,EACqC;AACrC,EAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AACjC,EAAA,IAAI;AACH,IAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,YAAY,cAAA,CAAe,MAAM,CAAA,IAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG,MAAM,IAAI,SAAA,EAAU;AAClH,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,cAAA,CAAe,MAAM,CAAA;AAC9C,IAAA,IAAI,cAAc,MAAA,CAAO,SAAA,IAAa,cAAc,IAAA,EAAM,MAAM,IAAI,SAAA,EAAU;AAC9E,IAAA,MAAM,QAAA,mBAAmC,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA;AAC3D,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,OAAO,MAAA,EAAQ;AACzB,MAAA,IAAI,SAAA,IAAa,EAAA,EAAI,MAAM,IAAI,SAAA,EAAU;AACzC,MAAA,MAAM,UAAA,GAAa,MAAA,CAAO,wBAAA,CAAyB,MAAA,EAAQ,GAAG,CAAA;AAC9D,MAAA,IAAI,CAAC,UAAA,EAAY,MAAM,IAAI,SAAA,EAAU;AACrC,MAAA,SAAA,IAAa,CAAA;AACb,MAAA,IAAI,CAAC,UAAA,CAAW,UAAA,IAAc,EAAE,OAAA,IAAW,UAAA,CAAA,IAC1C,CAAC,4BAAA,CAA6B,IAAA,CAAK,GAAG,CAAA,IACtC,OAAO,UAAA,CAAW,KAAA,KAAU,YAAY,UAAA,CAAW,KAAA,CAAM,SAAS,GAAA,EAAK;AACvE,QAAA,MAAM,IAAI,SAAA,EAAU;AAAA,MACrB;AACA,MAAA,QAAA,CAAS,GAAG,IAAI,UAAA,CAAW,KAAA;AAAA,IAC5B;AACA,IAAA,OAAO,QAAA;AAAA,EACR,CAAA,CAAA,MAAQ;AACP,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EACxE;AACD","file":"chunk-2XXIOHOO.js","sourcesContent":["export function hasControlCharacters(value: string): boolean {\n\tfor (let index = 0; index < value.length; index += 1) {\n\t\tconst code = value.charCodeAt(index)\n\t\tif (code <= 31 || code === 127) return true\n\t}\n\treturn false\n}\n\nconst SAFE_IDENTIFIER = /^[a-z][a-z0-9_.-]{0,63}$/i\nconst EMAIL_PATTERN = /@/u\nconst URL_PATTERN = /(?:https?:\\/\\/|\\/|\\?)/iu\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu\nconst NUMERIC_ID_PATTERN = /^\\d{5,}$/u\nconst OPAQUE_TOKEN_PATTERN = /^[a-z0-9+/_=-]{24,}$/iu\nconst SENSITIVE_KEY_PATTERN = /(?:authorization|authentication|cookie|credential|password|secret|token|bearer|session[_.-]?id|(?:api|access|private)[_.-]?key)/iu\n\nexport const isSensitivePerformanceKey = (value: string): boolean => SENSITIVE_KEY_PATTERN.test(value)\n\nexport function sanitizePerformanceEventName(value: string): string {\n\treturn SAFE_IDENTIFIER.test(value) && !hasControlCharacters(value)\n\t\t? value\n\t\t: 'custom_event'\n}\n\nexport function sanitizePerformanceLabelValue(value: string): string {\n\tconst trimmed = value.trim()\n\tif (trimmed.length === 0) return ''\n\tif (EMAIL_PATTERN.test(trimmed)) return '[email]'\n\tif (URL_PATTERN.test(trimmed)) return '[url]'\n\tif (UUID_PATTERN.test(trimmed)) return '[uuid]'\n\tif (NUMERIC_ID_PATTERN.test(trimmed)) return '[numeric-id]'\n\tif (OPAQUE_TOKEN_PATTERN.test(trimmed)) return '[opaque]'\n\treturn trimmed.length <= 64 && !hasControlCharacters(trimmed) ? trimmed : '[redacted]'\n}\n\n/** Captures bounded, plain label data without invoking user accessors. */\nexport function snapshotPerformanceLabels(\n\tlabels: Record<string, string> | undefined\n): Record<string, string> | undefined {\n\tif (labels === undefined) return undefined\n\ttry {\n\t\tif (!labels || typeof labels !== 'object' || isRuntimeProxy(labels) || Array.isArray(labels)) throw new TypeError()\n\t\tconst prototype = Object.getPrototypeOf(labels)\n\t\tif (prototype !== Object.prototype && prototype !== null) throw new TypeError()\n\t\tconst snapshot: Record<string, string> = Object.create(null) as Record<string, string>\n\t\tlet inspected = 0\n\t\tfor (const key in labels) {\n\t\t\tif (inspected >= 32) throw new TypeError()\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(labels, key)\n\t\t\tif (!descriptor) throw new TypeError()\n\t\t\tinspected += 1\n\t\t\tif (!descriptor.enumerable || !('value' in descriptor) ||\n\t\t\t\t!/^[a-z_][a-z0-9_.-]{0,63}$/i.test(key) ||\n\t\t\t\ttypeof descriptor.value !== 'string' || descriptor.value.length > 256) {\n\t\t\t\tthrow new TypeError()\n\t\t\t}\n\t\t\tsnapshot[key] = descriptor.value\n\t\t}\n\t\treturn snapshot\n\t} catch {\n\t\tthrow new Error('Performance event labels exceed safe key/value limits')\n\t}\n}\nimport {isRuntimeProxy} from './safe-object'\n"]}
@@ -0,0 +1,197 @@
1
+ import { isRuntimeProxy, isRuntimeError } from './chunk-RL2752FW.js';
2
+
3
+ // src/performance/core/export-errors.ts
4
+ var readDataProperty = (value, key) => {
5
+ if (!value || typeof value !== "object" && typeof value !== "function") return void 0;
6
+ try {
7
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
8
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
9
+ } catch {
10
+ return void 0;
11
+ }
12
+ };
13
+ function getPerformanceExportErrorMetadata(error) {
14
+ try {
15
+ if (isRuntimeProxy(error) || !isRuntimeError(error)) return void 0;
16
+ const retryable = readDataProperty(error, "retryable");
17
+ const code = readDataProperty(error, "code");
18
+ return typeof retryable === "boolean" && typeof code === "string" ? { retryable, code } : void 0;
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
23
+ function isPerformanceExportError(error) {
24
+ return getPerformanceExportErrorMetadata(error) !== void 0;
25
+ }
26
+ function createPerformanceExportError(message, options) {
27
+ const error = new Error(
28
+ message,
29
+ Object.prototype.hasOwnProperty.call(options, "cause") ? { cause: options.cause } : void 0
30
+ );
31
+ error.retryable = options.retryable;
32
+ error.code = options.code;
33
+ if (typeof options.statusCode === "number") {
34
+ error.statusCode = options.statusCode;
35
+ }
36
+ return error;
37
+ }
38
+ function classifyHttpStatus(statusCode) {
39
+ if (statusCode === 429) {
40
+ return { retryable: true, code: "http_rate_limited" };
41
+ }
42
+ if (statusCode >= 500) {
43
+ return { retryable: true, code: "http_server_error" };
44
+ }
45
+ if (statusCode >= 400) {
46
+ return { retryable: false, code: "http_client_error" };
47
+ }
48
+ return { retryable: false, code: "http_unexpected_status" };
49
+ }
50
+ function classifyFetchFailure(error) {
51
+ if (isPerformanceExportError(error)) {
52
+ return error;
53
+ }
54
+ if (isRuntimeProxy(error)) {
55
+ return createPerformanceExportError("Performance export failed", {
56
+ retryable: true,
57
+ code: "fetch_failed",
58
+ cause: error
59
+ });
60
+ }
61
+ const errorInstance = isRuntimeError(error);
62
+ const name = readDataProperty(error, "name");
63
+ const message = readDataProperty(error, "message");
64
+ let domExceptionName = name;
65
+ let domExceptionInstance = false;
66
+ if (typeof DOMException !== "undefined") {
67
+ try {
68
+ const nativeName = Object.getOwnPropertyDescriptor(DOMException.prototype, "name")?.get;
69
+ const detectedName = typeof nativeName === "function" ? Reflect.apply(nativeName, error, []) : void 0;
70
+ if (typeof detectedName === "string") {
71
+ domExceptionInstance = true;
72
+ domExceptionName ??= detectedName;
73
+ }
74
+ } catch {
75
+ }
76
+ }
77
+ if (domExceptionInstance && domExceptionName === "AbortError") {
78
+ return createPerformanceExportError(
79
+ typeof message === "string" && message ? message : "Performance export aborted",
80
+ {
81
+ retryable: true,
82
+ code: "fetch_aborted",
83
+ cause: error
84
+ }
85
+ );
86
+ }
87
+ if (errorInstance) {
88
+ return createPerformanceExportError(typeof message === "string" ? message : "Performance export failed", {
89
+ retryable: true,
90
+ code: "fetch_failed",
91
+ cause: error
92
+ });
93
+ }
94
+ const messageValue = typeof error === "string" ? error : typeof error === "number" || typeof error === "boolean" ? String(error) : "Performance export failed";
95
+ return createPerformanceExportError(messageValue, {
96
+ retryable: true,
97
+ code: "fetch_failed",
98
+ cause: error
99
+ });
100
+ }
101
+
102
+ // src/performance/core/event-export-utils.ts
103
+ var MAX_PERFORMANCE_TIMER_MS = 2147483647;
104
+ var MAX_PERFORMANCE_EXPORT_BATCH_COUNT = 256;
105
+ var MAX_PERFORMANCE_EXPORT_BATCH_BYTES = 1048576;
106
+ async function sleep(ms) {
107
+ await new Promise((resolve) => setTimeout(resolve, ms));
108
+ }
109
+ async function withPerformanceExportTimeout(operation, timeoutMs, label) {
110
+ let timer;
111
+ try {
112
+ return await Promise.race([
113
+ Promise.resolve(operation),
114
+ new Promise((_, reject) => {
115
+ timer = setTimeout(() => reject(createPerformanceExportError(
116
+ `${label} timed out after ${timeoutMs}ms`,
117
+ { retryable: true, code: "performance_export_timeout" }
118
+ )), timeoutMs);
119
+ })
120
+ ]);
121
+ } finally {
122
+ try {
123
+ if (timer !== void 0) clearTimeout(timer);
124
+ } catch {
125
+ }
126
+ }
127
+ }
128
+ function serializePerformanceEventRecord(record) {
129
+ try {
130
+ let remainingNodes = 1024;
131
+ let remainingStringBytes = MAX_PERFORMANCE_EXPORT_BATCH_BYTES;
132
+ const ancestors = /* @__PURE__ */ new WeakSet();
133
+ const snapshot = (value, depth = 0) => {
134
+ if (typeof value === "string") {
135
+ if (value.length > remainingStringBytes) throw new TypeError();
136
+ const bytes = Buffer.byteLength(value, "utf8");
137
+ if (bytes > remainingStringBytes) throw new TypeError();
138
+ remainingStringBytes -= bytes;
139
+ return value;
140
+ }
141
+ if (value === null || typeof value === "boolean") return value;
142
+ if (typeof value === "number") {
143
+ if (!Number.isFinite(value)) throw new TypeError();
144
+ return value;
145
+ }
146
+ if (value === void 0) return void 0;
147
+ if (typeof value !== "object" || isRuntimeProxy(value) || depth > 12 || remainingNodes-- <= 0 || ancestors.has(value)) {
148
+ throw new TypeError();
149
+ }
150
+ ancestors.add(value);
151
+ try {
152
+ const prototype = Object.getPrototypeOf(value);
153
+ if (Array.isArray(value)) {
154
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
155
+ const length = lengthDescriptor && "value" in lengthDescriptor ? lengthDescriptor.value : void 0;
156
+ if (!Number.isSafeInteger(length) || length < 0 || length > 256) throw new TypeError();
157
+ const result2 = [];
158
+ if (!Reflect.setPrototypeOf(result2, null)) throw new TypeError();
159
+ for (let index = 0; index < length; index += 1) {
160
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
161
+ if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError();
162
+ result2[index] = snapshot(descriptor.value, depth + 1);
163
+ }
164
+ return result2;
165
+ }
166
+ if (prototype !== Object.prototype && prototype !== null) throw new TypeError();
167
+ const result = /* @__PURE__ */ Object.create(null);
168
+ let entries = 0;
169
+ for (const key in value) {
170
+ if (entries >= 128) throw new TypeError();
171
+ if (key.length > remainingStringBytes) throw new TypeError();
172
+ const keyBytes = Buffer.byteLength(key, "utf8");
173
+ if (keyBytes > remainingStringBytes) throw new TypeError();
174
+ remainingStringBytes -= keyBytes;
175
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
176
+ if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError();
177
+ entries += 1;
178
+ const captured2 = snapshot(descriptor.value, depth + 1);
179
+ if (captured2 !== void 0) result[key] = captured2;
180
+ }
181
+ return result;
182
+ } finally {
183
+ ancestors.delete(value);
184
+ }
185
+ };
186
+ const captured = snapshot(record);
187
+ if (!captured || typeof captured !== "object" || typeof captured.recordedAt !== "number" || typeof captured.source !== "string" || !captured.event || typeof captured.event !== "object") return null;
188
+ const serialized = JSON.stringify(captured);
189
+ return { serialized, bytes: Buffer.byteLength(serialized, "utf8") };
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+
195
+ export { MAX_PERFORMANCE_EXPORT_BATCH_BYTES, MAX_PERFORMANCE_EXPORT_BATCH_COUNT, MAX_PERFORMANCE_TIMER_MS, classifyFetchFailure, classifyHttpStatus, createPerformanceExportError, getPerformanceExportErrorMetadata, serializePerformanceEventRecord, sleep, withPerformanceExportTimeout };
196
+ //# sourceMappingURL=chunk-3GMKPRQP.js.map
197
+ //# sourceMappingURL=chunk-3GMKPRQP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/performance/core/export-errors.ts","../src/performance/core/event-export-utils.ts"],"names":["result","captured"],"mappings":"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","file":"chunk-3GMKPRQP.js","sourcesContent":["import {isRuntimeError, isRuntimeProxy} from '../utils/safe-object'\n\nexport interface PerformanceExportError extends Error {\n\tstatusCode?: number\n\tretryable: boolean\n\tcode: string\n}\n\nexport interface PerformanceExportErrorMetadata {\n\tretryable: boolean\n\tcode: string\n}\n\nconst readDataProperty = (value: unknown, key: PropertyKey): unknown => {\n\tif (!value || (typeof value !== 'object' && typeof value !== 'function')) return undefined\n\ttry {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, key)\n\t\treturn descriptor && 'value' in descriptor ? descriptor.value : undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nexport function getPerformanceExportErrorMetadata(\n\terror: unknown\n): PerformanceExportErrorMetadata | undefined {\n\ttry {\n\t\tif (isRuntimeProxy(error) || !isRuntimeError(error)) return undefined\n\t\tconst retryable = readDataProperty(error, 'retryable')\n\t\tconst code = readDataProperty(error, 'code')\n\t\treturn typeof retryable === 'boolean' && typeof code === 'string'\n\t\t\t? {retryable, code}\n\t\t\t: undefined\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nexport function isPerformanceExportError(error: unknown): error is PerformanceExportError {\n\treturn getPerformanceExportErrorMetadata(error) !== undefined\n}\n\nexport function createPerformanceExportError(\n\tmessage: string,\n\toptions: {\n\t\tstatusCode?: number\n\t\tretryable: boolean\n\t\tcode: string\n\t\tcause?: unknown\n\t}\n): PerformanceExportError {\n\tconst error = new Error(\n\t\tmessage,\n\t\tObject.prototype.hasOwnProperty.call(options, 'cause') ? {cause: options.cause} : undefined\n\t) as PerformanceExportError\n\terror.retryable = options.retryable\n\terror.code = options.code\n\tif (typeof options.statusCode === 'number') {\n\t\terror.statusCode = options.statusCode\n\t}\n\treturn error\n}\n\nexport function classifyHttpStatus(statusCode: number): {retryable: boolean; code: string} {\n\tif (statusCode === 429) {\n\t\treturn {retryable: true, code: 'http_rate_limited'}\n\t}\n\tif (statusCode >= 500) {\n\t\treturn {retryable: true, code: 'http_server_error'}\n\t}\n\tif (statusCode >= 400) {\n\t\treturn {retryable: false, code: 'http_client_error'}\n\t}\n\treturn {retryable: false, code: 'http_unexpected_status'}\n}\n\nexport function classifyFetchFailure(error: unknown): PerformanceExportError {\n\tif (isPerformanceExportError(error)) {\n\t\treturn error\n\t}\n\tif (isRuntimeProxy(error)) {\n\t\treturn createPerformanceExportError('Performance export failed', {\n\t\t\tretryable: true, code: 'fetch_failed', cause: error\n\t\t})\n\t}\n\tconst errorInstance = isRuntimeError(error)\n\tconst name = readDataProperty(error, 'name')\n\tconst message = readDataProperty(error, 'message')\n\tlet domExceptionName = name\n\tlet domExceptionInstance = false\n\tif (typeof DOMException !== 'undefined') {\n\t\ttry {\n\t\t\tconst nativeName = Object.getOwnPropertyDescriptor(DOMException.prototype, 'name')?.get\n\t\t\tconst detectedName = typeof nativeName === 'function' ? Reflect.apply(nativeName, error, []) : undefined\n\t\t\tif (typeof detectedName === 'string') {\n\t\t\t\tdomExceptionInstance = true\n\t\t\t\tdomExceptionName ??= detectedName\n\t\t\t}\n\t\t} catch {\n\t\t\t// A non-DOMException fails the native brand check without prototype traversal.\n\t\t}\n\t}\n\tif (domExceptionInstance && domExceptionName === 'AbortError') {\n\t\treturn createPerformanceExportError(\n\t\t\ttypeof message === 'string' && message ? message : 'Performance export aborted', {\n\t\t\t\tretryable: true,\n\t\t\t\tcode: 'fetch_aborted',\n\t\t\t\tcause: error\n\t\t\t})\n\t}\n\tif (errorInstance) {\n\t\treturn createPerformanceExportError(typeof message === 'string' ? message : 'Performance export failed', {\n\t\t\tretryable: true,\n\t\t\tcode: 'fetch_failed',\n\t\t\tcause: error\n\t\t})\n\t}\n\tconst messageValue = typeof error === 'string' ? error\n\t\t: typeof error === 'number' || typeof error === 'boolean' ? String(error)\n\t\t\t: 'Performance export failed'\n\treturn createPerformanceExportError(messageValue, {\n\t\tretryable: true,\n\t\tcode: 'fetch_failed',\n\t\tcause: error\n\t})\n}\n","import type {PerformanceEventRecord} from '@ooopsstudio/core/contracts/performance'\n\nimport {isRuntimeProxy} from '../utils/safe-object'\n\nimport {createPerformanceExportError} from './export-errors'\n\nexport const MAX_PERFORMANCE_TIMER_MS = 2_147_483_647\nexport const MAX_PERFORMANCE_EXPORT_BATCH_COUNT = 256\nexport const MAX_PERFORMANCE_EXPORT_BATCH_BYTES = 1_048_576\n\nexport async function sleep(ms: number): Promise<void> {\n\tawait new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport async function withPerformanceExportTimeout<T>(\n\toperation: Promise<T> | T,\n\ttimeoutMs: number,\n\tlabel: string\n): Promise<T> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tPromise.resolve(operation),\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\ttimer = setTimeout(() => reject(createPerformanceExportError(\n\t\t\t\t\t`${label} timed out after ${timeoutMs}ms`,\n\t\t\t\t\t{retryable: true, code: 'performance_export_timeout'}\n\t\t\t\t)), timeoutMs)\n\t\t\t})\n\t\t])\n\t} finally {\n\t\ttry { if (timer !== undefined) clearTimeout(timer) } catch { /* delivery result remains authoritative */ }\n\t}\n}\n\nexport function serializePerformanceEventRecord(\n\trecord: PerformanceEventRecord\n): {serialized: string; bytes: number} | null {\n\ttry {\n\t\tlet remainingNodes = 1_024\n\t\tlet remainingStringBytes = MAX_PERFORMANCE_EXPORT_BATCH_BYTES\n\t\tconst ancestors = new WeakSet<object>()\n\t\tconst snapshot = (value: unknown, depth = 0): unknown => {\n\t\t\tif (typeof value === 'string') {\n\t\t\t\tif (value.length > remainingStringBytes) throw new TypeError()\n\t\t\t\tconst bytes = Buffer.byteLength(value, 'utf8')\n\t\t\t\tif (bytes > remainingStringBytes) throw new TypeError()\n\t\t\t\tremainingStringBytes -= bytes\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tif (value === null || typeof value === 'boolean') return value\n\t\t\tif (typeof value === 'number') {\n\t\t\t\tif (!Number.isFinite(value)) throw new TypeError()\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tif (value === undefined) return undefined\n\t\t\tif (typeof value !== 'object' || isRuntimeProxy(value) || depth > 12 || remainingNodes-- <= 0 || ancestors.has(value)) {\n\t\t\t\tthrow new TypeError()\n\t\t\t}\n\t\t\tancestors.add(value)\n\t\t\ttry {\n\t\t\t\tconst prototype = Object.getPrototypeOf(value)\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tconst lengthDescriptor = Object.getOwnPropertyDescriptor(value, 'length')\n\t\t\t\t\tconst length = lengthDescriptor && 'value' in lengthDescriptor ? lengthDescriptor.value : undefined\n\t\t\t\t\tif (!Number.isSafeInteger(length) || length < 0 || length > 256) throw new TypeError()\n\t\t\t\t\tconst result: unknown[] = []\n\t\t\t\t\tif (!Reflect.setPrototypeOf(result, null)) throw new TypeError()\n\t\t\t\t\tfor (let index = 0; index < length; index += 1) {\n\t\t\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, String(index))\n\t\t\t\t\t\tif (!descriptor?.enumerable || !('value' in descriptor)) throw new TypeError()\n\t\t\t\t\t\tresult[index] = snapshot(descriptor.value, depth + 1)\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\t\t\t\tif (prototype !== Object.prototype && prototype !== null) throw new TypeError()\n\t\t\t\tconst result: Record<string, unknown> = Object.create(null) as Record<string, unknown>\n\t\t\t\tlet entries = 0\n\t\t\t\tfor (const key in value) {\n\t\t\t\t\tif (entries >= 128) throw new TypeError()\n\t\t\t\t\tif (key.length > remainingStringBytes) throw new TypeError()\n\t\t\t\t\tconst keyBytes = Buffer.byteLength(key, 'utf8')\n\t\t\t\t\tif (keyBytes > remainingStringBytes) throw new TypeError()\n\t\t\t\t\tremainingStringBytes -= keyBytes\n\t\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, key)\n\t\t\t\t\tif (!descriptor?.enumerable || !('value' in descriptor)) throw new TypeError()\n\t\t\t\t\tentries += 1\n\t\t\t\t\tconst captured = snapshot(descriptor.value, depth + 1)\n\t\t\t\t\tif (captured !== undefined) result[key] = captured\n\t\t\t\t}\n\t\t\t\treturn result\n\t\t\t} finally { ancestors.delete(value) }\n\t\t}\n\t\tconst captured = snapshot(record) as PerformanceEventRecord\n\t\tif (!captured || typeof captured !== 'object' || typeof captured.recordedAt !== 'number' ||\n\t\t\ttypeof captured.source !== 'string' || !captured.event || typeof captured.event !== 'object') return null\n\t\tconst serialized = JSON.stringify(captured)\n\t\treturn {serialized, bytes: Buffer.byteLength(serialized, 'utf8')}\n\t} catch {\n\t\treturn null\n\t}\n}\n"]}