@specific.dev/cli 0.1.66 → 0.1.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +2 -2
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
  5. package/dist/admin/__next._full.txt +9 -9
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +4 -4
  8. package/dist/admin/__next._tree.txt +2 -2
  9. package/dist/admin/_next/static/chunks/47a5dab862795de7.js +1 -0
  10. package/dist/admin/_next/static/chunks/{77284e343252b102.js → 63a5ddab5f6a075d.js} +2 -2
  11. package/dist/admin/_next/static/chunks/{c7954d71061f1f9b.js → 71775bad64b386a3.js} +2 -2
  12. package/dist/admin/_next/static/chunks/8cd2655984f0da65.js +1 -0
  13. package/dist/admin/_next/static/chunks/bac545c7353852cd.css +4 -0
  14. package/dist/admin/_not-found/__next._full.txt +4 -4
  15. package/dist/admin/_not-found/__next._head.txt +1 -1
  16. package/dist/admin/_not-found/__next._index.txt +4 -4
  17. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  18. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  19. package/dist/admin/_not-found/__next._tree.txt +2 -2
  20. package/dist/admin/_not-found/index.html +1 -1
  21. package/dist/admin/_not-found/index.txt +4 -4
  22. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
  23. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  24. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
  25. package/dist/admin/databases/__next._full.txt +9 -9
  26. package/dist/admin/databases/__next._head.txt +1 -1
  27. package/dist/admin/databases/__next._index.txt +4 -4
  28. package/dist/admin/databases/__next._tree.txt +2 -2
  29. package/dist/admin/databases/index.html +1 -1
  30. package/dist/admin/databases/index.txt +9 -9
  31. package/dist/admin/fullscreen/__next._full.txt +5 -5
  32. package/dist/admin/fullscreen/__next._head.txt +1 -1
  33. package/dist/admin/fullscreen/__next._index.txt +4 -4
  34. package/dist/admin/fullscreen/__next._tree.txt +2 -2
  35. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
  36. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next._full.txt +5 -5
  38. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  39. package/dist/admin/fullscreen/databases/__next._index.txt +4 -4
  40. package/dist/admin/fullscreen/databases/__next._tree.txt +2 -2
  41. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
  42. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  43. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  44. package/dist/admin/fullscreen/databases/index.html +1 -1
  45. package/dist/admin/fullscreen/databases/index.txt +5 -5
  46. package/dist/admin/fullscreen/index.html +1 -1
  47. package/dist/admin/fullscreen/index.txt +5 -5
  48. package/dist/admin/index.html +1 -1
  49. package/dist/admin/index.txt +9 -9
  50. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +9 -0
  51. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +4 -0
  52. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +8 -0
  53. package/dist/admin/mail/__next._full.txt +27 -0
  54. package/dist/admin/mail/__next._head.txt +6 -0
  55. package/dist/admin/mail/__next._index.txt +7 -0
  56. package/dist/admin/mail/__next._tree.txt +5 -0
  57. package/dist/admin/mail/index.html +1 -0
  58. package/dist/admin/mail/index.txt +27 -0
  59. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
  60. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
  61. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  62. package/dist/admin/workflows/__next._full.txt +9 -9
  63. package/dist/admin/workflows/__next._head.txt +1 -1
  64. package/dist/admin/workflows/__next._index.txt +4 -4
  65. package/dist/admin/workflows/__next._tree.txt +2 -2
  66. package/dist/admin/workflows/index.html +1 -1
  67. package/dist/admin/workflows/index.txt +9 -9
  68. package/dist/cli.js +675 -183
  69. package/dist/docs/index.md +3 -0
  70. package/dist/docs/mail.md +66 -0
  71. package/dist/postinstall.js +1 -1
  72. package/package.json +7 -2
  73. package/dist/admin/_next/static/chunks/497f00630c8a5681.js +0 -1
  74. package/dist/admin/_next/static/chunks/8342a9e3e2851626.css +0 -4
  75. /package/dist/admin/_next/static/{Al0o3YauO07qHR3spSA9w → w4VP36_YGzWIvqWZUyEgj}/_buildManifest.js +0 -0
  76. /package/dist/admin/_next/static/{Al0o3YauO07qHR3spSA9w → w4VP36_YGzWIvqWZUyEgj}/_clientMiddlewareManifest.json +0 -0
  77. /package/dist/admin/_next/static/{Al0o3YauO07qHR3spSA9w → w4VP36_YGzWIvqWZUyEgj}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -238,15 +238,15 @@ var init_wsl_utils = __esm({
238
238
  const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
239
239
  return stdout.trim();
240
240
  };
241
- convertWslPathToWindows = async (path28) => {
242
- if (/^[a-z]+:\/\//i.test(path28)) {
243
- return path28;
241
+ convertWslPathToWindows = async (path30) => {
242
+ if (/^[a-z]+:\/\//i.test(path30)) {
243
+ return path30;
244
244
  }
245
245
  try {
246
- const { stdout } = await execFile2("wslpath", ["-aw", path28], { encoding: "utf8" });
246
+ const { stdout } = await execFile2("wslpath", ["-aw", path30], { encoding: "utf8" });
247
247
  return stdout.trim();
248
248
  } catch {
249
- return path28;
249
+ return path30;
250
250
  }
251
251
  };
252
252
  }
@@ -754,8 +754,8 @@ var require_dist = __commonJS({
754
754
  var $global, $module, $NaN = NaN;
755
755
  if ("undefined" != typeof window ? $global = window : "undefined" != typeof self ? $global = self : "undefined" != typeof global ? ($global = global).require = __require : $global = this, void 0 === $global || void 0 === $global.Array) throw new Error("no global object found");
756
756
  if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
757
- var fs30 = $global.require("fs");
758
- "object" == typeof fs30 && null !== fs30 && 0 !== Object.keys(fs30).length && ($global.fs = fs30);
757
+ var fs32 = $global.require("fs");
758
+ "object" == typeof fs32 && null !== fs32 && 0 !== Object.keys(fs32).length && ($global.fs = fs32);
759
759
  } catch (e) {
760
760
  }
761
761
  if (!$global.fs) {
@@ -183423,7 +183423,7 @@ ${frame}`;
183423
183423
  }
183424
183424
  });
183425
183425
 
183426
- // src/cli.tsx
183426
+ // src/cli-program.tsx
183427
183427
  import { Command } from "commander";
183428
183428
 
183429
183429
  // src/commands/init.tsx
@@ -184496,7 +184496,7 @@ function trackEvent(event, properties) {
184496
184496
  event,
184497
184497
  properties: {
184498
184498
  ...properties,
184499
- cli_version: "0.1.66",
184499
+ cli_version: "0.1.68",
184500
184500
  platform: process.platform,
184501
184501
  node_version: process.version,
184502
184502
  project_id: getProjectId(),
@@ -184868,6 +184868,10 @@ var BETA_REGISTRY = [
184868
184868
  {
184869
184869
  name: "temporal",
184870
184870
  description: "Managed Temporal workflow engine for durable workflows and background tasks"
184871
+ },
184872
+ {
184873
+ name: "mail",
184874
+ description: "Managed email sending via SMTP for transactional emails"
184871
184875
  }
184872
184876
  ];
184873
184877
 
@@ -184914,20 +184918,20 @@ function saveBetas(enabled, projectDir) {
184914
184918
  // src/commands/docs.tsx
184915
184919
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
184916
184920
  var docsDir = join8(__dirname2, "docs");
184917
- function docsCommand(path28) {
184918
- const docPath = resolveDocPath(path28);
184919
- if (!docPath) {
184921
+ var _embeddedDocs = null;
184922
+ function docsCommand(path30) {
184923
+ const content = resolveDocContent(path30);
184924
+ if (!content) {
184920
184925
  console.error(
184921
- `Documentation not found: ${path28 || "index"}
184926
+ `Documentation not found: ${path30 || "index"}
184922
184927
 
184923
184928
  Run 'specific docs' to see available topics.`
184924
184929
  );
184925
184930
  process.exit(1);
184926
184931
  }
184927
184932
  const enabledBetas = new Set(loadEnabledBetas());
184928
- let content = readFileSync6(docPath, "utf-8");
184929
- content = filterBetaTags(content, enabledBetas);
184930
- console.log(content);
184933
+ const filtered = filterBetaTags(content, enabledBetas);
184934
+ console.log(filtered);
184931
184935
  }
184932
184936
  function filterBetaTags(content, enabledBetas) {
184933
184937
  return content.replace(
@@ -184937,18 +184941,33 @@ function filterBetaTags(content, enabledBetas) {
184937
184941
  }
184938
184942
  );
184939
184943
  }
184940
- function resolveDocPath(path28) {
184941
- if (!path28) {
184944
+ function resolveDocContent(path30) {
184945
+ const normalized = path30?.replace(/^\/+|\/+$/g, "") || void 0;
184946
+ if (_embeddedDocs) {
184947
+ return resolveEmbeddedDoc(normalized);
184948
+ }
184949
+ return resolveFilesystemDoc(normalized);
184950
+ }
184951
+ function resolveEmbeddedDoc(path30) {
184952
+ if (!path30) {
184953
+ return _embeddedDocs.get("index.md") ?? null;
184954
+ }
184955
+ const direct = _embeddedDocs.get(`${path30}.md`);
184956
+ if (direct) return direct;
184957
+ return _embeddedDocs.get(`${path30}/index.md`) ?? null;
184958
+ }
184959
+ function resolveFilesystemDoc(path30) {
184960
+ if (!path30) {
184942
184961
  const indexPath2 = join8(docsDir, "index.md");
184943
- return existsSync7(indexPath2) ? indexPath2 : null;
184962
+ return existsSync7(indexPath2) ? readFileSync6(indexPath2, "utf-8") : null;
184944
184963
  }
184945
- const directPath = join8(docsDir, `${path28}.md`);
184964
+ const directPath = join8(docsDir, `${path30}.md`);
184946
184965
  if (existsSync7(directPath)) {
184947
- return directPath;
184966
+ return readFileSync6(directPath, "utf-8");
184948
184967
  }
184949
- const indexPath = join8(docsDir, path28, "index.md");
184968
+ const indexPath = join8(docsDir, path30, "index.md");
184950
184969
  if (existsSync7(indexPath)) {
184951
- return indexPath;
184970
+ return readFileSync6(indexPath, "utf-8");
184952
184971
  }
184953
184972
  return null;
184954
184973
  }
@@ -185073,6 +185092,17 @@ function parseReferenceString(str) {
185073
185092
  };
185074
185093
  }
185075
185094
  }
185095
+ const mailMatch = str.match(new RegExp(`^mail\\.(${id})\\.(\\w+)$`));
185096
+ if (mailMatch && mailMatch[1] && mailMatch[2]) {
185097
+ const attr = mailMatch[2];
185098
+ if (["host", "port", "user", "password", "from"].includes(attr)) {
185099
+ return {
185100
+ type: "mail",
185101
+ name: mailMatch[1],
185102
+ attribute: attr
185103
+ };
185104
+ }
185105
+ }
185076
185106
  const volumeMatch = str.match(new RegExp(`^volume\\.(${id})\\.(\\w+)$`));
185077
185107
  if (volumeMatch && volumeMatch[1] && volumeMatch[2]) {
185078
185108
  const attr = volumeMatch[2];
@@ -185117,7 +185147,7 @@ function parseReferenceString(str) {
185117
185147
  attribute: serviceMatch[2]
185118
185148
  };
185119
185149
  }
185120
- const knownPrefixes = ["build", "postgres", "redis", "storage", "temporal", "volume", "config", "secret", "endpoint", "service"];
185150
+ const knownPrefixes = ["build", "postgres", "redis", "storage", "temporal", "mail", "volume", "config", "secret", "endpoint", "service"];
185121
185151
  const prefixMatch = str.match(/^(\w+)\./);
185122
185152
  if (prefixMatch && knownPrefixes.includes(prefixMatch[1])) {
185123
185153
  throw new Error(`Invalid reference "\${${str}}". The prefix "${prefixMatch[1]}" is recognized but the reference format is invalid.`);
@@ -185467,6 +185497,16 @@ function parseTemporal(data) {
185467
185497
  }
185468
185498
  return result;
185469
185499
  }
185500
+ function parseMail(data) {
185501
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
185502
+ return [];
185503
+ }
185504
+ const result = [];
185505
+ for (const [name] of Object.entries(data)) {
185506
+ result.push({ name });
185507
+ }
185508
+ return result;
185509
+ }
185470
185510
  function parseConfigDevBlock(dev) {
185471
185511
  if (!dev) {
185472
185512
  return void 0;
@@ -185579,6 +185619,7 @@ async function parseConfig(hcl) {
185579
185619
  redis: parseRedis(json.redis),
185580
185620
  storage: parseStorage(json.storage),
185581
185621
  temporal: parseTemporal(json.temporal),
185622
+ mail: parseMail(json.mail),
185582
185623
  configs: parseConfigs(json.config),
185583
185624
  secrets: parseSecrets(json.secret),
185584
185625
  environments: parseEnvironments(json.environment)
@@ -186333,7 +186374,7 @@ var ReaddirpStream = class extends Readable {
186333
186374
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
186334
186375
  const statMethod = opts.lstat ? lstat : stat;
186335
186376
  if (wantBigintFsStats) {
186336
- this._stat = (path28) => statMethod(path28, { bigint: true });
186377
+ this._stat = (path30) => statMethod(path30, { bigint: true });
186337
186378
  } else {
186338
186379
  this._stat = statMethod;
186339
186380
  }
@@ -186358,8 +186399,8 @@ var ReaddirpStream = class extends Readable {
186358
186399
  const par = this.parent;
186359
186400
  const fil = par && par.files;
186360
186401
  if (fil && fil.length > 0) {
186361
- const { path: path28, depth } = par;
186362
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path28));
186402
+ const { path: path30, depth } = par;
186403
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
186363
186404
  const awaited = await Promise.all(slice);
186364
186405
  for (const entry of awaited) {
186365
186406
  if (!entry)
@@ -186399,20 +186440,20 @@ var ReaddirpStream = class extends Readable {
186399
186440
  this.reading = false;
186400
186441
  }
186401
186442
  }
186402
- async _exploreDir(path28, depth) {
186443
+ async _exploreDir(path30, depth) {
186403
186444
  let files;
186404
186445
  try {
186405
- files = await readdir(path28, this._rdOptions);
186446
+ files = await readdir(path30, this._rdOptions);
186406
186447
  } catch (error) {
186407
186448
  this._onError(error);
186408
186449
  }
186409
- return { files, depth, path: path28 };
186450
+ return { files, depth, path: path30 };
186410
186451
  }
186411
- async _formatEntry(dirent, path28) {
186452
+ async _formatEntry(dirent, path30) {
186412
186453
  let entry;
186413
186454
  const basename6 = this._isDirent ? dirent.name : dirent;
186414
186455
  try {
186415
- const fullPath = presolve(pjoin(path28, basename6));
186456
+ const fullPath = presolve(pjoin(path30, basename6));
186416
186457
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
186417
186458
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
186418
186459
  } catch (err) {
@@ -186812,16 +186853,16 @@ var delFromSet = (main, prop, item) => {
186812
186853
  };
186813
186854
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
186814
186855
  var FsWatchInstances = /* @__PURE__ */ new Map();
186815
- function createFsWatchInstance(path28, options2, listener, errHandler, emitRaw) {
186856
+ function createFsWatchInstance(path30, options2, listener, errHandler, emitRaw) {
186816
186857
  const handleEvent = (rawEvent, evPath) => {
186817
- listener(path28);
186818
- emitRaw(rawEvent, evPath, { watchedPath: path28 });
186819
- if (evPath && path28 !== evPath) {
186820
- fsWatchBroadcast(sp.resolve(path28, evPath), KEY_LISTENERS, sp.join(path28, evPath));
186858
+ listener(path30);
186859
+ emitRaw(rawEvent, evPath, { watchedPath: path30 });
186860
+ if (evPath && path30 !== evPath) {
186861
+ fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
186821
186862
  }
186822
186863
  };
186823
186864
  try {
186824
- return fs_watch(path28, {
186865
+ return fs_watch(path30, {
186825
186866
  persistent: options2.persistent
186826
186867
  }, handleEvent);
186827
186868
  } catch (error) {
@@ -186837,12 +186878,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
186837
186878
  listener(val1, val2, val3);
186838
186879
  });
186839
186880
  };
186840
- var setFsWatchListener = (path28, fullPath, options2, handlers) => {
186881
+ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
186841
186882
  const { listener, errHandler, rawEmitter } = handlers;
186842
186883
  let cont = FsWatchInstances.get(fullPath);
186843
186884
  let watcher;
186844
186885
  if (!options2.persistent) {
186845
- watcher = createFsWatchInstance(path28, options2, listener, errHandler, rawEmitter);
186886
+ watcher = createFsWatchInstance(path30, options2, listener, errHandler, rawEmitter);
186846
186887
  if (!watcher)
186847
186888
  return;
186848
186889
  return watcher.close.bind(watcher);
@@ -186853,7 +186894,7 @@ var setFsWatchListener = (path28, fullPath, options2, handlers) => {
186853
186894
  addAndConvert(cont, KEY_RAW, rawEmitter);
186854
186895
  } else {
186855
186896
  watcher = createFsWatchInstance(
186856
- path28,
186897
+ path30,
186857
186898
  options2,
186858
186899
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
186859
186900
  errHandler,
@@ -186868,7 +186909,7 @@ var setFsWatchListener = (path28, fullPath, options2, handlers) => {
186868
186909
  cont.watcherUnusable = true;
186869
186910
  if (isWindows && error.code === "EPERM") {
186870
186911
  try {
186871
- const fd = await open2(path28, "r");
186912
+ const fd = await open2(path30, "r");
186872
186913
  await fd.close();
186873
186914
  broadcastErr(error);
186874
186915
  } catch (err) {
@@ -186899,7 +186940,7 @@ var setFsWatchListener = (path28, fullPath, options2, handlers) => {
186899
186940
  };
186900
186941
  };
186901
186942
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
186902
- var setFsWatchFileListener = (path28, fullPath, options2, handlers) => {
186943
+ var setFsWatchFileListener = (path30, fullPath, options2, handlers) => {
186903
186944
  const { listener, rawEmitter } = handlers;
186904
186945
  let cont = FsWatchFileInstances.get(fullPath);
186905
186946
  const copts = cont && cont.options;
@@ -186921,7 +186962,7 @@ var setFsWatchFileListener = (path28, fullPath, options2, handlers) => {
186921
186962
  });
186922
186963
  const currmtime = curr.mtimeMs;
186923
186964
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
186924
- foreach(cont.listeners, (listener2) => listener2(path28, curr));
186965
+ foreach(cont.listeners, (listener2) => listener2(path30, curr));
186925
186966
  }
186926
186967
  })
186927
186968
  };
@@ -186951,13 +186992,13 @@ var NodeFsHandler = class {
186951
186992
  * @param listener on fs change
186952
186993
  * @returns closer for the watcher instance
186953
186994
  */
186954
- _watchWithNodeFs(path28, listener) {
186995
+ _watchWithNodeFs(path30, listener) {
186955
186996
  const opts = this.fsw.options;
186956
- const directory = sp.dirname(path28);
186957
- const basename6 = sp.basename(path28);
186997
+ const directory = sp.dirname(path30);
186998
+ const basename6 = sp.basename(path30);
186958
186999
  const parent = this.fsw._getWatchedDir(directory);
186959
187000
  parent.add(basename6);
186960
- const absolutePath = sp.resolve(path28);
187001
+ const absolutePath = sp.resolve(path30);
186961
187002
  const options2 = {
186962
187003
  persistent: opts.persistent
186963
187004
  };
@@ -186967,12 +187008,12 @@ var NodeFsHandler = class {
186967
187008
  if (opts.usePolling) {
186968
187009
  const enableBin = opts.interval !== opts.binaryInterval;
186969
187010
  options2.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
186970
- closer = setFsWatchFileListener(path28, absolutePath, options2, {
187011
+ closer = setFsWatchFileListener(path30, absolutePath, options2, {
186971
187012
  listener,
186972
187013
  rawEmitter: this.fsw._emitRaw
186973
187014
  });
186974
187015
  } else {
186975
- closer = setFsWatchListener(path28, absolutePath, options2, {
187016
+ closer = setFsWatchListener(path30, absolutePath, options2, {
186976
187017
  listener,
186977
187018
  errHandler: this._boundHandleError,
186978
187019
  rawEmitter: this.fsw._emitRaw
@@ -186988,13 +187029,13 @@ var NodeFsHandler = class {
186988
187029
  if (this.fsw.closed) {
186989
187030
  return;
186990
187031
  }
186991
- const dirname9 = sp.dirname(file);
187032
+ const dirname10 = sp.dirname(file);
186992
187033
  const basename6 = sp.basename(file);
186993
- const parent = this.fsw._getWatchedDir(dirname9);
187034
+ const parent = this.fsw._getWatchedDir(dirname10);
186994
187035
  let prevStats = stats;
186995
187036
  if (parent.has(basename6))
186996
187037
  return;
186997
- const listener = async (path28, newStats) => {
187038
+ const listener = async (path30, newStats) => {
186998
187039
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
186999
187040
  return;
187000
187041
  if (!newStats || newStats.mtimeMs === 0) {
@@ -187008,16 +187049,16 @@ var NodeFsHandler = class {
187008
187049
  this.fsw._emit(EV.CHANGE, file, newStats2);
187009
187050
  }
187010
187051
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
187011
- this.fsw._closeFile(path28);
187052
+ this.fsw._closeFile(path30);
187012
187053
  prevStats = newStats2;
187013
187054
  const closer2 = this._watchWithNodeFs(file, listener);
187014
187055
  if (closer2)
187015
- this.fsw._addPathCloser(path28, closer2);
187056
+ this.fsw._addPathCloser(path30, closer2);
187016
187057
  } else {
187017
187058
  prevStats = newStats2;
187018
187059
  }
187019
187060
  } catch (error) {
187020
- this.fsw._remove(dirname9, basename6);
187061
+ this.fsw._remove(dirname10, basename6);
187021
187062
  }
187022
187063
  } else if (parent.has(basename6)) {
187023
187064
  const at = newStats.atimeMs;
@@ -187044,7 +187085,7 @@ var NodeFsHandler = class {
187044
187085
  * @param item basename of this item
187045
187086
  * @returns true if no more processing is needed for this entry.
187046
187087
  */
187047
- async _handleSymlink(entry, directory, path28, item) {
187088
+ async _handleSymlink(entry, directory, path30, item) {
187048
187089
  if (this.fsw.closed) {
187049
187090
  return;
187050
187091
  }
@@ -187054,7 +187095,7 @@ var NodeFsHandler = class {
187054
187095
  this.fsw._incrReadyCount();
187055
187096
  let linkPath;
187056
187097
  try {
187057
- linkPath = await fsrealpath(path28);
187098
+ linkPath = await fsrealpath(path30);
187058
187099
  } catch (e) {
187059
187100
  this.fsw._emitReady();
187060
187101
  return true;
@@ -187064,12 +187105,12 @@ var NodeFsHandler = class {
187064
187105
  if (dir.has(item)) {
187065
187106
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
187066
187107
  this.fsw._symlinkPaths.set(full, linkPath);
187067
- this.fsw._emit(EV.CHANGE, path28, entry.stats);
187108
+ this.fsw._emit(EV.CHANGE, path30, entry.stats);
187068
187109
  }
187069
187110
  } else {
187070
187111
  dir.add(item);
187071
187112
  this.fsw._symlinkPaths.set(full, linkPath);
187072
- this.fsw._emit(EV.ADD, path28, entry.stats);
187113
+ this.fsw._emit(EV.ADD, path30, entry.stats);
187073
187114
  }
187074
187115
  this.fsw._emitReady();
187075
187116
  return true;
@@ -187099,9 +187140,9 @@ var NodeFsHandler = class {
187099
187140
  return;
187100
187141
  }
187101
187142
  const item = entry.path;
187102
- let path28 = sp.join(directory, item);
187143
+ let path30 = sp.join(directory, item);
187103
187144
  current.add(item);
187104
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path28, item)) {
187145
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
187105
187146
  return;
187106
187147
  }
187107
187148
  if (this.fsw.closed) {
@@ -187110,8 +187151,8 @@ var NodeFsHandler = class {
187110
187151
  }
187111
187152
  if (item === target || !target && !previous.has(item)) {
187112
187153
  this.fsw._incrReadyCount();
187113
- path28 = sp.join(dir, sp.relative(dir, path28));
187114
- this._addToNodeFs(path28, initialAdd, wh, depth + 1);
187154
+ path30 = sp.join(dir, sp.relative(dir, path30));
187155
+ this._addToNodeFs(path30, initialAdd, wh, depth + 1);
187115
187156
  }
187116
187157
  }).on(EV.ERROR, this._boundHandleError);
187117
187158
  return new Promise((resolve10, reject) => {
@@ -187180,13 +187221,13 @@ var NodeFsHandler = class {
187180
187221
  * @param depth Child path actually targeted for watch
187181
187222
  * @param target Child path actually targeted for watch
187182
187223
  */
187183
- async _addToNodeFs(path28, initialAdd, priorWh, depth, target) {
187224
+ async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
187184
187225
  const ready = this.fsw._emitReady;
187185
- if (this.fsw._isIgnored(path28) || this.fsw.closed) {
187226
+ if (this.fsw._isIgnored(path30) || this.fsw.closed) {
187186
187227
  ready();
187187
187228
  return false;
187188
187229
  }
187189
- const wh = this.fsw._getWatchHelpers(path28);
187230
+ const wh = this.fsw._getWatchHelpers(path30);
187190
187231
  if (priorWh) {
187191
187232
  wh.filterPath = (entry) => priorWh.filterPath(entry);
187192
187233
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -187202,8 +187243,8 @@ var NodeFsHandler = class {
187202
187243
  const follow = this.fsw.options.followSymlinks;
187203
187244
  let closer;
187204
187245
  if (stats.isDirectory()) {
187205
- const absPath = sp.resolve(path28);
187206
- const targetPath = follow ? await fsrealpath(path28) : path28;
187246
+ const absPath = sp.resolve(path30);
187247
+ const targetPath = follow ? await fsrealpath(path30) : path30;
187207
187248
  if (this.fsw.closed)
187208
187249
  return;
187209
187250
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -187213,29 +187254,29 @@ var NodeFsHandler = class {
187213
187254
  this.fsw._symlinkPaths.set(absPath, targetPath);
187214
187255
  }
187215
187256
  } else if (stats.isSymbolicLink()) {
187216
- const targetPath = follow ? await fsrealpath(path28) : path28;
187257
+ const targetPath = follow ? await fsrealpath(path30) : path30;
187217
187258
  if (this.fsw.closed)
187218
187259
  return;
187219
187260
  const parent = sp.dirname(wh.watchPath);
187220
187261
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
187221
187262
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
187222
- closer = await this._handleDir(parent, stats, initialAdd, depth, path28, wh, targetPath);
187263
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
187223
187264
  if (this.fsw.closed)
187224
187265
  return;
187225
187266
  if (targetPath !== void 0) {
187226
- this.fsw._symlinkPaths.set(sp.resolve(path28), targetPath);
187267
+ this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
187227
187268
  }
187228
187269
  } else {
187229
187270
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
187230
187271
  }
187231
187272
  ready();
187232
187273
  if (closer)
187233
- this.fsw._addPathCloser(path28, closer);
187274
+ this.fsw._addPathCloser(path30, closer);
187234
187275
  return false;
187235
187276
  } catch (error) {
187236
187277
  if (this.fsw._handleError(error)) {
187237
187278
  ready();
187238
- return path28;
187279
+ return path30;
187239
187280
  }
187240
187281
  }
187241
187282
  }
@@ -187278,24 +187319,24 @@ function createPattern(matcher) {
187278
187319
  }
187279
187320
  return () => false;
187280
187321
  }
187281
- function normalizePath(path28) {
187282
- if (typeof path28 !== "string")
187322
+ function normalizePath(path30) {
187323
+ if (typeof path30 !== "string")
187283
187324
  throw new Error("string expected");
187284
- path28 = sp2.normalize(path28);
187285
- path28 = path28.replace(/\\/g, "/");
187325
+ path30 = sp2.normalize(path30);
187326
+ path30 = path30.replace(/\\/g, "/");
187286
187327
  let prepend = false;
187287
- if (path28.startsWith("//"))
187328
+ if (path30.startsWith("//"))
187288
187329
  prepend = true;
187289
- path28 = path28.replace(DOUBLE_SLASH_RE, "/");
187330
+ path30 = path30.replace(DOUBLE_SLASH_RE, "/");
187290
187331
  if (prepend)
187291
- path28 = "/" + path28;
187292
- return path28;
187332
+ path30 = "/" + path30;
187333
+ return path30;
187293
187334
  }
187294
187335
  function matchPatterns(patterns, testString, stats) {
187295
- const path28 = normalizePath(testString);
187336
+ const path30 = normalizePath(testString);
187296
187337
  for (let index = 0; index < patterns.length; index++) {
187297
187338
  const pattern = patterns[index];
187298
- if (pattern(path28, stats)) {
187339
+ if (pattern(path30, stats)) {
187299
187340
  return true;
187300
187341
  }
187301
187342
  }
@@ -187333,19 +187374,19 @@ var toUnix = (string) => {
187333
187374
  }
187334
187375
  return str;
187335
187376
  };
187336
- var normalizePathToUnix = (path28) => toUnix(sp2.normalize(toUnix(path28)));
187337
- var normalizeIgnored = (cwd = "") => (path28) => {
187338
- if (typeof path28 === "string") {
187339
- return normalizePathToUnix(sp2.isAbsolute(path28) ? path28 : sp2.join(cwd, path28));
187377
+ var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
187378
+ var normalizeIgnored = (cwd = "") => (path30) => {
187379
+ if (typeof path30 === "string") {
187380
+ return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
187340
187381
  } else {
187341
- return path28;
187382
+ return path30;
187342
187383
  }
187343
187384
  };
187344
- var getAbsolutePath = (path28, cwd) => {
187345
- if (sp2.isAbsolute(path28)) {
187346
- return path28;
187385
+ var getAbsolutePath = (path30, cwd) => {
187386
+ if (sp2.isAbsolute(path30)) {
187387
+ return path30;
187347
187388
  }
187348
- return sp2.join(cwd, path28);
187389
+ return sp2.join(cwd, path30);
187349
187390
  };
187350
187391
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
187351
187392
  var DirEntry = class {
@@ -187410,10 +187451,10 @@ var WatchHelper = class {
187410
187451
  dirParts;
187411
187452
  followSymlinks;
187412
187453
  statMethod;
187413
- constructor(path28, follow, fsw) {
187454
+ constructor(path30, follow, fsw) {
187414
187455
  this.fsw = fsw;
187415
- const watchPath = path28;
187416
- this.path = path28 = path28.replace(REPLACER_RE, "");
187456
+ const watchPath = path30;
187457
+ this.path = path30 = path30.replace(REPLACER_RE, "");
187417
187458
  this.watchPath = watchPath;
187418
187459
  this.fullWatchPath = sp2.resolve(watchPath);
187419
187460
  this.dirParts = [];
@@ -187553,20 +187594,20 @@ var FSWatcher = class extends EventEmitter {
187553
187594
  this._closePromise = void 0;
187554
187595
  let paths = unifyPaths(paths_);
187555
187596
  if (cwd) {
187556
- paths = paths.map((path28) => {
187557
- const absPath = getAbsolutePath(path28, cwd);
187597
+ paths = paths.map((path30) => {
187598
+ const absPath = getAbsolutePath(path30, cwd);
187558
187599
  return absPath;
187559
187600
  });
187560
187601
  }
187561
- paths.forEach((path28) => {
187562
- this._removeIgnoredPath(path28);
187602
+ paths.forEach((path30) => {
187603
+ this._removeIgnoredPath(path30);
187563
187604
  });
187564
187605
  this._userIgnored = void 0;
187565
187606
  if (!this._readyCount)
187566
187607
  this._readyCount = 0;
187567
187608
  this._readyCount += paths.length;
187568
- Promise.all(paths.map(async (path28) => {
187569
- const res = await this._nodeFsHandler._addToNodeFs(path28, !_internal, void 0, 0, _origAdd);
187609
+ Promise.all(paths.map(async (path30) => {
187610
+ const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
187570
187611
  if (res)
187571
187612
  this._emitReady();
187572
187613
  return res;
@@ -187588,17 +187629,17 @@ var FSWatcher = class extends EventEmitter {
187588
187629
  return this;
187589
187630
  const paths = unifyPaths(paths_);
187590
187631
  const { cwd } = this.options;
187591
- paths.forEach((path28) => {
187592
- if (!sp2.isAbsolute(path28) && !this._closers.has(path28)) {
187632
+ paths.forEach((path30) => {
187633
+ if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
187593
187634
  if (cwd)
187594
- path28 = sp2.join(cwd, path28);
187595
- path28 = sp2.resolve(path28);
187635
+ path30 = sp2.join(cwd, path30);
187636
+ path30 = sp2.resolve(path30);
187596
187637
  }
187597
- this._closePath(path28);
187598
- this._addIgnoredPath(path28);
187599
- if (this._watched.has(path28)) {
187638
+ this._closePath(path30);
187639
+ this._addIgnoredPath(path30);
187640
+ if (this._watched.has(path30)) {
187600
187641
  this._addIgnoredPath({
187601
- path: path28,
187642
+ path: path30,
187602
187643
  recursive: true
187603
187644
  });
187604
187645
  }
@@ -187662,38 +187703,38 @@ var FSWatcher = class extends EventEmitter {
187662
187703
  * @param stats arguments to be passed with event
187663
187704
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
187664
187705
  */
187665
- async _emit(event, path28, stats) {
187706
+ async _emit(event, path30, stats) {
187666
187707
  if (this.closed)
187667
187708
  return;
187668
187709
  const opts = this.options;
187669
187710
  if (isWindows)
187670
- path28 = sp2.normalize(path28);
187711
+ path30 = sp2.normalize(path30);
187671
187712
  if (opts.cwd)
187672
- path28 = sp2.relative(opts.cwd, path28);
187673
- const args = [path28];
187713
+ path30 = sp2.relative(opts.cwd, path30);
187714
+ const args = [path30];
187674
187715
  if (stats != null)
187675
187716
  args.push(stats);
187676
187717
  const awf = opts.awaitWriteFinish;
187677
187718
  let pw;
187678
- if (awf && (pw = this._pendingWrites.get(path28))) {
187719
+ if (awf && (pw = this._pendingWrites.get(path30))) {
187679
187720
  pw.lastChange = /* @__PURE__ */ new Date();
187680
187721
  return this;
187681
187722
  }
187682
187723
  if (opts.atomic) {
187683
187724
  if (event === EVENTS.UNLINK) {
187684
- this._pendingUnlinks.set(path28, [event, ...args]);
187725
+ this._pendingUnlinks.set(path30, [event, ...args]);
187685
187726
  setTimeout(() => {
187686
- this._pendingUnlinks.forEach((entry, path29) => {
187727
+ this._pendingUnlinks.forEach((entry, path31) => {
187687
187728
  this.emit(...entry);
187688
187729
  this.emit(EVENTS.ALL, ...entry);
187689
- this._pendingUnlinks.delete(path29);
187730
+ this._pendingUnlinks.delete(path31);
187690
187731
  });
187691
187732
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
187692
187733
  return this;
187693
187734
  }
187694
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path28)) {
187735
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
187695
187736
  event = EVENTS.CHANGE;
187696
- this._pendingUnlinks.delete(path28);
187737
+ this._pendingUnlinks.delete(path30);
187697
187738
  }
187698
187739
  }
187699
187740
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -187711,16 +187752,16 @@ var FSWatcher = class extends EventEmitter {
187711
187752
  this.emitWithAll(event, args);
187712
187753
  }
187713
187754
  };
187714
- this._awaitWriteFinish(path28, awf.stabilityThreshold, event, awfEmit);
187755
+ this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
187715
187756
  return this;
187716
187757
  }
187717
187758
  if (event === EVENTS.CHANGE) {
187718
- const isThrottled = !this._throttle(EVENTS.CHANGE, path28, 50);
187759
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
187719
187760
  if (isThrottled)
187720
187761
  return this;
187721
187762
  }
187722
187763
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
187723
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path28) : path28;
187764
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
187724
187765
  let stats2;
187725
187766
  try {
187726
187767
  stats2 = await stat3(fullPath);
@@ -187751,23 +187792,23 @@ var FSWatcher = class extends EventEmitter {
187751
187792
  * @param timeout duration of time to suppress duplicate actions
187752
187793
  * @returns tracking object or false if action should be suppressed
187753
187794
  */
187754
- _throttle(actionType, path28, timeout) {
187795
+ _throttle(actionType, path30, timeout) {
187755
187796
  if (!this._throttled.has(actionType)) {
187756
187797
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
187757
187798
  }
187758
187799
  const action = this._throttled.get(actionType);
187759
187800
  if (!action)
187760
187801
  throw new Error("invalid throttle");
187761
- const actionPath = action.get(path28);
187802
+ const actionPath = action.get(path30);
187762
187803
  if (actionPath) {
187763
187804
  actionPath.count++;
187764
187805
  return false;
187765
187806
  }
187766
187807
  let timeoutObject;
187767
187808
  const clear = () => {
187768
- const item = action.get(path28);
187809
+ const item = action.get(path30);
187769
187810
  const count = item ? item.count : 0;
187770
- action.delete(path28);
187811
+ action.delete(path30);
187771
187812
  clearTimeout(timeoutObject);
187772
187813
  if (item)
187773
187814
  clearTimeout(item.timeoutObject);
@@ -187775,7 +187816,7 @@ var FSWatcher = class extends EventEmitter {
187775
187816
  };
187776
187817
  timeoutObject = setTimeout(clear, timeout);
187777
187818
  const thr = { timeoutObject, clear, count: 0 };
187778
- action.set(path28, thr);
187819
+ action.set(path30, thr);
187779
187820
  return thr;
187780
187821
  }
187781
187822
  _incrReadyCount() {
@@ -187789,44 +187830,44 @@ var FSWatcher = class extends EventEmitter {
187789
187830
  * @param event
187790
187831
  * @param awfEmit Callback to be called when ready for event to be emitted.
187791
187832
  */
187792
- _awaitWriteFinish(path28, threshold, event, awfEmit) {
187833
+ _awaitWriteFinish(path30, threshold, event, awfEmit) {
187793
187834
  const awf = this.options.awaitWriteFinish;
187794
187835
  if (typeof awf !== "object")
187795
187836
  return;
187796
187837
  const pollInterval = awf.pollInterval;
187797
187838
  let timeoutHandler;
187798
- let fullPath = path28;
187799
- if (this.options.cwd && !sp2.isAbsolute(path28)) {
187800
- fullPath = sp2.join(this.options.cwd, path28);
187839
+ let fullPath = path30;
187840
+ if (this.options.cwd && !sp2.isAbsolute(path30)) {
187841
+ fullPath = sp2.join(this.options.cwd, path30);
187801
187842
  }
187802
187843
  const now = /* @__PURE__ */ new Date();
187803
187844
  const writes = this._pendingWrites;
187804
187845
  function awaitWriteFinishFn(prevStat) {
187805
187846
  statcb(fullPath, (err, curStat) => {
187806
- if (err || !writes.has(path28)) {
187847
+ if (err || !writes.has(path30)) {
187807
187848
  if (err && err.code !== "ENOENT")
187808
187849
  awfEmit(err);
187809
187850
  return;
187810
187851
  }
187811
187852
  const now2 = Number(/* @__PURE__ */ new Date());
187812
187853
  if (prevStat && curStat.size !== prevStat.size) {
187813
- writes.get(path28).lastChange = now2;
187854
+ writes.get(path30).lastChange = now2;
187814
187855
  }
187815
- const pw = writes.get(path28);
187856
+ const pw = writes.get(path30);
187816
187857
  const df = now2 - pw.lastChange;
187817
187858
  if (df >= threshold) {
187818
- writes.delete(path28);
187859
+ writes.delete(path30);
187819
187860
  awfEmit(void 0, curStat);
187820
187861
  } else {
187821
187862
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
187822
187863
  }
187823
187864
  });
187824
187865
  }
187825
- if (!writes.has(path28)) {
187826
- writes.set(path28, {
187866
+ if (!writes.has(path30)) {
187867
+ writes.set(path30, {
187827
187868
  lastChange: now,
187828
187869
  cancelWait: () => {
187829
- writes.delete(path28);
187870
+ writes.delete(path30);
187830
187871
  clearTimeout(timeoutHandler);
187831
187872
  return event;
187832
187873
  }
@@ -187837,8 +187878,8 @@ var FSWatcher = class extends EventEmitter {
187837
187878
  /**
187838
187879
  * Determines whether user has asked to ignore this path.
187839
187880
  */
187840
- _isIgnored(path28, stats) {
187841
- if (this.options.atomic && DOT_RE.test(path28))
187881
+ _isIgnored(path30, stats) {
187882
+ if (this.options.atomic && DOT_RE.test(path30))
187842
187883
  return true;
187843
187884
  if (!this._userIgnored) {
187844
187885
  const { cwd } = this.options;
@@ -187848,17 +187889,17 @@ var FSWatcher = class extends EventEmitter {
187848
187889
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
187849
187890
  this._userIgnored = anymatch(list, void 0);
187850
187891
  }
187851
- return this._userIgnored(path28, stats);
187892
+ return this._userIgnored(path30, stats);
187852
187893
  }
187853
- _isntIgnored(path28, stat4) {
187854
- return !this._isIgnored(path28, stat4);
187894
+ _isntIgnored(path30, stat4) {
187895
+ return !this._isIgnored(path30, stat4);
187855
187896
  }
187856
187897
  /**
187857
187898
  * Provides a set of common helpers and properties relating to symlink handling.
187858
187899
  * @param path file or directory pattern being watched
187859
187900
  */
187860
- _getWatchHelpers(path28) {
187861
- return new WatchHelper(path28, this.options.followSymlinks, this);
187901
+ _getWatchHelpers(path30) {
187902
+ return new WatchHelper(path30, this.options.followSymlinks, this);
187862
187903
  }
187863
187904
  // Directory helpers
187864
187905
  // -----------------
@@ -187890,63 +187931,63 @@ var FSWatcher = class extends EventEmitter {
187890
187931
  * @param item base path of item/directory
187891
187932
  */
187892
187933
  _remove(directory, item, isDirectory) {
187893
- const path28 = sp2.join(directory, item);
187894
- const fullPath = sp2.resolve(path28);
187895
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path28) || this._watched.has(fullPath);
187896
- if (!this._throttle("remove", path28, 100))
187934
+ const path30 = sp2.join(directory, item);
187935
+ const fullPath = sp2.resolve(path30);
187936
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
187937
+ if (!this._throttle("remove", path30, 100))
187897
187938
  return;
187898
187939
  if (!isDirectory && this._watched.size === 1) {
187899
187940
  this.add(directory, item, true);
187900
187941
  }
187901
- const wp = this._getWatchedDir(path28);
187942
+ const wp = this._getWatchedDir(path30);
187902
187943
  const nestedDirectoryChildren = wp.getChildren();
187903
- nestedDirectoryChildren.forEach((nested) => this._remove(path28, nested));
187944
+ nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
187904
187945
  const parent = this._getWatchedDir(directory);
187905
187946
  const wasTracked = parent.has(item);
187906
187947
  parent.remove(item);
187907
187948
  if (this._symlinkPaths.has(fullPath)) {
187908
187949
  this._symlinkPaths.delete(fullPath);
187909
187950
  }
187910
- let relPath = path28;
187951
+ let relPath = path30;
187911
187952
  if (this.options.cwd)
187912
- relPath = sp2.relative(this.options.cwd, path28);
187953
+ relPath = sp2.relative(this.options.cwd, path30);
187913
187954
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
187914
187955
  const event = this._pendingWrites.get(relPath).cancelWait();
187915
187956
  if (event === EVENTS.ADD)
187916
187957
  return;
187917
187958
  }
187918
- this._watched.delete(path28);
187959
+ this._watched.delete(path30);
187919
187960
  this._watched.delete(fullPath);
187920
187961
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
187921
- if (wasTracked && !this._isIgnored(path28))
187922
- this._emit(eventName, path28);
187923
- this._closePath(path28);
187962
+ if (wasTracked && !this._isIgnored(path30))
187963
+ this._emit(eventName, path30);
187964
+ this._closePath(path30);
187924
187965
  }
187925
187966
  /**
187926
187967
  * Closes all watchers for a path
187927
187968
  */
187928
- _closePath(path28) {
187929
- this._closeFile(path28);
187930
- const dir = sp2.dirname(path28);
187931
- this._getWatchedDir(dir).remove(sp2.basename(path28));
187969
+ _closePath(path30) {
187970
+ this._closeFile(path30);
187971
+ const dir = sp2.dirname(path30);
187972
+ this._getWatchedDir(dir).remove(sp2.basename(path30));
187932
187973
  }
187933
187974
  /**
187934
187975
  * Closes only file-specific watchers
187935
187976
  */
187936
- _closeFile(path28) {
187937
- const closers = this._closers.get(path28);
187977
+ _closeFile(path30) {
187978
+ const closers = this._closers.get(path30);
187938
187979
  if (!closers)
187939
187980
  return;
187940
187981
  closers.forEach((closer) => closer());
187941
- this._closers.delete(path28);
187982
+ this._closers.delete(path30);
187942
187983
  }
187943
- _addPathCloser(path28, closer) {
187984
+ _addPathCloser(path30, closer) {
187944
187985
  if (!closer)
187945
187986
  return;
187946
- let list = this._closers.get(path28);
187987
+ let list = this._closers.get(path30);
187947
187988
  if (!list) {
187948
187989
  list = [];
187949
- this._closers.set(path28, list);
187990
+ this._closers.set(path30, list);
187950
187991
  }
187951
187992
  list.push(closer);
187952
187993
  }
@@ -188610,6 +188651,26 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
188610
188651
  throw new Error(`Unknown temporal attribute: ${String(value.attribute)}`);
188611
188652
  }
188612
188653
  }
188654
+ case "mail": {
188655
+ const mail = resources.get(value.name);
188656
+ if (!mail || mail.type !== "mail") {
188657
+ throw new Error(`Mail "${value.name}" not found`);
188658
+ }
188659
+ switch (value.attribute) {
188660
+ case "host":
188661
+ return mail.host;
188662
+ case "port":
188663
+ return String(mail.port);
188664
+ case "user":
188665
+ return "specific";
188666
+ case "password":
188667
+ return "specific";
188668
+ case "from":
188669
+ return "noreply@localhost";
188670
+ default:
188671
+ throw new Error(`Unknown mail attribute: ${String(value.attribute)}`);
188672
+ }
188673
+ }
188613
188674
  case "config": {
188614
188675
  const configValue = configs.get(value.name);
188615
188676
  if (configValue === void 0) {
@@ -188995,6 +189056,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
188995
189056
  import httpProxy from "http-proxy";
188996
189057
  var __dirname3 = path13.dirname(fileURLToPath3(import.meta.url));
188997
189058
  var adminDir = path13.join(__dirname3, "admin");
189059
+ var _embeddedAdmin = null;
188998
189060
  var HTTP_PORT = 80;
188999
189061
  var HTTPS_PORT = 443;
189000
189062
  var DOMAIN_SUFFIX = ".local.spcf.app";
@@ -189341,6 +189403,48 @@ function sendNotFound(res, requestedService, serviceMap) {
189341
189403
  </html>`);
189342
189404
  }
189343
189405
  function serveStaticFile(res, pathname) {
189406
+ if (_embeddedAdmin) {
189407
+ return serveEmbeddedFile(res, pathname);
189408
+ }
189409
+ return serveFilesystemFile(res, pathname);
189410
+ }
189411
+ function serveEmbeddedFile(res, pathname) {
189412
+ let filePath = pathname;
189413
+ if (filePath === "/" || filePath.endsWith("/")) {
189414
+ filePath = filePath + "index.html";
189415
+ }
189416
+ const relativePath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
189417
+ let content = _embeddedAdmin.get(relativePath);
189418
+ if (content) {
189419
+ const ext = path13.extname(relativePath).toLowerCase();
189420
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
189421
+ res.writeHead(200, { "Content-Type": contentType });
189422
+ res.end(content);
189423
+ return;
189424
+ }
189425
+ content = _embeddedAdmin.get(relativePath + ".html");
189426
+ if (content) {
189427
+ res.writeHead(200, { "Content-Type": "text/html" });
189428
+ res.end(content);
189429
+ return;
189430
+ }
189431
+ const indexKey = relativePath.endsWith("/") ? relativePath + "index.html" : relativePath + "/index.html";
189432
+ content = _embeddedAdmin.get(indexKey);
189433
+ if (content) {
189434
+ res.writeHead(200, { "Content-Type": "text/html" });
189435
+ res.end(content);
189436
+ return;
189437
+ }
189438
+ const notFound = _embeddedAdmin.get("404.html");
189439
+ if (notFound) {
189440
+ res.writeHead(404, { "Content-Type": "text/html" });
189441
+ res.end(notFound);
189442
+ return;
189443
+ }
189444
+ res.writeHead(404, { "Content-Type": "text/html" });
189445
+ res.end("<h1>Not Found</h1>");
189446
+ }
189447
+ function serveFilesystemFile(res, pathname) {
189344
189448
  let filePath = pathname;
189345
189449
  if (filePath === "/" || filePath.endsWith("/")) {
189346
189450
  filePath = filePath + "index.html";
@@ -189631,16 +189735,144 @@ function sleep3(ms) {
189631
189735
  return new Promise((resolve10) => setTimeout(resolve10, ms));
189632
189736
  }
189633
189737
 
189738
+ // src/lib/dev/mail-manager.ts
189739
+ import * as http2 from "http";
189740
+ import * as crypto3 from "crypto";
189741
+ async function startMailServer(mail, smtpPort, apiPort) {
189742
+ const emails = [];
189743
+ const { SMTPServer } = await import("smtp-server");
189744
+ const { simpleParser } = await import("mailparser");
189745
+ const smtpServer = new SMTPServer({
189746
+ authOptional: true,
189747
+ disabledCommands: ["STARTTLS"],
189748
+ onAuth(auth, _session, callback) {
189749
+ if (auth.username === "specific" && auth.password === "specific") {
189750
+ callback(null, { user: auth.username });
189751
+ } else {
189752
+ callback(new Error("Invalid credentials"));
189753
+ }
189754
+ },
189755
+ onData(stream, session, callback) {
189756
+ let rawData = "";
189757
+ stream.on("data", (chunk) => {
189758
+ rawData += chunk.toString();
189759
+ });
189760
+ stream.on("end", async () => {
189761
+ try {
189762
+ const parsed = await simpleParser(rawData);
189763
+ const email = {
189764
+ id: crypto3.randomUUID(),
189765
+ from: parsed.from?.text ?? "",
189766
+ to: Array.isArray(parsed.to) ? parsed.to.map((addr) => addr.text) : parsed.to ? [parsed.to.text] : [],
189767
+ subject: parsed.subject ?? "(no subject)",
189768
+ text: parsed.text,
189769
+ html: typeof parsed.html === "string" ? parsed.html : void 0,
189770
+ date: parsed.date ?? /* @__PURE__ */ new Date()
189771
+ };
189772
+ emails.push(email);
189773
+ } catch {
189774
+ emails.push({
189775
+ id: crypto3.randomUUID(),
189776
+ from: session.envelope.mailFrom ? session.envelope.mailFrom.address : "",
189777
+ to: session.envelope.rcptTo.map((r) => r.address),
189778
+ subject: "(parse error)",
189779
+ text: rawData,
189780
+ date: /* @__PURE__ */ new Date()
189781
+ });
189782
+ }
189783
+ callback();
189784
+ });
189785
+ }
189786
+ });
189787
+ const httpServer = http2.createServer((req, res) => {
189788
+ res.setHeader("Access-Control-Allow-Origin", "*");
189789
+ res.setHeader("Access-Control-Allow-Methods", "GET, DELETE, OPTIONS");
189790
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
189791
+ if (req.method === "OPTIONS") {
189792
+ res.writeHead(204);
189793
+ res.end();
189794
+ return;
189795
+ }
189796
+ const url = new URL(req.url ?? "/", `http://localhost:${apiPort}`);
189797
+ if (req.method === "GET" && url.pathname === "/api/emails") {
189798
+ const summaries = [...emails].reverse().map(({ id, from, to, subject, date }) => ({
189799
+ id,
189800
+ from,
189801
+ to,
189802
+ subject,
189803
+ date
189804
+ }));
189805
+ res.writeHead(200, { "Content-Type": "application/json" });
189806
+ res.end(JSON.stringify(summaries));
189807
+ return;
189808
+ }
189809
+ const emailMatch = url.pathname.match(/^\/api\/emails\/(.+)$/);
189810
+ if (req.method === "GET" && emailMatch) {
189811
+ const email = emails.find((e) => e.id === emailMatch[1]);
189812
+ if (email) {
189813
+ res.writeHead(200, { "Content-Type": "application/json" });
189814
+ res.end(JSON.stringify(email));
189815
+ } else {
189816
+ res.writeHead(404, { "Content-Type": "application/json" });
189817
+ res.end(JSON.stringify({ error: "Email not found" }));
189818
+ }
189819
+ return;
189820
+ }
189821
+ if (req.method === "DELETE" && url.pathname === "/api/emails") {
189822
+ emails.length = 0;
189823
+ res.writeHead(200, { "Content-Type": "application/json" });
189824
+ res.end(JSON.stringify({ ok: true }));
189825
+ return;
189826
+ }
189827
+ res.writeHead(404);
189828
+ res.end();
189829
+ });
189830
+ await new Promise((resolve10, reject) => {
189831
+ smtpServer.listen(smtpPort, "127.0.0.1", () => resolve10());
189832
+ smtpServer.on("error", reject);
189833
+ });
189834
+ await new Promise((resolve10, reject) => {
189835
+ httpServer.listen(apiPort, "127.0.0.1", () => resolve10());
189836
+ httpServer.on("error", reject);
189837
+ });
189838
+ const stop = async () => {
189839
+ await new Promise((resolve10) => {
189840
+ smtpServer.close(() => resolve10());
189841
+ });
189842
+ await new Promise((resolve10) => {
189843
+ httpServer.close(() => resolve10());
189844
+ });
189845
+ };
189846
+ const resource = {
189847
+ name: mail.name,
189848
+ type: "mail",
189849
+ port: smtpPort,
189850
+ url: `smtp://127.0.0.1:${smtpPort}`,
189851
+ host: "127.0.0.1",
189852
+ user: "",
189853
+ password: "",
189854
+ dbName: mail.name,
189855
+ stop
189856
+ };
189857
+ const mailServer = {
189858
+ smtpPort,
189859
+ apiPort,
189860
+ getEmails: () => emails,
189861
+ stop
189862
+ };
189863
+ return { resource, mailServer };
189864
+ }
189865
+
189634
189866
  // src/lib/dev/drizzle-gateway-manager.ts
189635
189867
  import * as net3 from "net";
189636
189868
  import * as fs18 from "fs";
189637
189869
  import * as path15 from "path";
189638
189870
  import { spawn as spawn4 } from "child_process";
189639
- import { randomUUID } from "crypto";
189871
+ import { randomUUID as randomUUID2 } from "crypto";
189640
189872
  function generateStoreJson(postgresInstances) {
189641
- const storeId = randomUUID();
189873
+ const storeId = randomUUID2();
189642
189874
  const slots = postgresInstances.map((pg) => {
189643
- const slotId = randomUUID();
189875
+ const slotId = randomUUID2();
189644
189876
  return [
189645
189877
  slotId,
189646
189878
  {
@@ -190158,7 +190390,7 @@ async function stopProcess4(proc) {
190158
190390
 
190159
190391
  // src/lib/dev/resource-starter.ts
190160
190392
  function findRequiredResources(service) {
190161
- const required = { postgres: [], redis: [], storage: [], temporal: [] };
190393
+ const required = { postgres: [], redis: [], storage: [], temporal: [], mail: [] };
190162
190394
  if (service.env) {
190163
190395
  for (const value of Object.values(service.env)) {
190164
190396
  if (typeof value !== "object" || value === null) continue;
@@ -190172,6 +190404,8 @@ function findRequiredResources(service) {
190172
190404
  required.storage.push(ref.name);
190173
190405
  } else if (ref.type === "temporal" && !required.temporal.includes(ref.name)) {
190174
190406
  required.temporal.push(ref.name);
190407
+ } else if (ref.type === "mail" && !required.mail.includes(ref.name)) {
190408
+ required.mail.push(ref.name);
190175
190409
  }
190176
190410
  }
190177
190411
  }
@@ -190193,26 +190427,30 @@ async function startResources(options2) {
190193
190427
  });
190194
190428
  const resources = /* @__PURE__ */ new Map();
190195
190429
  const electric = /* @__PURE__ */ new Map();
190430
+ const mailServers = /* @__PURE__ */ new Map();
190196
190431
  const startedResources = [];
190197
190432
  const startedElectric = [];
190198
190433
  let postgresConfigs;
190199
190434
  let redisConfigs;
190200
190435
  let storageConfigs;
190201
190436
  let temporalConfigs;
190437
+ let mailConfigs;
190202
190438
  if (selection.mode === "all") {
190203
190439
  postgresConfigs = config.postgres;
190204
190440
  redisConfigs = config.redis;
190205
190441
  storageConfigs = config.storage;
190206
190442
  temporalConfigs = config.temporal;
190443
+ mailConfigs = config.mail;
190207
190444
  } else {
190208
190445
  postgresConfigs = config.postgres.filter((p) => selection.postgres.includes(p.name));
190209
190446
  redisConfigs = config.redis.filter((r) => selection.redis.includes(r.name));
190210
190447
  storageConfigs = config.storage.filter((s) => selection.storage.includes(s.name));
190211
190448
  temporalConfigs = config.temporal.filter((t) => selection.temporal.includes(t.name));
190449
+ mailConfigs = config.mail.filter((m) => selection.mail.includes(m.name));
190212
190450
  }
190213
190451
  for (const pg of postgresConfigs) {
190214
190452
  if (signal?.cancelled) {
190215
- return { resources, electric, startedResources, startedElectric, cancelled: true };
190453
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190216
190454
  }
190217
190455
  const port = portAllocator.allocate(`postgres:${pg.name}`);
190218
190456
  log(`Starting postgres "${pg.name}" on port ${port}`);
@@ -190246,7 +190484,7 @@ async function startResources(options2) {
190246
190484
  }
190247
190485
  for (const redis of redisConfigs) {
190248
190486
  if (signal?.cancelled) {
190249
- return { resources, electric, startedResources, startedElectric, cancelled: true };
190487
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190250
190488
  }
190251
190489
  const port = portAllocator.allocate(`redis:${redis.name}`);
190252
190490
  log(`Starting redis "${redis.name}" on port ${port}`);
@@ -190270,7 +190508,7 @@ async function startResources(options2) {
190270
190508
  }
190271
190509
  for (const storage of storageConfigs) {
190272
190510
  if (signal?.cancelled) {
190273
- return { resources, electric, startedResources, startedElectric, cancelled: true };
190511
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190274
190512
  }
190275
190513
  const port = portAllocator.allocate(`storage:${storage.name}`);
190276
190514
  log(`Starting storage "${storage.name}" on port ${port}`);
@@ -190296,7 +190534,7 @@ async function startResources(options2) {
190296
190534
  }
190297
190535
  if (temporalConfigs.length > 0) {
190298
190536
  if (signal?.cancelled) {
190299
- return { resources, electric, startedResources, startedElectric, cancelled: true };
190537
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190300
190538
  }
190301
190539
  const grpcPort = portAllocator.allocate("temporal-grpc");
190302
190540
  const uiPort = portAllocator.allocate("temporal-ui");
@@ -190313,11 +190551,36 @@ async function startResources(options2) {
190313
190551
  log(`Temporal namespace "${instance.name}" ready`);
190314
190552
  }
190315
190553
  }
190554
+ for (const mail of mailConfigs) {
190555
+ if (signal?.cancelled) {
190556
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190557
+ }
190558
+ const smtpPort = portAllocator.allocate(`mail-smtp:${mail.name}`);
190559
+ const mailApiPort = portAllocator.allocate(`mail-api:${mail.name}`);
190560
+ log(`Starting mail "${mail.name}" on SMTP port ${smtpPort} (API: ${mailApiPort})`);
190561
+ callbacks.onResourceStarting?.(mail.name, "mail");
190562
+ const { resource, mailServer } = await startMailServer(mail, smtpPort, mailApiPort);
190563
+ resources.set(mail.name, resource);
190564
+ startedResources.push(resource);
190565
+ mailServers.set(mail.name, mailServer);
190566
+ callbacks.onResourceReady?.(mail.name, resource);
190567
+ log(`Mail "${mail.name}" ready`);
190568
+ await stateManager.registerDatabase(mail.name, {
190569
+ engine: "mail",
190570
+ port: smtpPort,
190571
+ host: "127.0.0.1",
190572
+ user: "",
190573
+ password: "",
190574
+ dbName: mail.name,
190575
+ url: `smtp://127.0.0.1:${smtpPort}`,
190576
+ mailApiPort
190577
+ });
190578
+ }
190316
190579
  if (shouldStartElectric) {
190317
190580
  const syncDatabases = detectSyncDatabases(config);
190318
190581
  for (const pgName of syncDatabases) {
190319
190582
  if (signal?.cancelled) {
190320
- return { resources, electric, startedResources, startedElectric, cancelled: true };
190583
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: true };
190321
190584
  }
190322
190585
  const pg = resources.get(pgName);
190323
190586
  if (!pg || pg.type !== "postgres") continue;
@@ -190337,7 +190600,7 @@ async function startResources(options2) {
190337
190600
  log(`Electric sync for "${pgName}" ready at ${electricInstance.url}`);
190338
190601
  }
190339
190602
  }
190340
- return { resources, electric, startedResources, startedElectric, cancelled: false };
190603
+ return { resources, electric, mail: mailServers, startedResources, startedElectric, cancelled: false };
190341
190604
  }
190342
190605
 
190343
190606
  // src/lib/dev/config-watcher.ts
@@ -190994,12 +191257,14 @@ function SecretInput({ secretName, onSubmit, onCancel }) {
190994
191257
  // src/lib/ui/ConfigInput.tsx
190995
191258
  import React5, { useState as useState4 } from "react";
190996
191259
  import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
190997
- function ConfigInput({ configName, onSubmit, onCancel }) {
191260
+ function ConfigInput({ configName, defaultValue, onSubmit, onCancel }) {
190998
191261
  const [value, setValue] = useState4("");
190999
191262
  useInput3((input, key) => {
191000
191263
  if (key.return) {
191001
191264
  if (value.trim() !== "") {
191002
191265
  onSubmit(value);
191266
+ } else if (defaultValue !== void 0) {
191267
+ onSubmit(defaultValue);
191003
191268
  }
191004
191269
  } else if (key.escape) {
191005
191270
  onCancel();
@@ -191009,7 +191274,7 @@ function ConfigInput({ configName, onSubmit, onCancel }) {
191009
191274
  setValue((prev) => prev + input);
191010
191275
  }
191011
191276
  });
191012
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
191277
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), defaultValue !== void 0 && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (default: ", defaultValue, ")"), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, defaultValue !== void 0 ? "(Press Enter to accept default, type to override, Esc to cancel)" : "(Press Enter to save, Esc to cancel)"));
191013
191278
  }
191014
191279
 
191015
191280
  // src/commands/dev.tsx
@@ -191415,6 +191680,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191415
191680
  const resourceStatus = /* @__PURE__ */ new Map();
191416
191681
  const syncDatabases = detectSyncDatabases(config2);
191417
191682
  let resources2;
191683
+ let mailServers = /* @__PURE__ */ new Map();
191418
191684
  try {
191419
191685
  const result = await startResources({
191420
191686
  config: config2,
@@ -191462,6 +191728,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191462
191728
  });
191463
191729
  if (result.cancelled) return;
191464
191730
  resources2 = result.resources;
191731
+ mailServers = result.mail;
191465
191732
  startedResources.push(...result.startedResources);
191466
191733
  } catch (err) {
191467
191734
  const errorMsg = `Failed to start resources: ${err instanceof Error ? err.message : String(err)}`;
@@ -191856,6 +192123,9 @@ Add them to the config block in specific.local`);
191856
192123
  }
191857
192124
  const projectId = hasProjectId() ? readProjectId() : void 0;
191858
192125
  const hasTemporal = config2.temporal.length > 0;
192126
+ const hasMail = config2.mail.length > 0;
192127
+ const firstMailServer = mailServers.size > 0 ? [...mailServers.values()][0] : void 0;
192128
+ const mailApiUrl = firstMailServer ? `http://127.0.0.1:${firstMailServer.apiPort}` : void 0;
191859
192129
  const getState = () => ({
191860
192130
  status: "running",
191861
192131
  services: config2.services.filter((svc) => runningServicePorts.has(svc.name) || svc.serve).map((svc) => ({
@@ -191872,7 +192142,9 @@ Add them to the config block in specific.local`);
191872
192142
  syncEnabled: r.type === "postgres" && syncDatabases.has(name)
191873
192143
  })),
191874
192144
  projectId,
191875
- hasTemporal
192145
+ hasTemporal,
192146
+ hasMail,
192147
+ mailApiUrl
191876
192148
  });
191877
192149
  const adminServer = await startAdminServer(getState);
191878
192150
  adminServerRef.current = adminServer;
@@ -193506,6 +193778,7 @@ ${errorMsg}`
193506
193778
  {
193507
193779
  key: currentConfig,
193508
193780
  configName: currentConfig,
193781
+ defaultValue: config.configs?.find((c) => c.name === currentConfig)?.default,
193509
193782
  onSubmit: handleConfigSubmit,
193510
193783
  onCancel: handleConfigCancel
193511
193784
  }
@@ -194307,11 +194580,226 @@ function betaCommand() {
194307
194580
  render8(/* @__PURE__ */ React10.createElement(BetaToggleUI, null));
194308
194581
  }
194309
194582
 
194310
- // src/cli.tsx
194583
+ // src/commands/update.tsx
194584
+ import React11, { useState as useState10, useEffect as useEffect8 } from "react";
194585
+ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } from "ink";
194586
+ import Spinner7 from "ink-spinner";
194587
+
194588
+ // src/lib/update.ts
194589
+ import * as fs30 from "fs";
194590
+ import * as path28 from "path";
194591
+ var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
194592
+ function compareVersions(a, b) {
194593
+ const partsA = a.split(".").map(Number);
194594
+ const partsB = b.split(".").map(Number);
194595
+ const len = Math.max(partsA.length, partsB.length);
194596
+ for (let i = 0; i < len; i++) {
194597
+ const numA = partsA[i] ?? 0;
194598
+ const numB = partsB[i] ?? 0;
194599
+ if (numA !== numB) return numA - numB;
194600
+ }
194601
+ return 0;
194602
+ }
194603
+ async function checkForUpdate() {
194604
+ const currentVersion = "0.1.68";
194605
+ const response = await fetch(`${BINARIES_BASE_URL}/latest`);
194606
+ if (!response.ok) {
194607
+ throw new Error(`Failed to check for updates: HTTP ${response.status}`);
194608
+ }
194609
+ const latestVersion = (await response.text()).trim();
194610
+ const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
194611
+ return { currentVersion, latestVersion, updateAvailable };
194612
+ }
194613
+ function getCurrentBinaryPath() {
194614
+ return process.execPath;
194615
+ }
194616
+ function isBinaryWritable() {
194617
+ const binaryPath = getCurrentBinaryPath();
194618
+ const dir = path28.dirname(binaryPath);
194619
+ try {
194620
+ fs30.accessSync(dir, fs30.constants.W_OK);
194621
+ return true;
194622
+ } catch {
194623
+ return false;
194624
+ }
194625
+ }
194626
+ async function performUpdate(version, onProgress) {
194627
+ const binaryPath = getCurrentBinaryPath();
194628
+ const binaryDir = path28.dirname(binaryPath);
194629
+ const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
194630
+ try {
194631
+ const { platform: platform5, arch: arch3 } = getPlatformInfo();
194632
+ const url = `${BINARIES_BASE_URL}/${version}/specific-${platform5}-${arch3}`;
194633
+ await downloadFile(url, tempPath, onProgress);
194634
+ const stat4 = fs30.statSync(tempPath);
194635
+ if (stat4.size === 0) {
194636
+ throw new Error("Downloaded binary is empty");
194637
+ }
194638
+ fs30.chmodSync(tempPath, 493);
194639
+ onProgress?.({ phase: "finalizing" });
194640
+ fs30.unlinkSync(binaryPath);
194641
+ fs30.renameSync(tempPath, binaryPath);
194642
+ } catch (error) {
194643
+ try {
194644
+ if (fs30.existsSync(tempPath)) {
194645
+ fs30.unlinkSync(tempPath);
194646
+ }
194647
+ } catch {
194648
+ }
194649
+ throw error;
194650
+ }
194651
+ }
194652
+
194653
+ // src/lib/background-update.ts
194654
+ import { spawn as spawn10 } from "child_process";
194655
+ import * as fs31 from "fs";
194656
+ import * as path29 from "path";
194657
+ import * as os9 from "os";
194658
+ var SPECIFIC_DIR = path29.join(os9.homedir(), ".specific");
194659
+ var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
194660
+ var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
194661
+ var RATE_LIMIT_MS = 60 * 60 * 1e3;
194662
+ var STALE_LOCK_MS = 10 * 60 * 1e3;
194663
+ function writeCheckTimestamp() {
194664
+ fs31.mkdirSync(SPECIFIC_DIR, { recursive: true });
194665
+ fs31.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194666
+ }
194667
+ function isRateLimited() {
194668
+ try {
194669
+ const content = fs31.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194670
+ const lastCheck = parseInt(content, 10);
194671
+ if (isNaN(lastCheck)) return false;
194672
+ return Date.now() - lastCheck < RATE_LIMIT_MS;
194673
+ } catch {
194674
+ return false;
194675
+ }
194676
+ }
194677
+ function maybeStartBackgroundUpdate() {
194678
+ try {
194679
+ if (true) return;
194680
+ if (process.env.SPECIFIC_BACKGROUND_UPDATE === "1") return;
194681
+ if (isRateLimited()) return;
194682
+ if (!isBinaryWritable()) return;
194683
+ const child = spawn10(process.execPath, [], {
194684
+ detached: true,
194685
+ stdio: "ignore",
194686
+ env: {
194687
+ ...process.env,
194688
+ SPECIFIC_BACKGROUND_UPDATE: "1"
194689
+ }
194690
+ });
194691
+ child.unref();
194692
+ } catch {
194693
+ }
194694
+ }
194695
+
194696
+ // src/commands/update.tsx
194697
+ function UpdateUI() {
194698
+ const { exit } = useApp6();
194699
+ const [state, setState] = useState10({ phase: "checking" });
194700
+ useEffect8(() => {
194701
+ if (state.phase !== "checking") return;
194702
+ let cancelled = false;
194703
+ async function check() {
194704
+ try {
194705
+ const result = await checkForUpdate();
194706
+ if (cancelled) return;
194707
+ if (!result.updateAvailable) {
194708
+ setState({ phase: "up-to-date", checkResult: result });
194709
+ return;
194710
+ }
194711
+ if (!isBinaryWritable()) {
194712
+ setState({ phase: "permission-error", checkResult: result });
194713
+ return;
194714
+ }
194715
+ setState({ phase: "downloading", checkResult: result });
194716
+ } catch (err) {
194717
+ if (cancelled) return;
194718
+ setState({
194719
+ phase: "error",
194720
+ error: err instanceof Error ? err.message : String(err)
194721
+ });
194722
+ }
194723
+ }
194724
+ check();
194725
+ return () => {
194726
+ cancelled = true;
194727
+ };
194728
+ }, [state.phase]);
194729
+ useEffect8(() => {
194730
+ if (state.phase !== "downloading" || !state.checkResult) return;
194731
+ let cancelled = false;
194732
+ async function download() {
194733
+ try {
194734
+ await performUpdate(state.checkResult.latestVersion, (progress) => {
194735
+ if (!cancelled) {
194736
+ setState((s) => ({ ...s, progress }));
194737
+ }
194738
+ });
194739
+ if (cancelled) return;
194740
+ trackEvent("cli_updated", {
194741
+ from_version: state.checkResult.currentVersion,
194742
+ to_version: state.checkResult.latestVersion
194743
+ });
194744
+ writeCheckTimestamp();
194745
+ setState((s) => ({ ...s, phase: "success" }));
194746
+ } catch (err) {
194747
+ if (cancelled) return;
194748
+ setState({
194749
+ phase: "error",
194750
+ checkResult: state.checkResult,
194751
+ error: err instanceof Error ? err.message : String(err)
194752
+ });
194753
+ }
194754
+ }
194755
+ download();
194756
+ return () => {
194757
+ cancelled = true;
194758
+ };
194759
+ }, [state.phase, state.checkResult]);
194760
+ useEffect8(() => {
194761
+ if (state.phase === "up-to-date" || state.phase === "success" || state.phase === "error" || state.phase === "permission-error") {
194762
+ const timer = setTimeout(() => exit(), 100);
194763
+ return () => clearTimeout(timer);
194764
+ }
194765
+ }, [state.phase, exit]);
194766
+ if (state.phase === "checking") {
194767
+ return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " Checking for updates..."));
194768
+ }
194769
+ if (state.phase === "up-to-date") {
194770
+ return /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "Already up to date (v", state.checkResult.currentVersion, ")");
194771
+ }
194772
+ if (state.phase === "permission-error") {
194773
+ const { currentVersion, latestVersion } = state.checkResult;
194774
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Update available: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Text11, { color: "yellow" }, "Permission denied. Re-run with sudo:"), /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, " sudo specific update"));
194775
+ }
194776
+ if (state.phase === "downloading") {
194777
+ const { currentVersion, latestVersion } = state.checkResult;
194778
+ const { progress } = state;
194779
+ const progressText = progress?.percent ? ` (${progress.percent}%)` : "";
194780
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Updating: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " ", progress?.phase === "finalizing" ? "Installing..." : `Downloading${progressText}`)));
194781
+ }
194782
+ if (state.phase === "success") {
194783
+ const { currentVersion, latestVersion } = state.checkResult;
194784
+ return /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "Updated successfully: v", currentVersion, " \u2192 v", latestVersion);
194785
+ }
194786
+ return /* @__PURE__ */ React11.createElement(Text11, { color: "red" }, "Update failed: ", state.error);
194787
+ }
194788
+ function updateCommand() {
194789
+ const distribution = "npm";
194790
+ if (distribution !== "binary") {
194791
+ console.log("This installation was installed via npm.");
194792
+ console.log("To update, run: npm update -g @specific.dev/cli");
194793
+ return;
194794
+ }
194795
+ render9(/* @__PURE__ */ React11.createElement(UpdateUI, null));
194796
+ }
194797
+
194798
+ // src/cli-program.tsx
194311
194799
  var program = new Command();
194312
194800
  var env = "production";
194313
194801
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194314
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.66").enablePositionalOptions();
194802
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.68").enablePositionalOptions();
194315
194803
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").action((options2) => initCommand(options2));
194316
194804
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194317
194805
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
@@ -194340,13 +194828,17 @@ program.command("clean").description("Remove .specific directory for a clean sla
194340
194828
  cleanCommand(options2.key);
194341
194829
  });
194342
194830
  program.command("beta").description("Manage beta feature flags").action(betaCommand);
194831
+ program.command("update").description("Update Specific CLI to the latest version").action(updateCommand);
194343
194832
  program.command("login").description("Log in to Specific").action(loginCommand);
194344
194833
  program.command("logout").description("Log out of Specific").action(logoutCommand);
194345
194834
  var commandName = process.argv[2] || "help";
194346
194835
  trackEvent("cli_command_invoked", { command: commandName });
194836
+ maybeStartBackgroundUpdate();
194347
194837
  process.on("beforeExit", async () => {
194348
194838
  await shutdown();
194349
194839
  });
194840
+
194841
+ // src/cli.tsx
194350
194842
  program.parse();
194351
194843
  /*! Bundled license information:
194352
194844