@cotestdev/mcp_playwright 0.0.58 → 0.0.59

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.
@@ -32,358 +32,18 @@ __export(program_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(program_exports);
34
34
  var import_child_process = require("child_process");
35
- var import_crypto = __toESM(require("crypto"));
36
35
  var import_fs = __toESM(require("fs"));
37
- var import_net = __toESM(require("net"));
38
36
  var import_os = __toESM(require("os"));
39
37
  var import_path = __toESM(require("path"));
40
- var import_socketConnection = require("./socketConnection");
41
- class Session {
42
- constructor(clientInfo, name, options) {
43
- this._nextMessageId = 1;
44
- this._callbacks = /* @__PURE__ */ new Map();
45
- this.name = name;
46
- this._clientInfo = clientInfo;
47
- this._config = options;
48
- }
49
- config() {
50
- return this._config;
51
- }
52
- isCompatible() {
53
- return this._clientInfo.version === this._config.version;
54
- }
55
- checkCompatible() {
56
- if (!this.isCompatible()) {
57
- throw new Error(`Client is v${this._clientInfo.version}, session '${this.name}' is v${this._config.version}. Run
58
-
59
- playwright-cli${this.name !== "default" ? ` -s=${this.name}` : ""} open
60
-
61
- to restart the browser session.`);
62
- }
63
- }
64
- async run(args) {
65
- this.checkCompatible();
66
- return await this._send("run", { args, cwd: process.cwd() });
67
- }
68
- async stop(quiet = false) {
69
- if (!await this.canConnect()) {
70
- if (!quiet)
71
- console.log(`Browser '${this.name}' is not open.`);
72
- return;
73
- }
74
- await this._stopDaemon();
75
- if (!quiet)
76
- console.log(`Browser '${this.name}' closed
77
- `);
78
- }
79
- async _send(method, params = {}) {
80
- const connection = await this._startDaemonIfNeeded();
81
- const messageId = this._nextMessageId++;
82
- const message = {
83
- id: messageId,
84
- method,
85
- params,
86
- version: this._config.version
87
- };
88
- const responsePromise = new Promise((resolve, reject) => {
89
- this._callbacks.set(messageId, { resolve, reject, method, params });
90
- });
91
- const [result] = await Promise.all([responsePromise, connection.send(message)]);
92
- return result;
93
- }
94
- disconnect() {
95
- if (!this._connection)
96
- return;
97
- for (const callback of this._callbacks.values())
98
- callback.reject(new Error("Session closed"));
99
- this._callbacks.clear();
100
- this._connection.close();
101
- this._connection = void 0;
102
- }
103
- async deleteData() {
104
- await this.stop();
105
- const dataDirs = await import_fs.default.promises.readdir(this._clientInfo.daemonProfilesDir).catch(() => []);
106
- const matchingEntries = dataDirs.filter((file) => file === `${this.name}.session` || file.startsWith(`ud-${this.name}-`));
107
- if (matchingEntries.length === 0) {
108
- console.log(`No user data found for browser '${this.name}'.`);
109
- return;
110
- }
111
- for (const entry of matchingEntries) {
112
- const userDataDir = import_path.default.resolve(this._clientInfo.daemonProfilesDir, entry);
113
- for (let i = 0; i < 5; i++) {
114
- try {
115
- await import_fs.default.promises.rm(userDataDir, { recursive: true });
116
- if (entry.startsWith("ud-"))
117
- console.log(`Deleted user data for browser '${this.name}'.`);
118
- break;
119
- } catch (e) {
120
- if (e.code === "ENOENT") {
121
- console.log(`No user data found for browser '${this.name}'.`);
122
- break;
123
- }
124
- await new Promise((resolve) => setTimeout(resolve, 1e3));
125
- if (i === 4)
126
- throw e;
127
- }
128
- }
129
- }
130
- }
131
- async _connect() {
132
- return await new Promise((resolve) => {
133
- const socket = import_net.default.createConnection(this._config.socketPath, () => {
134
- resolve({ socket });
135
- });
136
- socket.on("error", (error) => {
137
- if (import_os.default.platform() !== "win32")
138
- void import_fs.default.promises.unlink(this._config.socketPath).catch(() => {
139
- }).then(() => resolve({ error }));
140
- else
141
- resolve({ error });
142
- });
143
- });
144
- }
145
- async canConnect() {
146
- const { socket } = await this._connect();
147
- if (socket) {
148
- socket.destroy();
149
- return true;
150
- }
151
- return false;
152
- }
153
- async _startDaemonIfNeeded() {
154
- if (this._connection)
155
- return this._connection;
156
- let { socket } = await this._connect();
157
- if (!socket)
158
- socket = await this._startDaemon();
159
- this._connection = new import_socketConnection.SocketConnection(socket, this._config.version);
160
- this._connection.onmessage = (message) => this._onMessage(message);
161
- this._connection.onclose = () => this.disconnect();
162
- return this._connection;
163
- }
164
- _onMessage(object) {
165
- if (object.id && this._callbacks.has(object.id)) {
166
- const callback = this._callbacks.get(object.id);
167
- this._callbacks.delete(object.id);
168
- if (object.error)
169
- callback.reject(new Error(object.error));
170
- else
171
- callback.resolve(object.result);
172
- } else if (object.id) {
173
- throw new Error(`Unexpected message id: ${object.id}`);
174
- } else {
175
- throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`);
176
- }
177
- }
178
- _sessionFile(suffix) {
179
- return import_path.default.resolve(this._clientInfo.daemonProfilesDir, `${this.name}${suffix}`);
180
- }
181
- async _startDaemon() {
182
- await import_fs.default.promises.mkdir(this._clientInfo.daemonProfilesDir, { recursive: true });
183
- const cliPath = import_path.default.join(__dirname, "../../../cli.js");
184
- const sessionConfigFile = this._sessionFile(".session");
185
- this._config.version = this._clientInfo.version;
186
- await import_fs.default.promises.writeFile(sessionConfigFile, JSON.stringify(this._config, null, 2));
187
- const errLog = this._sessionFile(".err");
188
- const err = import_fs.default.openSync(errLog, "w");
189
- const args = [
190
- cliPath,
191
- "run-mcp-server",
192
- `--daemon-session=${sessionConfigFile}`
193
- ];
194
- const child = (0, import_child_process.spawn)(process.execPath, args, {
195
- detached: true,
196
- stdio: ["ignore", "pipe", err],
197
- cwd: process.cwd()
198
- // Will be used as root.
199
- });
200
- let signalled = false;
201
- const sigintHandler = () => {
202
- signalled = true;
203
- child.kill("SIGINT");
204
- };
205
- const sigtermHandler = () => {
206
- signalled = true;
207
- child.kill("SIGTERM");
208
- };
209
- process.on("SIGINT", sigintHandler);
210
- process.on("SIGTERM", sigtermHandler);
211
- let outLog = "";
212
- await new Promise((resolve, reject) => {
213
- child.stdout.on("data", (data) => {
214
- outLog += data.toString();
215
- if (!outLog.includes("<EOF>"))
216
- return;
217
- const errorMatch = outLog.match(/### Error\n([\s\S]*)<EOF>/);
218
- const error = errorMatch ? errorMatch[1].trim() : void 0;
219
- if (error) {
220
- const errLogContent = import_fs.default.readFileSync(errLog, "utf-8");
221
- const message = error + (errLogContent ? "\n" + errLogContent : "");
222
- reject(new Error(message));
223
- }
224
- const successMatch = outLog.match(/### Success\nDaemon listening on (.*)\n<EOF>/);
225
- if (successMatch)
226
- resolve();
227
- });
228
- child.on("close", (code) => {
229
- if (!signalled)
230
- reject(new Error(`Daemon process exited with code ${code}`));
231
- });
232
- });
233
- process.off("SIGINT", sigintHandler);
234
- process.off("SIGTERM", sigtermHandler);
235
- child.stdout.destroy();
236
- child.unref();
237
- const { socket } = await this._connect();
238
- if (socket) {
239
- console.log(`### Browser \`${this.name}\` opened with pid ${child.pid}.`);
240
- const resolvedConfig = await parseResolvedConfig(outLog);
241
- if (resolvedConfig) {
242
- this._config.resolvedConfig = resolvedConfig;
243
- console.log(`- ${this.name}:`);
244
- console.log(renderResolvedConfig(resolvedConfig).join("\n"));
245
- }
246
- console.log(`---`);
247
- await import_fs.default.promises.writeFile(sessionConfigFile, JSON.stringify(this._config, null, 2));
248
- return socket;
249
- }
250
- console.error(`Failed to connect to daemon at ${this._config.socketPath}`);
251
- process.exit(1);
252
- }
253
- async _stopDaemon() {
254
- let error;
255
- await this._send("stop").catch((e) => {
256
- error = e;
257
- });
258
- if (import_os.default.platform() !== "win32")
259
- await import_fs.default.promises.unlink(this._config.socketPath).catch(() => {
260
- });
261
- this.disconnect();
262
- if (!this._config.cli.persistent)
263
- await this.deleteSessionConfig();
264
- if (error && !error?.message?.includes("Session closed"))
265
- throw error;
266
- }
267
- async deleteSessionConfig() {
268
- await import_fs.default.promises.rm(this._sessionFile(".session")).catch(() => {
269
- });
270
- }
271
- }
272
- class SessionManager {
273
- constructor(clientInfo, sessions) {
274
- this.clientInfo = clientInfo;
275
- this.sessions = sessions;
276
- }
277
- static async create(clientInfo) {
278
- const dir = clientInfo.daemonProfilesDir;
279
- const sessions = /* @__PURE__ */ new Map();
280
- const files = await import_fs.default.promises.readdir(dir).catch(() => []);
281
- for (const file of files) {
282
- if (!file.endsWith(".session"))
283
- continue;
284
- try {
285
- const sessionName = import_path.default.basename(file, ".session");
286
- const sessionConfig = await import_fs.default.promises.readFile(import_path.default.join(dir, file), "utf-8").then((data) => JSON.parse(data));
287
- sessions.set(sessionName, new Session(clientInfo, sessionName, sessionConfig));
288
- } catch {
289
- }
290
- }
291
- return new SessionManager(clientInfo, sessions);
292
- }
293
- async open(args) {
294
- const sessionName = this._resolveSessionName(args.session);
295
- let session = this.sessions.get(sessionName);
296
- if (session)
297
- await session.stop(true);
298
- session = new Session(this.clientInfo, sessionName, sessionConfigFromArgs(this.clientInfo, sessionName, args));
299
- this.sessions.set(sessionName, session);
300
- await this.run(args);
301
- }
302
- async run(args) {
303
- const sessionName = this._resolveSessionName(args.session);
304
- const session = this.sessions.get(sessionName);
305
- if (!session) {
306
- console.log(`The browser '${sessionName}' is not open, please run open first`);
307
- console.log("");
308
- console.log(` playwright-cli${sessionName !== "default" ? ` -s=${sessionName}` : ""} open [params]`);
309
- process.exit(1);
310
- }
311
- for (const globalOption of globalOptions)
312
- delete args[globalOption];
313
- const result = await session.run(args);
314
- console.log(result.text);
315
- session.disconnect();
316
- }
317
- async close(options) {
318
- const sessionName = this._resolveSessionName(options.session);
319
- const session = this.sessions.get(sessionName);
320
- if (!session || !await session.canConnect()) {
321
- console.log(`Browser '${sessionName}' is not open.`);
322
- return;
323
- }
324
- await session.stop();
325
- }
326
- async deleteData(options) {
327
- const sessionName = this._resolveSessionName(options.session);
328
- const session = this.sessions.get(sessionName);
329
- if (!session) {
330
- console.log(`No user data found for browser '${sessionName}'.`);
331
- return;
332
- }
333
- await session.deleteData();
334
- this.sessions.delete(sessionName);
335
- }
336
- _resolveSessionName(sessionName) {
337
- if (sessionName)
338
- return sessionName;
339
- if (process.env.PLAYWRIGHT_CLI_SESSION)
340
- return process.env.PLAYWRIGHT_CLI_SESSION;
341
- return "default";
342
- }
343
- }
344
- function createClientInfo(packageLocation) {
345
- const packageJSON = require(packageLocation);
346
- const workspaceDir = findWorkspaceDir(process.cwd());
347
- const version = process.env.PLAYWRIGHT_CLI_VERSION_FOR_TEST || packageJSON.version;
348
- const hash = import_crypto.default.createHash("sha1");
349
- hash.update(workspaceDir || packageLocation);
350
- const workspaceDirHash = hash.digest("hex").substring(0, 16);
351
- return {
352
- version,
353
- workspaceDir,
354
- workspaceDirHash,
355
- daemonProfilesDir: daemonProfilesDir(workspaceDirHash)
356
- };
38
+ var import_registry = require("./registry");
39
+ var import_session = require("./session");
40
+ function resolveSessionName(sessionName) {
41
+ if (sessionName)
42
+ return sessionName;
43
+ if (process.env.PLAYWRIGHT_CLI_SESSION)
44
+ return process.env.PLAYWRIGHT_CLI_SESSION;
45
+ return "default";
357
46
  }
358
- function findWorkspaceDir(startDir) {
359
- let dir = startDir;
360
- for (let i = 0; i < 10; i++) {
361
- if (import_fs.default.existsSync(import_path.default.join(dir, ".playwright")))
362
- return dir;
363
- const parentDir = import_path.default.dirname(dir);
364
- if (parentDir === dir)
365
- break;
366
- dir = parentDir;
367
- }
368
- return void 0;
369
- }
370
- const baseDaemonDir = (() => {
371
- if (process.env.PLAYWRIGHT_DAEMON_SESSION_DIR)
372
- return process.env.PLAYWRIGHT_DAEMON_SESSION_DIR;
373
- let localCacheDir;
374
- if (process.platform === "linux")
375
- localCacheDir = process.env.XDG_CACHE_HOME || import_path.default.join(import_os.default.homedir(), ".cache");
376
- if (process.platform === "darwin")
377
- localCacheDir = import_path.default.join(import_os.default.homedir(), "Library", "Caches");
378
- if (process.platform === "win32")
379
- localCacheDir = process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local");
380
- if (!localCacheDir)
381
- throw new Error("Unsupported platform: " + process.platform);
382
- return import_path.default.join(localCacheDir, "ms-playwright", "daemon");
383
- })();
384
- const daemonProfilesDir = (workspaceDirHash) => {
385
- return import_path.default.join(baseDaemonDir, workspaceDirHash);
386
- };
387
47
  const globalOptions = [
388
48
  "browser",
389
49
  "config",
@@ -400,12 +60,12 @@ const booleanOptions = [
400
60
  "help",
401
61
  "version"
402
62
  ];
403
- async function program(packageLocation) {
404
- const clientInfo = createClientInfo(packageLocation);
63
+ async function program() {
64
+ const clientInfo = (0, import_registry.createClientInfo)();
405
65
  const help = require("./help.json");
406
66
  const argv = process.argv.slice(2);
407
67
  const boolean = [...help.booleanOptions, ...booleanOptions];
408
- const args = require("minimist")(argv, { boolean });
68
+ const args = require("minimist")(argv, { boolean, string: ["_"] });
409
69
  for (const [key, value] of Object.entries(args)) {
410
70
  if (key !== "_" && typeof value !== "boolean")
411
71
  args[key] = String(value);
@@ -445,38 +105,77 @@ async function program(packageLocation) {
445
105
  console.log(help.global);
446
106
  process.exit(1);
447
107
  }
448
- const sessionManager = await SessionManager.create(clientInfo);
108
+ const registry = await import_registry.Registry.load();
109
+ const sessionName = resolveSessionName(args.session);
449
110
  switch (commandName) {
450
111
  case "list": {
451
- if (args.all)
452
- await listAllSessions(clientInfo);
453
- else
454
- await listSessions(sessionManager);
112
+ await listSessions(registry, clientInfo, args.all);
455
113
  return;
456
114
  }
457
115
  case "close-all": {
458
- const sessions = sessionManager.sessions;
459
- for (const session of sessions.values())
460
- await session.stop(true);
116
+ const entries = registry.entries(clientInfo);
117
+ for (const entry of entries)
118
+ await new import_session.Session(clientInfo, entry.config).stop(true);
461
119
  return;
462
120
  }
463
- case "delete-data":
464
- await sessionManager.deleteData(args);
121
+ case "delete-data": {
122
+ const entry = registry.entry(clientInfo, sessionName);
123
+ if (!entry) {
124
+ console.log(`No user data found for browser '${sessionName}'.`);
125
+ return;
126
+ }
127
+ await new import_session.Session(clientInfo, entry.config).deleteData();
465
128
  return;
466
- case "kill-all":
129
+ }
130
+ case "kill-all": {
467
131
  await killAllDaemons();
468
132
  return;
469
- case "open":
470
- await sessionManager.open(args);
133
+ }
134
+ case "open": {
135
+ const entry = registry.entry(clientInfo, sessionName);
136
+ if (entry)
137
+ await new import_session.Session(clientInfo, entry.config).stop(true);
138
+ const session2 = new import_session.Session(clientInfo, sessionConfigFromArgs(clientInfo, sessionName, args));
139
+ for (const globalOption of globalOptions)
140
+ delete args[globalOption];
141
+ const result = await session2.run(args);
142
+ console.log(result.text);
471
143
  return;
144
+ }
472
145
  case "close":
473
- await sessionManager.close(args);
146
+ const closeEntry = registry.entry(clientInfo, sessionName);
147
+ const session = closeEntry ? new import_session.Session(clientInfo, closeEntry.config) : void 0;
148
+ if (!session || !await session.canConnect()) {
149
+ console.log(`Browser '${sessionName}' is not open.`);
150
+ return;
151
+ }
152
+ await session.stop();
474
153
  return;
475
154
  case "install":
476
155
  await install(args);
477
156
  return;
478
- default:
479
- await sessionManager.run(args);
157
+ case "show": {
158
+ const daemonScript = import_path.default.join(__dirname, "devtoolsApp.js");
159
+ const child = (0, import_child_process.spawn)(process.execPath, [daemonScript], {
160
+ detached: true,
161
+ stdio: "ignore"
162
+ });
163
+ child.unref();
164
+ return;
165
+ }
166
+ default: {
167
+ const defaultEntry = registry.entry(clientInfo, sessionName);
168
+ if (!defaultEntry) {
169
+ console.log(`The browser '${sessionName}' is not open, please run open first`);
170
+ console.log("");
171
+ console.log(` playwright-cli${sessionName !== "default" ? ` -s=${sessionName}` : ""} open [params]`);
172
+ process.exit(1);
173
+ }
174
+ for (const globalOption of globalOptions)
175
+ delete args[globalOption];
176
+ const result = await new import_session.Session(clientInfo, defaultEntry.config).run(args);
177
+ console.log(result.text);
178
+ }
480
179
  }
481
180
  }
482
181
  async function install(args) {
@@ -561,7 +260,9 @@ function sessionConfigFromArgs(clientInfo, sessionName, args) {
561
260
  if (!args.persistent && args.profile)
562
261
  args.persistent = true;
563
262
  return {
263
+ name: sessionName,
564
264
  version: clientInfo.version,
265
+ timestamp: 0,
565
266
  socketPath: daemonSocketPath(clientInfo, sessionName),
566
267
  cli: {
567
268
  headed: args.headed,
@@ -613,55 +314,34 @@ async function killAllDaemons() {
613
314
  else if (killed > 0)
614
315
  console.log(`Killed ${killed} daemon process${killed === 1 ? "" : "es"}.`);
615
316
  }
616
- async function listSessions(sessionManager) {
617
- const sessions = sessionManager.sessions;
618
- console.log("### Browsers");
619
- await gcAndPrintSessions([...sessions.values()]);
620
- }
621
- async function listAllSessions(clientInfo) {
622
- const hashes = await import_fs.default.promises.readdir(baseDaemonDir).catch(() => []);
623
- const sessionsByWorkspace = /* @__PURE__ */ new Map();
624
- for (const hash of hashes) {
625
- const hashDir = import_path.default.join(baseDaemonDir, hash);
626
- const stat = await import_fs.default.promises.stat(hashDir).catch(() => null);
627
- if (!stat?.isDirectory())
628
- continue;
629
- const files = await import_fs.default.promises.readdir(hashDir).catch(() => []);
630
- for (const file of files) {
631
- if (!file.endsWith(".session"))
317
+ async function listSessions(registry, clientInfo, all) {
318
+ if (all) {
319
+ const entries = registry.entryMap();
320
+ if (entries.size === 0) {
321
+ console.log("No browsers found.");
322
+ return;
323
+ }
324
+ for (const [workspace, list] of entries) {
325
+ if (!list.length)
632
326
  continue;
633
- try {
634
- const sessionName = import_path.default.basename(file, ".session");
635
- const sessionConfig = await import_fs.default.promises.readFile(import_path.default.join(hashDir, file), "utf-8").then((data) => JSON.parse(data));
636
- const session = new Session(clientInfo, sessionName, sessionConfig);
637
- const workspaceKey = sessionConfig.workspaceDir || "<global>";
638
- if (!sessionsByWorkspace.has(workspaceKey))
639
- sessionsByWorkspace.set(workspaceKey, []);
640
- sessionsByWorkspace.get(workspaceKey).push(session);
641
- } catch {
642
- }
327
+ console.log(`${workspace}:`);
328
+ await gcAndPrintSessions(list.map((entry) => new import_session.Session(clientInfo, entry.config)));
643
329
  }
644
- }
645
- if (sessionsByWorkspace.size === 0) {
646
- console.log("No browsers found.");
647
- return;
648
- }
649
- const sortedWorkspaces = [...sessionsByWorkspace.keys()].sort();
650
- for (const workspace of sortedWorkspaces) {
651
- const sessions = sessionsByWorkspace.get(workspace);
652
- console.log(`${workspace}:`);
653
- await gcAndPrintSessions(sessions);
330
+ } else {
331
+ console.log("### Browsers");
332
+ const entries = registry.entries(clientInfo);
333
+ await gcAndPrintSessions(entries.map((entry) => new import_session.Session(clientInfo, entry.config)));
654
334
  }
655
335
  }
656
336
  async function gcAndPrintSessions(sessions) {
657
337
  const running = [];
658
338
  const stopped = [];
659
- for (const session of sessions.values()) {
339
+ for (const session of sessions) {
660
340
  const canConnect = await session.canConnect();
661
341
  if (canConnect) {
662
342
  running.push(session);
663
343
  } else {
664
- if (session.config().cli.persistent)
344
+ if (session.config.cli.persistent)
665
345
  stopped.push(session);
666
346
  else
667
347
  await session.deleteSessionConfig();
@@ -676,44 +356,16 @@ async function gcAndPrintSessions(sessions) {
676
356
  }
677
357
  async function renderSessionStatus(session) {
678
358
  const text = [];
679
- const config = session.config();
359
+ const config = session.config;
680
360
  const canConnect = await session.canConnect();
681
361
  text.push(`- ${session.name}:`);
682
362
  text.push(` - status: ${canConnect ? "open" : "closed"}`);
683
363
  if (canConnect && !session.isCompatible())
684
364
  text.push(` - version: v${config.version} [incompatible please re-open]`);
685
365
  if (config.resolvedConfig)
686
- text.push(...renderResolvedConfig(config.resolvedConfig));
366
+ text.push(...(0, import_session.renderResolvedConfig)(config.resolvedConfig));
687
367
  return text.join("\n");
688
368
  }
689
- function renderResolvedConfig(resolvedConfig) {
690
- const channel = resolvedConfig.browser.launchOptions.channel ?? resolvedConfig.browser.browserName;
691
- const lines = [];
692
- if (channel)
693
- lines.push(` - browser-type: ${channel}`);
694
- if (resolvedConfig.browser.isolated)
695
- lines.push(` - user-data-dir: <in-memory>`);
696
- else
697
- lines.push(` - user-data-dir: ${resolvedConfig.browser.userDataDir}`);
698
- lines.push(` - headed: ${!resolvedConfig.browser.launchOptions.headless}`);
699
- return lines;
700
- }
701
- async function parseResolvedConfig(errLog) {
702
- const marker = "### Config\n```json\n";
703
- const markerIndex = errLog.indexOf(marker);
704
- if (markerIndex === -1)
705
- return null;
706
- const jsonStart = markerIndex + marker.length;
707
- const jsonEnd = errLog.indexOf("\n```", jsonStart);
708
- if (jsonEnd === -1)
709
- throw null;
710
- const jsonString = errLog.substring(jsonStart, jsonEnd).trim();
711
- try {
712
- return JSON.parse(jsonString);
713
- } catch {
714
- return null;
715
- }
716
- }
717
369
  // Annotate the CommonJS export names for ESM import in node:
718
370
  0 && (module.exports = {
719
371
  program