@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.
- package/dist/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +2 -2
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/__next._full.txt +9 -9
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +4 -4
- package/dist/admin/__next._tree.txt +2 -2
- package/dist/admin/_next/static/chunks/47a5dab862795de7.js +1 -0
- package/dist/admin/_next/static/chunks/{77284e343252b102.js → 63a5ddab5f6a075d.js} +2 -2
- package/dist/admin/_next/static/chunks/{c7954d71061f1f9b.js → 71775bad64b386a3.js} +2 -2
- package/dist/admin/_next/static/chunks/8cd2655984f0da65.js +1 -0
- package/dist/admin/_next/static/chunks/bac545c7353852cd.css +4 -0
- package/dist/admin/_not-found/__next._full.txt +4 -4
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +4 -4
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +2 -2
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +4 -4
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/databases/__next._full.txt +9 -9
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +4 -4
- package/dist/admin/databases/__next._tree.txt +2 -2
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +9 -9
- package/dist/admin/fullscreen/__next._full.txt +5 -5
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +4 -4
- package/dist/admin/fullscreen/__next._tree.txt +2 -2
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +5 -5
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +4 -4
- package/dist/admin/fullscreen/databases/__next._tree.txt +2 -2
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +5 -5
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +5 -5
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +9 -9
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +9 -0
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +4 -0
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +8 -0
- package/dist/admin/mail/__next._full.txt +27 -0
- package/dist/admin/mail/__next._head.txt +6 -0
- package/dist/admin/mail/__next._index.txt +7 -0
- package/dist/admin/mail/__next._tree.txt +5 -0
- package/dist/admin/mail/index.html +1 -0
- package/dist/admin/mail/index.txt +27 -0
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +9 -9
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +4 -4
- package/dist/admin/workflows/__next._tree.txt +2 -2
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +9 -9
- package/dist/cli.js +675 -183
- package/dist/docs/index.md +3 -0
- package/dist/docs/mail.md +66 -0
- package/dist/postinstall.js +1 -1
- package/package.json +7 -2
- package/dist/admin/_next/static/chunks/497f00630c8a5681.js +0 -1
- package/dist/admin/_next/static/chunks/8342a9e3e2851626.css +0 -4
- /package/dist/admin/_next/static/{Al0o3YauO07qHR3spSA9w → w4VP36_YGzWIvqWZUyEgj}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{Al0o3YauO07qHR3spSA9w → w4VP36_YGzWIvqWZUyEgj}/_clientMiddlewareManifest.json +0 -0
- /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 (
|
|
242
|
-
if (/^[a-z]+:\/\//i.test(
|
|
243
|
-
return
|
|
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",
|
|
246
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path30], { encoding: "utf8" });
|
|
247
247
|
return stdout.trim();
|
|
248
248
|
} catch {
|
|
249
|
-
return
|
|
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
|
|
758
|
-
"object" == typeof
|
|
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.
|
|
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
|
-
|
|
184918
|
-
|
|
184919
|
-
|
|
184921
|
+
var _embeddedDocs = null;
|
|
184922
|
+
function docsCommand(path30) {
|
|
184923
|
+
const content = resolveDocContent(path30);
|
|
184924
|
+
if (!content) {
|
|
184920
184925
|
console.error(
|
|
184921
|
-
`Documentation not found: ${
|
|
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
|
-
|
|
184929
|
-
|
|
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
|
|
184941
|
-
|
|
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, `${
|
|
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,
|
|
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 = (
|
|
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:
|
|
186362
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
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(
|
|
186443
|
+
async _exploreDir(path30, depth) {
|
|
186403
186444
|
let files;
|
|
186404
186445
|
try {
|
|
186405
|
-
files = await readdir(
|
|
186446
|
+
files = await readdir(path30, this._rdOptions);
|
|
186406
186447
|
} catch (error) {
|
|
186407
186448
|
this._onError(error);
|
|
186408
186449
|
}
|
|
186409
|
-
return { files, depth, path:
|
|
186450
|
+
return { files, depth, path: path30 };
|
|
186410
186451
|
}
|
|
186411
|
-
async _formatEntry(dirent,
|
|
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(
|
|
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(
|
|
186856
|
+
function createFsWatchInstance(path30, options2, listener, errHandler, emitRaw) {
|
|
186816
186857
|
const handleEvent = (rawEvent, evPath) => {
|
|
186817
|
-
listener(
|
|
186818
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
186819
|
-
if (evPath &&
|
|
186820
|
-
fsWatchBroadcast(sp.resolve(
|
|
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(
|
|
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 = (
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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 = (
|
|
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(
|
|
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(
|
|
186995
|
+
_watchWithNodeFs(path30, listener) {
|
|
186955
186996
|
const opts = this.fsw.options;
|
|
186956
|
-
const directory = sp.dirname(
|
|
186957
|
-
const basename6 = sp.basename(
|
|
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(
|
|
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(
|
|
187011
|
+
closer = setFsWatchFileListener(path30, absolutePath, options2, {
|
|
186971
187012
|
listener,
|
|
186972
187013
|
rawEmitter: this.fsw._emitRaw
|
|
186973
187014
|
});
|
|
186974
187015
|
} else {
|
|
186975
|
-
closer = setFsWatchListener(
|
|
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
|
|
187032
|
+
const dirname10 = sp.dirname(file);
|
|
186992
187033
|
const basename6 = sp.basename(file);
|
|
186993
|
-
const parent = this.fsw._getWatchedDir(
|
|
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 (
|
|
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(
|
|
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(
|
|
187056
|
+
this.fsw._addPathCloser(path30, closer2);
|
|
187016
187057
|
} else {
|
|
187017
187058
|
prevStats = newStats2;
|
|
187018
187059
|
}
|
|
187019
187060
|
} catch (error) {
|
|
187020
|
-
this.fsw._remove(
|
|
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,
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
|
187143
|
+
let path30 = sp.join(directory, item);
|
|
187103
187144
|
current.add(item);
|
|
187104
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
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
|
-
|
|
187114
|
-
this._addToNodeFs(
|
|
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(
|
|
187224
|
+
async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
|
|
187184
187225
|
const ready = this.fsw._emitReady;
|
|
187185
|
-
if (this.fsw._isIgnored(
|
|
187226
|
+
if (this.fsw._isIgnored(path30) || this.fsw.closed) {
|
|
187186
187227
|
ready();
|
|
187187
187228
|
return false;
|
|
187188
187229
|
}
|
|
187189
|
-
const wh = this.fsw._getWatchHelpers(
|
|
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(
|
|
187206
|
-
const targetPath = follow ? await fsrealpath(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
187282
|
-
if (typeof
|
|
187322
|
+
function normalizePath(path30) {
|
|
187323
|
+
if (typeof path30 !== "string")
|
|
187283
187324
|
throw new Error("string expected");
|
|
187284
|
-
|
|
187285
|
-
|
|
187325
|
+
path30 = sp2.normalize(path30);
|
|
187326
|
+
path30 = path30.replace(/\\/g, "/");
|
|
187286
187327
|
let prepend = false;
|
|
187287
|
-
if (
|
|
187328
|
+
if (path30.startsWith("//"))
|
|
187288
187329
|
prepend = true;
|
|
187289
|
-
|
|
187330
|
+
path30 = path30.replace(DOUBLE_SLASH_RE, "/");
|
|
187290
187331
|
if (prepend)
|
|
187291
|
-
|
|
187292
|
-
return
|
|
187332
|
+
path30 = "/" + path30;
|
|
187333
|
+
return path30;
|
|
187293
187334
|
}
|
|
187294
187335
|
function matchPatterns(patterns, testString, stats) {
|
|
187295
|
-
const
|
|
187336
|
+
const path30 = normalizePath(testString);
|
|
187296
187337
|
for (let index = 0; index < patterns.length; index++) {
|
|
187297
187338
|
const pattern = patterns[index];
|
|
187298
|
-
if (pattern(
|
|
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 = (
|
|
187337
|
-
var normalizeIgnored = (cwd = "") => (
|
|
187338
|
-
if (typeof
|
|
187339
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
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
|
|
187382
|
+
return path30;
|
|
187342
187383
|
}
|
|
187343
187384
|
};
|
|
187344
|
-
var getAbsolutePath = (
|
|
187345
|
-
if (sp2.isAbsolute(
|
|
187346
|
-
return
|
|
187385
|
+
var getAbsolutePath = (path30, cwd) => {
|
|
187386
|
+
if (sp2.isAbsolute(path30)) {
|
|
187387
|
+
return path30;
|
|
187347
187388
|
}
|
|
187348
|
-
return sp2.join(cwd,
|
|
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(
|
|
187454
|
+
constructor(path30, follow, fsw) {
|
|
187414
187455
|
this.fsw = fsw;
|
|
187415
|
-
const watchPath =
|
|
187416
|
-
this.path =
|
|
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((
|
|
187557
|
-
const absPath = getAbsolutePath(
|
|
187597
|
+
paths = paths.map((path30) => {
|
|
187598
|
+
const absPath = getAbsolutePath(path30, cwd);
|
|
187558
187599
|
return absPath;
|
|
187559
187600
|
});
|
|
187560
187601
|
}
|
|
187561
|
-
paths.forEach((
|
|
187562
|
-
this._removeIgnoredPath(
|
|
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 (
|
|
187569
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
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((
|
|
187592
|
-
if (!sp2.isAbsolute(
|
|
187632
|
+
paths.forEach((path30) => {
|
|
187633
|
+
if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
|
|
187593
187634
|
if (cwd)
|
|
187594
|
-
|
|
187595
|
-
|
|
187635
|
+
path30 = sp2.join(cwd, path30);
|
|
187636
|
+
path30 = sp2.resolve(path30);
|
|
187596
187637
|
}
|
|
187597
|
-
this._closePath(
|
|
187598
|
-
this._addIgnoredPath(
|
|
187599
|
-
if (this._watched.has(
|
|
187638
|
+
this._closePath(path30);
|
|
187639
|
+
this._addIgnoredPath(path30);
|
|
187640
|
+
if (this._watched.has(path30)) {
|
|
187600
187641
|
this._addIgnoredPath({
|
|
187601
|
-
path:
|
|
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,
|
|
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
|
-
|
|
187711
|
+
path30 = sp2.normalize(path30);
|
|
187671
187712
|
if (opts.cwd)
|
|
187672
|
-
|
|
187673
|
-
const args = [
|
|
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(
|
|
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(
|
|
187725
|
+
this._pendingUnlinks.set(path30, [event, ...args]);
|
|
187685
187726
|
setTimeout(() => {
|
|
187686
|
-
this._pendingUnlinks.forEach((entry,
|
|
187727
|
+
this._pendingUnlinks.forEach((entry, path31) => {
|
|
187687
187728
|
this.emit(...entry);
|
|
187688
187729
|
this.emit(EVENTS.ALL, ...entry);
|
|
187689
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
187735
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
|
|
187695
187736
|
event = EVENTS.CHANGE;
|
|
187696
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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(
|
|
187809
|
+
const item = action.get(path30);
|
|
187769
187810
|
const count = item ? item.count : 0;
|
|
187770
|
-
action.delete(
|
|
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(
|
|
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(
|
|
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 =
|
|
187799
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
187800
|
-
fullPath = sp2.join(this.options.cwd,
|
|
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(
|
|
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(
|
|
187854
|
+
writes.get(path30).lastChange = now2;
|
|
187814
187855
|
}
|
|
187815
|
-
const pw = writes.get(
|
|
187856
|
+
const pw = writes.get(path30);
|
|
187816
187857
|
const df = now2 - pw.lastChange;
|
|
187817
187858
|
if (df >= threshold) {
|
|
187818
|
-
writes.delete(
|
|
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(
|
|
187826
|
-
writes.set(
|
|
187866
|
+
if (!writes.has(path30)) {
|
|
187867
|
+
writes.set(path30, {
|
|
187827
187868
|
lastChange: now,
|
|
187828
187869
|
cancelWait: () => {
|
|
187829
|
-
writes.delete(
|
|
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(
|
|
187841
|
-
if (this.options.atomic && DOT_RE.test(
|
|
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(
|
|
187892
|
+
return this._userIgnored(path30, stats);
|
|
187852
187893
|
}
|
|
187853
|
-
_isntIgnored(
|
|
187854
|
-
return !this._isIgnored(
|
|
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(
|
|
187861
|
-
return new WatchHelper(
|
|
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
|
|
187894
|
-
const fullPath = sp2.resolve(
|
|
187895
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
187896
|
-
if (!this._throttle("remove",
|
|
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(
|
|
187942
|
+
const wp = this._getWatchedDir(path30);
|
|
187902
187943
|
const nestedDirectoryChildren = wp.getChildren();
|
|
187903
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
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 =
|
|
187951
|
+
let relPath = path30;
|
|
187911
187952
|
if (this.options.cwd)
|
|
187912
|
-
relPath = sp2.relative(this.options.cwd,
|
|
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(
|
|
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(
|
|
187922
|
-
this._emit(eventName,
|
|
187923
|
-
this._closePath(
|
|
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(
|
|
187929
|
-
this._closeFile(
|
|
187930
|
-
const dir = sp2.dirname(
|
|
187931
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
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(
|
|
187937
|
-
const closers = this._closers.get(
|
|
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(
|
|
187982
|
+
this._closers.delete(path30);
|
|
187942
187983
|
}
|
|
187943
|
-
_addPathCloser(
|
|
187984
|
+
_addPathCloser(path30, closer) {
|
|
187944
187985
|
if (!closer)
|
|
187945
187986
|
return;
|
|
187946
|
-
let list = this._closers.get(
|
|
187987
|
+
let list = this._closers.get(path30);
|
|
187947
187988
|
if (!list) {
|
|
187948
187989
|
list = [];
|
|
187949
|
-
this._closers.set(
|
|
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 =
|
|
189873
|
+
const storeId = randomUUID2();
|
|
189642
189874
|
const slots = postgresInstances.map((pg) => {
|
|
189643
|
-
const slotId =
|
|
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/
|
|
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.
|
|
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
|
|