@nodetool-ai/game-renderer 0.8.5 → 0.8.7

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 (80) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/build-expo.d.ts +76 -0
  3. package/dist/build-expo.d.ts.map +1 -0
  4. package/dist/build-expo.js +454 -0
  5. package/dist/build-expo.js.map +1 -0
  6. package/dist/build-tauri.d.ts +7 -0
  7. package/dist/build-tauri.d.ts.map +1 -1
  8. package/dist/build-tauri.js +2 -1
  9. package/dist/build-tauri.js.map +1 -1
  10. package/dist/build3d.d.ts.map +1 -1
  11. package/dist/build3d.js +3 -2
  12. package/dist/build3d.js.map +1 -1
  13. package/dist/frame-budget.d.ts +16 -0
  14. package/dist/frame-budget.d.ts.map +1 -1
  15. package/dist/frame-budget.js +19 -1
  16. package/dist/frame-budget.js.map +1 -1
  17. package/dist/game3d-capture-page.js +223 -223
  18. package/dist/game3d-player.js +320 -296
  19. package/dist/particles/curves.d.ts.map +1 -1
  20. package/dist/particles/curves.js +38 -14
  21. package/dist/particles/curves.js.map +1 -1
  22. package/dist/particles/index.d.ts +1 -1
  23. package/dist/particles/index.d.ts.map +1 -1
  24. package/dist/particles/index.js +1 -1
  25. package/dist/particles/index.js.map +1 -1
  26. package/dist/particles/particles3d.d.ts +35 -0
  27. package/dist/particles/particles3d.d.ts.map +1 -1
  28. package/dist/particles/particles3d.js +99 -2
  29. package/dist/particles/particles3d.js.map +1 -1
  30. package/dist/particles/pass3d.d.ts +9 -2
  31. package/dist/particles/pass3d.d.ts.map +1 -1
  32. package/dist/particles/pass3d.js +40 -7
  33. package/dist/particles/pass3d.js.map +1 -1
  34. package/dist/perf.d.ts +102 -0
  35. package/dist/perf.d.ts.map +1 -0
  36. package/dist/perf.js +238 -0
  37. package/dist/perf.js.map +1 -0
  38. package/dist/player-v1.js +456 -412
  39. package/dist/renderer3d/assets/cache.d.ts +3 -1
  40. package/dist/renderer3d/assets/cache.d.ts.map +1 -1
  41. package/dist/renderer3d/assets/cache.js +25 -1
  42. package/dist/renderer3d/assets/cache.js.map +1 -1
  43. package/dist/renderer3d/environment/index.d.ts +4 -0
  44. package/dist/renderer3d/environment/index.d.ts.map +1 -1
  45. package/dist/renderer3d/environment/index.js +30 -2
  46. package/dist/renderer3d/environment/index.js.map +1 -1
  47. package/dist/renderer3d/hud/paint.d.ts.map +1 -1
  48. package/dist/renderer3d/hud/paint.js +15 -0
  49. package/dist/renderer3d/hud/paint.js.map +1 -1
  50. package/dist/renderer3d/index.d.ts +7 -1
  51. package/dist/renderer3d/index.d.ts.map +1 -1
  52. package/dist/renderer3d/index.js +19 -8
  53. package/dist/renderer3d/index.js.map +1 -1
  54. package/dist/renderer3d/scene-sync.d.ts.map +1 -1
  55. package/dist/renderer3d/scene-sync.js +42 -18
  56. package/dist/renderer3d/scene-sync.js.map +1 -1
  57. package/dist/renderer3d/shadows/cascades.d.ts +25 -2
  58. package/dist/renderer3d/shadows/cascades.d.ts.map +1 -1
  59. package/dist/renderer3d/shadows/cascades.js +67 -11
  60. package/dist/renderer3d/shadows/cascades.js.map +1 -1
  61. package/dist/renderer3d/types.d.ts +7 -1
  62. package/dist/renderer3d/types.d.ts.map +1 -1
  63. package/dist/smoke.d.ts.map +1 -1
  64. package/dist/smoke.js +7 -45
  65. package/dist/smoke.js.map +1 -1
  66. package/dist/standalone-player.js +4 -2
  67. package/dist/standalone-player.js.map +1 -1
  68. package/dist/standalone-player3d.d.ts +13 -0
  69. package/dist/standalone-player3d.d.ts.map +1 -1
  70. package/dist/standalone-player3d.js +25 -8
  71. package/dist/standalone-player3d.js.map +1 -1
  72. package/dist/static-server.d.ts +10 -0
  73. package/dist/static-server.d.ts.map +1 -0
  74. package/dist/static-server.js +52 -0
  75. package/dist/static-server.js.map +1 -0
  76. package/dist/ui/controller.d.ts +2 -0
  77. package/dist/ui/controller.d.ts.map +1 -1
  78. package/dist/ui/controller.js +13 -0
  79. package/dist/ui/controller.js.map +1 -1
  80. package/package.json +15 -5
package/dist/perf.d.ts ADDED
@@ -0,0 +1,102 @@
1
+ import { type GameInputFrame3D } from "@nodetool-ai/protocol";
2
+ export interface GamePerfOptions {
3
+ /** Route replayed with `advance` before live play. */
4
+ readonly inputs?: readonly Omit<GameInputFrame3D, "loaded">[];
5
+ /** Measured seconds of live play. */
6
+ readonly seconds?: number;
7
+ /** Seconds of live play before the measurement, so loading and first compiles stay out of it. */
8
+ readonly warmupSeconds?: number;
9
+ /** Uses SwiftShader instead of the machine GPU. Frame times then say nothing about real hardware. */
10
+ readonly software?: boolean;
11
+ /** Keeps Chromium's frame rate limit and vsync. Without it, frame times show the real cost of a frame. */
12
+ readonly vsync?: boolean;
13
+ readonly executablePath?: string;
14
+ readonly signal?: AbortSignal;
15
+ /** Writes a Chrome CPU profile of the measured window to this path and summarizes its hottest functions. */
16
+ readonly profile?: string;
17
+ }
18
+ export interface GamePerfProfileEntry {
19
+ readonly functionName: string;
20
+ readonly url: string;
21
+ readonly line: number;
22
+ readonly selfMs: number;
23
+ /** Share of the sampled time outside idle. */
24
+ readonly percent: number;
25
+ }
26
+ export interface GamePerfProfile {
27
+ readonly path: string;
28
+ /** False when the player kept its minified names, which happens outside a source checkout. */
29
+ readonly readableNames: boolean;
30
+ readonly sampledMs: number;
31
+ readonly top: readonly GamePerfProfileEntry[];
32
+ /** Hot functions that should not run in steady play: program builds, scene walks, serializers and snapshots. */
33
+ readonly suspects: readonly GamePerfProfileEntry[];
34
+ }
35
+ export interface GamePerfPercentiles {
36
+ readonly p50: number;
37
+ readonly p95: number;
38
+ readonly p99: number;
39
+ readonly max: number;
40
+ }
41
+ export type GamePerfMetric = "frameMs" | "drawCalls" | "triangles" | "programGrowth";
42
+ export interface GamePerfOverrun {
43
+ readonly metric: GamePerfMetric;
44
+ readonly value: number;
45
+ readonly budget: number;
46
+ }
47
+ export interface GamePerfReport {
48
+ /** True when the player loaded and played without errors. Budget overruns do not change it. */
49
+ readonly ok: boolean;
50
+ readonly gpu: {
51
+ readonly renderer: string;
52
+ readonly adapterType: "hardware" | "software" | "unknown";
53
+ };
54
+ readonly canvas: {
55
+ readonly width: number;
56
+ readonly height: number;
57
+ };
58
+ readonly vsync: boolean;
59
+ readonly routeTicks: number;
60
+ readonly seconds: number;
61
+ /** Frames the player rendered in the measured window. */
62
+ readonly renderedFrames: number;
63
+ readonly fps: number;
64
+ /** Time between consecutive rendered frames. */
65
+ readonly frameMs: GamePerfPercentiles;
66
+ /** Draw calls and triangles per frame, including shadow and depth passes. */
67
+ readonly drawCalls: GamePerfPercentiles;
68
+ readonly triangles: GamePerfPercentiles;
69
+ /** Shader programs at the start and end of the measured window. Growth means new program variants during play. */
70
+ readonly programs: {
71
+ readonly start: number;
72
+ readonly end: number;
73
+ readonly growth: number;
74
+ };
75
+ readonly ticks: {
76
+ readonly start: number;
77
+ readonly end: number;
78
+ };
79
+ readonly budget: {
80
+ readonly limits: Readonly<Record<GamePerfMetric, number>>;
81
+ readonly overruns: readonly GamePerfOverrun[];
82
+ };
83
+ readonly status: string;
84
+ readonly profile?: GamePerfProfile;
85
+ readonly warnings: readonly string[];
86
+ readonly errors: readonly string[];
87
+ }
88
+ /** Self time per function of a Chrome CPU profile, hottest first. */
89
+ export declare function summarizeGameCpuProfile(value: unknown, limit?: number): {
90
+ sampledMs: number;
91
+ top: GamePerfProfileEntry[];
92
+ suspects: GamePerfProfileEntry[];
93
+ };
94
+ /** Nearest-rank percentiles, the same method `game bench` uses. */
95
+ export declare function gamePerfPercentiles(samples: readonly number[]): GamePerfPercentiles;
96
+ /**
97
+ * Plays an exported 3D player in Chromium and measures frame time during live play. The route runs first
98
+ * with `advance`, then the player resumes and the window after the warmup is measured.
99
+ */
100
+ export declare function measureStandaloneGame3D(directory: string, options?: GamePerfOptions): Promise<GamePerfReport>;
101
+ export declare function formatGamePerfOverrun(overrun: GamePerfOverrun): string;
102
+ //# sourceMappingURL=perf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.d.ts","sourceRoot":"","sources":["../src/perf.ts"],"names":[],"mappings":"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"}
package/dist/perf.js ADDED
@@ -0,0 +1,238 @@
1
+ import { access, readFile, realpath, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { z } from "zod";
5
+ import { gameFrameBudgets } from "@nodetool-ai/protocol";
6
+ import { resolveGameFrameBudgets, resolveGameTimeBudgets } from "./frame-budget.js";
7
+ import { launchStandaloneChromium, serveStandaloneDirectory } from "./static-server.js";
8
+ const perfDocument = z.object({ schemaVersion: z.literal(3), performance: z.object({ budgets: gameFrameBudgets.optional() }).optional() });
9
+ const frameStats = z.object({ frameNumber: z.number(), tick: z.number(), drawCalls: z.number(), triangles: z.number(), programs: z.number() });
10
+ const measured = z.object({
11
+ intervals: z.array(z.number()), drawCalls: z.array(z.number()), triangles: z.array(z.number()),
12
+ start: frameStats, end: frameStats, elapsedMs: z.number()
13
+ });
14
+ const ROUTE_CHUNK = 10_000;
15
+ /** Function names that should not be hot in steady play. Each one marks repeated per-frame or per-tick work. */
16
+ const SUSPECT_FUNCTIONS = /^(getProgram|getParameters|getProgramCacheKey|compile|traverse|takeSnapshot|restoreSnapshot|stringify|parse|structuredClone|computeBoundingBox|setupMaterials|updateMatrixWorld)$/;
17
+ const cpuProfile = z.object({
18
+ nodes: z.array(z.object({ id: z.number(), callFrame: z.object({ functionName: z.string(), url: z.string(), lineNumber: z.number() }) })),
19
+ samples: z.array(z.number()).default([]),
20
+ timeDeltas: z.array(z.number()).default([])
21
+ });
22
+ /** Self time per function of a Chrome CPU profile, hottest first. */
23
+ export function summarizeGameCpuProfile(value, limit = 20) {
24
+ const profile = cpuProfile.parse(value);
25
+ const frames = new Map(profile.nodes.map((node) => [node.id, node.callFrame]));
26
+ const selfMicros = new Map();
27
+ let total = 0;
28
+ for (let index = 0; index < profile.samples.length; index++) {
29
+ const frame = frames.get(profile.samples[index]);
30
+ const micros = profile.timeDeltas[index + 1] ?? 0;
31
+ if (!frame || frame.functionName === "(idle)") {
32
+ continue;
33
+ }
34
+ total += micros;
35
+ const key = `${frame.functionName}\u0000${frame.url}\u0000${frame.lineNumber}`;
36
+ const entry = selfMicros.get(key) ?? { functionName: frame.functionName || "(anonymous)", url: frame.url, line: frame.lineNumber + 1, micros: 0 };
37
+ entry.micros += micros;
38
+ selfMicros.set(key, entry);
39
+ }
40
+ const entries = [...selfMicros.values()].sort((left, right) => right.micros - left.micros).map((entry) => ({
41
+ functionName: entry.functionName, url: entry.url.replace(/^https?:\/\/[^/]+\//, ""), line: entry.line,
42
+ selfMs: Math.round(entry.micros) / 1000, percent: total > 0 ? Math.round(entry.micros / total * 1000) / 10 : 0
43
+ }));
44
+ return { sampledMs: Math.round(total) / 1000, top: entries.slice(0, limit), suspects: entries.filter((entry) => SUSPECT_FUNCTIONS.test(entry.functionName) && entry.percent >= 0.5) };
45
+ }
46
+ /**
47
+ * Replaces the exported player with a bundle that keeps function names, so a CPU profile is readable. It needs
48
+ * the package source, which only a checkout has. Returns false when the source is missing.
49
+ */
50
+ async function bundleReadablePlayer(directory) {
51
+ const source = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "standalone-player3d.ts");
52
+ try {
53
+ await access(source);
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ const { build } = await import("esbuild");
59
+ await build({ entryPoints: [source], outfile: join(directory, "game3d-player.js"), bundle: true, platform: "browser", target: "es2022", format: "esm",
60
+ conditions: ["nodetool-dev"], logLevel: "warning", allowOverwrite: true });
61
+ return true;
62
+ }
63
+ /** Nearest-rank percentiles, the same method `game bench` uses. */
64
+ export function gamePerfPercentiles(samples) {
65
+ if (samples.length === 0) {
66
+ return { p50: 0, p95: 0, p99: 0, max: 0 };
67
+ }
68
+ const sorted = [...samples].sort((left, right) => left - right);
69
+ const percentile = (fraction) => sorted[Math.ceil(sorted.length * fraction) - 1];
70
+ return { p50: percentile(0.5), p95: percentile(0.95), p99: percentile(0.99), max: sorted[sorted.length - 1] };
71
+ }
72
+ function chromiumArgs(options) {
73
+ const gpu = options.software
74
+ ? ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"]
75
+ : [...process.platform === "darwin" ? ["--use-angle=metal"] : [], "--enable-gpu", "--ignore-gpu-blocklist"];
76
+ return options.vsync ? gpu : [...gpu, "--disable-gpu-vsync", "--disable-frame-rate-limit"];
77
+ }
78
+ /**
79
+ * Plays an exported 3D player in Chromium and measures frame time during live play. The route runs first
80
+ * with `advance`, then the player resumes and the window after the warmup is measured.
81
+ */
82
+ export async function measureStandaloneGame3D(directory, options = {}) {
83
+ const seconds = options.seconds ?? 10;
84
+ const warmupSeconds = options.warmupSeconds ?? 2;
85
+ const inputs = options.inputs ?? [];
86
+ if (!Number.isFinite(seconds) || seconds < 0.5 || seconds > 600) {
87
+ throw new Error("Perf seconds must be between 0.5 and 600");
88
+ }
89
+ if (!Number.isFinite(warmupSeconds) || warmupSeconds < 0 || warmupSeconds > 120) {
90
+ throw new Error("Perf warmup must be between 0 and 120 seconds");
91
+ }
92
+ options.signal?.throwIfAborted();
93
+ const root = await realpath(directory);
94
+ const document = perfDocument.parse(JSON.parse(await readFile(join(root, "game.json"), "utf8")));
95
+ const budgets = document.performance?.budgets;
96
+ const counts = resolveGameFrameBudgets(budgets);
97
+ const limits = { frameMs: resolveGameTimeBudgets(budgets).frameMs, drawCalls: counts.drawCalls, triangles: counts.triangles, programGrowth: 0 };
98
+ const errors = new Set();
99
+ const warnings = [];
100
+ let browser;
101
+ let server;
102
+ const closeBrowser = () => { void browser?.close(); };
103
+ options.signal?.addEventListener("abort", closeBrowser, { once: true });
104
+ try {
105
+ const readableNames = options.profile ? await bundleReadablePlayer(root) : false;
106
+ if (options.profile && !readableNames) {
107
+ warnings.push("The profile keeps the shipped player's minified function names. Run game perf from a source checkout for readable names.");
108
+ }
109
+ server = await serveStandaloneDirectory(root);
110
+ const origin = server.origin;
111
+ const launchOptions = { headless: true, args: chromiumArgs(options) };
112
+ if (options.executablePath) {
113
+ launchOptions.executablePath = options.executablePath;
114
+ }
115
+ browser = await launchStandaloneChromium(launchOptions);
116
+ options.signal?.throwIfAborted();
117
+ const context = await browser.newContext({ serviceWorkers: "block", viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
118
+ await context.route("**/*", (route) => {
119
+ if (new URL(route.request().url()).origin === origin) {
120
+ return route.continue();
121
+ }
122
+ errors.add(`Outside network request: ${route.request().url()}`);
123
+ return route.abort("blockedbyclient");
124
+ });
125
+ const page = await context.newPage();
126
+ page.setDefaultTimeout(120_000);
127
+ page.on("pageerror", (error) => errors.add(error.message));
128
+ page.on("console", (message) => { if (message.type() === "error") {
129
+ errors.add(message.text());
130
+ } });
131
+ await page.goto(origin);
132
+ await page.waitForFunction(() => Boolean(window.nativeGame3DPlayer) || /failed|error|missing|could not/i.test(window.document.getElementById("status")?.textContent ?? ""));
133
+ const loadStatus = await page.locator("#status").textContent() ?? "";
134
+ if (!(await page.evaluate(() => Boolean(window.nativeGame3DPlayer)))) {
135
+ throw new Error(`Player did not start: ${loadStatus}`);
136
+ }
137
+ const gpu = await page.evaluate(() => {
138
+ const context = window.document.createElement("canvas").getContext("webgl2");
139
+ const debug = context?.getExtension("WEBGL_debug_renderer_info");
140
+ const renderer = context && debug ? context.getParameter(debug.UNMASKED_RENDERER_WEBGL) : "unknown";
141
+ return typeof renderer === "string" ? renderer : "unknown";
142
+ });
143
+ const adapterType = /swiftshader|llvmpipe|lavapipe|software/i.test(gpu) ? "software" : gpu === "unknown" ? "unknown" : "hardware";
144
+ if (!options.software && adapterType !== "hardware") {
145
+ warnings.push(`Chromium used a ${adapterType} renderer (${gpu}). Frame times do not reflect the machine GPU.`);
146
+ }
147
+ for (let index = 0; index < inputs.length; index += ROUTE_CHUNK) {
148
+ options.signal?.throwIfAborted();
149
+ await page.evaluate(async (chunk) => { await window.nativeGame3DPlayer.advance(chunk); }, inputs.slice(index, index + ROUTE_CHUNK));
150
+ }
151
+ await page.evaluate(async (warmupMs) => {
152
+ window.nativeGame3DPlayer.resume();
153
+ await new Promise((resolve) => { setTimeout(resolve, warmupMs); });
154
+ }, warmupSeconds * 1000);
155
+ const cdp = options.profile ? await context.newCDPSession(page) : undefined;
156
+ if (cdp) {
157
+ await cdp.send("Profiler.enable");
158
+ await cdp.send("Profiler.setSamplingInterval", { interval: 200 });
159
+ await cdp.send("Profiler.start");
160
+ }
161
+ const raw = await page.evaluate(async (measureMs) => {
162
+ const player = window.nativeGame3DPlayer;
163
+ const start = player.stats();
164
+ const intervals = [];
165
+ const drawCalls = [];
166
+ const triangles = [];
167
+ let lastFrame = start.frameNumber;
168
+ const began = performance.now();
169
+ let lastRendered = began;
170
+ let now = began;
171
+ await new Promise((resolve) => {
172
+ const sample = (time) => {
173
+ now = time;
174
+ const stats = player.stats();
175
+ if (stats.frameNumber !== lastFrame) {
176
+ intervals.push(time - lastRendered);
177
+ drawCalls.push(stats.drawCalls);
178
+ triangles.push(stats.triangles);
179
+ lastFrame = stats.frameNumber;
180
+ lastRendered = time;
181
+ }
182
+ if (time - began < measureMs) {
183
+ requestAnimationFrame(sample);
184
+ }
185
+ else {
186
+ resolve();
187
+ }
188
+ };
189
+ requestAnimationFrame(sample);
190
+ });
191
+ return { intervals, drawCalls, triangles, start, end: player.stats(), elapsedMs: now - began };
192
+ }, seconds * 1000);
193
+ let profile;
194
+ if (cdp && options.profile) {
195
+ const { profile: recorded } = await cdp.send("Profiler.stop");
196
+ await writeFile(options.profile, JSON.stringify(recorded));
197
+ profile = { path: options.profile, readableNames, ...summarizeGameCpuProfile(recorded) };
198
+ }
199
+ const result = measured.parse(raw);
200
+ const canvas = await page.evaluate(() => {
201
+ const element = window.document.getElementById("game");
202
+ return element instanceof HTMLCanvasElement ? { width: element.width, height: element.height } : { width: 0, height: 0 };
203
+ });
204
+ const status = await page.locator("#status").textContent() ?? "";
205
+ if (/failed|error|missing|unavailable|could not|lost/i.test(status)) {
206
+ errors.add(`Player status: ${status}`);
207
+ }
208
+ // The first interval starts at the first rAF, not at a rendered frame, so it is left out.
209
+ const frameMs = gamePerfPercentiles(result.intervals.slice(1));
210
+ const drawCalls = gamePerfPercentiles(result.drawCalls);
211
+ const triangles = gamePerfPercentiles(result.triangles);
212
+ const renderedFrames = result.end.frameNumber - result.start.frameNumber;
213
+ if (renderedFrames < 2) {
214
+ errors.add(`Player rendered ${renderedFrames} frames in ${seconds} s of live play`);
215
+ }
216
+ const growth = result.end.programs - result.start.programs;
217
+ const sample = { frameMs: frameMs.p95, drawCalls: drawCalls.p95, triangles: triangles.p95, programGrowth: growth };
218
+ const overruns = Object.keys(limits).flatMap((metric) => sample[metric] > limits[metric] ? [{ metric, value: sample[metric], budget: limits[metric] }] : []);
219
+ return { ok: errors.size === 0, gpu: { renderer: gpu, adapterType }, canvas, vsync: options.vsync === true, routeTicks: inputs.length, seconds,
220
+ renderedFrames, fps: result.elapsedMs > 0 ? renderedFrames * 1000 / result.elapsedMs : 0, frameMs, drawCalls, triangles,
221
+ programs: { start: result.start.programs, end: result.end.programs, growth }, ticks: { start: result.start.tick, end: result.end.tick },
222
+ budget: { limits, overruns }, status, profile, warnings, errors: [...errors] };
223
+ }
224
+ finally {
225
+ options.signal?.removeEventListener("abort", closeBrowser);
226
+ await browser?.close();
227
+ await server?.close();
228
+ }
229
+ }
230
+ export function formatGamePerfOverrun(overrun) {
231
+ switch (overrun.metric) {
232
+ case "frameMs": return `Perf budget exceeded: frame p95 ${overrun.value.toFixed(2)} ms (budget ${overrun.budget} ms)`;
233
+ case "programGrowth": return `Perf budget exceeded: ${overrun.value} new shader programs during steady play (budget 0)`;
234
+ case "drawCalls": return `Perf budget exceeded: draw call p95 ${overrun.value} (budget ${overrun.budget})`;
235
+ case "triangles": return `Perf budget exceeded: triangle p95 ${overrun.value} (budget ${overrun.budget})`;
236
+ }
237
+ }
238
+ //# sourceMappingURL=perf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perf.js","sourceRoot":"","sources":["../src/perf.ts"],"names":[],"mappings":"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"}