@bigbinary/neeto-playwright-commons 3.0.8 → 3.1.1

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 CHANGED
@@ -132,6 +132,7 @@ import { COMMON_SELECTORS } from "@bigbinary/neeto-playwright-common";
132
132
  - [MailerUtils](./docs/utils/mailer-utils.md)
133
133
  - [EmailDeliveryUtils](./docs/utils/email-delivery-utils.md)
134
134
  - [NeetoAuthServer](./docs/utils/neeto-auth-server.md)
135
+ - [NeetoChatWidget](./docs/utils/neeto-chat-widget.md)
135
136
  - [Util functions](./docs/utils)
136
137
  - [POMs](./docs/poms)
137
138
  - [Integration](./docs/integration)
package/index.cjs.js CHANGED
@@ -60529,7 +60529,7 @@ class RailsEmailUtils {
60529
60529
  });
60530
60530
  return otp || undefined;
60531
60531
  };
60532
- getEmailAttachment = async (attachmentName, messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, timeout = 10_000, } = {}, shouldThrowErrorOnTimeout = true) => {
60532
+ getEmailAttachment = async ({ name, type }, messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, timeout = 10_000, } = {}, shouldThrowErrorOnTimeout = true) => {
60533
60533
  const attachmentDetails = (await this.neetoPlaywrightUtilities.executeRecursively({
60534
60534
  callback: async () => {
60535
60535
  const railsEmail = await this.railsEmailClient.getLatestEmail({
@@ -60538,7 +60538,11 @@ class RailsEmailUtils {
60538
60538
  });
60539
60539
  if (!railsEmail)
60540
60540
  return null;
60541
- const attachment = railsEmail.attachments?.find(att => att.name.includes(attachmentName));
60541
+ const attachment = railsEmail.attachments?.find(att => {
60542
+ const matchesName = name ? att.name?.includes(name) : true;
60543
+ const matchesType = type ? att.type?.startsWith(type) : true;
60544
+ return matchesName && matchesType;
60545
+ });
60542
60546
  if (!attachment)
60543
60547
  return null;
60544
60548
  return {
@@ -60722,19 +60726,26 @@ class MailerUtils {
60722
60726
  return codes?.[0];
60723
60727
  };
60724
60728
  generateRandomEmail = () => faker.faker.internet.email({ provider: process.env.FASTMAIL_DOMAIN_NAME });
60725
- getEmailAttachment = async (attachmentName, messageSearchCriteria = {}, { timeout = 10_000, receivedAfter = dateTimeOneHourAgo(), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
60726
- if (IS_DEV_ENV) {
60727
- return this.railsEmailUtils.getEmailAttachment(attachmentName, messageSearchCriteria, { receivedAfter, expectedEmailCount, timeout: timeout / 3 }, shouldThrowErrorOnTimeout);
60728
- }
60729
+ getEmailAttachment = async ({ name, type }, messageSearchCriteria = {}, { timeout = 10_000, receivedAfter = dateTimeOneHourAgo(), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
60730
+ if (IS_DEV_ENV)
60731
+ return this.railsEmailUtils.getEmailAttachment({ name, type }, messageSearchCriteria, { receivedAfter, expectedEmailCount, timeout: timeout / 3 }, shouldThrowErrorOnTimeout);
60729
60732
  const { blobId, attachments: attachmentNameAndTypes } = await this.findMessage(messageSearchCriteria, { expectedEmailCount, receivedAfter, timeout }, shouldThrowErrorOnTimeout);
60730
- const attachment = attachmentNameAndTypes.find(attachment => attachment.name.includes(attachmentName));
60733
+ const attachment = attachmentNameAndTypes.find(att => {
60734
+ const matchesName = name ? att.name?.includes(name) : true;
60735
+ const matchesType = type ? att.type?.startsWith(type) : true;
60736
+ return matchesName && matchesType;
60737
+ });
60731
60738
  if (!attachment)
60732
60739
  throw new Error("No such attachment exists");
60733
60740
  const emailAttachment = await this.fastmailApi.fetchAttachments(blobId, attachment.name);
60734
60741
  const buffer = await emailAttachment.body();
60735
60742
  const parsedEmail = await mailparserExports.simpleParser(buffer);
60736
60743
  const attachments = parsedEmail.attachments;
60737
- return attachments.find(attachment => attachment.filename.includes(attachmentName));
60744
+ return attachments.find(att => {
60745
+ const matchesName = name ? att.filename?.includes(name) : true;
60746
+ const matchesType = type ? att.contentType?.startsWith(type) : true;
60747
+ return matchesName && matchesType;
60748
+ });
60738
60749
  };
60739
60750
  }
60740
60751
 
@@ -125124,22 +125135,22 @@ async function warmup({ urls = DEFAULT_WARMUP_URLS, timeout = 60_000, } = {}) {
125124
125135
  }
125125
125136
  }
125126
125137
 
125127
- const CONFIG = {
125138
+ const CONFIG$1 = {
125128
125139
  DIR: "/tmp/neeto-auth-web",
125129
125140
  LOG: "/tmp/neeto-auth-server.log",
125130
125141
  PORT: 9000,
125131
125142
  MAX_WAIT_MS: 120_000,
125132
125143
  EMAIL_INIT: "/tmp/neeto-auth-web/config/initializers/playwright_email_capture.rb",
125133
125144
  };
125134
- const SCRIPTS_DIR = path__namespace.join(__dirname, "scripts", "neeto-auth");
125145
+ const SCRIPTS_DIR$1 = path__namespace.join(__dirname, "scripts", "neeto-auth");
125135
125146
  const escapeRubyString = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
125136
125147
  const buildEmailCaptureInitializer = (targetApp) => {
125137
- const template = fs__namespace.readFileSync(path__namespace.join(SCRIPTS_DIR, "playwright_email_capture.rb.template"), "utf-8");
125148
+ const template = fs__namespace.readFileSync(path__namespace.join(SCRIPTS_DIR$1, "playwright_email_capture.rb.template"), "utf-8");
125138
125149
  return template.replace("{{TARGET_APP}}", escapeRubyString(targetApp));
125139
125150
  };
125140
- const log = (msg) => console.log(`[NeetoAuth] ${msg}`);
125141
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
125142
- const httpCheck = (port, timeout = 3000) => new Promise(resolve => {
125151
+ const log$1 = (msg) => console.log(`[NeetoAuth] ${msg}`);
125152
+ const sleep$1 = (ms) => new Promise(resolve => setTimeout(resolve, ms));
125153
+ const httpCheck$1 = (port, timeout = 3000) => new Promise(resolve => {
125143
125154
  const req = http__namespace.get(`http://localhost:${port}/`, () => resolve(true));
125144
125155
  req.on("error", () => resolve(false));
125145
125156
  req.setTimeout(timeout, () => {
@@ -125166,7 +125177,7 @@ class NeetoAuthServer {
125166
125177
  while (Date.now() < deadline) {
125167
125178
  try {
125168
125179
  process.kill(pid, 0);
125169
- await sleep(500);
125180
+ await sleep$1(500);
125170
125181
  }
125171
125182
  catch {
125172
125183
  return;
@@ -125175,27 +125186,27 @@ class NeetoAuthServer {
125175
125186
  };
125176
125187
  killServerOnPort = async () => {
125177
125188
  try {
125178
- child_process.execSync(`lsof -ti tcp:${CONFIG.PORT} | xargs kill -9`, {
125189
+ child_process.execSync(`lsof -ti tcp:${CONFIG$1.PORT} | xargs kill -9`, {
125179
125190
  stdio: "ignore",
125180
125191
  });
125181
- await sleep(1000);
125192
+ await sleep$1(1000);
125182
125193
  }
125183
125194
  catch {
125184
125195
  // No process on that port — nothing to kill.
125185
125196
  }
125186
125197
  };
125187
125198
  ensureSetup = () => {
125188
- if (fs__namespace.existsSync(`${CONFIG.DIR}/Gemfile`)) {
125189
- log("Already set up, skipping.");
125199
+ if (fs__namespace.existsSync(`${CONFIG$1.DIR}/Gemfile`)) {
125200
+ log$1("Already set up, skipping.");
125190
125201
  return;
125191
125202
  }
125192
125203
  if (!process.env.GITHUB_TOKEN) {
125193
125204
  throw new Error("[NeetoAuth] GITHUB_TOKEN is not set. Cannot clone neeto-auth-web.");
125194
125205
  }
125195
- log("Running first-time setup...");
125196
- const setupScript = path__namespace.join(SCRIPTS_DIR, "setup.sh");
125206
+ log$1("Running first-time setup...");
125207
+ const setupScript = path__namespace.join(SCRIPTS_DIR$1, "setup.sh");
125197
125208
  try {
125198
- child_process.execSync(`bash "${setupScript}" "${CONFIG.DIR}"`, {
125209
+ child_process.execSync(`bash "${setupScript}" "${CONFIG$1.DIR}"`, {
125199
125210
  stdio: "inherit",
125200
125211
  env: this.serverEnv,
125201
125212
  });
@@ -125203,73 +125214,73 @@ class NeetoAuthServer {
125203
125214
  catch {
125204
125215
  throw new Error("[NeetoAuth] First-time setup failed. Check the output above for details.");
125205
125216
  }
125206
- log("Setup complete.");
125217
+ log$1("Setup complete.");
125207
125218
  };
125208
- waitForServer = async (timeoutMs = CONFIG.MAX_WAIT_MS) => {
125219
+ waitForServer = async (timeoutMs = CONFIG$1.MAX_WAIT_MS) => {
125209
125220
  const startTime = Date.now();
125210
125221
  while (Date.now() - startTime < timeoutMs) {
125211
125222
  if (this.process !== null && this.process.exitCode !== null) {
125212
- const logContent = fs__namespace.existsSync(CONFIG.LOG)
125213
- ? fs__namespace.readFileSync(CONFIG.LOG, "utf-8")
125223
+ const logContent = fs__namespace.existsSync(CONFIG$1.LOG)
125224
+ ? fs__namespace.readFileSync(CONFIG$1.LOG, "utf-8")
125214
125225
  : "<no log file>";
125215
125226
  throw new Error(`[NeetoAuth] Server process exited unexpectedly with code ${this.process.exitCode}.\nLog:\n${logContent}`);
125216
125227
  }
125217
- if (await httpCheck(CONFIG.PORT))
125228
+ if (await httpCheck$1(CONFIG$1.PORT))
125218
125229
  return;
125219
- await sleep(3000);
125230
+ await sleep$1(3000);
125220
125231
  }
125221
- const logContent = fs__namespace.existsSync(CONFIG.LOG)
125222
- ? fs__namespace.readFileSync(CONFIG.LOG, "utf-8")
125232
+ const logContent = fs__namespace.existsSync(CONFIG$1.LOG)
125233
+ ? fs__namespace.readFileSync(CONFIG$1.LOG, "utf-8")
125223
125234
  : "<no log file>";
125224
125235
  throw new Error(`[NeetoAuth] Server did not start within ${timeoutMs / 1000}s.\nLog:\n${logContent}`);
125225
125236
  };
125226
125237
  injectEmailCaptureInitializer = () => {
125227
- fs__namespace.writeFileSync(CONFIG.EMAIL_INIT, buildEmailCaptureInitializer(this.targetApp));
125238
+ fs__namespace.writeFileSync(CONFIG$1.EMAIL_INIT, buildEmailCaptureInitializer(this.targetApp));
125228
125239
  };
125229
125240
  isRunningForCurrentApp = () => {
125230
- if (!fs__namespace.existsSync(CONFIG.EMAIL_INIT))
125241
+ if (!fs__namespace.existsSync(CONFIG$1.EMAIL_INIT))
125231
125242
  return false;
125232
- const currentContent = fs__namespace.readFileSync(CONFIG.EMAIL_INIT, "utf-8");
125243
+ const currentContent = fs__namespace.readFileSync(CONFIG$1.EMAIL_INIT, "utf-8");
125233
125244
  return currentContent === buildEmailCaptureInitializer(this.targetApp);
125234
125245
  };
125235
125246
  start = async () => {
125236
125247
  if (IS_STAGING_ENV)
125237
125248
  return;
125238
- if (await httpCheck(CONFIG.PORT)) {
125249
+ if (await httpCheck$1(CONFIG$1.PORT)) {
125239
125250
  if (this.isRunningForCurrentApp()) {
125240
- log("Server already running for this app.");
125251
+ log$1("Server already running for this app.");
125241
125252
  return;
125242
125253
  }
125243
- log("Server running for a different app — restarting...");
125254
+ log$1("Server running for a different app — restarting...");
125244
125255
  await this.killServerOnPort();
125245
125256
  }
125246
125257
  this.ensureSetup();
125247
125258
  this.injectEmailCaptureInitializer();
125248
- log("Starting server...");
125249
- const logStream = fs__namespace.createWriteStream(CONFIG.LOG);
125259
+ log$1("Starting server...");
125260
+ const logStream = fs__namespace.createWriteStream(CONFIG$1.LOG);
125250
125261
  this.process = child_process.spawn("bundle", [
125251
125262
  "exec",
125252
125263
  "rails",
125253
125264
  "server",
125254
125265
  "-p",
125255
- CONFIG.PORT.toString(),
125266
+ CONFIG$1.PORT.toString(),
125256
125267
  "-b",
125257
125268
  "0.0.0.0",
125258
125269
  ], {
125259
- cwd: CONFIG.DIR,
125270
+ cwd: CONFIG$1.DIR,
125260
125271
  stdio: ["ignore", "pipe", "pipe"],
125261
125272
  env: this.serverEnv,
125262
125273
  });
125263
125274
  this.process.stdout?.pipe(logStream);
125264
125275
  this.process.stderr?.pipe(logStream);
125265
125276
  await this.waitForServer();
125266
- log("Server ready.");
125277
+ log$1("Server ready.");
125267
125278
  };
125268
125279
  stop = async () => {
125269
125280
  if (IS_STAGING_ENV || !this.process)
125270
125281
  return;
125271
125282
  const pid = this.process.pid;
125272
- log("Stopping server...");
125283
+ log$1("Stopping server...");
125273
125284
  this.process.kill("SIGTERM");
125274
125285
  this.process = null;
125275
125286
  if (pid) {
@@ -125278,6 +125289,238 @@ class NeetoAuthServer {
125278
125289
  };
125279
125290
  }
125280
125291
 
125292
+ const CONFIG = {
125293
+ DIR: "/tmp/neeto-chat-widget",
125294
+ LOG: "/tmp/neeto-chat-widget.log",
125295
+ PORT: 8081,
125296
+ MAX_WAIT_MS: 180_000,
125297
+ LOCK_RETRY_MS: 500,
125298
+ SERVER_POLL_INTERVAL_MS: 3000,
125299
+ WEBPACK_DEV_FILE: "/tmp/neeto-chat-widget/webpack.dev.js",
125300
+ LOCK_DIR: "/tmp/neeto-chat-widget.lock",
125301
+ STATE_FILE: "/tmp/neeto-chat-widget.state.json",
125302
+ };
125303
+ const DEFAULT_STATE = { pid: null, refCount: 0 };
125304
+ const WIDGET_BASE_URL = `http://localhost:${CONFIG.PORT}`;
125305
+ const SCRIPTS_DIR = path__namespace.join(__dirname, "scripts", "neeto-chat-widget");
125306
+ const log = (msg) => console.log(`[NeetoChatWidget] ${msg}`);
125307
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
125308
+ const httpCheck = (timeout = 3000) => new Promise(resolve => {
125309
+ const req = http__namespace.get(`${WIDGET_BASE_URL}/main.js`, () => resolve(true));
125310
+ req.on("error", () => resolve(false));
125311
+ req.setTimeout(timeout, () => {
125312
+ req.destroy();
125313
+ resolve(false);
125314
+ });
125315
+ });
125316
+ class NeetoChatWidget {
125317
+ process = null;
125318
+ hasLease = false;
125319
+ logStream = null;
125320
+ get serverEnv() {
125321
+ return { ...process.env };
125322
+ }
125323
+ withLock = async (fn) => {
125324
+ while (true) {
125325
+ try {
125326
+ fs$4.mkdirSync(CONFIG.LOCK_DIR);
125327
+ break;
125328
+ }
125329
+ catch (error) {
125330
+ const nodeError = error;
125331
+ if (nodeError.code !== "EEXIST")
125332
+ throw error;
125333
+ await sleep(CONFIG.LOCK_RETRY_MS);
125334
+ }
125335
+ }
125336
+ try {
125337
+ return await Promise.resolve(fn());
125338
+ }
125339
+ finally {
125340
+ fs$4.rmSync(CONFIG.LOCK_DIR, { force: true, recursive: true });
125341
+ }
125342
+ };
125343
+ parseState = (raw) => {
125344
+ if (!raw || typeof raw !== "object")
125345
+ return DEFAULT_STATE;
125346
+ const state = raw;
125347
+ const pid = typeof state.pid === "number" ? state.pid : null;
125348
+ const refCount = typeof state.refCount === "number" && state.refCount > 0
125349
+ ? state.refCount
125350
+ : 0;
125351
+ return { pid, refCount };
125352
+ };
125353
+ readState = () => {
125354
+ if (!fs$4.existsSync(CONFIG.STATE_FILE))
125355
+ return DEFAULT_STATE;
125356
+ try {
125357
+ return this.parseState(JSON.parse(fs$4.readFileSync(CONFIG.STATE_FILE, "utf-8")));
125358
+ }
125359
+ catch {
125360
+ return DEFAULT_STATE;
125361
+ }
125362
+ };
125363
+ writeState = (state) => {
125364
+ fs$4.writeFileSync(CONFIG.STATE_FILE, JSON.stringify(state));
125365
+ };
125366
+ clearState = () => {
125367
+ fs$4.existsSync(CONFIG.STATE_FILE) && fs$4.rmSync(CONFIG.STATE_FILE, { force: true });
125368
+ };
125369
+ getLogContent = () => fs$4.existsSync(CONFIG.LOG)
125370
+ ? fs$4.readFileSync(CONFIG.LOG, "utf-8")
125371
+ : "<no log file>";
125372
+ ensureSetup = () => {
125373
+ if (fs$4.existsSync(CONFIG.WEBPACK_DEV_FILE)) {
125374
+ this.patchWebpackPublicPath();
125375
+ log("Already set up, skipping.");
125376
+ return;
125377
+ }
125378
+ fs$4.existsSync(CONFIG.DIR) &&
125379
+ fs$4.rmSync(CONFIG.DIR, { force: true, recursive: true });
125380
+ const setupScript = path__namespace.join(SCRIPTS_DIR, "setup.sh");
125381
+ log("Running first-time setup...");
125382
+ try {
125383
+ child_process.execSync(`bash "${setupScript}" "${CONFIG.DIR}"`, {
125384
+ stdio: "inherit",
125385
+ env: this.serverEnv,
125386
+ });
125387
+ }
125388
+ catch {
125389
+ throw new Error("[NeetoChatWidget] First-time setup failed. Check the output above for details.");
125390
+ }
125391
+ this.patchWebpackPublicPath();
125392
+ log("Setup complete.");
125393
+ };
125394
+ patchWebpackPublicPath = () => {
125395
+ if (!fs$4.existsSync(CONFIG.WEBPACK_DEV_FILE)) {
125396
+ throw new Error(`[NeetoChatWidget] Missing webpack config at ${CONFIG.WEBPACK_DEV_FILE}`);
125397
+ }
125398
+ const fileContent = fs$4.readFileSync(CONFIG.WEBPACK_DEV_FILE, "utf-8");
125399
+ const targetPublicPath = `publicPath: "${WIDGET_BASE_URL}/"`;
125400
+ if (fileContent.includes(targetPublicPath))
125401
+ return;
125402
+ const oldConfig = ` devServer: {\n contentBase: "./src",\n allowedHosts: [".lvh.me"],\n },\n`;
125403
+ const newConfig = ` devServer: {\n contentBase: "./src",\n allowedHosts: [".lvh.me"],\n publicPath: "${WIDGET_BASE_URL}/",\n },\n output: {\n publicPath: "${WIDGET_BASE_URL}/",\n },\n`;
125404
+ if (!fileContent.includes(oldConfig)) {
125405
+ throw new Error("[NeetoChatWidget] Unable to patch webpack.dev.js for local widget serving.");
125406
+ }
125407
+ fs$4.writeFileSync(CONFIG.WEBPACK_DEV_FILE, fileContent.replace(oldConfig, newConfig));
125408
+ };
125409
+ closeLogStream = () => {
125410
+ if (this.logStream) {
125411
+ this.logStream.end();
125412
+ this.logStream = null;
125413
+ }
125414
+ };
125415
+ waitForProcessExit = async (pid) => {
125416
+ const deadline = Date.now() + 10_000;
125417
+ while (Date.now() < deadline) {
125418
+ try {
125419
+ process.kill(pid, 0);
125420
+ await sleep(500);
125421
+ }
125422
+ catch {
125423
+ return;
125424
+ }
125425
+ }
125426
+ };
125427
+ killServer = (pid) => {
125428
+ if (!pid)
125429
+ return;
125430
+ try {
125431
+ process.kill(pid, "SIGTERM");
125432
+ }
125433
+ catch {
125434
+ // process may already be gone
125435
+ }
125436
+ };
125437
+ killProcessOnWidgetPort = async () => {
125438
+ try {
125439
+ child_process.execSync(`lsof -ti tcp:${CONFIG.PORT} | xargs kill -9`, {
125440
+ stdio: "ignore",
125441
+ });
125442
+ await sleep(1000);
125443
+ }
125444
+ catch {
125445
+ // No process on that port — nothing to kill.
125446
+ }
125447
+ };
125448
+ waitForServer = async (timeoutMs = CONFIG.MAX_WAIT_MS) => {
125449
+ const startTime = Date.now();
125450
+ while (Date.now() - startTime < timeoutMs) {
125451
+ if (this.process !== null && this.process.exitCode !== null) {
125452
+ throw new Error(`[NeetoChatWidget] Server process exited unexpectedly with code ${this.process.exitCode}.\nLog:\n${this.getLogContent()}`);
125453
+ }
125454
+ if (await httpCheck())
125455
+ return;
125456
+ await sleep(CONFIG.SERVER_POLL_INTERVAL_MS);
125457
+ }
125458
+ throw new Error(`[NeetoChatWidget] Widget did not start within ${timeoutMs / 1000}s.\nLog:\n${this.getLogContent()}`);
125459
+ };
125460
+ start = async () => {
125461
+ if (IS_STAGING_ENV)
125462
+ return;
125463
+ await this.withLock(async () => {
125464
+ const state = this.readState();
125465
+ if (await httpCheck()) {
125466
+ this.hasLease = true;
125467
+ this.writeState({ ...state, refCount: state.refCount + 1 });
125468
+ log("Server already running.");
125469
+ return;
125470
+ }
125471
+ this.ensureSetup();
125472
+ log("Starting server...");
125473
+ this.logStream = fs$4.createWriteStream(CONFIG.LOG);
125474
+ this.process = child_process.spawn("yarn", ["start"], {
125475
+ cwd: CONFIG.DIR,
125476
+ stdio: ["ignore", "pipe", "pipe"],
125477
+ });
125478
+ this.process.stdout?.pipe(this.logStream);
125479
+ this.process.stderr?.pipe(this.logStream);
125480
+ try {
125481
+ await this.waitForServer();
125482
+ }
125483
+ catch (error) {
125484
+ const pid = this.process?.pid;
125485
+ this.killServer(pid);
125486
+ pid && (await this.waitForProcessExit(pid));
125487
+ (await httpCheck()) && (await this.killProcessOnWidgetPort());
125488
+ this.process = null;
125489
+ this.closeLogStream();
125490
+ throw error;
125491
+ }
125492
+ this.hasLease = true;
125493
+ this.writeState({
125494
+ pid: this.process.pid ?? null,
125495
+ refCount: state.refCount + 1,
125496
+ });
125497
+ log("Server ready.");
125498
+ });
125499
+ };
125500
+ stop = async () => {
125501
+ if (IS_STAGING_ENV || !this.hasLease)
125502
+ return;
125503
+ await this.withLock(async () => {
125504
+ const state = this.readState();
125505
+ const nextRefCount = Math.max(state.refCount - 1, 0);
125506
+ this.hasLease = false;
125507
+ if (nextRefCount > 0) {
125508
+ this.writeState({ ...state, refCount: nextRefCount });
125509
+ return;
125510
+ }
125511
+ if (state.pid) {
125512
+ log("Stopping server...");
125513
+ this.killServer(state.pid);
125514
+ await this.waitForProcessExit(state.pid);
125515
+ (await httpCheck()) && (await this.killProcessOnWidgetPort());
125516
+ }
125517
+ this.process = null;
125518
+ this.closeLogStream();
125519
+ this.clearState();
125520
+ });
125521
+ };
125522
+ }
125523
+
125281
125524
  const NEETO_EMAIL_DELIVERY_TESTING_BASE_URL = "/neeto_email_delivery/api/v1/testing";
125282
125525
  class NeetoEmailDeliveryApi {
125283
125526
  neetoPlaywrightUtilities;
@@ -126226,6 +126469,7 @@ exports.CREDENTIALS = CREDENTIALS;
126226
126469
  exports.CURRENT_TIME_RANGES = CURRENT_TIME_RANGES;
126227
126470
  exports.CUSTOM_DOMAIN_SELECTORS = CUSTOM_DOMAIN_SELECTORS;
126228
126471
  exports.CUSTOM_DOMAIN_SUFFIX = CUSTOM_DOMAIN_SUFFIX;
126472
+ exports.ColorPickerUtils = ColorPickerUtils;
126229
126473
  exports.CustomCommands = CustomCommands;
126230
126474
  exports.CustomDomainApi = CustomDomainApi;
126231
126475
  exports.CustomDomainPage = CustomDomainPage;
@@ -126293,6 +126537,7 @@ exports.NEETO_ROUTES = NEETO_ROUTES;
126293
126537
  exports.NEETO_SEO_SELECTORS = NEETO_SEO_SELECTORS;
126294
126538
  exports.NEETO_TEXT_MODIFIER_SELECTORS = NEETO_TEXT_MODIFIER_SELECTORS;
126295
126539
  exports.NeetoAuthServer = NeetoAuthServer;
126540
+ exports.NeetoChatWidget = NeetoChatWidget;
126296
126541
  exports.NeetoEmailDeliveryApi = NeetoEmailDeliveryApi;
126297
126542
  exports.NeetoTowerApi = NeetoTowerApi;
126298
126543
  exports.ONBOARDING_SELECTORS = ONBOARDING_SELECTORS;