@ogygia/devtools 0.11.4 → 0.13.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.
- package/README.md +1 -1
- package/dist/app.d.ts +3 -0
- package/dist/app.js +9 -0
- package/dist/devtools/BoundaryOverlay.svelte +10 -6
- package/dist/devtools/Devtools.svelte +24 -18
- package/dist/devtools/IslandDetail.svelte +7 -5
- package/dist/devtools/PageTab.svelte +4 -2
- package/dist/devtools/ProfilerTab.svelte +8 -2
- package/dist/devtools/SessionTab.svelte +21 -8
- package/dist/devtools/highlight.js +14 -7
- package/dist/devtools/ledger-dom.js +6 -9
- package/dist/devtools/names.js +18 -17
- package/dist/devtools/page.d.ts +2 -0
- package/dist/devtools/page.js +37 -17
- package/dist/devtools/profile-store.d.ts +4 -9
- package/dist/devtools/profile-store.js +26 -21
- package/dist/devtools/session.d.ts +2 -0
- package/dist/devtools/session.js +48 -42
- package/dist/devtools/testing-engine.js +10 -1
- package/dist/devtools/ui-app.d.ts +3 -1
- package/dist/devtools/ui-app.js +9 -3
- package/dist/devtools/ui.js +83 -40
- package/dist/messages/build.d.ts +21 -0
- package/dist/messages/build.js +56 -0
- package/dist/messages/index.d.ts +3 -0
- package/dist/messages/index.js +3 -0
- package/dist/messages/runtime.d.ts +63 -0
- package/dist/messages/runtime.js +122 -0
- package/dist/profiler/async-io.d.ts +12 -7
- package/dist/profiler/async-io.js +94 -82
- package/dist/profiler/beacon-standalone.js +15 -6
- package/dist/profiler/busy.d.ts +13 -0
- package/dist/profiler/busy.js +45 -0
- package/dist/profiler/client.js +5 -24
- package/dist/profiler/embedded-maps.js +9 -5
- package/dist/profiler/gc.d.ts +5 -0
- package/dist/profiler/gc.js +75 -11
- package/dist/profiler/index.d.ts +23 -2
- package/dist/profiler/index.js +648 -373
- package/dist/profiler/net.d.ts +14 -6
- package/dist/profiler/net.js +61 -45
- package/dist/profiler/patterns.js +29 -1
- package/dist/profiler/source-scan.js +24 -12
- package/dist/profiler/standalone.js +12 -4
- package/dist/profiler/storage/index.d.ts +14 -3
- package/dist/profiler/storage/index.js +41 -41
- package/dist/profiler/storage/postgres.d.ts +18 -5
- package/dist/profiler/storage/postgres.js +124 -85
- package/dist/profiler/storage/redis.d.ts +18 -5
- package/dist/profiler/storage/redis.js +160 -131
- package/dist/profiler/storage/sqlite.d.ts +15 -2
- package/dist/profiler/storage/sqlite.js +54 -48
- package/dist/profiler/ui/ComponentsTable.svelte +2 -2
- package/dist/profiler/ui/Dashboard.svelte +2 -3
- package/dist/profiler/ui/DownloadHtml.svelte +1 -0
- package/dist/profiler/ui/ExportButton.svelte +1 -0
- package/dist/profiler/ui/Flame.svelte +10 -4
- package/dist/profiler/ui/Flame.svelte.d.ts +0 -5
- package/dist/profiler/ui/FrameDetails.svelte +13 -2
- package/dist/profiler/ui/FrameDetails.svelte.d.ts +0 -6
- package/dist/profiler/ui/FunctionsTable.svelte +2 -2
- package/dist/profiler/ui/IslandsTable.svelte +2 -2
- package/dist/profiler/ui/OnThisPage.svelte +12 -4
- package/dist/profiler/ui/RunView.svelte +31 -27
- package/dist/profiler/ui/ShareLink.svelte +6 -1
- package/dist/profiler/ui/Sidebar.svelte +14 -9
- package/dist/profiler/ui/Treemap.svelte +11 -4
- package/dist/profiler/ui/Treemap.svelte.d.ts +0 -6
- package/dist/profiler/ui/WaitingTable.svelte +2 -2
- package/dist/profiler/ui/fake-progress.svelte.d.ts +7 -12
- package/dist/profiler/ui/fake-progress.svelte.js +57 -36
- package/dist/profiler/ui/press.js +4 -2
- package/dist/profiler/ui/row-anchor.svelte.d.ts +0 -6
- package/dist/profiler/ui/row-anchor.svelte.js +13 -8
- package/dist/profiler/ui/sort.svelte.d.ts +9 -6
- package/dist/profiler/ui/sort.svelte.js +34 -28
- package/dist/profiler/visit.d.ts +0 -1
- package/dist/profiler/visit.js +1 -2
- package/dist/server.js +27 -10
- package/dist/vite.js +3 -2
- package/package.json +18 -9
|
@@ -13,6 +13,8 @@ export declare function session_waiting(): boolean;
|
|
|
13
13
|
/** `button#buy.primary` — a short readable name for an element. */
|
|
14
14
|
export declare function describe_el(el: Element | null | undefined): string;
|
|
15
15
|
interface Recorder {
|
|
16
|
+
/** Ends this recording, once: a second call gets the first call's data, and touches nothing of
|
|
17
|
+
* a recording started after it. */
|
|
16
18
|
stop(): Promise<SessionData$1>;
|
|
17
19
|
started: number;
|
|
18
20
|
}
|
package/dist/devtools/session.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { all_regions, region_name } from "./regions.js";
|
|
2
|
+
import { STALE, flights, machine } from "@ogygia/machine";
|
|
2
3
|
import { add_sink, snapshot } from "@ogygia/core/events";
|
|
3
4
|
// src/devtools/session.ts
|
|
4
5
|
/**
|
|
@@ -126,6 +127,17 @@ function describe_el(el) {
|
|
|
126
127
|
const text = (el.textContent ?? "").trim().slice(0, 24);
|
|
127
128
|
return text ? `${s} "${text}${(el.textContent ?? "").trim().length > 24 ? "…" : ""}"` : s;
|
|
128
129
|
}
|
|
130
|
+
/** One recording's run. Leaving `recording`, whichever way, takes its watchers off, once. */
|
|
131
|
+
const run_definition = machine()({
|
|
132
|
+
name: "session-recorder",
|
|
133
|
+
initial: "recording",
|
|
134
|
+
steps: {
|
|
135
|
+
recording: ["saved", "stopped"],
|
|
136
|
+
saved: [],
|
|
137
|
+
stopped: []
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
/** The recording now: the newest started and not yet stopped. */
|
|
129
141
|
let active = null;
|
|
130
142
|
/** the last finished session (its data + report), for every tab: the Record tab writes it, the
|
|
131
143
|
* island detail reads its island's part (the report type lives in session-insights.ts) */
|
|
@@ -147,6 +159,8 @@ function recording() {
|
|
|
147
159
|
/** Start recording (or go on with the parts earlier pages saved — `resume_session_if_any`). */
|
|
148
160
|
function start_session(prior) {
|
|
149
161
|
if (active) return active;
|
|
162
|
+
const run = run_definition.create();
|
|
163
|
+
const s = run.scope;
|
|
150
164
|
const from = performance.now();
|
|
151
165
|
const seq0 = snapshot().at(-1)?.seq ?? -1;
|
|
152
166
|
const interactions = [];
|
|
@@ -160,20 +174,21 @@ function start_session(prior) {
|
|
|
160
174
|
for (const r of all_regions()) if (r.fp && !names[r.fp]) names[r.fp] = region_name(r.entry);
|
|
161
175
|
};
|
|
162
176
|
note_names();
|
|
163
|
-
|
|
177
|
+
s.defer(add_sink((e) => {
|
|
164
178
|
if (e.name === "nav.start") note_names();
|
|
165
|
-
});
|
|
166
|
-
const observers = [];
|
|
179
|
+
}));
|
|
167
180
|
const watch = (type, cb, extra = {}) => {
|
|
168
181
|
try {
|
|
169
|
-
const po = new PerformanceObserver((l) => cb(l.getEntries()));
|
|
182
|
+
const po = s.watch(new PerformanceObserver((l) => cb(l.getEntries())));
|
|
170
183
|
po.observe({
|
|
171
184
|
type,
|
|
172
185
|
...extra
|
|
173
186
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
187
|
+
s.defer(() => {
|
|
188
|
+
try {
|
|
189
|
+
const left = po.takeRecords();
|
|
190
|
+
if (left.length) cb(left);
|
|
191
|
+
} catch {}
|
|
177
192
|
});
|
|
178
193
|
} catch {}
|
|
179
194
|
};
|
|
@@ -234,7 +249,7 @@ function start_session(prior) {
|
|
|
234
249
|
}
|
|
235
250
|
});
|
|
236
251
|
const mut_log = [];
|
|
237
|
-
const mo = new MutationObserver((records) => {
|
|
252
|
+
const mo = s.watch(new MutationObserver((records) => {
|
|
238
253
|
const now = r1(performance.now());
|
|
239
254
|
const seen = /* @__PURE__ */ new Set();
|
|
240
255
|
for (const m of records) {
|
|
@@ -249,7 +264,7 @@ function start_session(prior) {
|
|
|
249
264
|
});
|
|
250
265
|
}
|
|
251
266
|
}
|
|
252
|
-
});
|
|
267
|
+
}));
|
|
253
268
|
const clicks = [];
|
|
254
269
|
const on_click = (e) => {
|
|
255
270
|
if (clicks.length >= 2e3) return;
|
|
@@ -263,7 +278,7 @@ function start_session(prior) {
|
|
|
263
278
|
...el && looks_clickable(el) ? { clickable: true } : {}
|
|
264
279
|
});
|
|
265
280
|
};
|
|
266
|
-
|
|
281
|
+
s.on(globalThis, "click", on_click, {
|
|
267
282
|
capture: true,
|
|
268
283
|
passive: true
|
|
269
284
|
});
|
|
@@ -278,13 +293,12 @@ function start_session(prior) {
|
|
|
278
293
|
until: performance.now() + 50
|
|
279
294
|
};
|
|
280
295
|
};
|
|
281
|
-
const
|
|
296
|
+
for (const type of [
|
|
282
297
|
"click",
|
|
283
298
|
"submit",
|
|
284
299
|
"keydown",
|
|
285
300
|
"change"
|
|
286
|
-
]
|
|
287
|
-
for (const type of INPUTS) addEventListener(type, on_input, {
|
|
301
|
+
]) s.on(globalThis, type, on_input, {
|
|
288
302
|
capture: true,
|
|
289
303
|
passive: true
|
|
290
304
|
});
|
|
@@ -303,7 +317,7 @@ function start_session(prior) {
|
|
|
303
317
|
fp: by ? island_of(by)?.getAttribute("data-og-fp") ?? null : input?.fp ?? null
|
|
304
318
|
});
|
|
305
319
|
};
|
|
306
|
-
navigation
|
|
320
|
+
if (navigation) s.on(navigation, "navigate", on_navigate);
|
|
307
321
|
const requests = [];
|
|
308
322
|
watch("resource", (es) => {
|
|
309
323
|
for (const r of es) {
|
|
@@ -340,9 +354,10 @@ function start_session(prior) {
|
|
|
340
354
|
t: r1(performance.now()),
|
|
341
355
|
message: "unhandled rejection: " + String(e.reason?.message ?? e.reason).slice(0, 280)
|
|
342
356
|
});
|
|
343
|
-
|
|
344
|
-
|
|
357
|
+
s.on(globalThis, "error", on_error);
|
|
358
|
+
s.on(globalThis, "unhandledrejection", on_rej);
|
|
345
359
|
const console_error = console.error;
|
|
360
|
+
s.defer(() => console.error = console_error);
|
|
346
361
|
console.error = (...args) => {
|
|
347
362
|
if (errors.length < 200) errors.push({
|
|
348
363
|
t: r1(performance.now()),
|
|
@@ -363,23 +378,6 @@ function start_session(prior) {
|
|
|
363
378
|
cpu_off = "no-policy";
|
|
364
379
|
}
|
|
365
380
|
const heap0 = performance.memory?.usedJSHeapSize ?? null;
|
|
366
|
-
/** stop watching (the entries still queued count too) */
|
|
367
|
-
const detach = () => {
|
|
368
|
-
for (const { po, cb } of observers) try {
|
|
369
|
-
const left = po.takeRecords();
|
|
370
|
-
if (left.length) cb(left);
|
|
371
|
-
po.disconnect();
|
|
372
|
-
} catch {}
|
|
373
|
-
mo.disconnect();
|
|
374
|
-
off_nav();
|
|
375
|
-
removeEventListener("error", on_error);
|
|
376
|
-
removeEventListener("unhandledrejection", on_rej);
|
|
377
|
-
removeEventListener("click", on_click, { capture: true });
|
|
378
|
-
for (const type of INPUTS) removeEventListener(type, on_input, { capture: true });
|
|
379
|
-
navigation?.removeEventListener("navigate", on_navigate);
|
|
380
|
-
removeEventListener("pagehide", on_hide);
|
|
381
|
-
console.error = console_error;
|
|
382
|
-
};
|
|
383
381
|
/** this document's part of the session */
|
|
384
382
|
const collect = (cpu) => {
|
|
385
383
|
const heap1 = performance.memory?.usedJSHeapSize ?? null;
|
|
@@ -416,9 +414,7 @@ function start_session(prior) {
|
|
|
416
414
|
};
|
|
417
415
|
};
|
|
418
416
|
const on_hide = (e) => {
|
|
419
|
-
if (e.persisted ||
|
|
420
|
-
detach();
|
|
421
|
-
active = null;
|
|
417
|
+
if (e.persisted || !run.move("recording", "saved")) return;
|
|
422
418
|
const strip = (s) => JSON.parse(JSON.stringify({
|
|
423
419
|
...s,
|
|
424
420
|
cpu: null
|
|
@@ -430,12 +426,14 @@ function start_session(prior) {
|
|
|
430
426
|
}]));
|
|
431
427
|
} catch {}
|
|
432
428
|
};
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
429
|
+
s.on(globalThis, "pagehide", on_hide);
|
|
430
|
+
s.defer(() => {
|
|
431
|
+
if (active === rec) active = null;
|
|
432
|
+
});
|
|
433
|
+
const stopping = flights({
|
|
434
|
+
name: "session stop",
|
|
435
|
+
run: async () => {
|
|
436
|
+
run.move("recording", "stopped");
|
|
439
437
|
let cpu = null;
|
|
440
438
|
if (prof) try {
|
|
441
439
|
cpu = await prof.stop();
|
|
@@ -448,6 +446,14 @@ function start_session(prior) {
|
|
|
448
446
|
} catch {}
|
|
449
447
|
return prior?.length ? merge_parts(prior, here) : here;
|
|
450
448
|
}
|
|
449
|
+
});
|
|
450
|
+
const stop = async () => {
|
|
451
|
+
const data = await stopping.get();
|
|
452
|
+
return data === STALE ? stop() : data;
|
|
453
|
+
};
|
|
454
|
+
const rec = {
|
|
455
|
+
started: prior?.length ? prior[0].data.from + (prior[0].origin - performance.timeOrigin) : from,
|
|
456
|
+
stop
|
|
451
457
|
};
|
|
452
458
|
active = rec;
|
|
453
459
|
return rec;
|
|
@@ -5,6 +5,7 @@ import { region_name_by_fp } from "./regions.js";
|
|
|
5
5
|
import { ensure_region_names } from "./names.js";
|
|
6
6
|
import { TESTING_ENGINE_VERSION, analyze_session, apply_attr_rules, session_timeline, testing_lean } from "@ogygia/core/insights";
|
|
7
7
|
import { charge, nearest_attr, own_cost } from "@ogygia/core/internal/instruments";
|
|
8
|
+
import { scope } from "@ogygia/machine";
|
|
8
9
|
import { snapshot } from "@ogygia/core/events";
|
|
9
10
|
// src/devtools/testing-engine.ts
|
|
10
11
|
/**
|
|
@@ -202,6 +203,14 @@ async function record_stop(blocked = []) {
|
|
|
202
203
|
/** Resolves when no island is mid-hydration and nothing woke for `quiet` ms (or `timeout` passed). */
|
|
203
204
|
async function settled(quiet = 300, timeout = 1e4) {
|
|
204
205
|
const until = performance.now() + timeout;
|
|
206
|
+
const steps = scope();
|
|
207
|
+
try {
|
|
208
|
+
return await settle_poll(steps, quiet, until);
|
|
209
|
+
} finally {
|
|
210
|
+
steps.close();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function settle_poll(steps, quiet, until) {
|
|
205
214
|
for (;;) {
|
|
206
215
|
const ev = snapshot();
|
|
207
216
|
const started = /* @__PURE__ */ new Set();
|
|
@@ -214,7 +223,7 @@ async function settled(quiet = 300, timeout = 1e4) {
|
|
|
214
223
|
}
|
|
215
224
|
if (!started.size && performance.now() - last >= quiet && document.readyState === "complete") return true;
|
|
216
225
|
if (performance.now() > until) return false;
|
|
217
|
-
await new Promise((r) =>
|
|
226
|
+
await steps.task(() => new Promise((r) => steps.timeout(r, 50)));
|
|
218
227
|
}
|
|
219
228
|
}
|
|
220
229
|
export { hydration_json, note_engine_files, page_json, record_start, record_stop, settled };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/** Mounts the dock into `root`. Returns its teardown: the app (its Alt+O listener and timers with
|
|
2
|
+
* it) unmounted and its observers off. */
|
|
1
3
|
export declare function mount_app(root: ShadowRoot, opts: {
|
|
2
4
|
csr_true: boolean;
|
|
3
5
|
start_open: boolean;
|
|
4
6
|
hide?: () => void;
|
|
5
|
-
}): void;
|
|
7
|
+
}): () => void;
|
package/dist/devtools/ui-app.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { install_page_hook } from "./page.js";
|
|
2
2
|
import { set_highlight_root } from "./highlight.js";
|
|
3
|
+
import { scope } from "@ogygia/machine";
|
|
3
4
|
import { names } from "virtual:ogygia/devtools-meta";
|
|
4
|
-
import { mount } from "svelte";
|
|
5
|
+
import { mount, unmount } from "svelte";
|
|
5
6
|
import Devtools from "./Devtools.svelte";
|
|
6
7
|
// src/devtools/ui-app.ts
|
|
7
8
|
/**
|
|
@@ -10,6 +11,8 @@ import Devtools from "./Devtools.svelte";
|
|
|
10
11
|
* only the launcher in the runtime chunk: this file, the components and their CSS load when someone
|
|
11
12
|
* opens the dock (a dynamic import the bundler splits off). On the dev server it loads at once.
|
|
12
13
|
*/
|
|
14
|
+
/** Mounts the dock into `root`. Returns its teardown: the app (its Alt+O listener and timers with
|
|
15
|
+
* it) unmounted and its observers off. */
|
|
13
16
|
function mount_app(root, opts) {
|
|
14
17
|
install_page_hook();
|
|
15
18
|
set_highlight_root(root);
|
|
@@ -17,7 +20,7 @@ function mount_app(root, opts) {
|
|
|
17
20
|
...names,
|
|
18
21
|
...window.__ogygia_region_names
|
|
19
22
|
};
|
|
20
|
-
mount(Devtools, {
|
|
23
|
+
const app = mount(Devtools, {
|
|
21
24
|
target: root,
|
|
22
25
|
props: {
|
|
23
26
|
csrTrue: opts.csr_true,
|
|
@@ -26,12 +29,15 @@ function mount_app(root, opts) {
|
|
|
26
29
|
}
|
|
27
30
|
});
|
|
28
31
|
adopt_scoped_styles(root);
|
|
29
|
-
const
|
|
32
|
+
const s = scope();
|
|
33
|
+
s.defer(() => void unmount(app));
|
|
34
|
+
const mo = s.watch(new MutationObserver(() => adopt_scoped_styles(root)));
|
|
30
35
|
mo.observe(document.head, { childList: true });
|
|
31
36
|
mo.observe(root, {
|
|
32
37
|
childList: true,
|
|
33
38
|
subtree: true
|
|
34
39
|
});
|
|
40
|
+
return () => s.close();
|
|
35
41
|
}
|
|
36
42
|
/** the dock's `<style>`s a page's Content-Security-Policy refused, already re-made as sheets */
|
|
37
43
|
const remade = /* @__PURE__ */ new WeakSet();
|
package/dist/devtools/ui.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { OgygiaError } from "../messages/runtime.js";
|
|
1
2
|
import { TESTING_ENGINE_VERSION, devtools_switched_on, switch_in_url, testing_lean, without_switch, write_switch } from "@ogygia/core/insights";
|
|
3
|
+
import { import_again } from "@ogygia/core/internal/instruments";
|
|
4
|
+
import { STALE, flights, machine } from "@ogygia/machine";
|
|
2
5
|
// src/devtools/ui.ts
|
|
3
6
|
/**
|
|
4
7
|
* The devtools LAUNCHER — the only part of devtools the runtime chunk carries. Called from the runtime
|
|
@@ -17,13 +20,30 @@ import { TESTING_ENGINE_VERSION, devtools_switched_on, switch_in_url, testing_le
|
|
|
17
20
|
* The switch's cookie also starts the rest, for that browser only: the server writes its event
|
|
18
21
|
* side-channel and the page is measured from the start (runtime/beacon.ts).
|
|
19
22
|
*/
|
|
23
|
+
/** The dock's code: after a failed download, asked for again under a fresh URL. */
|
|
24
|
+
const import_app = import_again(() => import("./ui-app.js"));
|
|
20
25
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
21
26
|
const LAZY = typeof __OGYGIA_DEVTOOLS_LAZY__ !== "undefined" ? __OGYGIA_DEVTOOLS_LAZY__ : false;
|
|
22
27
|
const LAYOUT_KEY = "ogygia:devtools:layout:v4";
|
|
23
|
-
|
|
28
|
+
const dock_definition = machine()({
|
|
29
|
+
name: "devtools-dock",
|
|
30
|
+
initial: "button",
|
|
31
|
+
steps: {
|
|
32
|
+
button: ["loading", "off"],
|
|
33
|
+
loading: [
|
|
34
|
+
"button",
|
|
35
|
+
"mounted",
|
|
36
|
+
"off"
|
|
37
|
+
],
|
|
38
|
+
mounted: ["off"],
|
|
39
|
+
off: []
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
/** This module's dock, until devtools is turned off. */
|
|
43
|
+
let dock = null;
|
|
24
44
|
let launch_opts;
|
|
25
45
|
function install_devtools_ui(opts) {
|
|
26
|
-
if (!DEVTOOLS || typeof document === "undefined" ||
|
|
46
|
+
if (!DEVTOOLS || typeof document === "undefined" || dock) return;
|
|
27
47
|
if (document.querySelector("meta[name=\"ogygia-devtools\"][content=\"off\"]")) return;
|
|
28
48
|
launch_opts = opts;
|
|
29
49
|
install_testing_api();
|
|
@@ -37,10 +57,9 @@ function install_devtools_ui(opts) {
|
|
|
37
57
|
}
|
|
38
58
|
/** The launcher (and, on the dev server or for a dock left open, the dock). */
|
|
39
59
|
function mount_launcher(opts) {
|
|
40
|
-
if (
|
|
41
|
-
mounted = true;
|
|
60
|
+
if (dock || document.querySelector("[data-ogygia-devtools-host]")) return;
|
|
42
61
|
try {
|
|
43
|
-
if (sessionStorage.getItem("ogygia:devtools:session")) import("./session.js").then((m) => m.resume_session_if_any());
|
|
62
|
+
if (sessionStorage.getItem("ogygia:devtools:session")) import("./session.js").then((m) => m.resume_session_if_any(), (error) => console.error(new OgygiaError("devtools_dock_load_failed"), error));
|
|
44
63
|
} catch {}
|
|
45
64
|
const host = document.createElement("div");
|
|
46
65
|
host.setAttribute("data-ogygia-devtools-host", "");
|
|
@@ -48,66 +67,85 @@ function mount_launcher(opts) {
|
|
|
48
67
|
document.documentElement.appendChild(host);
|
|
49
68
|
const root = host.attachShadow({ mode: "open" });
|
|
50
69
|
const csr_true = opts?.csr_true ?? false;
|
|
51
|
-
|
|
70
|
+
const here = {
|
|
71
|
+
machine: dock_definition.create(),
|
|
72
|
+
host,
|
|
73
|
+
launcher: new AbortController()
|
|
74
|
+
};
|
|
75
|
+
dock = here;
|
|
76
|
+
const state = here.machine;
|
|
52
77
|
let want_open = false;
|
|
53
|
-
|
|
54
|
-
let on_key = null;
|
|
78
|
+
const button = document.createElement("button");
|
|
55
79
|
const load = () => {
|
|
56
|
-
if (loading) return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
if (!state.move("button", "loading")) return;
|
|
81
|
+
state.scope.task(() => import_app()).then((app) => {
|
|
82
|
+
if (app === STALE || !state.move("loading", "mounted")) return;
|
|
83
|
+
here.launcher.abort();
|
|
84
|
+
button.remove();
|
|
85
|
+
state.scope.defer(app.mount_app(root, {
|
|
62
86
|
csr_true,
|
|
63
87
|
start_open: want_open,
|
|
64
88
|
...LAZY ? { hide: devtools_off } : {}
|
|
65
|
-
});
|
|
89
|
+
}));
|
|
90
|
+
}, (error) => {
|
|
91
|
+
if (!state.move("loading", "button")) return;
|
|
92
|
+
want_open = false;
|
|
93
|
+
button.textContent = "og devtools";
|
|
94
|
+
console.error(new OgygiaError("devtools_dock_load_failed"), error);
|
|
66
95
|
});
|
|
67
96
|
};
|
|
68
|
-
button = document.createElement("button");
|
|
69
97
|
button.setAttribute("data-og-panel-toggle", "");
|
|
70
98
|
button.title = "ogygia devtools (Alt+O)";
|
|
71
99
|
button.textContent = "og devtools";
|
|
72
100
|
button.style.cssText = "position:fixed;right:24px;bottom:8px;padding:7px 12px;border-radius:999px;border:1px solid rgba(148,163,184,.35);background:#0b1220;color:#e2e8f0;font:600 12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;cursor:pointer;opacity:.55;box-shadow:0 4px 16px rgba(0,0,0,.4)";
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
b.onmouseleave = () => b.style.opacity = ".55";
|
|
101
|
+
button.onmouseenter = () => button.style.opacity = "1";
|
|
102
|
+
button.onmouseleave = () => button.style.opacity = ".55";
|
|
76
103
|
const open = () => {
|
|
77
104
|
want_open = true;
|
|
78
|
-
|
|
105
|
+
button.textContent = "loading…";
|
|
79
106
|
load();
|
|
80
107
|
};
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
button.onclick = open;
|
|
109
|
+
addEventListener("keydown", (e) => {
|
|
83
110
|
if (e.altKey && (e.key === "o" || e.key === "O")) {
|
|
84
111
|
e.preventDefault();
|
|
85
112
|
open();
|
|
86
113
|
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
root.appendChild(b);
|
|
114
|
+
}, { signal: here.launcher.signal });
|
|
115
|
+
root.appendChild(button);
|
|
90
116
|
if (was_open()) load();
|
|
91
|
-
else if (!LAZY) after_load_idle(load);
|
|
117
|
+
else if (!LAZY) after_load_idle(state.scope, load);
|
|
92
118
|
}
|
|
93
|
-
|
|
119
|
+
/** Runs `fn` once the page is loaded and the browser idle, unless `s` closes first. */
|
|
120
|
+
function after_load_idle(s, fn) {
|
|
94
121
|
const idle = () => {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
|
|
122
|
+
const host = globalThis;
|
|
123
|
+
if (!host.requestIdleCallback) {
|
|
124
|
+
s.timeout(fn, 1e3);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const id = host.requestIdleCallback(fn, { timeout: 3e3 });
|
|
128
|
+
s.defer(() => host.cancelIdleCallback?.(id));
|
|
98
129
|
};
|
|
99
|
-
if (document.readyState === "complete")
|
|
100
|
-
else
|
|
130
|
+
if (document.readyState === "complete") s.timeout(idle, 500);
|
|
131
|
+
else s.on(globalThis, "load", () => s.timeout(idle, 500), { once: true });
|
|
101
132
|
}
|
|
102
133
|
function install_testing_api() {
|
|
103
134
|
if (window.__ogygia_testing) return;
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
135
|
+
const import_engine = import_again(() => import("./testing-engine.js"));
|
|
136
|
+
const load_engine = flights({
|
|
137
|
+
name: "testing engine",
|
|
138
|
+
run: async () => {
|
|
139
|
+
const from = performance.now();
|
|
140
|
+
const m = await import_engine();
|
|
141
|
+
m.note_engine_files(from, performance.now());
|
|
142
|
+
return m;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const engine = async () => {
|
|
146
|
+
const m = await load_engine.get();
|
|
147
|
+
return m === STALE ? engine() : m;
|
|
148
|
+
};
|
|
111
149
|
window.__ogygia_testing = {
|
|
112
150
|
version: TESTING_ENGINE_VERSION,
|
|
113
151
|
page: async (cpu_fns) => (await engine()).page_json(cpu_fns),
|
|
@@ -142,8 +180,13 @@ function devtools_off() {
|
|
|
142
180
|
try {
|
|
143
181
|
localStorage.removeItem(LAYOUT_KEY);
|
|
144
182
|
} catch {}
|
|
183
|
+
if (dock) {
|
|
184
|
+
dock.machine.go("off");
|
|
185
|
+
dock.launcher.abort();
|
|
186
|
+
dock.host.remove();
|
|
187
|
+
dock = null;
|
|
188
|
+
}
|
|
145
189
|
document.querySelector("[data-ogygia-devtools-host]")?.remove();
|
|
146
|
-
mounted = false;
|
|
147
190
|
}
|
|
148
191
|
function install_console_api() {
|
|
149
192
|
const say = (on) => on ? "ogygia devtools: on in this browser (stays on; ogygia.devtools.off() to hide it)" : "ogygia devtools: off in this browser";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MessagesOf } from "@ogygia/core/internal/messages";
|
|
2
|
+
/** The errors: `throw new OgygiaError(code, args)`. */
|
|
3
|
+
export declare const DEVTOOLS_BUILD_ERRORS: {};
|
|
4
|
+
/** The warnings: `warn(code, args, ...rest)`. */
|
|
5
|
+
export declare const DEVTOOLS_BUILD_WARNINGS: {
|
|
6
|
+
devtools_on_in_build: {
|
|
7
|
+
text: () => string;
|
|
8
|
+
details: () => string;
|
|
9
|
+
};
|
|
10
|
+
devtools_profiler_secret_empty: {
|
|
11
|
+
text: () => string;
|
|
12
|
+
details: () => string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
type Own = MessagesOf<typeof DEVTOOLS_BUILD_ERRORS, typeof DEVTOOLS_BUILD_WARNINGS>;
|
|
16
|
+
/** `throw new OgygiaError(code, args)`; `warn(code, args, ...rest)` logs; `line(code, args)` is the
|
|
17
|
+
* line as a string. */
|
|
18
|
+
export declare const OgygiaError: Own["OgygiaError"];
|
|
19
|
+
export declare const warn: Own["warn"];
|
|
20
|
+
export declare const line: Own["line"];
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { define_messages, prose } from "@ogygia/core/internal/messages";
|
|
2
|
+
// src/messages/build.ts
|
|
3
|
+
/**
|
|
4
|
+
* EVERY MESSAGE THE DEVTOOLS' VITE PLUGIN PRINTS, in one place: its code, its wording, what to do
|
|
5
|
+
* about it (core's `@ogygia/core/internal/messages` says how the system works).
|
|
6
|
+
*
|
|
7
|
+
* warn('devtools_on_in_build');
|
|
8
|
+
*
|
|
9
|
+
* The plugin runs in Node, never in a page, so the wording always prints, then the code's page. The
|
|
10
|
+
* profiler's server half has its own (./runtime.ts).
|
|
11
|
+
*/
|
|
12
|
+
/** The errors: `throw new OgygiaError(code, args)`. */
|
|
13
|
+
const DEVTOOLS_BUILD_ERRORS = {};
|
|
14
|
+
/** The warnings: `warn(code, args, ...rest)`. */
|
|
15
|
+
const DEVTOOLS_BUILD_WARNINGS = {
|
|
16
|
+
devtools_on_in_build: {
|
|
17
|
+
text: () => prose`
|
|
18
|
+
devtools is ON for this build: its pages carry the devtools launcher (the dock loads only
|
|
19
|
+
when opened). Keep it off for production: list \`devtools()\` only when
|
|
20
|
+
\`mode !== 'production'\`.
|
|
21
|
+
`,
|
|
22
|
+
details: () => prose`
|
|
23
|
+
A build ran with the devtools dock on, so every page it serves carries the devtools
|
|
24
|
+
launcher. The dock itself loads only when someone opens it. Said once per build, so a
|
|
25
|
+
dock left on by accident is seen before it ships.
|
|
26
|
+
|
|
27
|
+
To keep it out of production, list \`devtools()\` only when
|
|
28
|
+
\`mode !== 'production'\`.
|
|
29
|
+
`
|
|
30
|
+
},
|
|
31
|
+
devtools_profiler_secret_empty: {
|
|
32
|
+
text: () => prose`
|
|
33
|
+
profiler: \`profiler.secret\` is EMPTY in this build — the variable it reads is not set in
|
|
34
|
+
the BUILD environment. The UI will be DISABLED in production unless OGYGIA_PROFILER_SECRET
|
|
35
|
+
exists at runtime (on Amplify / Lambda it does not): /__profiler will answer "installed but
|
|
36
|
+
disabled". Set the variable where the build runs, and keep the \`profiler\` option
|
|
37
|
+
unconditional.
|
|
38
|
+
`,
|
|
39
|
+
details: () => prose`
|
|
40
|
+
The profiler was given a \`secret\`, but it is empty while the app builds: the environment
|
|
41
|
+
variable it reads is not set where the build runs. The secret is baked in at build time,
|
|
42
|
+
so the profiler's UI stays disabled in production unless \`OGYGIA_PROFILER_SECRET\` is
|
|
43
|
+
set on the running server, which some hosts never do.
|
|
44
|
+
|
|
45
|
+
Set the variable in the build environment, and keep the \`profiler\` option
|
|
46
|
+
unconditional.
|
|
47
|
+
`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const own = define_messages(DEVTOOLS_BUILD_ERRORS, DEVTOOLS_BUILD_WARNINGS);
|
|
51
|
+
/** `throw new OgygiaError(code, args)`; `warn(code, args, ...rest)` logs; `line(code, args)` is the
|
|
52
|
+
* line as a string. */
|
|
53
|
+
const OgygiaError = own.OgygiaError;
|
|
54
|
+
const warn = own.warn;
|
|
55
|
+
const line = own.line;
|
|
56
|
+
export { DEVTOOLS_BUILD_ERRORS, DEVTOOLS_BUILD_WARNINGS, OgygiaError, line, warn };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { MessagesOf } from "@ogygia/core/internal/messages";
|
|
2
|
+
/** The errors: `throw new OgygiaError(code, args)`, or `console.error(new OgygiaError(…), err)` for a
|
|
3
|
+
* failure that is caught. */
|
|
4
|
+
export declare const DEVTOOLS_ERRORS: {
|
|
5
|
+
devtools_profiler_store_init_failed: {
|
|
6
|
+
text: () => string;
|
|
7
|
+
details: () => string;
|
|
8
|
+
};
|
|
9
|
+
devtools_profiler_mount_failed: {
|
|
10
|
+
text: () => string;
|
|
11
|
+
details: () => string;
|
|
12
|
+
};
|
|
13
|
+
devtools_profiler_alloc_sampling_unavailable: {
|
|
14
|
+
text: () => string;
|
|
15
|
+
details: () => string;
|
|
16
|
+
};
|
|
17
|
+
devtools_profiler_gc_attribution_failed: {
|
|
18
|
+
text: () => string;
|
|
19
|
+
details: () => string;
|
|
20
|
+
};
|
|
21
|
+
devtools_profiler_put_report_failed: {
|
|
22
|
+
text: () => string;
|
|
23
|
+
details: () => string;
|
|
24
|
+
};
|
|
25
|
+
devtools_profiler_get_report_failed: {
|
|
26
|
+
text: () => string;
|
|
27
|
+
details: () => string;
|
|
28
|
+
};
|
|
29
|
+
devtools_dock_load_failed: {
|
|
30
|
+
text: () => string;
|
|
31
|
+
details: () => string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/** The warnings: `warn(code, args, ...rest)`. */
|
|
35
|
+
export declare const DEVTOOLS_WARNINGS: {
|
|
36
|
+
devtools_profiler_session_rejected: {
|
|
37
|
+
text: (a: {
|
|
38
|
+
path: string;
|
|
39
|
+
diagnosis: string;
|
|
40
|
+
reason: string;
|
|
41
|
+
}) => string;
|
|
42
|
+
details: () => string;
|
|
43
|
+
};
|
|
44
|
+
devtools_profiler_sink_undelivered: {
|
|
45
|
+
text: (a: {
|
|
46
|
+
rows: string;
|
|
47
|
+
}) => string;
|
|
48
|
+
details: () => string;
|
|
49
|
+
};
|
|
50
|
+
devtools_profiler_background_stopped: {
|
|
51
|
+
text: (a: {
|
|
52
|
+
loop: string;
|
|
53
|
+
}) => string;
|
|
54
|
+
details: () => string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
type Own = MessagesOf<typeof DEVTOOLS_ERRORS, typeof DEVTOOLS_WARNINGS>;
|
|
58
|
+
/** `throw new OgygiaError(code, args)`; `warn(code, args, ...rest)` logs; `line(code, args)` is the
|
|
59
|
+
* line as a string. Wording and link in dev and while prerendering, the link alone otherwise. */
|
|
60
|
+
export declare const OgygiaError: Own["OgygiaError"];
|
|
61
|
+
export declare const warn: Own["warn"];
|
|
62
|
+
export declare const line: Own["line"];
|
|
63
|
+
export {};
|