@specific.dev/cli 0.1.48 → 0.1.49
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.__PAGE__.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- 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 +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/__next.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.databases.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/cli.js +474 -248
- package/dist/docs/builds.md +22 -0
- package/dist/docs/postgres.md +15 -0
- package/dist/docs/services.md +62 -8
- package/dist/postinstall.js +141 -0
- package/package.json +3 -2
- /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_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 (path24) => {
|
|
242
|
+
if (/^[a-z]+:\/\//i.test(path24)) {
|
|
243
|
+
return path24;
|
|
244
244
|
}
|
|
245
245
|
try {
|
|
246
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
246
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path24], { encoding: "utf8" });
|
|
247
247
|
return stdout.trim();
|
|
248
248
|
} catch {
|
|
249
|
-
return
|
|
249
|
+
return path24;
|
|
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 fs26 = $global.require("fs");
|
|
758
|
+
"object" == typeof fs26 && null !== fs26 && 0 !== Object.keys(fs26).length && ($global.fs = fs26);
|
|
759
759
|
} catch (e) {
|
|
760
760
|
}
|
|
761
761
|
if (!$global.fs) {
|
|
@@ -183055,13 +183055,13 @@ function removeCA() {
|
|
|
183055
183055
|
}
|
|
183056
183056
|
}
|
|
183057
183057
|
function installCAToTrustStore(certPath) {
|
|
183058
|
-
const
|
|
183059
|
-
if (
|
|
183058
|
+
const platform4 = os.platform();
|
|
183059
|
+
if (platform4 === "darwin") {
|
|
183060
183060
|
execSync(
|
|
183061
183061
|
`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`,
|
|
183062
183062
|
{ stdio: "inherit" }
|
|
183063
183063
|
);
|
|
183064
|
-
} else if (
|
|
183064
|
+
} else if (platform4 === "linux") {
|
|
183065
183065
|
if (fs.existsSync("/usr/local/share/ca-certificates")) {
|
|
183066
183066
|
execSync(
|
|
183067
183067
|
`sudo cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
|
|
@@ -183078,7 +183078,7 @@ function installCAToTrustStore(certPath) {
|
|
|
183078
183078
|
throw new Error("Could not detect Linux certificate trust mechanism");
|
|
183079
183079
|
}
|
|
183080
183080
|
} else {
|
|
183081
|
-
throw new Error(`Unsupported platform: ${
|
|
183081
|
+
throw new Error(`Unsupported platform: ${platform4}`);
|
|
183082
183082
|
}
|
|
183083
183083
|
}
|
|
183084
183084
|
function generateCertificate(domain, keys = []) {
|
|
@@ -183873,7 +183873,7 @@ function trackEvent(event, properties) {
|
|
|
183873
183873
|
event,
|
|
183874
183874
|
properties: {
|
|
183875
183875
|
...properties,
|
|
183876
|
-
cli_version: "0.1.
|
|
183876
|
+
cli_version: "0.1.49",
|
|
183877
183877
|
platform: process.platform,
|
|
183878
183878
|
node_version: process.version,
|
|
183879
183879
|
project_id: getProjectId(),
|
|
@@ -183888,6 +183888,11 @@ async function shutdown() {
|
|
|
183888
183888
|
}
|
|
183889
183889
|
}
|
|
183890
183890
|
|
|
183891
|
+
// src/lib/ui/interactive.ts
|
|
183892
|
+
function isInteractive() {
|
|
183893
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
183894
|
+
}
|
|
183895
|
+
|
|
183891
183896
|
// src/commands/init.tsx
|
|
183892
183897
|
var SPECIFIC_INSTRUCTIONS = `Use Specific to handle all infrastructure and development environments: like deployments, databases, real-time sync, services, secrets, configuration and environment variables. Run \`specific docs\` to learn how to use Specific. ALWAYS run \`specific check\` after making changes to the Specific configuration.`;
|
|
183893
183898
|
var CURSOR_MDC_CONTENT = `---
|
|
@@ -184156,8 +184161,77 @@ function InitUI() {
|
|
|
184156
184161
|
" "
|
|
184157
184162
|
))));
|
|
184158
184163
|
}
|
|
184159
|
-
|
|
184160
|
-
|
|
184164
|
+
var VALID_AGENT_IDS = options.map((o) => o.id);
|
|
184165
|
+
function initCommand(opts) {
|
|
184166
|
+
if (isInteractive()) {
|
|
184167
|
+
render2(/* @__PURE__ */ React2.createElement(InitUI, null));
|
|
184168
|
+
return;
|
|
184169
|
+
}
|
|
184170
|
+
if (!opts.agent || opts.agent.length === 0) {
|
|
184171
|
+
console.error(
|
|
184172
|
+
`Error: --agent is required in non-interactive environments.
|
|
184173
|
+
Usage: specific init --agent cursor claude codex
|
|
184174
|
+
Valid agents: ${VALID_AGENT_IDS.join(", ")}`
|
|
184175
|
+
);
|
|
184176
|
+
process.exit(1);
|
|
184177
|
+
}
|
|
184178
|
+
const invalid = opts.agent.filter((a) => !VALID_AGENT_IDS.includes(a));
|
|
184179
|
+
if (invalid.length > 0) {
|
|
184180
|
+
console.error(
|
|
184181
|
+
`Error: Unknown agent(s): ${invalid.join(", ")}
|
|
184182
|
+
Valid agents: ${VALID_AGENT_IDS.join(", ")}`
|
|
184183
|
+
);
|
|
184184
|
+
process.exit(1);
|
|
184185
|
+
}
|
|
184186
|
+
const checked = {};
|
|
184187
|
+
for (const agent of opts.agent) {
|
|
184188
|
+
checked[agent] = true;
|
|
184189
|
+
}
|
|
184190
|
+
const result = configureAgents(checked);
|
|
184191
|
+
trackEvent("project_initialized", {
|
|
184192
|
+
agents: opts.agent
|
|
184193
|
+
});
|
|
184194
|
+
const selectedAgents = opts.agent.length;
|
|
184195
|
+
const agentChanges = result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0;
|
|
184196
|
+
console.log("\u2713 Coding agents configured");
|
|
184197
|
+
if (result.agents.filesCreated.length > 0) {
|
|
184198
|
+
console.log(` Created: ${result.agents.filesCreated.join(", ")}`);
|
|
184199
|
+
}
|
|
184200
|
+
if (result.agents.filesModified.length > 0) {
|
|
184201
|
+
console.log(` Modified: ${result.agents.filesModified.join(", ")}`);
|
|
184202
|
+
}
|
|
184203
|
+
if (!agentChanges && selectedAgents > 0) {
|
|
184204
|
+
console.log(" No changes needed (files already configured)");
|
|
184205
|
+
}
|
|
184206
|
+
if (result.git) {
|
|
184207
|
+
const gitChanges = result.git.filesCreated.length > 0 || result.git.filesModified.length > 0;
|
|
184208
|
+
console.log("\u2713 Git configured");
|
|
184209
|
+
if (result.git.filesCreated.length > 0) {
|
|
184210
|
+
console.log(` Created: ${result.git.filesCreated.join(", ")}`);
|
|
184211
|
+
}
|
|
184212
|
+
if (result.git.filesModified.length > 0) {
|
|
184213
|
+
console.log(` Modified: ${result.git.filesModified.join(", ")}`);
|
|
184214
|
+
}
|
|
184215
|
+
if (!gitChanges) {
|
|
184216
|
+
console.log(" No changes needed (.gitignore already configured)");
|
|
184217
|
+
}
|
|
184218
|
+
}
|
|
184219
|
+
if (!caFilesExist()) {
|
|
184220
|
+
console.log(
|
|
184221
|
+
"\u26A0 TLS certificates not installed (run `specific init` in a terminal to set up)"
|
|
184222
|
+
);
|
|
184223
|
+
}
|
|
184224
|
+
if (result.showManualInstructions) {
|
|
184225
|
+
console.log("\n\u2713 Manual configuration selected");
|
|
184226
|
+
console.log("\n Add this to your agent's system prompt:");
|
|
184227
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
184228
|
+
console.log(` ${SPECIFIC_INSTRUCTIONS}`);
|
|
184229
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
184230
|
+
console.log(
|
|
184231
|
+
"\n We also recommend allowing your agent to run `specific docs *`"
|
|
184232
|
+
);
|
|
184233
|
+
console.log(" and `specific check *` without confirmation.");
|
|
184234
|
+
}
|
|
184161
184235
|
}
|
|
184162
184236
|
|
|
184163
184237
|
// src/commands/docs.tsx
|
|
@@ -184166,11 +184240,11 @@ import { join as join6, dirname as dirname2 } from "path";
|
|
|
184166
184240
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
184167
184241
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
184168
184242
|
var docsDir = join6(__dirname2, "docs");
|
|
184169
|
-
function docsCommand(
|
|
184170
|
-
const docPath = resolveDocPath(
|
|
184243
|
+
function docsCommand(path24) {
|
|
184244
|
+
const docPath = resolveDocPath(path24);
|
|
184171
184245
|
if (!docPath) {
|
|
184172
184246
|
console.error(
|
|
184173
|
-
`Documentation not found: ${
|
|
184247
|
+
`Documentation not found: ${path24 || "index"}
|
|
184174
184248
|
|
|
184175
184249
|
Run 'specific docs' to see available topics.`
|
|
184176
184250
|
);
|
|
@@ -184179,16 +184253,16 @@ Run 'specific docs' to see available topics.`
|
|
|
184179
184253
|
const content = readFileSync5(docPath, "utf-8");
|
|
184180
184254
|
console.log(content);
|
|
184181
184255
|
}
|
|
184182
|
-
function resolveDocPath(
|
|
184183
|
-
if (!
|
|
184256
|
+
function resolveDocPath(path24) {
|
|
184257
|
+
if (!path24) {
|
|
184184
184258
|
const indexPath2 = join6(docsDir, "index.md");
|
|
184185
184259
|
return existsSync5(indexPath2) ? indexPath2 : null;
|
|
184186
184260
|
}
|
|
184187
|
-
const directPath = join6(docsDir, `${
|
|
184261
|
+
const directPath = join6(docsDir, `${path24}.md`);
|
|
184188
184262
|
if (existsSync5(directPath)) {
|
|
184189
184263
|
return directPath;
|
|
184190
184264
|
}
|
|
184191
|
-
const indexPath = join6(docsDir,
|
|
184265
|
+
const indexPath = join6(docsDir, path24, "index.md");
|
|
184192
184266
|
if (existsSync5(indexPath)) {
|
|
184193
184267
|
return indexPath;
|
|
184194
184268
|
}
|
|
@@ -184283,7 +184357,7 @@ function parseReferenceString(str) {
|
|
|
184283
184357
|
attribute: "port"
|
|
184284
184358
|
};
|
|
184285
184359
|
}
|
|
184286
|
-
const serviceEndpointMatch = str.match(/^service\.(\w+)\.endpoint\.(\w+)\.(url|host|port)$/);
|
|
184360
|
+
const serviceEndpointMatch = str.match(/^service\.(\w+)\.endpoint\.(\w+)\.(url|private_url|host|port|public_url)$/);
|
|
184287
184361
|
if (serviceEndpointMatch && serviceEndpointMatch[1] && serviceEndpointMatch[2] && serviceEndpointMatch[3]) {
|
|
184288
184362
|
return {
|
|
184289
184363
|
type: "service",
|
|
@@ -184292,7 +184366,7 @@ function parseReferenceString(str) {
|
|
|
184292
184366
|
attribute: serviceEndpointMatch[3]
|
|
184293
184367
|
};
|
|
184294
184368
|
}
|
|
184295
|
-
const serviceMatch = str.match(/^service\.(\w+)\.(url|host|port)$/);
|
|
184369
|
+
const serviceMatch = str.match(/^service\.(\w+)\.(url|private_url|host|port|public_url)$/);
|
|
184296
184370
|
if (serviceMatch && serviceMatch[1] && serviceMatch[2]) {
|
|
184297
184371
|
return {
|
|
184298
184372
|
type: "service",
|
|
@@ -184397,6 +184471,17 @@ function parseBuilds(buildData) {
|
|
|
184397
184471
|
if (dev) {
|
|
184398
184472
|
build.dev = dev;
|
|
184399
184473
|
}
|
|
184474
|
+
const env2 = parseEnv(fieldObj.env);
|
|
184475
|
+
if (env2) {
|
|
184476
|
+
for (const [key, value] of Object.entries(env2)) {
|
|
184477
|
+
if (typeof value === "string")
|
|
184478
|
+
continue;
|
|
184479
|
+
if (value.type === "service" && value.attribute === "public_url")
|
|
184480
|
+
continue;
|
|
184481
|
+
throw new Error(`Build "${name}" env var "${key}" uses an unsupported reference type. Build env vars only support string literals and \${service.<name>.public_url} references.`);
|
|
184482
|
+
}
|
|
184483
|
+
build.env = env2;
|
|
184484
|
+
}
|
|
184400
184485
|
builds.push(build);
|
|
184401
184486
|
}
|
|
184402
184487
|
return builds;
|
|
@@ -184704,6 +184789,26 @@ function validateEndpointReferences(config) {
|
|
|
184704
184789
|
});
|
|
184705
184790
|
}
|
|
184706
184791
|
}
|
|
184792
|
+
if (serviceRef.attribute === "public_url") {
|
|
184793
|
+
const endpointName = serviceRef.endpointName;
|
|
184794
|
+
if (endpointName) {
|
|
184795
|
+
const targetEndpoint = targetEndpoints.find((e) => e.name === endpointName);
|
|
184796
|
+
if (targetEndpoint && !targetEndpoint.public) {
|
|
184797
|
+
errors.push({
|
|
184798
|
+
service: service.name,
|
|
184799
|
+
message: `Service "${service.name}" references public_url of endpoint "${endpointName}" on service "${serviceRef.serviceName}" in env var "${key}", but that endpoint is not public. Add \`public = true\` to the endpoint.`
|
|
184800
|
+
});
|
|
184801
|
+
}
|
|
184802
|
+
} else {
|
|
184803
|
+
const defaultEndpoint = targetEndpoints.length === 1 ? targetEndpoints[0] : targetEndpoints.find((e) => e.name === "default");
|
|
184804
|
+
if (defaultEndpoint && !defaultEndpoint.public) {
|
|
184805
|
+
errors.push({
|
|
184806
|
+
service: service.name,
|
|
184807
|
+
message: `Service "${service.name}" references public_url of service "${serviceRef.serviceName}" in env var "${key}", but its endpoint is not public. Add \`public = true\` to the endpoint.`
|
|
184808
|
+
});
|
|
184809
|
+
}
|
|
184810
|
+
}
|
|
184811
|
+
}
|
|
184707
184812
|
}
|
|
184708
184813
|
if (typeof value === "object" && value.type === "endpoint") {
|
|
184709
184814
|
const endpointRef = value;
|
|
@@ -184860,7 +184965,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
184860
184965
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
184861
184966
|
const statMethod = opts.lstat ? lstat : stat;
|
|
184862
184967
|
if (wantBigintFsStats) {
|
|
184863
|
-
this._stat = (
|
|
184968
|
+
this._stat = (path24) => statMethod(path24, { bigint: true });
|
|
184864
184969
|
} else {
|
|
184865
184970
|
this._stat = statMethod;
|
|
184866
184971
|
}
|
|
@@ -184885,8 +184990,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
184885
184990
|
const par = this.parent;
|
|
184886
184991
|
const fil = par && par.files;
|
|
184887
184992
|
if (fil && fil.length > 0) {
|
|
184888
|
-
const { path:
|
|
184889
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
184993
|
+
const { path: path24, depth } = par;
|
|
184994
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
|
|
184890
184995
|
const awaited = await Promise.all(slice);
|
|
184891
184996
|
for (const entry of awaited) {
|
|
184892
184997
|
if (!entry)
|
|
@@ -184926,20 +185031,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
184926
185031
|
this.reading = false;
|
|
184927
185032
|
}
|
|
184928
185033
|
}
|
|
184929
|
-
async _exploreDir(
|
|
185034
|
+
async _exploreDir(path24, depth) {
|
|
184930
185035
|
let files;
|
|
184931
185036
|
try {
|
|
184932
|
-
files = await readdir(
|
|
185037
|
+
files = await readdir(path24, this._rdOptions);
|
|
184933
185038
|
} catch (error) {
|
|
184934
185039
|
this._onError(error);
|
|
184935
185040
|
}
|
|
184936
|
-
return { files, depth, path:
|
|
185041
|
+
return { files, depth, path: path24 };
|
|
184937
185042
|
}
|
|
184938
|
-
async _formatEntry(dirent,
|
|
185043
|
+
async _formatEntry(dirent, path24) {
|
|
184939
185044
|
let entry;
|
|
184940
185045
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
184941
185046
|
try {
|
|
184942
|
-
const fullPath = presolve(pjoin(
|
|
185047
|
+
const fullPath = presolve(pjoin(path24, basename5));
|
|
184943
185048
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
184944
185049
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
184945
185050
|
} catch (err) {
|
|
@@ -185339,16 +185444,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
185339
185444
|
};
|
|
185340
185445
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
185341
185446
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
185342
|
-
function createFsWatchInstance(
|
|
185447
|
+
function createFsWatchInstance(path24, options2, listener, errHandler, emitRaw) {
|
|
185343
185448
|
const handleEvent = (rawEvent, evPath) => {
|
|
185344
|
-
listener(
|
|
185345
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
185346
|
-
if (evPath &&
|
|
185347
|
-
fsWatchBroadcast(sp.resolve(
|
|
185449
|
+
listener(path24);
|
|
185450
|
+
emitRaw(rawEvent, evPath, { watchedPath: path24 });
|
|
185451
|
+
if (evPath && path24 !== evPath) {
|
|
185452
|
+
fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
|
|
185348
185453
|
}
|
|
185349
185454
|
};
|
|
185350
185455
|
try {
|
|
185351
|
-
return fs_watch(
|
|
185456
|
+
return fs_watch(path24, {
|
|
185352
185457
|
persistent: options2.persistent
|
|
185353
185458
|
}, handleEvent);
|
|
185354
185459
|
} catch (error) {
|
|
@@ -185364,12 +185469,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
185364
185469
|
listener(val1, val2, val3);
|
|
185365
185470
|
});
|
|
185366
185471
|
};
|
|
185367
|
-
var setFsWatchListener = (
|
|
185472
|
+
var setFsWatchListener = (path24, fullPath, options2, handlers) => {
|
|
185368
185473
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
185369
185474
|
let cont = FsWatchInstances.get(fullPath);
|
|
185370
185475
|
let watcher;
|
|
185371
185476
|
if (!options2.persistent) {
|
|
185372
|
-
watcher = createFsWatchInstance(
|
|
185477
|
+
watcher = createFsWatchInstance(path24, options2, listener, errHandler, rawEmitter);
|
|
185373
185478
|
if (!watcher)
|
|
185374
185479
|
return;
|
|
185375
185480
|
return watcher.close.bind(watcher);
|
|
@@ -185380,7 +185485,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
|
|
|
185380
185485
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
185381
185486
|
} else {
|
|
185382
185487
|
watcher = createFsWatchInstance(
|
|
185383
|
-
|
|
185488
|
+
path24,
|
|
185384
185489
|
options2,
|
|
185385
185490
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
185386
185491
|
errHandler,
|
|
@@ -185395,7 +185500,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
|
|
|
185395
185500
|
cont.watcherUnusable = true;
|
|
185396
185501
|
if (isWindows && error.code === "EPERM") {
|
|
185397
185502
|
try {
|
|
185398
|
-
const fd = await open2(
|
|
185503
|
+
const fd = await open2(path24, "r");
|
|
185399
185504
|
await fd.close();
|
|
185400
185505
|
broadcastErr(error);
|
|
185401
185506
|
} catch (err) {
|
|
@@ -185426,7 +185531,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
|
|
|
185426
185531
|
};
|
|
185427
185532
|
};
|
|
185428
185533
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
185429
|
-
var setFsWatchFileListener = (
|
|
185534
|
+
var setFsWatchFileListener = (path24, fullPath, options2, handlers) => {
|
|
185430
185535
|
const { listener, rawEmitter } = handlers;
|
|
185431
185536
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
185432
185537
|
const copts = cont && cont.options;
|
|
@@ -185448,7 +185553,7 @@ var setFsWatchFileListener = (path23, fullPath, options2, handlers) => {
|
|
|
185448
185553
|
});
|
|
185449
185554
|
const currmtime = curr.mtimeMs;
|
|
185450
185555
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
185451
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
185556
|
+
foreach(cont.listeners, (listener2) => listener2(path24, curr));
|
|
185452
185557
|
}
|
|
185453
185558
|
})
|
|
185454
185559
|
};
|
|
@@ -185478,13 +185583,13 @@ var NodeFsHandler = class {
|
|
|
185478
185583
|
* @param listener on fs change
|
|
185479
185584
|
* @returns closer for the watcher instance
|
|
185480
185585
|
*/
|
|
185481
|
-
_watchWithNodeFs(
|
|
185586
|
+
_watchWithNodeFs(path24, listener) {
|
|
185482
185587
|
const opts = this.fsw.options;
|
|
185483
|
-
const directory = sp.dirname(
|
|
185484
|
-
const basename5 = sp.basename(
|
|
185588
|
+
const directory = sp.dirname(path24);
|
|
185589
|
+
const basename5 = sp.basename(path24);
|
|
185485
185590
|
const parent = this.fsw._getWatchedDir(directory);
|
|
185486
185591
|
parent.add(basename5);
|
|
185487
|
-
const absolutePath = sp.resolve(
|
|
185592
|
+
const absolutePath = sp.resolve(path24);
|
|
185488
185593
|
const options2 = {
|
|
185489
185594
|
persistent: opts.persistent
|
|
185490
185595
|
};
|
|
@@ -185494,12 +185599,12 @@ var NodeFsHandler = class {
|
|
|
185494
185599
|
if (opts.usePolling) {
|
|
185495
185600
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
185496
185601
|
options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
185497
|
-
closer = setFsWatchFileListener(
|
|
185602
|
+
closer = setFsWatchFileListener(path24, absolutePath, options2, {
|
|
185498
185603
|
listener,
|
|
185499
185604
|
rawEmitter: this.fsw._emitRaw
|
|
185500
185605
|
});
|
|
185501
185606
|
} else {
|
|
185502
|
-
closer = setFsWatchListener(
|
|
185607
|
+
closer = setFsWatchListener(path24, absolutePath, options2, {
|
|
185503
185608
|
listener,
|
|
185504
185609
|
errHandler: this._boundHandleError,
|
|
185505
185610
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -185521,7 +185626,7 @@ var NodeFsHandler = class {
|
|
|
185521
185626
|
let prevStats = stats;
|
|
185522
185627
|
if (parent.has(basename5))
|
|
185523
185628
|
return;
|
|
185524
|
-
const listener = async (
|
|
185629
|
+
const listener = async (path24, newStats) => {
|
|
185525
185630
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
185526
185631
|
return;
|
|
185527
185632
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -185535,11 +185640,11 @@ var NodeFsHandler = class {
|
|
|
185535
185640
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
185536
185641
|
}
|
|
185537
185642
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
185538
|
-
this.fsw._closeFile(
|
|
185643
|
+
this.fsw._closeFile(path24);
|
|
185539
185644
|
prevStats = newStats2;
|
|
185540
185645
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
185541
185646
|
if (closer2)
|
|
185542
|
-
this.fsw._addPathCloser(
|
|
185647
|
+
this.fsw._addPathCloser(path24, closer2);
|
|
185543
185648
|
} else {
|
|
185544
185649
|
prevStats = newStats2;
|
|
185545
185650
|
}
|
|
@@ -185571,7 +185676,7 @@ var NodeFsHandler = class {
|
|
|
185571
185676
|
* @param item basename of this item
|
|
185572
185677
|
* @returns true if no more processing is needed for this entry.
|
|
185573
185678
|
*/
|
|
185574
|
-
async _handleSymlink(entry, directory,
|
|
185679
|
+
async _handleSymlink(entry, directory, path24, item) {
|
|
185575
185680
|
if (this.fsw.closed) {
|
|
185576
185681
|
return;
|
|
185577
185682
|
}
|
|
@@ -185581,7 +185686,7 @@ var NodeFsHandler = class {
|
|
|
185581
185686
|
this.fsw._incrReadyCount();
|
|
185582
185687
|
let linkPath;
|
|
185583
185688
|
try {
|
|
185584
|
-
linkPath = await fsrealpath(
|
|
185689
|
+
linkPath = await fsrealpath(path24);
|
|
185585
185690
|
} catch (e) {
|
|
185586
185691
|
this.fsw._emitReady();
|
|
185587
185692
|
return true;
|
|
@@ -185591,12 +185696,12 @@ var NodeFsHandler = class {
|
|
|
185591
185696
|
if (dir.has(item)) {
|
|
185592
185697
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
185593
185698
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
185594
|
-
this.fsw._emit(EV.CHANGE,
|
|
185699
|
+
this.fsw._emit(EV.CHANGE, path24, entry.stats);
|
|
185595
185700
|
}
|
|
185596
185701
|
} else {
|
|
185597
185702
|
dir.add(item);
|
|
185598
185703
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
185599
|
-
this.fsw._emit(EV.ADD,
|
|
185704
|
+
this.fsw._emit(EV.ADD, path24, entry.stats);
|
|
185600
185705
|
}
|
|
185601
185706
|
this.fsw._emitReady();
|
|
185602
185707
|
return true;
|
|
@@ -185626,9 +185731,9 @@ var NodeFsHandler = class {
|
|
|
185626
185731
|
return;
|
|
185627
185732
|
}
|
|
185628
185733
|
const item = entry.path;
|
|
185629
|
-
let
|
|
185734
|
+
let path24 = sp.join(directory, item);
|
|
185630
185735
|
current.add(item);
|
|
185631
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
185736
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
|
|
185632
185737
|
return;
|
|
185633
185738
|
}
|
|
185634
185739
|
if (this.fsw.closed) {
|
|
@@ -185637,8 +185742,8 @@ var NodeFsHandler = class {
|
|
|
185637
185742
|
}
|
|
185638
185743
|
if (item === target || !target && !previous.has(item)) {
|
|
185639
185744
|
this.fsw._incrReadyCount();
|
|
185640
|
-
|
|
185641
|
-
this._addToNodeFs(
|
|
185745
|
+
path24 = sp.join(dir, sp.relative(dir, path24));
|
|
185746
|
+
this._addToNodeFs(path24, initialAdd, wh, depth + 1);
|
|
185642
185747
|
}
|
|
185643
185748
|
}).on(EV.ERROR, this._boundHandleError);
|
|
185644
185749
|
return new Promise((resolve7, reject) => {
|
|
@@ -185707,13 +185812,13 @@ var NodeFsHandler = class {
|
|
|
185707
185812
|
* @param depth Child path actually targeted for watch
|
|
185708
185813
|
* @param target Child path actually targeted for watch
|
|
185709
185814
|
*/
|
|
185710
|
-
async _addToNodeFs(
|
|
185815
|
+
async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
|
|
185711
185816
|
const ready = this.fsw._emitReady;
|
|
185712
|
-
if (this.fsw._isIgnored(
|
|
185817
|
+
if (this.fsw._isIgnored(path24) || this.fsw.closed) {
|
|
185713
185818
|
ready();
|
|
185714
185819
|
return false;
|
|
185715
185820
|
}
|
|
185716
|
-
const wh = this.fsw._getWatchHelpers(
|
|
185821
|
+
const wh = this.fsw._getWatchHelpers(path24);
|
|
185717
185822
|
if (priorWh) {
|
|
185718
185823
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
185719
185824
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -185729,8 +185834,8 @@ var NodeFsHandler = class {
|
|
|
185729
185834
|
const follow = this.fsw.options.followSymlinks;
|
|
185730
185835
|
let closer;
|
|
185731
185836
|
if (stats.isDirectory()) {
|
|
185732
|
-
const absPath = sp.resolve(
|
|
185733
|
-
const targetPath = follow ? await fsrealpath(
|
|
185837
|
+
const absPath = sp.resolve(path24);
|
|
185838
|
+
const targetPath = follow ? await fsrealpath(path24) : path24;
|
|
185734
185839
|
if (this.fsw.closed)
|
|
185735
185840
|
return;
|
|
185736
185841
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -185740,29 +185845,29 @@ var NodeFsHandler = class {
|
|
|
185740
185845
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
185741
185846
|
}
|
|
185742
185847
|
} else if (stats.isSymbolicLink()) {
|
|
185743
|
-
const targetPath = follow ? await fsrealpath(
|
|
185848
|
+
const targetPath = follow ? await fsrealpath(path24) : path24;
|
|
185744
185849
|
if (this.fsw.closed)
|
|
185745
185850
|
return;
|
|
185746
185851
|
const parent = sp.dirname(wh.watchPath);
|
|
185747
185852
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
185748
185853
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
185749
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
185854
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
|
|
185750
185855
|
if (this.fsw.closed)
|
|
185751
185856
|
return;
|
|
185752
185857
|
if (targetPath !== void 0) {
|
|
185753
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
185858
|
+
this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
|
|
185754
185859
|
}
|
|
185755
185860
|
} else {
|
|
185756
185861
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
185757
185862
|
}
|
|
185758
185863
|
ready();
|
|
185759
185864
|
if (closer)
|
|
185760
|
-
this.fsw._addPathCloser(
|
|
185865
|
+
this.fsw._addPathCloser(path24, closer);
|
|
185761
185866
|
return false;
|
|
185762
185867
|
} catch (error) {
|
|
185763
185868
|
if (this.fsw._handleError(error)) {
|
|
185764
185869
|
ready();
|
|
185765
|
-
return
|
|
185870
|
+
return path24;
|
|
185766
185871
|
}
|
|
185767
185872
|
}
|
|
185768
185873
|
}
|
|
@@ -185805,24 +185910,24 @@ function createPattern(matcher) {
|
|
|
185805
185910
|
}
|
|
185806
185911
|
return () => false;
|
|
185807
185912
|
}
|
|
185808
|
-
function normalizePath(
|
|
185809
|
-
if (typeof
|
|
185913
|
+
function normalizePath(path24) {
|
|
185914
|
+
if (typeof path24 !== "string")
|
|
185810
185915
|
throw new Error("string expected");
|
|
185811
|
-
|
|
185812
|
-
|
|
185916
|
+
path24 = sp2.normalize(path24);
|
|
185917
|
+
path24 = path24.replace(/\\/g, "/");
|
|
185813
185918
|
let prepend = false;
|
|
185814
|
-
if (
|
|
185919
|
+
if (path24.startsWith("//"))
|
|
185815
185920
|
prepend = true;
|
|
185816
|
-
|
|
185921
|
+
path24 = path24.replace(DOUBLE_SLASH_RE, "/");
|
|
185817
185922
|
if (prepend)
|
|
185818
|
-
|
|
185819
|
-
return
|
|
185923
|
+
path24 = "/" + path24;
|
|
185924
|
+
return path24;
|
|
185820
185925
|
}
|
|
185821
185926
|
function matchPatterns(patterns, testString, stats) {
|
|
185822
|
-
const
|
|
185927
|
+
const path24 = normalizePath(testString);
|
|
185823
185928
|
for (let index = 0; index < patterns.length; index++) {
|
|
185824
185929
|
const pattern = patterns[index];
|
|
185825
|
-
if (pattern(
|
|
185930
|
+
if (pattern(path24, stats)) {
|
|
185826
185931
|
return true;
|
|
185827
185932
|
}
|
|
185828
185933
|
}
|
|
@@ -185860,19 +185965,19 @@ var toUnix = (string) => {
|
|
|
185860
185965
|
}
|
|
185861
185966
|
return str;
|
|
185862
185967
|
};
|
|
185863
|
-
var normalizePathToUnix = (
|
|
185864
|
-
var normalizeIgnored = (cwd = "") => (
|
|
185865
|
-
if (typeof
|
|
185866
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
185968
|
+
var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
|
|
185969
|
+
var normalizeIgnored = (cwd = "") => (path24) => {
|
|
185970
|
+
if (typeof path24 === "string") {
|
|
185971
|
+
return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
|
|
185867
185972
|
} else {
|
|
185868
|
-
return
|
|
185973
|
+
return path24;
|
|
185869
185974
|
}
|
|
185870
185975
|
};
|
|
185871
|
-
var getAbsolutePath = (
|
|
185872
|
-
if (sp2.isAbsolute(
|
|
185873
|
-
return
|
|
185976
|
+
var getAbsolutePath = (path24, cwd) => {
|
|
185977
|
+
if (sp2.isAbsolute(path24)) {
|
|
185978
|
+
return path24;
|
|
185874
185979
|
}
|
|
185875
|
-
return sp2.join(cwd,
|
|
185980
|
+
return sp2.join(cwd, path24);
|
|
185876
185981
|
};
|
|
185877
185982
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
185878
185983
|
var DirEntry = class {
|
|
@@ -185937,10 +186042,10 @@ var WatchHelper = class {
|
|
|
185937
186042
|
dirParts;
|
|
185938
186043
|
followSymlinks;
|
|
185939
186044
|
statMethod;
|
|
185940
|
-
constructor(
|
|
186045
|
+
constructor(path24, follow, fsw) {
|
|
185941
186046
|
this.fsw = fsw;
|
|
185942
|
-
const watchPath =
|
|
185943
|
-
this.path =
|
|
186047
|
+
const watchPath = path24;
|
|
186048
|
+
this.path = path24 = path24.replace(REPLACER_RE, "");
|
|
185944
186049
|
this.watchPath = watchPath;
|
|
185945
186050
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
185946
186051
|
this.dirParts = [];
|
|
@@ -186080,20 +186185,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186080
186185
|
this._closePromise = void 0;
|
|
186081
186186
|
let paths = unifyPaths(paths_);
|
|
186082
186187
|
if (cwd) {
|
|
186083
|
-
paths = paths.map((
|
|
186084
|
-
const absPath = getAbsolutePath(
|
|
186188
|
+
paths = paths.map((path24) => {
|
|
186189
|
+
const absPath = getAbsolutePath(path24, cwd);
|
|
186085
186190
|
return absPath;
|
|
186086
186191
|
});
|
|
186087
186192
|
}
|
|
186088
|
-
paths.forEach((
|
|
186089
|
-
this._removeIgnoredPath(
|
|
186193
|
+
paths.forEach((path24) => {
|
|
186194
|
+
this._removeIgnoredPath(path24);
|
|
186090
186195
|
});
|
|
186091
186196
|
this._userIgnored = void 0;
|
|
186092
186197
|
if (!this._readyCount)
|
|
186093
186198
|
this._readyCount = 0;
|
|
186094
186199
|
this._readyCount += paths.length;
|
|
186095
|
-
Promise.all(paths.map(async (
|
|
186096
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
186200
|
+
Promise.all(paths.map(async (path24) => {
|
|
186201
|
+
const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
|
|
186097
186202
|
if (res)
|
|
186098
186203
|
this._emitReady();
|
|
186099
186204
|
return res;
|
|
@@ -186115,17 +186220,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186115
186220
|
return this;
|
|
186116
186221
|
const paths = unifyPaths(paths_);
|
|
186117
186222
|
const { cwd } = this.options;
|
|
186118
|
-
paths.forEach((
|
|
186119
|
-
if (!sp2.isAbsolute(
|
|
186223
|
+
paths.forEach((path24) => {
|
|
186224
|
+
if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
|
|
186120
186225
|
if (cwd)
|
|
186121
|
-
|
|
186122
|
-
|
|
186226
|
+
path24 = sp2.join(cwd, path24);
|
|
186227
|
+
path24 = sp2.resolve(path24);
|
|
186123
186228
|
}
|
|
186124
|
-
this._closePath(
|
|
186125
|
-
this._addIgnoredPath(
|
|
186126
|
-
if (this._watched.has(
|
|
186229
|
+
this._closePath(path24);
|
|
186230
|
+
this._addIgnoredPath(path24);
|
|
186231
|
+
if (this._watched.has(path24)) {
|
|
186127
186232
|
this._addIgnoredPath({
|
|
186128
|
-
path:
|
|
186233
|
+
path: path24,
|
|
186129
186234
|
recursive: true
|
|
186130
186235
|
});
|
|
186131
186236
|
}
|
|
@@ -186189,38 +186294,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186189
186294
|
* @param stats arguments to be passed with event
|
|
186190
186295
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
186191
186296
|
*/
|
|
186192
|
-
async _emit(event,
|
|
186297
|
+
async _emit(event, path24, stats) {
|
|
186193
186298
|
if (this.closed)
|
|
186194
186299
|
return;
|
|
186195
186300
|
const opts = this.options;
|
|
186196
186301
|
if (isWindows)
|
|
186197
|
-
|
|
186302
|
+
path24 = sp2.normalize(path24);
|
|
186198
186303
|
if (opts.cwd)
|
|
186199
|
-
|
|
186200
|
-
const args = [
|
|
186304
|
+
path24 = sp2.relative(opts.cwd, path24);
|
|
186305
|
+
const args = [path24];
|
|
186201
186306
|
if (stats != null)
|
|
186202
186307
|
args.push(stats);
|
|
186203
186308
|
const awf = opts.awaitWriteFinish;
|
|
186204
186309
|
let pw;
|
|
186205
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
186310
|
+
if (awf && (pw = this._pendingWrites.get(path24))) {
|
|
186206
186311
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
186207
186312
|
return this;
|
|
186208
186313
|
}
|
|
186209
186314
|
if (opts.atomic) {
|
|
186210
186315
|
if (event === EVENTS.UNLINK) {
|
|
186211
|
-
this._pendingUnlinks.set(
|
|
186316
|
+
this._pendingUnlinks.set(path24, [event, ...args]);
|
|
186212
186317
|
setTimeout(() => {
|
|
186213
|
-
this._pendingUnlinks.forEach((entry,
|
|
186318
|
+
this._pendingUnlinks.forEach((entry, path25) => {
|
|
186214
186319
|
this.emit(...entry);
|
|
186215
186320
|
this.emit(EVENTS.ALL, ...entry);
|
|
186216
|
-
this._pendingUnlinks.delete(
|
|
186321
|
+
this._pendingUnlinks.delete(path25);
|
|
186217
186322
|
});
|
|
186218
186323
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
186219
186324
|
return this;
|
|
186220
186325
|
}
|
|
186221
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
186326
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
|
|
186222
186327
|
event = EVENTS.CHANGE;
|
|
186223
|
-
this._pendingUnlinks.delete(
|
|
186328
|
+
this._pendingUnlinks.delete(path24);
|
|
186224
186329
|
}
|
|
186225
186330
|
}
|
|
186226
186331
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -186238,16 +186343,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186238
186343
|
this.emitWithAll(event, args);
|
|
186239
186344
|
}
|
|
186240
186345
|
};
|
|
186241
|
-
this._awaitWriteFinish(
|
|
186346
|
+
this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
|
|
186242
186347
|
return this;
|
|
186243
186348
|
}
|
|
186244
186349
|
if (event === EVENTS.CHANGE) {
|
|
186245
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
186350
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
|
|
186246
186351
|
if (isThrottled)
|
|
186247
186352
|
return this;
|
|
186248
186353
|
}
|
|
186249
186354
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
186250
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
186355
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
|
|
186251
186356
|
let stats2;
|
|
186252
186357
|
try {
|
|
186253
186358
|
stats2 = await stat3(fullPath);
|
|
@@ -186278,23 +186383,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186278
186383
|
* @param timeout duration of time to suppress duplicate actions
|
|
186279
186384
|
* @returns tracking object or false if action should be suppressed
|
|
186280
186385
|
*/
|
|
186281
|
-
_throttle(actionType,
|
|
186386
|
+
_throttle(actionType, path24, timeout) {
|
|
186282
186387
|
if (!this._throttled.has(actionType)) {
|
|
186283
186388
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
186284
186389
|
}
|
|
186285
186390
|
const action = this._throttled.get(actionType);
|
|
186286
186391
|
if (!action)
|
|
186287
186392
|
throw new Error("invalid throttle");
|
|
186288
|
-
const actionPath = action.get(
|
|
186393
|
+
const actionPath = action.get(path24);
|
|
186289
186394
|
if (actionPath) {
|
|
186290
186395
|
actionPath.count++;
|
|
186291
186396
|
return false;
|
|
186292
186397
|
}
|
|
186293
186398
|
let timeoutObject;
|
|
186294
186399
|
const clear = () => {
|
|
186295
|
-
const item = action.get(
|
|
186400
|
+
const item = action.get(path24);
|
|
186296
186401
|
const count = item ? item.count : 0;
|
|
186297
|
-
action.delete(
|
|
186402
|
+
action.delete(path24);
|
|
186298
186403
|
clearTimeout(timeoutObject);
|
|
186299
186404
|
if (item)
|
|
186300
186405
|
clearTimeout(item.timeoutObject);
|
|
@@ -186302,7 +186407,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186302
186407
|
};
|
|
186303
186408
|
timeoutObject = setTimeout(clear, timeout);
|
|
186304
186409
|
const thr = { timeoutObject, clear, count: 0 };
|
|
186305
|
-
action.set(
|
|
186410
|
+
action.set(path24, thr);
|
|
186306
186411
|
return thr;
|
|
186307
186412
|
}
|
|
186308
186413
|
_incrReadyCount() {
|
|
@@ -186316,44 +186421,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186316
186421
|
* @param event
|
|
186317
186422
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
186318
186423
|
*/
|
|
186319
|
-
_awaitWriteFinish(
|
|
186424
|
+
_awaitWriteFinish(path24, threshold, event, awfEmit) {
|
|
186320
186425
|
const awf = this.options.awaitWriteFinish;
|
|
186321
186426
|
if (typeof awf !== "object")
|
|
186322
186427
|
return;
|
|
186323
186428
|
const pollInterval = awf.pollInterval;
|
|
186324
186429
|
let timeoutHandler;
|
|
186325
|
-
let fullPath =
|
|
186326
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
186327
|
-
fullPath = sp2.join(this.options.cwd,
|
|
186430
|
+
let fullPath = path24;
|
|
186431
|
+
if (this.options.cwd && !sp2.isAbsolute(path24)) {
|
|
186432
|
+
fullPath = sp2.join(this.options.cwd, path24);
|
|
186328
186433
|
}
|
|
186329
186434
|
const now = /* @__PURE__ */ new Date();
|
|
186330
186435
|
const writes = this._pendingWrites;
|
|
186331
186436
|
function awaitWriteFinishFn(prevStat) {
|
|
186332
186437
|
statcb(fullPath, (err, curStat) => {
|
|
186333
|
-
if (err || !writes.has(
|
|
186438
|
+
if (err || !writes.has(path24)) {
|
|
186334
186439
|
if (err && err.code !== "ENOENT")
|
|
186335
186440
|
awfEmit(err);
|
|
186336
186441
|
return;
|
|
186337
186442
|
}
|
|
186338
186443
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
186339
186444
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
186340
|
-
writes.get(
|
|
186445
|
+
writes.get(path24).lastChange = now2;
|
|
186341
186446
|
}
|
|
186342
|
-
const pw = writes.get(
|
|
186447
|
+
const pw = writes.get(path24);
|
|
186343
186448
|
const df = now2 - pw.lastChange;
|
|
186344
186449
|
if (df >= threshold) {
|
|
186345
|
-
writes.delete(
|
|
186450
|
+
writes.delete(path24);
|
|
186346
186451
|
awfEmit(void 0, curStat);
|
|
186347
186452
|
} else {
|
|
186348
186453
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
186349
186454
|
}
|
|
186350
186455
|
});
|
|
186351
186456
|
}
|
|
186352
|
-
if (!writes.has(
|
|
186353
|
-
writes.set(
|
|
186457
|
+
if (!writes.has(path24)) {
|
|
186458
|
+
writes.set(path24, {
|
|
186354
186459
|
lastChange: now,
|
|
186355
186460
|
cancelWait: () => {
|
|
186356
|
-
writes.delete(
|
|
186461
|
+
writes.delete(path24);
|
|
186357
186462
|
clearTimeout(timeoutHandler);
|
|
186358
186463
|
return event;
|
|
186359
186464
|
}
|
|
@@ -186364,8 +186469,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186364
186469
|
/**
|
|
186365
186470
|
* Determines whether user has asked to ignore this path.
|
|
186366
186471
|
*/
|
|
186367
|
-
_isIgnored(
|
|
186368
|
-
if (this.options.atomic && DOT_RE.test(
|
|
186472
|
+
_isIgnored(path24, stats) {
|
|
186473
|
+
if (this.options.atomic && DOT_RE.test(path24))
|
|
186369
186474
|
return true;
|
|
186370
186475
|
if (!this._userIgnored) {
|
|
186371
186476
|
const { cwd } = this.options;
|
|
@@ -186375,17 +186480,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186375
186480
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
186376
186481
|
this._userIgnored = anymatch(list, void 0);
|
|
186377
186482
|
}
|
|
186378
|
-
return this._userIgnored(
|
|
186483
|
+
return this._userIgnored(path24, stats);
|
|
186379
186484
|
}
|
|
186380
|
-
_isntIgnored(
|
|
186381
|
-
return !this._isIgnored(
|
|
186485
|
+
_isntIgnored(path24, stat4) {
|
|
186486
|
+
return !this._isIgnored(path24, stat4);
|
|
186382
186487
|
}
|
|
186383
186488
|
/**
|
|
186384
186489
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
186385
186490
|
* @param path file or directory pattern being watched
|
|
186386
186491
|
*/
|
|
186387
|
-
_getWatchHelpers(
|
|
186388
|
-
return new WatchHelper(
|
|
186492
|
+
_getWatchHelpers(path24) {
|
|
186493
|
+
return new WatchHelper(path24, this.options.followSymlinks, this);
|
|
186389
186494
|
}
|
|
186390
186495
|
// Directory helpers
|
|
186391
186496
|
// -----------------
|
|
@@ -186417,63 +186522,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186417
186522
|
* @param item base path of item/directory
|
|
186418
186523
|
*/
|
|
186419
186524
|
_remove(directory, item, isDirectory) {
|
|
186420
|
-
const
|
|
186421
|
-
const fullPath = sp2.resolve(
|
|
186422
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
186423
|
-
if (!this._throttle("remove",
|
|
186525
|
+
const path24 = sp2.join(directory, item);
|
|
186526
|
+
const fullPath = sp2.resolve(path24);
|
|
186527
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
|
|
186528
|
+
if (!this._throttle("remove", path24, 100))
|
|
186424
186529
|
return;
|
|
186425
186530
|
if (!isDirectory && this._watched.size === 1) {
|
|
186426
186531
|
this.add(directory, item, true);
|
|
186427
186532
|
}
|
|
186428
|
-
const wp = this._getWatchedDir(
|
|
186533
|
+
const wp = this._getWatchedDir(path24);
|
|
186429
186534
|
const nestedDirectoryChildren = wp.getChildren();
|
|
186430
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
186535
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
|
|
186431
186536
|
const parent = this._getWatchedDir(directory);
|
|
186432
186537
|
const wasTracked = parent.has(item);
|
|
186433
186538
|
parent.remove(item);
|
|
186434
186539
|
if (this._symlinkPaths.has(fullPath)) {
|
|
186435
186540
|
this._symlinkPaths.delete(fullPath);
|
|
186436
186541
|
}
|
|
186437
|
-
let relPath =
|
|
186542
|
+
let relPath = path24;
|
|
186438
186543
|
if (this.options.cwd)
|
|
186439
|
-
relPath = sp2.relative(this.options.cwd,
|
|
186544
|
+
relPath = sp2.relative(this.options.cwd, path24);
|
|
186440
186545
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
186441
186546
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
186442
186547
|
if (event === EVENTS.ADD)
|
|
186443
186548
|
return;
|
|
186444
186549
|
}
|
|
186445
|
-
this._watched.delete(
|
|
186550
|
+
this._watched.delete(path24);
|
|
186446
186551
|
this._watched.delete(fullPath);
|
|
186447
186552
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
186448
|
-
if (wasTracked && !this._isIgnored(
|
|
186449
|
-
this._emit(eventName,
|
|
186450
|
-
this._closePath(
|
|
186553
|
+
if (wasTracked && !this._isIgnored(path24))
|
|
186554
|
+
this._emit(eventName, path24);
|
|
186555
|
+
this._closePath(path24);
|
|
186451
186556
|
}
|
|
186452
186557
|
/**
|
|
186453
186558
|
* Closes all watchers for a path
|
|
186454
186559
|
*/
|
|
186455
|
-
_closePath(
|
|
186456
|
-
this._closeFile(
|
|
186457
|
-
const dir = sp2.dirname(
|
|
186458
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
186560
|
+
_closePath(path24) {
|
|
186561
|
+
this._closeFile(path24);
|
|
186562
|
+
const dir = sp2.dirname(path24);
|
|
186563
|
+
this._getWatchedDir(dir).remove(sp2.basename(path24));
|
|
186459
186564
|
}
|
|
186460
186565
|
/**
|
|
186461
186566
|
* Closes only file-specific watchers
|
|
186462
186567
|
*/
|
|
186463
|
-
_closeFile(
|
|
186464
|
-
const closers = this._closers.get(
|
|
186568
|
+
_closeFile(path24) {
|
|
186569
|
+
const closers = this._closers.get(path24);
|
|
186465
186570
|
if (!closers)
|
|
186466
186571
|
return;
|
|
186467
186572
|
closers.forEach((closer) => closer());
|
|
186468
|
-
this._closers.delete(
|
|
186573
|
+
this._closers.delete(path24);
|
|
186469
186574
|
}
|
|
186470
|
-
_addPathCloser(
|
|
186575
|
+
_addPathCloser(path24, closer) {
|
|
186471
186576
|
if (!closer)
|
|
186472
186577
|
return;
|
|
186473
|
-
let list = this._closers.get(
|
|
186578
|
+
let list = this._closers.get(path24);
|
|
186474
186579
|
if (!list) {
|
|
186475
186580
|
list = [];
|
|
186476
|
-
this._closers.set(
|
|
186581
|
+
this._closers.set(path24, list);
|
|
186477
186582
|
}
|
|
186478
186583
|
list.push(closer);
|
|
186479
186584
|
}
|
|
@@ -186591,7 +186696,6 @@ var StablePortAllocator = class {
|
|
|
186591
186696
|
import * as fs14 from "fs";
|
|
186592
186697
|
import * as path10 from "path";
|
|
186593
186698
|
import * as net from "net";
|
|
186594
|
-
import * as os7 from "os";
|
|
186595
186699
|
import { spawn } from "child_process";
|
|
186596
186700
|
|
|
186597
186701
|
// src/lib/bin/types.ts
|
|
@@ -186634,15 +186738,27 @@ import * as path9 from "path";
|
|
|
186634
186738
|
import * as os6 from "os";
|
|
186635
186739
|
import { createReadStream } from "fs";
|
|
186636
186740
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
186741
|
+
function getLibraryEnv(binary) {
|
|
186742
|
+
if (!binary.libraryPath) {
|
|
186743
|
+
return {};
|
|
186744
|
+
}
|
|
186745
|
+
const platform4 = os6.platform();
|
|
186746
|
+
if (platform4 === "darwin") {
|
|
186747
|
+
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
186748
|
+
} else if (platform4 === "linux") {
|
|
186749
|
+
return { LD_LIBRARY_PATH: binary.libraryPath };
|
|
186750
|
+
}
|
|
186751
|
+
return {};
|
|
186752
|
+
}
|
|
186637
186753
|
function getBinBaseDir() {
|
|
186638
186754
|
return path9.join(os6.homedir(), ".specific", "bin");
|
|
186639
186755
|
}
|
|
186640
186756
|
function getPlatformInfo() {
|
|
186641
|
-
const
|
|
186757
|
+
const platform4 = os6.platform();
|
|
186642
186758
|
const arch3 = os6.arch();
|
|
186643
|
-
if (
|
|
186759
|
+
if (platform4 !== "darwin" && platform4 !== "linux") {
|
|
186644
186760
|
throw new Error(
|
|
186645
|
-
`Unsupported platform: ${
|
|
186761
|
+
`Unsupported platform: ${platform4}. Only macOS and Linux are supported.`
|
|
186646
186762
|
);
|
|
186647
186763
|
}
|
|
186648
186764
|
const archStr = arch3;
|
|
@@ -186656,7 +186772,7 @@ function getPlatformInfo() {
|
|
|
186656
186772
|
`Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
|
|
186657
186773
|
);
|
|
186658
186774
|
}
|
|
186659
|
-
return { platform:
|
|
186775
|
+
return { platform: platform4, arch: mappedArch };
|
|
186660
186776
|
}
|
|
186661
186777
|
function getBinaryDir(definition, version, platformInfo) {
|
|
186662
186778
|
return path9.join(
|
|
@@ -186832,7 +186948,7 @@ var postgresBinary = {
|
|
|
186832
186948
|
// Archive contains bin/ and lib/ directories at root
|
|
186833
186949
|
stripComponents: 0,
|
|
186834
186950
|
// Core PostgreSQL executables (in bin/ directory)
|
|
186835
|
-
executables: ["bin/postgres", "bin/initdb"],
|
|
186951
|
+
executables: ["bin/postgres", "bin/initdb", "bin/psql"],
|
|
186836
186952
|
// Library directory relative to root (for setting LD_LIBRARY_PATH)
|
|
186837
186953
|
libraryDir: "lib"
|
|
186838
186954
|
};
|
|
@@ -186904,18 +187020,6 @@ var drizzleGatewayBinary = {
|
|
|
186904
187020
|
};
|
|
186905
187021
|
|
|
186906
187022
|
// src/lib/dev/database-manager.ts
|
|
186907
|
-
function getLibraryEnv(binary) {
|
|
186908
|
-
if (!binary.libraryPath) {
|
|
186909
|
-
return {};
|
|
186910
|
-
}
|
|
186911
|
-
const platform5 = os7.platform();
|
|
186912
|
-
if (platform5 === "darwin") {
|
|
186913
|
-
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
186914
|
-
} else if (platform5 === "linux") {
|
|
186915
|
-
return { LD_LIBRARY_PATH: binary.libraryPath };
|
|
186916
|
-
}
|
|
186917
|
-
return {};
|
|
186918
|
-
}
|
|
186919
187023
|
async function startPostgres(pg, port, dataDir, onProgress) {
|
|
186920
187024
|
const binary = await ensureBinary(postgresBinary, void 0, onProgress);
|
|
186921
187025
|
const dbDataPath = path10.join(process.cwd(), dataDir, pg.name);
|
|
@@ -187271,7 +187375,7 @@ config {
|
|
|
187271
187375
|
this.name = "MissingConfigError";
|
|
187272
187376
|
}
|
|
187273
187377
|
};
|
|
187274
|
-
function resolveEnvValue(value, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts) {
|
|
187378
|
+
function resolveEnvValue(value, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts, publicUrls) {
|
|
187275
187379
|
if (typeof value === "string") {
|
|
187276
187380
|
return value;
|
|
187277
187381
|
}
|
|
@@ -187312,11 +187416,25 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
|
|
|
187312
187416
|
}
|
|
187313
187417
|
switch (serviceRef.attribute) {
|
|
187314
187418
|
case "url":
|
|
187419
|
+
case "private_url":
|
|
187315
187420
|
return `localhost:${endpoint.port}`;
|
|
187316
187421
|
case "host":
|
|
187317
187422
|
return "localhost";
|
|
187318
187423
|
case "port":
|
|
187319
187424
|
return String(endpoint.port);
|
|
187425
|
+
case "public_url": {
|
|
187426
|
+
if (!publicUrls) {
|
|
187427
|
+
throw new Error("public_url reference used but no public URL map provided");
|
|
187428
|
+
}
|
|
187429
|
+
const k8sName = endpointName === "default" ? serviceRef.serviceName : `${serviceRef.serviceName}-${endpointName}`;
|
|
187430
|
+
const publicUrl = publicUrls.get(k8sName);
|
|
187431
|
+
if (!publicUrl) {
|
|
187432
|
+
throw new Error(
|
|
187433
|
+
`No public URL found for service "${serviceRef.serviceName}" endpoint "${endpointName}"`
|
|
187434
|
+
);
|
|
187435
|
+
}
|
|
187436
|
+
return publicUrl;
|
|
187437
|
+
}
|
|
187320
187438
|
default:
|
|
187321
187439
|
throw new Error(`Unknown service attribute: ${serviceRef.attribute}`);
|
|
187322
187440
|
}
|
|
@@ -187421,7 +187539,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
|
|
|
187421
187539
|
throw new Error(`Unknown reference type`);
|
|
187422
187540
|
}
|
|
187423
187541
|
}
|
|
187424
|
-
function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts) {
|
|
187542
|
+
function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts, publicUrls) {
|
|
187425
187543
|
if (!env2) {
|
|
187426
187544
|
return {};
|
|
187427
187545
|
}
|
|
@@ -187434,7 +187552,8 @@ function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpo
|
|
|
187434
187552
|
configs,
|
|
187435
187553
|
servicePort,
|
|
187436
187554
|
serviceEndpoints,
|
|
187437
|
-
currentServicePorts
|
|
187555
|
+
currentServicePorts,
|
|
187556
|
+
publicUrls
|
|
187438
187557
|
);
|
|
187439
187558
|
}
|
|
187440
187559
|
return resolved;
|
|
@@ -187458,7 +187577,7 @@ function resolveEnvForExec(env2, resources, secrets, configs) {
|
|
|
187458
187577
|
}
|
|
187459
187578
|
|
|
187460
187579
|
// src/lib/dev/service-runner.ts
|
|
187461
|
-
function startService(service, resources, secrets, configs, endpointPorts, serviceEndpoints, onLog) {
|
|
187580
|
+
function startService(service, resources, secrets, configs, endpointPorts, serviceEndpoints, onLog, publicUrls) {
|
|
187462
187581
|
const command = service.dev?.command ?? service.command;
|
|
187463
187582
|
if (!command) {
|
|
187464
187583
|
throw new Error(`Service "${service.name}" has no command`);
|
|
@@ -187475,7 +187594,8 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
187475
187594
|
configs,
|
|
187476
187595
|
defaultPort,
|
|
187477
187596
|
serviceEndpoints,
|
|
187478
|
-
endpointPorts
|
|
187597
|
+
endpointPorts,
|
|
187598
|
+
publicUrls
|
|
187479
187599
|
);
|
|
187480
187600
|
const child = spawn2(command, {
|
|
187481
187601
|
shell: true,
|
|
@@ -188720,7 +188840,7 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
188720
188840
|
// src/lib/dev/proxy-registry.ts
|
|
188721
188841
|
import * as fs19 from "fs";
|
|
188722
188842
|
import * as path16 from "path";
|
|
188723
|
-
import * as
|
|
188843
|
+
import * as os7 from "os";
|
|
188724
188844
|
import * as net4 from "net";
|
|
188725
188845
|
var ProxyRegistryManager = class {
|
|
188726
188846
|
proxyDir;
|
|
@@ -188730,7 +188850,7 @@ var ProxyRegistryManager = class {
|
|
|
188730
188850
|
isOwner = false;
|
|
188731
188851
|
registryWatcher = null;
|
|
188732
188852
|
constructor() {
|
|
188733
|
-
this.proxyDir = path16.join(
|
|
188853
|
+
this.proxyDir = path16.join(os7.homedir(), ".specific", "proxy");
|
|
188734
188854
|
this.ownerPath = path16.join(this.proxyDir, "owner.json");
|
|
188735
188855
|
this.registryPath = path16.join(this.proxyDir, "registry.json");
|
|
188736
188856
|
this.lockPath = path16.join(this.proxyDir, "registry.lock");
|
|
@@ -189175,11 +189295,6 @@ function ConfigInput({ configName, onSubmit, onCancel }) {
|
|
|
189175
189295
|
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)"));
|
|
189176
189296
|
}
|
|
189177
189297
|
|
|
189178
|
-
// src/lib/ui/interactive.ts
|
|
189179
|
-
function isInteractive() {
|
|
189180
|
-
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
189181
|
-
}
|
|
189182
|
-
|
|
189183
189298
|
// src/commands/dev.tsx
|
|
189184
189299
|
var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
|
|
189185
189300
|
function DevUI({ instanceKey, tunnelEnabled }) {
|
|
@@ -189305,6 +189420,9 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
189305
189420
|
// Stop all tunnels
|
|
189306
189421
|
...tunnelsRef.current.map((tunnel) => tunnel.stop())
|
|
189307
189422
|
]);
|
|
189423
|
+
if (tunnelsRef.current.length > 0) {
|
|
189424
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1500));
|
|
189425
|
+
}
|
|
189308
189426
|
electricInstancesRef.current = [];
|
|
189309
189427
|
drizzleGatewayRef.current = null;
|
|
189310
189428
|
proxyRef.current = null;
|
|
@@ -189724,6 +189842,39 @@ Add them to the config block in specific.local`);
|
|
|
189724
189842
|
serviceEndpointPorts.set(service.name, endpointPorts);
|
|
189725
189843
|
serviceEndpoints.set(service.name, endpointInfos);
|
|
189726
189844
|
}
|
|
189845
|
+
const serviceInfos = [];
|
|
189846
|
+
const exposedServices = [];
|
|
189847
|
+
for (const service of config2.services) {
|
|
189848
|
+
const endpointInfos = serviceEndpoints.get(service.name) || [];
|
|
189849
|
+
for (const info of endpointInfos) {
|
|
189850
|
+
const endpointConfig = service.endpoints.find(
|
|
189851
|
+
(e) => e.name === info.endpointName
|
|
189852
|
+
);
|
|
189853
|
+
if (endpointConfig?.public) {
|
|
189854
|
+
const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
|
|
189855
|
+
exposedServices.push({ name: proxyName, port: info.port });
|
|
189856
|
+
serviceInfos.push({
|
|
189857
|
+
serviceName: proxyName,
|
|
189858
|
+
port: info.port
|
|
189859
|
+
});
|
|
189860
|
+
}
|
|
189861
|
+
}
|
|
189862
|
+
}
|
|
189863
|
+
const publicUrls = /* @__PURE__ */ new Map();
|
|
189864
|
+
let subdomainAllocator;
|
|
189865
|
+
if (tunnelEnabled && exposedServices.length > 0) {
|
|
189866
|
+
subdomainAllocator = new StableSubdomainAllocator(process.cwd(), instanceKey);
|
|
189867
|
+
const multipleServices = exposedServices.length > 1;
|
|
189868
|
+
for (const svc of exposedServices) {
|
|
189869
|
+
const subdomain = subdomainAllocator.allocate(svc.name, multipleServices);
|
|
189870
|
+
publicUrls.set(svc.name, `${subdomain}.tunnel.spcf.app`);
|
|
189871
|
+
}
|
|
189872
|
+
} else {
|
|
189873
|
+
const keySuffix = instanceKey === "default" ? "" : `.${instanceKey}`;
|
|
189874
|
+
for (const svc of exposedServices) {
|
|
189875
|
+
publicUrls.set(svc.name, `${svc.name}${keySuffix}.local.spcf.app`);
|
|
189876
|
+
}
|
|
189877
|
+
}
|
|
189727
189878
|
const services2 = [];
|
|
189728
189879
|
for (const service of config2.services) {
|
|
189729
189880
|
if (cancelled) break;
|
|
@@ -189747,7 +189898,8 @@ Add them to the config block in specific.local`);
|
|
|
189747
189898
|
configs,
|
|
189748
189899
|
endpointPorts,
|
|
189749
189900
|
serviceEndpoints,
|
|
189750
|
-
(line) => addLog(line, colorMap)
|
|
189901
|
+
(line) => addLog(line, colorMap),
|
|
189902
|
+
publicUrls
|
|
189751
189903
|
);
|
|
189752
189904
|
services2.push(running);
|
|
189753
189905
|
startedServices.push(running);
|
|
@@ -189775,30 +189927,11 @@ Add them to the config block in specific.local`);
|
|
|
189775
189927
|
}
|
|
189776
189928
|
}
|
|
189777
189929
|
if (cancelled) return;
|
|
189778
|
-
const serviceInfos = [];
|
|
189779
|
-
const exposedServices = [];
|
|
189780
|
-
for (const service of config2.services) {
|
|
189781
|
-
const endpointInfos = serviceEndpoints.get(service.name) || [];
|
|
189782
|
-
for (const info of endpointInfos) {
|
|
189783
|
-
const endpointConfig = service.endpoints.find(
|
|
189784
|
-
(e) => e.name === info.endpointName
|
|
189785
|
-
);
|
|
189786
|
-
if (endpointConfig?.public) {
|
|
189787
|
-
const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
|
|
189788
|
-
exposedServices.push({ name: proxyName, port: info.port });
|
|
189789
|
-
serviceInfos.push({
|
|
189790
|
-
serviceName: proxyName,
|
|
189791
|
-
port: info.port
|
|
189792
|
-
});
|
|
189793
|
-
}
|
|
189794
|
-
}
|
|
189795
|
-
}
|
|
189796
189930
|
if (tunnelEnabled) {
|
|
189797
189931
|
writeLog("system", "Tunnel mode enabled, starting tunnels for public services");
|
|
189798
189932
|
if (exposedServices.length === 0) {
|
|
189799
189933
|
writeLog("system", "No public services to tunnel");
|
|
189800
189934
|
} else {
|
|
189801
|
-
const subdomainAllocator = new StableSubdomainAllocator(process.cwd(), instanceKey);
|
|
189802
189935
|
const multipleServices = exposedServices.length > 1;
|
|
189803
189936
|
const tunnelInstances = [];
|
|
189804
189937
|
const tunnelStatusMap = /* @__PURE__ */ new Map();
|
|
@@ -191654,27 +191787,48 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
191654
191787
|
|
|
191655
191788
|
// src/commands/psql.tsx
|
|
191656
191789
|
import { spawn as spawn7 } from "child_process";
|
|
191657
|
-
|
|
191658
|
-
|
|
191659
|
-
|
|
191660
|
-
|
|
191661
|
-
|
|
191662
|
-
|
|
191790
|
+
import * as fs24 from "fs";
|
|
191791
|
+
import * as path22 from "path";
|
|
191792
|
+
async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
|
|
191793
|
+
let startedResources = [];
|
|
191794
|
+
let ownsInstances = false;
|
|
191795
|
+
let stateManager = null;
|
|
191796
|
+
const cleanup = async () => {
|
|
191797
|
+
if (ownsInstances) {
|
|
191798
|
+
for (const resource of startedResources) {
|
|
191799
|
+
await resource.stop().catch(() => {
|
|
191800
|
+
});
|
|
191801
|
+
}
|
|
191802
|
+
if (stateManager) {
|
|
191803
|
+
await stateManager.releaseOwnership().catch(() => {
|
|
191804
|
+
});
|
|
191805
|
+
}
|
|
191806
|
+
}
|
|
191807
|
+
};
|
|
191808
|
+
const configPath = path22.join(process.cwd(), "specific.hcl");
|
|
191809
|
+
if (!fs24.existsSync(configPath)) {
|
|
191810
|
+
console.error("Error: No specific.hcl found in current directory");
|
|
191663
191811
|
process.exit(1);
|
|
191664
191812
|
}
|
|
191665
|
-
|
|
191813
|
+
let config;
|
|
191814
|
+
try {
|
|
191815
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191816
|
+
config = await parseConfig(hcl);
|
|
191817
|
+
} catch (err) {
|
|
191818
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
191819
|
+
process.exit(1);
|
|
191820
|
+
}
|
|
191821
|
+
const availableDatabases = config.postgres.map((p) => p.name);
|
|
191666
191822
|
if (availableDatabases.length === 0) {
|
|
191667
|
-
console.error("Error: No databases
|
|
191823
|
+
console.error("Error: No postgres databases defined in specific.hcl");
|
|
191668
191824
|
process.exit(1);
|
|
191669
191825
|
}
|
|
191670
191826
|
let targetDb;
|
|
191827
|
+
if (databaseName && !availableDatabases.includes(databaseName)) {
|
|
191828
|
+
extraArgs = [databaseName, ...extraArgs];
|
|
191829
|
+
databaseName = void 0;
|
|
191830
|
+
}
|
|
191671
191831
|
if (databaseName) {
|
|
191672
|
-
if (!existingInstances.databases[databaseName]) {
|
|
191673
|
-
console.error(
|
|
191674
|
-
`Error: Database "${databaseName}" not found. Available: ${availableDatabases.join(", ")}`
|
|
191675
|
-
);
|
|
191676
|
-
process.exit(1);
|
|
191677
|
-
}
|
|
191678
191832
|
targetDb = databaseName;
|
|
191679
191833
|
} else {
|
|
191680
191834
|
if (availableDatabases.length > 1) {
|
|
@@ -191685,24 +191839,95 @@ async function psqlCommand(databaseName, instanceKey = "default") {
|
|
|
191685
191839
|
}
|
|
191686
191840
|
targetDb = availableDatabases[0];
|
|
191687
191841
|
}
|
|
191688
|
-
|
|
191689
|
-
|
|
191842
|
+
stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
191843
|
+
await stateManager.cleanStaleState();
|
|
191844
|
+
const existingInstances = await stateManager.getExistingInstances();
|
|
191845
|
+
let dbState;
|
|
191846
|
+
if (existingInstances && existingInstances.databases[targetDb]) {
|
|
191847
|
+
const db = existingInstances.databases[targetDb];
|
|
191848
|
+
dbState = { host: db.host, port: db.port, user: db.user, password: db.password, dbName: db.dbName };
|
|
191849
|
+
} else {
|
|
191850
|
+
console.error("Starting database...");
|
|
191851
|
+
try {
|
|
191852
|
+
await stateManager.claimOwnership("psql");
|
|
191853
|
+
ownsInstances = true;
|
|
191854
|
+
} catch (err) {
|
|
191855
|
+
console.error(
|
|
191856
|
+
`Error: ${err instanceof Error ? err.message : String(err)}`
|
|
191857
|
+
);
|
|
191858
|
+
process.exit(1);
|
|
191859
|
+
}
|
|
191860
|
+
try {
|
|
191861
|
+
const result = await startResources({
|
|
191862
|
+
config,
|
|
191863
|
+
selection: {
|
|
191864
|
+
mode: "required",
|
|
191865
|
+
postgres: [targetDb],
|
|
191866
|
+
redis: [],
|
|
191867
|
+
storage: []
|
|
191868
|
+
},
|
|
191869
|
+
stateManager,
|
|
191870
|
+
dataDir: `.specific/keys/${instanceKey}/data`,
|
|
191871
|
+
portAllocator: new PortAllocator(),
|
|
191872
|
+
callbacks: {
|
|
191873
|
+
log: (msg) => console.error(msg)
|
|
191874
|
+
},
|
|
191875
|
+
startElectric: false
|
|
191876
|
+
});
|
|
191877
|
+
startedResources = result.startedResources;
|
|
191878
|
+
const resource = result.resources.get(targetDb);
|
|
191879
|
+
if (!resource) {
|
|
191880
|
+
console.error(`Error: Failed to start database "${targetDb}"`);
|
|
191881
|
+
await cleanup();
|
|
191882
|
+
process.exit(1);
|
|
191883
|
+
}
|
|
191884
|
+
dbState = {
|
|
191885
|
+
host: resource.host,
|
|
191886
|
+
port: resource.port,
|
|
191887
|
+
user: resource.user,
|
|
191888
|
+
password: resource.password,
|
|
191889
|
+
dbName: resource.dbName
|
|
191890
|
+
};
|
|
191891
|
+
} catch (err) {
|
|
191892
|
+
console.error(
|
|
191893
|
+
`Error: Failed to start database: ${err instanceof Error ? err.message : String(err)}`
|
|
191894
|
+
);
|
|
191895
|
+
await cleanup();
|
|
191896
|
+
process.exit(1);
|
|
191897
|
+
}
|
|
191898
|
+
}
|
|
191899
|
+
const binary = await ensureBinary(postgresBinary);
|
|
191900
|
+
const psqlPath = binary.executables["psql"];
|
|
191901
|
+
const libraryEnv = getLibraryEnv(binary);
|
|
191902
|
+
let child = null;
|
|
191903
|
+
const handleSignal = async (signal) => {
|
|
191904
|
+
if (child && !child.killed) {
|
|
191905
|
+
child.kill(signal);
|
|
191906
|
+
}
|
|
191907
|
+
await cleanup();
|
|
191908
|
+
};
|
|
191909
|
+
process.on("SIGINT", () => handleSignal("SIGINT"));
|
|
191910
|
+
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
191911
|
+
child = spawn7(psqlPath, ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName, ...extraArgs], {
|
|
191690
191912
|
cwd: process.cwd(),
|
|
191691
191913
|
env: {
|
|
191692
191914
|
...process.env,
|
|
191915
|
+
...libraryEnv,
|
|
191693
191916
|
PGPASSWORD: dbState.password
|
|
191694
191917
|
},
|
|
191695
191918
|
stdio: "inherit"
|
|
191696
191919
|
});
|
|
191697
|
-
child.on("exit", (code, signal) => {
|
|
191920
|
+
child.on("exit", async (code, signal) => {
|
|
191921
|
+
await cleanup();
|
|
191698
191922
|
if (signal) {
|
|
191699
191923
|
process.kill(process.pid, signal);
|
|
191700
191924
|
} else {
|
|
191701
191925
|
process.exit(code ?? 0);
|
|
191702
191926
|
}
|
|
191703
191927
|
});
|
|
191704
|
-
child.on("error", (err) => {
|
|
191928
|
+
child.on("error", async (err) => {
|
|
191705
191929
|
console.error(`Error: Failed to start psql: ${err.message}`);
|
|
191930
|
+
await cleanup();
|
|
191706
191931
|
process.exit(1);
|
|
191707
191932
|
});
|
|
191708
191933
|
}
|
|
@@ -191711,21 +191936,21 @@ async function psqlCommand(databaseName, instanceKey = "default") {
|
|
|
191711
191936
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
191712
191937
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
191713
191938
|
import Spinner6 from "ink-spinner";
|
|
191714
|
-
import * as
|
|
191715
|
-
import * as
|
|
191939
|
+
import * as fs25 from "fs";
|
|
191940
|
+
import * as path23 from "path";
|
|
191716
191941
|
function CleanUI({ instanceKey }) {
|
|
191717
191942
|
const [state, setState] = useState7({ status: "checking" });
|
|
191718
191943
|
useEffect5(() => {
|
|
191719
191944
|
async function clean() {
|
|
191720
191945
|
const projectRoot = process.cwd();
|
|
191721
|
-
const specificDir =
|
|
191722
|
-
if (!
|
|
191946
|
+
const specificDir = path23.join(projectRoot, ".specific");
|
|
191947
|
+
if (!fs25.existsSync(specificDir)) {
|
|
191723
191948
|
setState({ status: "nothing" });
|
|
191724
191949
|
return;
|
|
191725
191950
|
}
|
|
191726
191951
|
if (instanceKey) {
|
|
191727
|
-
const keyDir =
|
|
191728
|
-
if (!
|
|
191952
|
+
const keyDir = path23.join(specificDir, "keys", instanceKey);
|
|
191953
|
+
if (!fs25.existsSync(keyDir)) {
|
|
191729
191954
|
setState({ status: "nothing" });
|
|
191730
191955
|
return;
|
|
191731
191956
|
}
|
|
@@ -191741,7 +191966,7 @@ function CleanUI({ instanceKey }) {
|
|
|
191741
191966
|
await stateManager.cleanStaleState();
|
|
191742
191967
|
setState({ status: "cleaning" });
|
|
191743
191968
|
try {
|
|
191744
|
-
|
|
191969
|
+
fs25.rmSync(keyDir, { recursive: true, force: true });
|
|
191745
191970
|
setState({ status: "success" });
|
|
191746
191971
|
} catch (err) {
|
|
191747
191972
|
setState({
|
|
@@ -191750,10 +191975,10 @@ function CleanUI({ instanceKey }) {
|
|
|
191750
191975
|
});
|
|
191751
191976
|
}
|
|
191752
191977
|
} else {
|
|
191753
|
-
const keysDir =
|
|
191754
|
-
if (
|
|
191755
|
-
const keys =
|
|
191756
|
-
(f) =>
|
|
191978
|
+
const keysDir = path23.join(specificDir, "keys");
|
|
191979
|
+
if (fs25.existsSync(keysDir)) {
|
|
191980
|
+
const keys = fs25.readdirSync(keysDir).filter(
|
|
191981
|
+
(f) => fs25.statSync(path23.join(keysDir, f)).isDirectory()
|
|
191757
191982
|
);
|
|
191758
191983
|
for (const key of keys) {
|
|
191759
191984
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -191778,7 +192003,7 @@ function CleanUI({ instanceKey }) {
|
|
|
191778
192003
|
}
|
|
191779
192004
|
setState({ status: "cleaning" });
|
|
191780
192005
|
try {
|
|
191781
|
-
|
|
192006
|
+
fs25.rmSync(specificDir, { recursive: true, force: true });
|
|
191782
192007
|
setState({ status: "success" });
|
|
191783
192008
|
} catch (err) {
|
|
191784
192009
|
setState({
|
|
@@ -191865,8 +192090,8 @@ function logoutCommand() {
|
|
|
191865
192090
|
var program = new Command();
|
|
191866
192091
|
var env = "production";
|
|
191867
192092
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
191868
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
191869
|
-
program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
|
|
192093
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.49").enablePositionalOptions();
|
|
192094
|
+
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));
|
|
191870
192095
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
191871
192096
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
|
191872
192097
|
program.command("dev").description("Start local development environment").option("-k, --key <key>", "Namespace for isolated dev environment (auto-detected from git worktree if not specified)").option("--tunnel", "Expose public services via localtunnel URLs").action((options2) => {
|
|
@@ -191881,9 +192106,10 @@ program.command("exec <service> [args...]").description("Run a one-off command w
|
|
|
191881
192106
|
const key = options2.key ?? getDefaultKey();
|
|
191882
192107
|
await execCommand(service, filteredArgs, key);
|
|
191883
192108
|
});
|
|
191884
|
-
program.command("psql [database]").description("Connect to a
|
|
192109
|
+
program.command("psql [database] [args...]").description("Connect to a Postgres database").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").passThroughOptions().action((database, args, options2) => {
|
|
192110
|
+
const filteredArgs = args[0] === "--" ? args.slice(1) : args;
|
|
191885
192111
|
const key = options2.key ?? getDefaultKey();
|
|
191886
|
-
psqlCommand(database, key);
|
|
192112
|
+
psqlCommand(database, key, filteredArgs);
|
|
191887
192113
|
});
|
|
191888
192114
|
program.command("clean").description("Remove .specific directory for a clean slate").option("-k, --key <key>", "Clean only the specified dev environment key").action((options2) => {
|
|
191889
192115
|
cleanCommand(options2.key);
|