@jxsuite/import 0.40.9 → 0.40.12

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.
@@ -26,7 +26,8 @@
26
26
  * ignored it would produce a popover that opens into nothing.
27
27
  */
28
28
 
29
- import type { JxElement } from "@jxsuite/schema/types";
29
+ import { getNestedStyle, isString } from "@jxsuite/schema/guards";
30
+ import type { JxElement, JxStyle } from "@jxsuite/schema/types";
30
31
  import { POPOVER_DEFAULT_MODE, POPOVER_INVOKER_TAGS } from "@jxsuite/schema/overlays";
31
32
 
32
33
  /** Attributes naming the panel an invoker controls, in the order they are trusted. */
@@ -41,22 +42,14 @@ const CONCEALING = new Set(["display", "visibility", "opacity"]);
41
42
  /** A concealed panel is only a popover if it is also lifted out of the flow. */
42
43
  const POSITIONED = new Set(["absolute", "fixed"]);
43
44
 
44
- function styleOf(node: JxElement): Record<string, unknown> | undefined {
45
- return node.style as Record<string, unknown> | undefined;
46
- }
47
-
48
- function attributesOf(node: JxElement): Record<string, unknown> | undefined {
49
- return node.attributes as Record<string, unknown> | undefined;
50
- }
51
-
52
45
  function childrenOf(node: JxElement): JxElement[] {
53
46
  return Array.isArray(node.children)
54
- ? (node.children.filter((child) => typeof child !== "string") as JxElement[])
47
+ ? node.children.filter((child): child is JxElement => !isString(child))
55
48
  : [];
56
49
  }
57
50
 
58
51
  /** Whether a style hides the node outright, and by which declaration. */
59
- function concealedBy(style: Record<string, unknown> | undefined): string[] {
52
+ function concealedBy(style: JxStyle | undefined): string[] {
60
53
  if (!style) {
61
54
  return [];
62
55
  }
@@ -84,11 +77,11 @@ function concealedBy(style: Record<string, unknown> | undefined): string[] {
84
77
  * explicit statements of the same intent.
85
78
  */
86
79
  export function looksLikePanel(node: JxElement): boolean {
87
- const style = styleOf(node);
80
+ const { style } = node;
88
81
  if (concealedBy(style).length === 0) {
89
82
  return false;
90
83
  }
91
- const attributes = attributesOf(node);
84
+ const { attributes } = node;
92
85
  if (attributes?.["role"] === "dialog" || attributes?.["inert"] !== undefined) {
93
86
  return true;
94
87
  }
@@ -97,13 +90,13 @@ export function looksLikePanel(node: JxElement): boolean {
97
90
 
98
91
  /** The id an invoker names, from whichever attribute carries it. */
99
92
  export function targetIdOf(node: JxElement): string | null {
100
- const attributes = attributesOf(node);
93
+ const { attributes } = node;
101
94
  if (!attributes) {
102
95
  return null;
103
96
  }
104
97
  for (const name of TARGET_ATTRIBUTES) {
105
98
  const raw = attributes[name];
106
- if (typeof raw !== "string" || raw.length === 0) {
99
+ if (!isString(raw) || raw.length === 0) {
107
100
  continue;
108
101
  }
109
102
  const id = raw.startsWith("#") ? raw.slice(1) : name === "href" ? "" : raw;
@@ -120,7 +113,7 @@ function looksLikeInvoker(node: JxElement): boolean {
120
113
  if (tag !== "button" && tag !== "input" && tag !== "a") {
121
114
  return false;
122
115
  }
123
- const attributes = attributesOf(node);
116
+ const { attributes } = node;
124
117
  if (!attributes) {
125
118
  return false;
126
119
  }
@@ -133,8 +126,8 @@ function looksLikeInvoker(node: JxElement): boolean {
133
126
  function indexById(root: JxElement): Map<string, JxElement> {
134
127
  const index = new Map<string, JxElement>();
135
128
  const visit = (node: JxElement): void => {
136
- const id = attributesOf(node)?.["id"];
137
- if (typeof id === "string" && !index.has(id)) {
129
+ const id = node.attributes?.["id"];
130
+ if (isString(id) && !index.has(id)) {
138
131
  index.set(id, node);
139
132
  }
140
133
  for (const child of childrenOf(node)) {
@@ -183,7 +176,7 @@ function panelBesideAncestor(ancestry: readonly JxElement[]): JxElement | null {
183
176
  * writing `block` there would be inventing a fact: the element's own default is the honest answer,
184
177
  * so nothing is written and the warning is accepted.
185
178
  */
186
- function openValueFor(property: string, closed: unknown): string | null {
179
+ function openValueFor(property: string, closed: JxStyle[string]): string | null {
187
180
  if (property === "opacity" && (closed === 0 || closed === "0")) {
188
181
  return "1";
189
182
  }
@@ -197,12 +190,12 @@ function openValueFor(property: string, closed: unknown): string | null {
197
190
  * Move a panel's closed-state declarations into `:popover-open`, where they describe the open one.
198
191
  *
199
192
  * `node` is always a panel `looksLikePanel` already accepted, and that requires a concealing style
200
- * declaration to exist (`concealedBy(style).length === 0` rejects it otherwise) — so
201
- * `styleOf(node)` is never empty here.
193
+ * declaration to exist (`concealedBy(style).length === 0` rejects it otherwise) — so `node.style`
194
+ * is never empty here.
202
195
  */
203
196
  function liftConcealment(node: JxElement): void {
204
- const style = styleOf(node)!;
205
- const open = (style[":popover-open"] as Record<string, unknown> | undefined) ?? {};
197
+ const style = node.style!;
198
+ const open = getNestedStyle(style, ":popover-open") ?? {};
206
199
  for (const property of Object.keys(style)) {
207
200
  if (!CONCEALING.has(property)) {
208
201
  continue;
@@ -213,12 +206,12 @@ function liftConcealment(node: JxElement): void {
213
206
  design and is carried across unchanged, because the base rule is no longer where it belongs. */
214
207
  const openValue = isConcealment ? openValueFor(property, value) : value;
215
208
  if (openValue !== null) {
216
- (open as Record<string, unknown>)[property] = openValue;
209
+ open[property] = openValue;
217
210
  }
218
211
  delete style[property];
219
212
  }
220
213
  if (Object.keys(open).length > 0) {
221
- (style as Record<string, unknown>)[":popover-open"] = open;
214
+ style[":popover-open"] = open;
222
215
  }
223
216
  }
224
217
 
@@ -248,14 +241,15 @@ export function applyPopovers(root: JxElement): PopoverResult {
248
241
 
249
242
  if (panel && panel !== node && !claimed.has(panel) && looksLikePanel(panel)) {
250
243
  const tag = String(node.tagName ?? "").toLowerCase();
251
- const attributes = (node.attributes ??= {}) as Record<string, unknown>;
244
+ node.attributes ??= {};
245
+ const { attributes } = node;
252
246
 
253
247
  /* `popovertarget` exists only on button and input. An anchor that is really a button
254
248
  becomes one; an anchor that navigates keeps its job, because a dead link is a worse
255
249
  defect than an unconverted menu. */
256
250
  if (!POPOVER_INVOKER_TAGS.has(tag)) {
257
251
  const { href } = attributes;
258
- const navigates = typeof href === "string" && href.length > 0 && href !== "#";
252
+ const navigates = isString(href) && href.length > 0 && href !== "#";
259
253
  if (navigates) {
260
254
  result.skippedNavigatingLinks += 1;
261
255
  for (const child of childrenOf(node)) {
@@ -268,9 +262,10 @@ export function applyPopovers(root: JxElement): PopoverResult {
268
262
  delete attributes["href"];
269
263
  }
270
264
 
271
- const panelAttributes = (panel.attributes ??= {}) as Record<string, unknown>;
265
+ panel.attributes ??= {};
266
+ const { attributes: panelAttributes } = panel;
272
267
  let { id: panelId } = panelAttributes;
273
- if (typeof panelId !== "string" || panelId.length === 0) {
268
+ if (!isString(panelId) || panelId.length === 0) {
274
269
  generated += 1;
275
270
  panelId = `jx-popover-${generated}`;
276
271
  panelAttributes["id"] = panelId;
@@ -3,6 +3,7 @@
3
3
  * same depth-first element-index order as the browser DOM walk in style-capture.ts.
4
4
  */
5
5
 
6
+ import { isString } from "@jxsuite/schema/guards";
6
7
  import type { JxElement, JxStyle } from "@jxsuite/schema/types";
7
8
  import type { DiffedStyle } from "./style-diff.ts";
8
9
  import { parseWidthQuery } from "./breakpoint-plan.ts";
@@ -38,7 +39,7 @@ export function orderBreakpointKeys(
38
39
  names: readonly string[],
39
40
  queries: Record<string, string> | undefined,
40
41
  ): string[] {
41
- const rank = (name: string): { band: number; width: number } => {
42
+ const rank = (name: string) => {
42
43
  const parsed = queries?.[name] === undefined ? null : parseWidthQuery(queries[name]!);
43
44
  if (!parsed) {
44
45
  return { band: 2, width: 0 };
@@ -83,7 +84,7 @@ export function applyStylesToTree(
83
84
 
84
85
  let childIdx = 0;
85
86
  for (const child of root.children) {
86
- if (typeof child === "string") {
87
+ if (isString(child)) {
87
88
  continue;
88
89
  }
89
90
  walkAndApply(child, [childIdx], baseMap, mediaMaps);
@@ -115,12 +116,12 @@ function walkAndApply(
115
116
  const delta = deltaMap.get(key);
116
117
  if (delta) {
117
118
  const filtered = { ...delta };
118
- dropDerivedGeometry(node, filtered, (node.style ?? {}) as Record<string, unknown>);
119
+ dropDerivedGeometry(node, filtered, node.style ?? {});
119
120
  if (Object.keys(filtered).length > 0) {
120
121
  if (!node.style) {
121
122
  node.style = {};
122
123
  }
123
- (node.style as Record<string, unknown>)[`@${bpName}`] = filtered;
124
+ node.style[`@${bpName}`] = filtered;
124
125
  }
125
126
  }
126
127
  }
@@ -132,7 +133,7 @@ function walkAndApply(
132
133
 
133
134
  let childIdx = 0;
134
135
  for (const child of node.children) {
135
- if (typeof child === "string") {
136
+ if (isString(child)) {
136
137
  continue;
137
138
  }
138
139
  walkAndApply(child, [...path, childIdx], baseMap, mediaMaps);
@@ -53,11 +53,11 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
53
53
  */
54
54
  const result = await page.evaluate((srcsetSeparator: string) => {
55
55
  const separator = new RegExp(srcsetSeparator);
56
- const assets: { url: string; source: string }[] = [];
56
+ const assets: DiscoveredAsset[] = [];
57
57
  const seen = new Set<string>();
58
58
  let inlineSvgCount = 0;
59
59
 
60
- function add(url: string, source: string) {
60
+ function add(url: string, source: DiscoveredAsset["source"]) {
61
61
  if (!url || seen.has(url)) {
62
62
  return;
63
63
  }
@@ -98,8 +98,10 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
98
98
  }
99
99
 
100
100
  // Img[src]
101
- for (const el of document.querySelectorAll("img[src]")) {
102
- add((el as HTMLImageElement).src, "img-src");
101
+ /* Each selector below names its element by tag, so the type argument states what the
102
+ selector already guarantees. */
103
+ for (const el of document.querySelectorAll<HTMLImageElement>("img[src]")) {
104
+ add(el.src, "img-src");
103
105
  }
104
106
 
105
107
  // Img[srcset] and source[srcset]
@@ -112,13 +114,13 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
112
114
  }
113
115
 
114
116
  // Picture > source (type-based, not just srcset)
115
- for (const el of document.querySelectorAll("picture > source[src]")) {
116
- add((el as HTMLSourceElement).src, "picture-source");
117
+ for (const el of document.querySelectorAll<HTMLSourceElement>("picture > source[src]")) {
118
+ add(el.src, "picture-source");
117
119
  }
118
120
 
119
121
  // Video[poster]
120
- for (const el of document.querySelectorAll("video[poster]")) {
121
- add((el as HTMLVideoElement).poster, "video-poster");
122
+ for (const el of document.querySelectorAll<HTMLVideoElement>("video[poster]")) {
123
+ add(el.poster, "video-poster");
122
124
  }
123
125
 
124
126
  // Inline style url() on any element
@@ -141,8 +143,7 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
141
143
  }
142
144
 
143
145
  // @font-face from stylesheets + retain full source CSS (F0)
144
- const stylesheets: { href: string | null; cssText: string | null; fontFaceRules: string[] }[] =
145
- [];
146
+ const stylesheets: CapturedStylesheet[] = [];
146
147
  for (const sheet of document.styleSheets) {
147
148
  let rules: CSSRuleList | null = null;
148
149
  let fullCssText: string | null = null;
@@ -174,18 +175,18 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
174
175
  }
175
176
 
176
177
  // Favicon and other link icons
177
- for (const el of document.querySelectorAll(
178
+ for (const el of document.querySelectorAll<HTMLLinkElement>(
178
179
  'link[rel~="icon"], link[rel="shortcut icon"], link[rel="apple-touch-icon"]',
179
180
  )) {
180
- const { href } = el as HTMLLinkElement;
181
+ const { href } = el;
181
182
  if (href) {
182
183
  add(href, "favicon");
183
184
  }
184
185
  }
185
186
 
186
187
  // Open Graph images
187
- for (const el of document.querySelectorAll('meta[property="og:image"]')) {
188
- const { content } = el as HTMLMetaElement;
188
+ for (const el of document.querySelectorAll<HTMLMetaElement>('meta[property="og:image"]')) {
189
+ const { content } = el;
189
190
  if (content) {
190
191
  add(content, "og-image");
191
192
  }
@@ -194,11 +195,7 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
194
195
  // Count inline SVGs (kept inline, not downloaded)
195
196
  inlineSvgCount = document.querySelectorAll("svg").length;
196
197
 
197
- return { assets, inlineSvgCount, stylesheets } as {
198
- assets: { url: string; source: string }[];
199
- inlineSvgCount: number;
200
- stylesheets: { href: string | null; cssText: string | null; fontFaceRules: string[] }[];
201
- };
198
+ return { assets, inlineSvgCount, stylesheets };
202
199
  }, SRCSET_SEPARATOR.source);
203
200
 
204
201
  // R2: For cross-origin sheets we couldn't access (cssText=null, has href), fetch them
@@ -256,5 +253,5 @@ export async function collectAssets(page: ImportPage): Promise<AssetCollectionRe
256
253
  }
257
254
  }
258
255
 
259
- return result as AssetCollectionResult;
256
+ return result;
260
257
  }
@@ -179,12 +179,12 @@ export async function downloadAssets(
179
179
  const relativePath = `/assets/${subdir}/${filename}`;
180
180
 
181
181
  try {
182
- const headers: Record<string, string> = {
182
+ const headers = new Headers({
183
183
  "User-Agent":
184
184
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
185
- };
185
+ });
186
186
  if (sourceUrl) {
187
- headers["Referer"] = sourceUrl;
187
+ headers.set("Referer", sourceUrl);
188
188
  }
189
189
  const response = await fetch(url, { headers, redirect: "follow" });
190
190
  if (!response.ok) {
@@ -5,6 +5,7 @@
5
5
  * style.backgroundImage (url() references) - $media nested style objects with backgroundImage
6
6
  */
7
7
 
8
+ import { isNestedStyle, isString } from "@jxsuite/schema/guards";
8
9
  import type { JxElement, JxStyle } from "@jxsuite/schema/types";
9
10
  import { SRCSET_SEPARATOR } from "./srcset.ts";
10
11
 
@@ -52,26 +53,25 @@ export function rewriteAssetUrls(
52
53
  * the importer would fetch one set of URLs and rewrite another — the attribute would be
53
54
  * reassembled around fragments nothing had downloaded.
54
55
  */
55
- function rewriteSrcset(srcset: string): { srcset: string; collapsedTo: string | null } {
56
+ function rewriteSrcset(srcset: string) {
56
57
  const locals = new Set<string>();
57
58
  let unresolved = false;
58
59
 
59
- const rewritten = srcset
60
- .split(SRCSET_SEPARATOR)
61
- .map((entry) => {
62
- const parts = entry.trim().split(/\s+/);
63
- const [url = ""] = parts;
64
- const local = rewriteUrl(url);
65
- if (local) {
66
- count += 1;
67
- parts[0] = local;
68
- locals.add(local);
69
- } else {
70
- unresolved = true;
71
- }
72
- return parts.join(" ");
73
- })
74
- .join(", ");
60
+ const candidates: string[] = [];
61
+ for (const entry of srcset.split(SRCSET_SEPARATOR)) {
62
+ const parts = entry.trim().split(/\s+/);
63
+ const [url = ""] = parts;
64
+ const local = rewriteUrl(url);
65
+ if (local) {
66
+ count += 1;
67
+ parts[0] = local;
68
+ locals.add(local);
69
+ } else {
70
+ unresolved = true;
71
+ }
72
+ candidates.push(parts.join(" "));
73
+ }
74
+ const rewritten = candidates.join(", ");
75
75
 
76
76
  /* Every candidate now points at the ONE file the family kept, so the attribute has become a
77
77
  list of the same path repeated with different width descriptors — which lies to the browser
@@ -95,20 +95,20 @@ export function rewriteAssetUrls(
95
95
 
96
96
  function rewriteStyleObject(style: JxStyle): void {
97
97
  for (const [key, value] of Object.entries(style)) {
98
- if (typeof value === "string" && value.includes("url(")) {
99
- (style as Record<string, unknown>)[key] = rewriteCssUrls(value);
100
- } else if (typeof value === "object" && value !== null && key.startsWith("@")) {
101
- rewriteStyleObject(value as JxStyle);
98
+ if (isString(value) && value.includes("url(")) {
99
+ style[key] = rewriteCssUrls(value);
100
+ } else if (isNestedStyle(value) && key.startsWith("@")) {
101
+ rewriteStyleObject(value);
102
102
  }
103
103
  }
104
104
  }
105
105
 
106
106
  function walk(node: JxElement): void {
107
- const attrs = node.attributes as Record<string, unknown> | undefined;
107
+ const attrs = node.attributes;
108
108
 
109
109
  if (attrs) {
110
110
  // Src attribute (img, video, audio, source, etc.)
111
- if (typeof attrs.src === "string") {
111
+ if (isString(attrs.src)) {
112
112
  const rewritten = rewriteUrl(attrs.src);
113
113
  if (rewritten) {
114
114
  attrs.src = rewritten;
@@ -117,8 +117,8 @@ export function rewriteAssetUrls(
117
117
  }
118
118
 
119
119
  // Href on non-anchor elements (could be link icon, etc.)
120
- if (typeof attrs.href === "string") {
121
- const tag = (typeof node.tagName === "string" ? node.tagName : "").toLowerCase();
120
+ if (isString(attrs.href)) {
121
+ const tag = (isString(node.tagName) ? node.tagName : "").toLowerCase();
122
122
  if (tag !== "a") {
123
123
  const rewritten = rewriteUrl(attrs.href);
124
124
  if (rewritten) {
@@ -129,7 +129,7 @@ export function rewriteAssetUrls(
129
129
  }
130
130
 
131
131
  // Poster attribute (video)
132
- if (typeof attrs.poster === "string") {
132
+ if (isString(attrs.poster)) {
133
133
  const rewritten = rewriteUrl(attrs.poster);
134
134
  if (rewritten) {
135
135
  attrs.poster = rewritten;
@@ -138,7 +138,7 @@ export function rewriteAssetUrls(
138
138
  }
139
139
 
140
140
  // Srcset attribute
141
- if (typeof attrs.srcset === "string") {
141
+ if (isString(attrs.srcset)) {
142
142
  const result = rewriteSrcset(attrs.srcset);
143
143
  if (result.collapsedTo === null) {
144
144
  attrs.srcset = result.srcset;
@@ -161,7 +161,7 @@ export function rewriteAssetUrls(
161
161
  // Recurse
162
162
  if (Array.isArray(node.children)) {
163
163
  for (const child of node.children) {
164
- if (typeof child !== "string") {
164
+ if (!isString(child)) {
165
165
  walk(child);
166
166
  }
167
167
  }
@@ -20,6 +20,8 @@
20
20
  * @docs studio/design/breakpoints
21
21
  */
22
22
 
23
+ import { IMPORT_MAX_BREAKPOINTS } from "@jxsuite/protocol/types";
24
+
23
25
  /** One breakpoint: what to call it, what to emit, and the viewport width to sample it at. */
24
26
  export interface Breakpoint {
25
27
  /** Jx breakpoint name, e.g. `--768`. */
@@ -60,8 +62,11 @@ export const DEFAULT_BREAKPOINT_POLICY: BreakpointPolicy = {
60
62
  rounding: "nearest",
61
63
  };
62
64
 
63
- /** The most breakpoints a project may be asked to keep. Past this, "limit" is not limiting. */
64
- export const MAX_BREAKPOINTS = 12;
65
+ /**
66
+ * The most breakpoints a project may be asked to keep. Past this, "limit" is not limiting. The
67
+ * value is the wire contract's, so a client that offers a count offers the same ceiling.
68
+ */
69
+ export const MAX_BREAKPOINTS = IMPORT_MAX_BREAKPOINTS;
65
70
 
66
71
  /** The viewport widths an override may name. Outside this a "width" is a typo, not a breakpoint. */
67
72
  export const MIN_BREAKPOINT_WIDTH = 120;
package/src/capture.ts CHANGED
@@ -20,20 +20,24 @@
20
20
  export type PageArgs<Params extends unknown[]> = { [K in keyof Params]: Params[K] };
21
21
 
22
22
  export interface ImportPage {
23
+ /**
24
+ * Resolves once navigation settles. Puppeteer resolves with the main response (or `null`), a
25
+ * stand-in may resolve with nothing; the import never reads the value either way.
26
+ */
23
27
  goto: (
24
28
  url: string,
25
29
  options?: {
26
30
  waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
27
31
  timeout?: number;
28
32
  },
29
- ) => Promise<unknown>;
30
- setViewport: (viewport: { width: number; height: number }) => Promise<unknown>;
33
+ ) => Promise<object | null> | Promise<void>;
34
+ setViewport: (viewport: { width: number; height: number }) => Promise<void>;
31
35
  evaluate: <Params extends unknown[], R>(
32
36
  fn: (...params: PageArgs<Params>) => R,
33
37
  ...args: Params
34
38
  ) => Promise<Awaited<R>>;
35
39
  screenshot: (options?: { fullPage?: boolean; type?: "png" }) => Promise<Uint8Array>;
36
- close: () => Promise<unknown>;
40
+ close: () => Promise<void>;
37
41
  }
38
42
 
39
43
  export interface ImportBrowser {
@@ -126,9 +130,11 @@ export async function capturePage(
126
130
 
127
131
  const links: string[] = [];
128
132
  const { origin } = location;
129
- for (const a of document.querySelectorAll("a[href]")) {
133
+ /* The selector names HTML anchors, whose `href` is the resolved URL string. An SVG `<a href>`
134
+ also matches and is read the same way; that has always been the case here. */
135
+ for (const a of document.querySelectorAll<HTMLAnchorElement>("a[href]")) {
130
136
  try {
131
- const { href } = new URL((a as HTMLAnchorElement).href, location.href);
137
+ const { href } = new URL(a.href, location.href);
132
138
  if (href.startsWith(origin) && !href.includes("#")) {
133
139
  links.push(href);
134
140
  }
package/src/cli.ts CHANGED
@@ -179,11 +179,12 @@ if (maxDepth > 0) {
179
179
 
180
180
  // The interactive flow runs inside an async function rather than via top-level await so a test can
181
181
  // Dynamic-import this entry and await the same sequence (see create/index.ts for the rationale).
182
- async function main() {
182
+ // The URL is passed in because a hoisted declaration cannot see the module-level check above.
183
+ async function main(siteUrl: string) {
183
184
  try {
184
185
  const result = await importSite(
185
186
  {
186
- url: url as string,
187
+ url: siteUrl,
187
188
  outDir,
188
189
  breakpoints,
189
190
  maxDepth,
@@ -254,4 +255,4 @@ async function main() {
254
255
  }
255
256
 
256
257
  // oxlint-disable-next-line unicorn/prefer-top-level-await
257
- export const ready = main();
258
+ export const ready = main(url);