@avsbhq/cli 3.8.1 → 3.8.2
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/LICENSE +21 -0
- package/README.md +17 -8
- package/dist/cli.js +1362 -749
- package/dist/types/avsb.d.ts +9 -5
- package/dist/types/variation-code.d.ts +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
const require =
|
|
2
|
+
import { createRequire as __cliCreateRequire } from "node:module";
|
|
3
|
+
const require = __cliCreateRequire(import.meta.url);
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -1150,8 +1150,8 @@ var require_command = __commonJS({
|
|
|
1150
1150
|
"node_modules/commander/lib/command.js"(exports) {
|
|
1151
1151
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
1152
1152
|
var childProcess = __require("node:child_process");
|
|
1153
|
-
var
|
|
1154
|
-
var
|
|
1153
|
+
var path23 = __require("node:path");
|
|
1154
|
+
var fs19 = __require("node:fs");
|
|
1155
1155
|
var process11 = __require("node:process");
|
|
1156
1156
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1157
1157
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2132,7 +2132,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2132
2132
|
* @param {string} subcommandName
|
|
2133
2133
|
*/
|
|
2134
2134
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2135
|
-
if (
|
|
2135
|
+
if (fs19.existsSync(executableFile)) return;
|
|
2136
2136
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2137
2137
|
const executableMissing = `'${executableFile}' does not exist
|
|
2138
2138
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2150,11 +2150,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2150
2150
|
let launchWithNode = false;
|
|
2151
2151
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2152
2152
|
function findFile(baseDir, baseName) {
|
|
2153
|
-
const localBin =
|
|
2154
|
-
if (
|
|
2155
|
-
if (sourceExt.includes(
|
|
2153
|
+
const localBin = path23.resolve(baseDir, baseName);
|
|
2154
|
+
if (fs19.existsSync(localBin)) return localBin;
|
|
2155
|
+
if (sourceExt.includes(path23.extname(baseName))) return void 0;
|
|
2156
2156
|
const foundExt = sourceExt.find(
|
|
2157
|
-
(ext) =>
|
|
2157
|
+
(ext) => fs19.existsSync(`${localBin}${ext}`)
|
|
2158
2158
|
);
|
|
2159
2159
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2160
2160
|
return void 0;
|
|
@@ -2166,21 +2166,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2166
2166
|
if (this._scriptPath) {
|
|
2167
2167
|
let resolvedScriptPath;
|
|
2168
2168
|
try {
|
|
2169
|
-
resolvedScriptPath =
|
|
2169
|
+
resolvedScriptPath = fs19.realpathSync(this._scriptPath);
|
|
2170
2170
|
} catch {
|
|
2171
2171
|
resolvedScriptPath = this._scriptPath;
|
|
2172
2172
|
}
|
|
2173
|
-
executableDir =
|
|
2174
|
-
|
|
2173
|
+
executableDir = path23.resolve(
|
|
2174
|
+
path23.dirname(resolvedScriptPath),
|
|
2175
2175
|
executableDir
|
|
2176
2176
|
);
|
|
2177
2177
|
}
|
|
2178
2178
|
if (executableDir) {
|
|
2179
2179
|
let localFile = findFile(executableDir, executableFile);
|
|
2180
2180
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2181
|
-
const legacyName =
|
|
2181
|
+
const legacyName = path23.basename(
|
|
2182
2182
|
this._scriptPath,
|
|
2183
|
-
|
|
2183
|
+
path23.extname(this._scriptPath)
|
|
2184
2184
|
);
|
|
2185
2185
|
if (legacyName !== this._name) {
|
|
2186
2186
|
localFile = findFile(
|
|
@@ -2191,7 +2191,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2191
2191
|
}
|
|
2192
2192
|
executableFile = localFile || executableFile;
|
|
2193
2193
|
}
|
|
2194
|
-
launchWithNode = sourceExt.includes(
|
|
2194
|
+
launchWithNode = sourceExt.includes(path23.extname(executableFile));
|
|
2195
2195
|
let proc;
|
|
2196
2196
|
if (process11.platform !== "win32") {
|
|
2197
2197
|
if (launchWithNode) {
|
|
@@ -3038,7 +3038,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3038
3038
|
* @return {Command}
|
|
3039
3039
|
*/
|
|
3040
3040
|
nameFromFilename(filename) {
|
|
3041
|
-
this._name =
|
|
3041
|
+
this._name = path23.basename(filename, path23.extname(filename));
|
|
3042
3042
|
return this;
|
|
3043
3043
|
}
|
|
3044
3044
|
/**
|
|
@@ -3052,9 +3052,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3052
3052
|
* @param {string} [path]
|
|
3053
3053
|
* @return {(string|null|Command)}
|
|
3054
3054
|
*/
|
|
3055
|
-
executableDir(
|
|
3056
|
-
if (
|
|
3057
|
-
this._executableDir =
|
|
3055
|
+
executableDir(path24) {
|
|
3056
|
+
if (path24 === void 0) return this._executableDir;
|
|
3057
|
+
this._executableDir = path24;
|
|
3058
3058
|
return this;
|
|
3059
3059
|
}
|
|
3060
3060
|
/**
|
|
@@ -3227,13 +3227,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3227
3227
|
* @param {(string | Function)} text - string to add, or a function returning a string
|
|
3228
3228
|
* @return {Command} `this` command for chaining
|
|
3229
3229
|
*/
|
|
3230
|
-
addHelpText(
|
|
3230
|
+
addHelpText(position2, text) {
|
|
3231
3231
|
const allowedValues = ["beforeAll", "before", "after", "afterAll"];
|
|
3232
|
-
if (!allowedValues.includes(
|
|
3232
|
+
if (!allowedValues.includes(position2)) {
|
|
3233
3233
|
throw new Error(`Unexpected value for position to addHelpText.
|
|
3234
3234
|
Expecting one of '${allowedValues.join("', '")}'`);
|
|
3235
3235
|
}
|
|
3236
|
-
const helpEvent = `${
|
|
3236
|
+
const helpEvent = `${position2}Help`;
|
|
3237
3237
|
this.on(helpEvent, (context) => {
|
|
3238
3238
|
let helpStr;
|
|
3239
3239
|
if (typeof text === "function") {
|
|
@@ -4418,15 +4418,15 @@ var require_route = __commonJS({
|
|
|
4418
4418
|
};
|
|
4419
4419
|
}
|
|
4420
4420
|
function wrapConversion(toModel, graph) {
|
|
4421
|
-
const
|
|
4421
|
+
const path23 = [graph[toModel].parent, toModel];
|
|
4422
4422
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
4423
4423
|
let cur = graph[toModel].parent;
|
|
4424
4424
|
while (graph[cur].parent) {
|
|
4425
|
-
|
|
4425
|
+
path23.unshift(graph[cur].parent);
|
|
4426
4426
|
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
4427
4427
|
cur = graph[cur].parent;
|
|
4428
4428
|
}
|
|
4429
|
-
fn.conversion =
|
|
4429
|
+
fn.conversion = path23;
|
|
4430
4430
|
return fn;
|
|
4431
4431
|
}
|
|
4432
4432
|
module.exports = function(fromModel) {
|
|
@@ -10482,10 +10482,10 @@ var require_lib2 = __commonJS({
|
|
|
10482
10482
|
exports.analyse = analyse;
|
|
10483
10483
|
var detectFile = (filepath, opts = {}) => new Promise((resolve3, reject) => {
|
|
10484
10484
|
let fd;
|
|
10485
|
-
const
|
|
10485
|
+
const fs19 = (0, node_1.default)();
|
|
10486
10486
|
const handler = (err, buffer) => {
|
|
10487
10487
|
if (fd) {
|
|
10488
|
-
|
|
10488
|
+
fs19.closeSync(fd);
|
|
10489
10489
|
}
|
|
10490
10490
|
if (err) {
|
|
10491
10491
|
reject(err);
|
|
@@ -10497,9 +10497,9 @@ var require_lib2 = __commonJS({
|
|
|
10497
10497
|
};
|
|
10498
10498
|
const sampleSize = (opts === null || opts === void 0 ? void 0 : opts.sampleSize) || 0;
|
|
10499
10499
|
if (sampleSize > 0) {
|
|
10500
|
-
fd =
|
|
10500
|
+
fd = fs19.openSync(filepath, "r");
|
|
10501
10501
|
let sample = Buffer.allocUnsafe(sampleSize);
|
|
10502
|
-
|
|
10502
|
+
fs19.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
|
|
10503
10503
|
if (err) {
|
|
10504
10504
|
handler(err, null);
|
|
10505
10505
|
} else {
|
|
@@ -10511,22 +10511,22 @@ var require_lib2 = __commonJS({
|
|
|
10511
10511
|
});
|
|
10512
10512
|
return;
|
|
10513
10513
|
}
|
|
10514
|
-
|
|
10514
|
+
fs19.readFile(filepath, handler);
|
|
10515
10515
|
});
|
|
10516
10516
|
exports.detectFile = detectFile;
|
|
10517
10517
|
var detectFileSync = (filepath, opts = {}) => {
|
|
10518
|
-
const
|
|
10518
|
+
const fs19 = (0, node_1.default)();
|
|
10519
10519
|
if (opts && opts.sampleSize) {
|
|
10520
|
-
const fd =
|
|
10520
|
+
const fd = fs19.openSync(filepath, "r");
|
|
10521
10521
|
let sample = Buffer.allocUnsafe(opts.sampleSize);
|
|
10522
|
-
const bytesRead =
|
|
10522
|
+
const bytesRead = fs19.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
|
|
10523
10523
|
if (bytesRead < opts.sampleSize) {
|
|
10524
10524
|
sample = sample.subarray(0, bytesRead);
|
|
10525
10525
|
}
|
|
10526
|
-
|
|
10526
|
+
fs19.closeSync(fd);
|
|
10527
10527
|
return (0, exports.detect)(sample);
|
|
10528
10528
|
}
|
|
10529
|
-
return (0, exports.detect)(
|
|
10529
|
+
return (0, exports.detect)(fs19.readFileSync(filepath));
|
|
10530
10530
|
};
|
|
10531
10531
|
exports.detectFileSync = detectFileSync;
|
|
10532
10532
|
exports.default = {
|
|
@@ -29097,9 +29097,9 @@ var require_websocket_server = __commonJS({
|
|
|
29097
29097
|
});
|
|
29098
29098
|
|
|
29099
29099
|
// src/cli.ts
|
|
29100
|
-
import
|
|
29100
|
+
import fs18 from "node:fs";
|
|
29101
29101
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29102
|
-
import
|
|
29102
|
+
import path22 from "node:path";
|
|
29103
29103
|
|
|
29104
29104
|
// node_modules/commander/esm.mjs
|
|
29105
29105
|
var import_index = __toESM(require_commander(), 1);
|
|
@@ -29119,7 +29119,7 @@ var {
|
|
|
29119
29119
|
} = import_index.default;
|
|
29120
29120
|
|
|
29121
29121
|
// src/commands/init.ts
|
|
29122
|
-
import
|
|
29122
|
+
import path10 from "node:path";
|
|
29123
29123
|
|
|
29124
29124
|
// node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
29125
29125
|
var isUpKey = (key, keybindings = []) => (
|
|
@@ -30382,16 +30382,16 @@ var esm_default2 = createPrompt((config, done) => {
|
|
|
30382
30382
|
} else if (isNumberKey(key)) {
|
|
30383
30383
|
const selectedIndex = Number(key.name) - 1;
|
|
30384
30384
|
let selectableIndex = -1;
|
|
30385
|
-
const
|
|
30385
|
+
const position2 = items.findIndex((item) => {
|
|
30386
30386
|
if (Separator.isSeparator(item))
|
|
30387
30387
|
return false;
|
|
30388
30388
|
selectableIndex++;
|
|
30389
30389
|
return selectableIndex === selectedIndex;
|
|
30390
30390
|
});
|
|
30391
|
-
const selectedItem = items[
|
|
30391
|
+
const selectedItem = items[position2];
|
|
30392
30392
|
if (selectedItem && isSelectable(selectedItem)) {
|
|
30393
|
-
setActive(
|
|
30394
|
-
setItems(items.map((choice, i) => i ===
|
|
30393
|
+
setActive(position2);
|
|
30394
|
+
setItems(items.map((choice, i) => i === position2 ? toggle(choice) : choice));
|
|
30395
30395
|
}
|
|
30396
30396
|
}
|
|
30397
30397
|
});
|
|
@@ -31442,15 +31442,15 @@ var esm_default11 = createPrompt((config, done) => {
|
|
|
31442
31442
|
} else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
|
|
31443
31443
|
const selectedIndex = Number(rl.line) - 1;
|
|
31444
31444
|
let selectableIndex = -1;
|
|
31445
|
-
const
|
|
31445
|
+
const position2 = items.findIndex((item2) => {
|
|
31446
31446
|
if (Separator.isSeparator(item2))
|
|
31447
31447
|
return false;
|
|
31448
31448
|
selectableIndex++;
|
|
31449
31449
|
return selectableIndex === selectedIndex;
|
|
31450
31450
|
});
|
|
31451
|
-
const item = items[
|
|
31451
|
+
const item = items[position2];
|
|
31452
31452
|
if (item != null && isSelectable3(item)) {
|
|
31453
|
-
setActive(
|
|
31453
|
+
setActive(position2);
|
|
31454
31454
|
}
|
|
31455
31455
|
searchTimeoutRef.current = setTimeout(() => {
|
|
31456
31456
|
rl.clearLine(0);
|
|
@@ -31529,9 +31529,9 @@ var import_run_async = __toESM(require_run_async(), 1);
|
|
|
31529
31529
|
var import_mute_stream2 = __toESM(require_lib(), 1);
|
|
31530
31530
|
import readline3 from "node:readline";
|
|
31531
31531
|
var _ = {
|
|
31532
|
-
set: (obj,
|
|
31532
|
+
set: (obj, path23 = "", value) => {
|
|
31533
31533
|
let pointer = obj;
|
|
31534
|
-
|
|
31534
|
+
path23.split(".").forEach((key, index, arr) => {
|
|
31535
31535
|
if (key === "__proto__" || key === "constructor")
|
|
31536
31536
|
return;
|
|
31537
31537
|
if (index === arr.length - 1) {
|
|
@@ -31542,8 +31542,8 @@ var _ = {
|
|
|
31542
31542
|
pointer = pointer[key];
|
|
31543
31543
|
});
|
|
31544
31544
|
},
|
|
31545
|
-
get: (obj,
|
|
31546
|
-
const travel = (regexp) => String.prototype.split.call(
|
|
31545
|
+
get: (obj, path23 = "", defaultValue) => {
|
|
31546
|
+
const travel = (regexp) => String.prototype.split.call(path23, regexp).filter(Boolean).reduce(
|
|
31547
31547
|
// @ts-expect-error implicit any on res[key]
|
|
31548
31548
|
(res, key) => res == null ? res : res[key],
|
|
31549
31549
|
obj
|
|
@@ -31853,7 +31853,8 @@ var EXIT_FOR_CODE = {
|
|
|
31853
31853
|
cli_not_found: EXIT.failure,
|
|
31854
31854
|
cli_conflict: EXIT.conflict,
|
|
31855
31855
|
cli_io_failed: EXIT.failure,
|
|
31856
|
-
cli_failed: EXIT.failure
|
|
31856
|
+
cli_failed: EXIT.failure,
|
|
31857
|
+
cli_network: EXIT.failure
|
|
31857
31858
|
};
|
|
31858
31859
|
var CliError = class extends Error {
|
|
31859
31860
|
code;
|
|
@@ -32062,9 +32063,9 @@ import os2 from "node:os";
|
|
|
32062
32063
|
import tty from "node:tty";
|
|
32063
32064
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
|
|
32064
32065
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
32065
|
-
const
|
|
32066
|
+
const position2 = argv.indexOf(prefix + flag);
|
|
32066
32067
|
const terminatorPosition = argv.indexOf("--");
|
|
32067
|
-
return
|
|
32068
|
+
return position2 !== -1 && (terminatorPosition === -1 || position2 < terminatorPosition);
|
|
32068
32069
|
}
|
|
32069
32070
|
var { env } = process4;
|
|
32070
32071
|
var flagForceColor;
|
|
@@ -33105,6 +33106,14 @@ function label(key, value) {
|
|
|
33105
33106
|
if (isQuiet()) return;
|
|
33106
33107
|
writeOut(` ${source_default.dim(key + ":")} ${value}`);
|
|
33107
33108
|
}
|
|
33109
|
+
function field(key, value) {
|
|
33110
|
+
if (isJsonOutput()) return;
|
|
33111
|
+
if (isQuiet()) {
|
|
33112
|
+
writeOut(`${key}: ${value}`);
|
|
33113
|
+
return;
|
|
33114
|
+
}
|
|
33115
|
+
writeOut(` ${source_default.dim(key + ":")} ${value}`);
|
|
33116
|
+
}
|
|
33108
33117
|
function fileTree(lines) {
|
|
33109
33118
|
if (isQuiet()) return;
|
|
33110
33119
|
for (const line2 of lines) {
|
|
@@ -33117,6 +33126,62 @@ function line(msg = "") {
|
|
|
33117
33126
|
writeOut(msg);
|
|
33118
33127
|
}
|
|
33119
33128
|
|
|
33129
|
+
// src/lib/apiErrorDetails.ts
|
|
33130
|
+
function isRecord(value) {
|
|
33131
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33132
|
+
}
|
|
33133
|
+
function readPath(value) {
|
|
33134
|
+
if (!Array.isArray(value)) return void 0;
|
|
33135
|
+
const path23 = value.filter((part) => typeof part === "string" || typeof part === "number");
|
|
33136
|
+
return path23.length === value.length ? path23 : void 0;
|
|
33137
|
+
}
|
|
33138
|
+
function readValidationIssues(details) {
|
|
33139
|
+
const raw = details?.issues;
|
|
33140
|
+
if (!Array.isArray(raw)) return [];
|
|
33141
|
+
const issues = [];
|
|
33142
|
+
for (const entry of raw) {
|
|
33143
|
+
if (!isRecord(entry) || typeof entry.message !== "string") continue;
|
|
33144
|
+
const path23 = readPath(entry.path) ?? [];
|
|
33145
|
+
const param = typeof entry.param === "string" ? entry.param : path23.join(".");
|
|
33146
|
+
issues.push({ path: path23, param, message: entry.message });
|
|
33147
|
+
}
|
|
33148
|
+
return issues;
|
|
33149
|
+
}
|
|
33150
|
+
function readCompilationErrors(details) {
|
|
33151
|
+
const raw = details?.compilationErrors;
|
|
33152
|
+
if (!Array.isArray(raw)) return [];
|
|
33153
|
+
const errors = [];
|
|
33154
|
+
for (const entry of raw) {
|
|
33155
|
+
if (!isRecord(entry) || typeof entry.file !== "string" || typeof entry.message !== "string") continue;
|
|
33156
|
+
const path23 = readPath(entry.path);
|
|
33157
|
+
errors.push({
|
|
33158
|
+
file: entry.file,
|
|
33159
|
+
line: typeof entry.line === "number" ? entry.line : 0,
|
|
33160
|
+
column: typeof entry.column === "number" ? entry.column : 0,
|
|
33161
|
+
message: entry.message,
|
|
33162
|
+
...path23 ? { path: path23 } : {}
|
|
33163
|
+
});
|
|
33164
|
+
}
|
|
33165
|
+
return errors;
|
|
33166
|
+
}
|
|
33167
|
+
function position(file, line2, column) {
|
|
33168
|
+
if (line2 <= 0) return file;
|
|
33169
|
+
return column > 0 ? `${file}:${line2}:${column}` : `${file}:${line2}`;
|
|
33170
|
+
}
|
|
33171
|
+
function describeErrorDetails(details, labelForPath) {
|
|
33172
|
+
const lines = [];
|
|
33173
|
+
for (const issue of readValidationIssues(details)) {
|
|
33174
|
+
const label2 = issue.path.length > 0 ? labelForPath?.(issue.path) : void 0;
|
|
33175
|
+
const where = label2 ? `${label2} (${issue.param})` : issue.param || "(request body)";
|
|
33176
|
+
lines.push(`${where}: ${issue.message}`);
|
|
33177
|
+
}
|
|
33178
|
+
for (const error2 of readCompilationErrors(details)) {
|
|
33179
|
+
const label2 = error2.path ? labelForPath?.(error2.path) : void 0;
|
|
33180
|
+
lines.push(`${position(label2 ?? error2.file, error2.line, error2.column)}: ${error2.message}`);
|
|
33181
|
+
}
|
|
33182
|
+
return lines;
|
|
33183
|
+
}
|
|
33184
|
+
|
|
33120
33185
|
// src/lib/tokenShape.ts
|
|
33121
33186
|
var CREDENTIAL_NAMES = {
|
|
33122
33187
|
personalAccessToken: "personal access token",
|
|
@@ -33171,7 +33236,7 @@ var ApiRequestError = class extends Error {
|
|
|
33171
33236
|
return exitCodeForStatus(this.status);
|
|
33172
33237
|
}
|
|
33173
33238
|
};
|
|
33174
|
-
function
|
|
33239
|
+
function isRecord2(value) {
|
|
33175
33240
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33176
33241
|
}
|
|
33177
33242
|
function asString(value) {
|
|
@@ -33185,16 +33250,16 @@ function parseRetryAfter(raw) {
|
|
|
33185
33250
|
function toApiRequestError(input) {
|
|
33186
33251
|
const retryAfterSeconds = parseRetryAfter(input.retryAfter);
|
|
33187
33252
|
const headerRequestId = asString(input.requestId ?? void 0);
|
|
33188
|
-
if (
|
|
33253
|
+
if (isRecord2(input.body)) {
|
|
33189
33254
|
const raw = input.body.error;
|
|
33190
|
-
if (
|
|
33255
|
+
if (isRecord2(raw)) {
|
|
33191
33256
|
return new ApiRequestError({
|
|
33192
33257
|
status: input.status,
|
|
33193
33258
|
message: asString(raw.message) ?? `API error ${input.status}`,
|
|
33194
33259
|
code: asString(raw.code),
|
|
33195
33260
|
docUrl: asString(raw.docUrl),
|
|
33196
33261
|
requestId: asString(raw.requestId) ?? headerRequestId,
|
|
33197
|
-
details:
|
|
33262
|
+
details: isRecord2(raw.details) ? raw.details : void 0,
|
|
33198
33263
|
retryAfterSeconds
|
|
33199
33264
|
});
|
|
33200
33265
|
}
|
|
@@ -33215,13 +33280,13 @@ function toApiRequestError(input) {
|
|
|
33215
33280
|
}
|
|
33216
33281
|
return new ApiRequestError({
|
|
33217
33282
|
status: input.status,
|
|
33218
|
-
message: `API error ${input.status}`,
|
|
33283
|
+
message: input.host ? `${input.host} answered HTTP ${input.status}, which is not an A vs B API response. Check the API URL.` : `API error ${input.status}`,
|
|
33219
33284
|
requestId: headerRequestId,
|
|
33220
33285
|
retryAfterSeconds
|
|
33221
33286
|
});
|
|
33222
33287
|
}
|
|
33223
|
-
function describeApiFailure(error2) {
|
|
33224
|
-
const lines =
|
|
33288
|
+
function describeApiFailure(error2, labelForPath) {
|
|
33289
|
+
const lines = describeErrorDetails(error2.details, labelForPath);
|
|
33225
33290
|
if (error2.status === 401) {
|
|
33226
33291
|
lines.push(
|
|
33227
33292
|
`Check the credential: the CLI signs in with a ${CREDENTIAL_NAMES.personalAccessToken} (${CREDENTIAL_NAMES.personalAccessTokenPrefix}\u2026). Run \`avsb whoami\` to see which one is in use, or \`avsb login\` to replace it.`
|
|
@@ -33296,31 +33361,31 @@ function exitCodeFor(error2) {
|
|
|
33296
33361
|
if (error2 instanceof CliError) return error2.exitCode;
|
|
33297
33362
|
return EXIT.failure;
|
|
33298
33363
|
}
|
|
33299
|
-
function hintsFor(error2) {
|
|
33300
|
-
if (error2 instanceof ApiRequestError) return describeApiFailure(error2);
|
|
33364
|
+
function hintsFor(error2, labelForPath) {
|
|
33365
|
+
if (error2 instanceof ApiRequestError) return describeApiFailure(error2, labelForPath);
|
|
33301
33366
|
if (error2 instanceof CliError) return error2.hints;
|
|
33302
33367
|
return [];
|
|
33303
33368
|
}
|
|
33304
|
-
function failCommand(error2) {
|
|
33369
|
+
function failCommand(error2, options = {}) {
|
|
33305
33370
|
const body = toJsonErrorBody(error2);
|
|
33306
33371
|
if (isJsonOutput()) {
|
|
33307
33372
|
writeOut(JSON.stringify({ ok: false, command: commandName, error: body }, null, 2));
|
|
33308
33373
|
}
|
|
33309
33374
|
error(body.message);
|
|
33310
|
-
for (const hint of hintsFor(error2)) errorDetail(hint);
|
|
33375
|
+
for (const hint of hintsFor(error2, options.labelForPath)) errorDetail(hint);
|
|
33311
33376
|
process.exit(exitCodeFor(error2));
|
|
33312
33377
|
}
|
|
33313
33378
|
|
|
33314
33379
|
// src/lib/credentials.ts
|
|
33315
33380
|
var CONFIG_VERSION = 2;
|
|
33316
|
-
function
|
|
33381
|
+
function isRecord3(value) {
|
|
33317
33382
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33318
33383
|
}
|
|
33319
33384
|
function isNonEmptyString(value) {
|
|
33320
33385
|
return typeof value === "string" && value.trim().length > 0;
|
|
33321
33386
|
}
|
|
33322
33387
|
function isStoredLogin(value) {
|
|
33323
|
-
return
|
|
33388
|
+
return isRecord3(value) && isNonEmptyString(value.orgId) && typeof value.orgName === "string" && isNonEmptyString(value.token);
|
|
33324
33389
|
}
|
|
33325
33390
|
function parseConfigDocument(raw) {
|
|
33326
33391
|
let parsed;
|
|
@@ -33329,7 +33394,7 @@ function parseConfigDocument(raw) {
|
|
|
33329
33394
|
} catch {
|
|
33330
33395
|
return { status: "unreadable", detail: "it is not valid JSON" };
|
|
33331
33396
|
}
|
|
33332
|
-
if (!
|
|
33397
|
+
if (!isRecord3(parsed)) {
|
|
33333
33398
|
return { status: "unreadable", detail: "it does not contain a JSON object" };
|
|
33334
33399
|
}
|
|
33335
33400
|
if (parsed.version !== CONFIG_VERSION) {
|
|
@@ -33534,6 +33599,17 @@ var FRAMEWORK_IDS = [
|
|
|
33534
33599
|
"node",
|
|
33535
33600
|
"none"
|
|
33536
33601
|
];
|
|
33602
|
+
var BUNDLERS = [
|
|
33603
|
+
{ id: "vite", dependency: "vite" },
|
|
33604
|
+
{ id: "create-react-app", dependency: "react-scripts" },
|
|
33605
|
+
{ id: "vue-cli", dependency: "@vue/cli-service" }
|
|
33606
|
+
];
|
|
33607
|
+
function detectBundler(deps) {
|
|
33608
|
+
for (const bundler of BUNDLERS) {
|
|
33609
|
+
if (Object.prototype.hasOwnProperty.call(deps, bundler.dependency)) return bundler;
|
|
33610
|
+
}
|
|
33611
|
+
return null;
|
|
33612
|
+
}
|
|
33537
33613
|
function parsePackageJson(raw) {
|
|
33538
33614
|
let parsed;
|
|
33539
33615
|
try {
|
|
@@ -33566,12 +33642,13 @@ function allDependencies(pkg2) {
|
|
|
33566
33642
|
}
|
|
33567
33643
|
function detectFramework(pkg2) {
|
|
33568
33644
|
if (!pkg2) {
|
|
33569
|
-
return { id: "none", matchedDependency: null, usesVite: false, usesSvelteKit: false };
|
|
33645
|
+
return { id: "none", matchedDependency: null, usesVite: false, usesSvelteKit: false, bundler: null };
|
|
33570
33646
|
}
|
|
33571
33647
|
const deps = allDependencies(pkg2);
|
|
33572
33648
|
const has = (name) => Object.prototype.hasOwnProperty.call(deps, name);
|
|
33573
33649
|
const usesVite = has("vite");
|
|
33574
33650
|
const usesSvelteKit = has("@sveltejs/kit");
|
|
33651
|
+
const bundler = detectBundler(deps);
|
|
33575
33652
|
const ordered = [
|
|
33576
33653
|
{ id: "nuxt", dependency: "nuxt" },
|
|
33577
33654
|
{ id: "next", dependency: "next" },
|
|
@@ -33588,11 +33665,12 @@ function detectFramework(pkg2) {
|
|
|
33588
33665
|
id: candidate.id,
|
|
33589
33666
|
matchedDependency: candidate.dependency,
|
|
33590
33667
|
usesVite,
|
|
33591
|
-
usesSvelteKit
|
|
33668
|
+
usesSvelteKit,
|
|
33669
|
+
bundler
|
|
33592
33670
|
};
|
|
33593
33671
|
}
|
|
33594
33672
|
}
|
|
33595
|
-
return { id: "node", matchedDependency: null, usesVite, usesSvelteKit };
|
|
33673
|
+
return { id: "node", matchedDependency: null, usesVite, usesSvelteKit, bundler };
|
|
33596
33674
|
}
|
|
33597
33675
|
function isFrameworkId(value) {
|
|
33598
33676
|
return FRAMEWORK_IDS.includes(value);
|
|
@@ -33640,9 +33718,26 @@ function installArgv(manager, npmPackage) {
|
|
|
33640
33718
|
}
|
|
33641
33719
|
|
|
33642
33720
|
// src/lib/frameworkProfiles.ts
|
|
33643
|
-
var
|
|
33644
|
-
var
|
|
33645
|
-
var
|
|
33721
|
+
var BROWSER_ENV_FILE = ".env.local";
|
|
33722
|
+
var UNPREFIXED_CAVEAT = "No Vite, Create React App or Vue CLI in this project, so the browser variable prefix is your bundler's call. Rename AVSB_SDK_KEY to the prefix your bundler exposes to the browser, or pass the key to the provider directly.";
|
|
33723
|
+
var BUNDLER_VARIABLES = {
|
|
33724
|
+
vite: { envVar: "VITE_AVSB_SDK_KEY", label: "Vite", prefix: "VITE_" },
|
|
33725
|
+
"create-react-app": { envVar: "REACT_APP_AVSB_SDK_KEY", label: "Create React App", prefix: "REACT_APP_" },
|
|
33726
|
+
"vue-cli": { envVar: "VUE_APP_AVSB_SDK_KEY", label: "Vue CLI", prefix: "VUE_APP_" }
|
|
33727
|
+
};
|
|
33728
|
+
function browserVariable(detection) {
|
|
33729
|
+
const bundler = detection.bundler;
|
|
33730
|
+
const applies = bundler !== null && (bundler.id === "vite" || bundler.id === "create-react-app" && detection.id === "react" || bundler.id === "vue-cli" && detection.id === "vue");
|
|
33731
|
+
if (!applies || bundler === null) {
|
|
33732
|
+
return { envVar: "AVSB_SDK_KEY", envVarReason: null, caveat: UNPREFIXED_CAVEAT };
|
|
33733
|
+
}
|
|
33734
|
+
const known = BUNDLER_VARIABLES[bundler.id];
|
|
33735
|
+
return {
|
|
33736
|
+
envVar: known.envVar,
|
|
33737
|
+
envVarReason: `found ${bundler.dependency}, and ${known.label} only exposes ${known.prefix} variables to the browser`,
|
|
33738
|
+
caveat: null
|
|
33739
|
+
};
|
|
33740
|
+
}
|
|
33646
33741
|
function buildFrameworkProfile(detection) {
|
|
33647
33742
|
switch (detection.id) {
|
|
33648
33743
|
case "next":
|
|
@@ -33654,6 +33749,7 @@ function buildFrameworkProfile(detection) {
|
|
|
33654
33749
|
envVar: "AVSB_SDK_KEY",
|
|
33655
33750
|
exampleFile: "avsb-example.tsx",
|
|
33656
33751
|
caveat: null,
|
|
33752
|
+
envVarReason: null,
|
|
33657
33753
|
docsPath: "/docs/integrations/nextjs-app-router"
|
|
33658
33754
|
};
|
|
33659
33755
|
case "nuxt":
|
|
@@ -33665,6 +33761,7 @@ function buildFrameworkProfile(detection) {
|
|
|
33665
33761
|
envVar: "NUXT_PUBLIC_AVSB_SDK_KEY",
|
|
33666
33762
|
exampleFile: "components/AvsbExample.vue",
|
|
33667
33763
|
caveat: 'Add "@avsbhq/nuxt" to the modules array in nuxt.config.ts. The module reads the key from this variable.',
|
|
33764
|
+
envVarReason: null,
|
|
33668
33765
|
docsPath: "/docs/integrations/nuxt"
|
|
33669
33766
|
};
|
|
33670
33767
|
case "angular":
|
|
@@ -33676,6 +33773,7 @@ function buildFrameworkProfile(detection) {
|
|
|
33676
33773
|
envVar: null,
|
|
33677
33774
|
exampleFile: "src/app/avsb-example.component.ts",
|
|
33678
33775
|
caveat: "Angular reads configuration from src/environments/environment.ts rather than an environment file, so the example carries the key inline. Move it into your environment file if you keep one per build target.",
|
|
33776
|
+
envVarReason: null,
|
|
33679
33777
|
docsPath: "/docs/integrations/angular-cli"
|
|
33680
33778
|
};
|
|
33681
33779
|
case "svelte":
|
|
@@ -33687,15 +33785,15 @@ function buildFrameworkProfile(detection) {
|
|
|
33687
33785
|
envVar: "PUBLIC_AVSB_SDK_KEY",
|
|
33688
33786
|
exampleFile: "src/lib/AvsbExample.svelte",
|
|
33689
33787
|
caveat: null,
|
|
33788
|
+
envVarReason: null,
|
|
33690
33789
|
docsPath: "/docs/integrations/sveltekit"
|
|
33691
33790
|
} : {
|
|
33692
33791
|
id: "svelte",
|
|
33693
33792
|
label: "Svelte",
|
|
33694
33793
|
sdkPackage: "@avsbhq/svelte",
|
|
33695
|
-
envFile:
|
|
33696
|
-
|
|
33794
|
+
envFile: BROWSER_ENV_FILE,
|
|
33795
|
+
...browserVariable(detection),
|
|
33697
33796
|
exampleFile: "src/AvsbExample.svelte",
|
|
33698
|
-
caveat: detection.usesVite ? null : UNPREFIXED_CAVEAT,
|
|
33699
33797
|
docsPath: "/docs/integrations/vite-svelte"
|
|
33700
33798
|
};
|
|
33701
33799
|
case "vue":
|
|
@@ -33703,10 +33801,9 @@ function buildFrameworkProfile(detection) {
|
|
|
33703
33801
|
id: "vue",
|
|
33704
33802
|
label: "Vue",
|
|
33705
33803
|
sdkPackage: "@avsbhq/vue",
|
|
33706
|
-
envFile:
|
|
33707
|
-
|
|
33804
|
+
envFile: BROWSER_ENV_FILE,
|
|
33805
|
+
...browserVariable(detection),
|
|
33708
33806
|
exampleFile: "src/AvsbExample.vue",
|
|
33709
|
-
caveat: detection.usesVite ? null : UNPREFIXED_CAVEAT,
|
|
33710
33807
|
docsPath: "/docs/integrations/vite-vue"
|
|
33711
33808
|
};
|
|
33712
33809
|
case "solid":
|
|
@@ -33714,10 +33811,9 @@ function buildFrameworkProfile(detection) {
|
|
|
33714
33811
|
id: "solid",
|
|
33715
33812
|
label: "Solid",
|
|
33716
33813
|
sdkPackage: "@avsbhq/solid",
|
|
33717
|
-
envFile:
|
|
33718
|
-
|
|
33814
|
+
envFile: BROWSER_ENV_FILE,
|
|
33815
|
+
...browserVariable(detection),
|
|
33719
33816
|
exampleFile: "src/avsb-example.tsx",
|
|
33720
|
-
caveat: detection.usesVite ? null : UNPREFIXED_CAVEAT,
|
|
33721
33817
|
docsPath: "/docs/integrations/vite-solid"
|
|
33722
33818
|
};
|
|
33723
33819
|
case "react":
|
|
@@ -33725,10 +33821,9 @@ function buildFrameworkProfile(detection) {
|
|
|
33725
33821
|
id: "react",
|
|
33726
33822
|
label: "React",
|
|
33727
33823
|
sdkPackage: "@avsbhq/react",
|
|
33728
|
-
envFile:
|
|
33729
|
-
|
|
33824
|
+
envFile: BROWSER_ENV_FILE,
|
|
33825
|
+
...browserVariable(detection),
|
|
33730
33826
|
exampleFile: "src/avsb-example.tsx",
|
|
33731
|
-
caveat: detection.usesVite ? null : UNPREFIXED_CAVEAT,
|
|
33732
33827
|
docsPath: "/docs/integrations/vite-react"
|
|
33733
33828
|
};
|
|
33734
33829
|
case "node":
|
|
@@ -33740,6 +33835,7 @@ function buildFrameworkProfile(detection) {
|
|
|
33740
33835
|
envVar: "AVSB_SDK_KEY",
|
|
33741
33836
|
exampleFile: "avsb-example.ts",
|
|
33742
33837
|
caveat: null,
|
|
33838
|
+
envVarReason: null,
|
|
33743
33839
|
docsPath: "/docs/developer-reference/avsb-node"
|
|
33744
33840
|
};
|
|
33745
33841
|
case "none":
|
|
@@ -33754,6 +33850,7 @@ function buildFrameworkProfile(detection) {
|
|
|
33754
33850
|
// a feature flags project set up from a folder with no package.json was
|
|
33755
33851
|
// being told it was a web experiments project.
|
|
33756
33852
|
caveat: "No package.json here, so no framework was detected. Run this in your application directory to have the SDK wired up for you.",
|
|
33853
|
+
envVarReason: null,
|
|
33757
33854
|
docsPath: "/docs/getting-started/installing-the-snippet"
|
|
33758
33855
|
};
|
|
33759
33856
|
}
|
|
@@ -33762,13 +33859,19 @@ function buildFrameworkProfile(detection) {
|
|
|
33762
33859
|
// src/lib/heartbeatWait.ts
|
|
33763
33860
|
var HEARTBEAT_INTERVAL_MS = 3e3;
|
|
33764
33861
|
var HEARTBEAT_TIMEOUT_MS = 12e4;
|
|
33765
|
-
var CLOCK_ALLOWANCE_MS =
|
|
33862
|
+
var CLOCK_ALLOWANCE_MS = 5e3;
|
|
33766
33863
|
function isFreshHeartbeat(lastSdkSeenAt, startedAt, allowanceMs = CLOCK_ALLOWANCE_MS) {
|
|
33767
33864
|
if (!lastSdkSeenAt) return false;
|
|
33768
33865
|
const seenAt = Date.parse(lastSdkSeenAt);
|
|
33769
33866
|
if (Number.isNaN(seenAt)) return false;
|
|
33770
33867
|
return seenAt >= startedAt - allowanceMs;
|
|
33771
33868
|
}
|
|
33869
|
+
function isNewerThan(stamp, previous) {
|
|
33870
|
+
if (!stamp || !previous) return false;
|
|
33871
|
+
const seenAt = Date.parse(stamp);
|
|
33872
|
+
const before = Date.parse(previous);
|
|
33873
|
+
return !Number.isNaN(seenAt) && !Number.isNaN(before) && seenAt > before;
|
|
33874
|
+
}
|
|
33772
33875
|
async function waitForHeartbeat(options) {
|
|
33773
33876
|
const timeoutMs = options.timeoutMs ?? HEARTBEAT_TIMEOUT_MS;
|
|
33774
33877
|
const intervalMs = options.intervalMs ?? HEARTBEAT_INTERVAL_MS;
|
|
@@ -33788,7 +33891,8 @@ async function waitForHeartbeat(options) {
|
|
|
33788
33891
|
previousSeenAt = sample.lastSdkSeenAt;
|
|
33789
33892
|
first = false;
|
|
33790
33893
|
}
|
|
33791
|
-
|
|
33894
|
+
const arrivedWhileWatching = isNewerThan(sample.lastSdkSeenAt, previousSeenAt);
|
|
33895
|
+
if (sample.lastSdkSeenAt && (arrivedWhileWatching || isFreshHeartbeat(sample.lastSdkSeenAt, options.startedAt))) {
|
|
33792
33896
|
return {
|
|
33793
33897
|
seen: true,
|
|
33794
33898
|
at: sample.lastSdkSeenAt,
|
|
@@ -33809,6 +33913,7 @@ function heartbeatTimeoutGuidance(input) {
|
|
|
33809
33913
|
const seconds = Math.round(input.waitedMs / 1e3);
|
|
33810
33914
|
const lines = [
|
|
33811
33915
|
`No check-in from ${input.environmentName} in ${seconds}s. Everything above is written; only the proof is missing.`,
|
|
33916
|
+
`${turnFlagOnCommand(input.flagKey ?? null, input.environmentKey ?? "production")} An environment where nothing has been turned on has no configuration for the SDK to load, and the SDK checks in only after it loads one.`,
|
|
33812
33917
|
"Start your app once, then run `avsb init --wait 120` again to watch for it.",
|
|
33813
33918
|
`Check that the key your code loads is ${input.sdkKey}.`,
|
|
33814
33919
|
`Check that outbound requests to ${input.cdnHost} and ${input.appHost} are allowed.`
|
|
@@ -33820,14 +33925,109 @@ function heartbeatTimeoutGuidance(input) {
|
|
|
33820
33925
|
}
|
|
33821
33926
|
return lines;
|
|
33822
33927
|
}
|
|
33928
|
+
function turnFlagOnCommand(flagKey, environmentKey) {
|
|
33929
|
+
return `Turn a flag on with \`avsb flags toggle ${flagKey ?? "<key>"} --env ${environmentKey} --on\`.`;
|
|
33930
|
+
}
|
|
33823
33931
|
function defaultSleep(ms) {
|
|
33824
33932
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
33825
33933
|
}
|
|
33826
33934
|
|
|
33935
|
+
// src/lib/networkError.ts
|
|
33936
|
+
var DNS_CODES = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL"]);
|
|
33937
|
+
var TIMEOUT_CODES = /* @__PURE__ */ new Set(["ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_HEADERS_TIMEOUT", "ESOCKETTIMEDOUT"]);
|
|
33938
|
+
var RESET_CODES = /* @__PURE__ */ new Set(["ECONNRESET", "EPIPE", "UND_ERR_SOCKET", "ECONNABORTED"]);
|
|
33939
|
+
function isRecord4(value) {
|
|
33940
|
+
return typeof value === "object" && value !== null;
|
|
33941
|
+
}
|
|
33942
|
+
function networkErrorCode(err) {
|
|
33943
|
+
let current = err;
|
|
33944
|
+
for (let depth = 0; depth < 5 && isRecord4(current); depth++) {
|
|
33945
|
+
if (typeof current.code === "string") return current.code;
|
|
33946
|
+
if (Array.isArray(current.errors)) {
|
|
33947
|
+
for (const inner of current.errors) {
|
|
33948
|
+
const code = networkErrorCode(inner);
|
|
33949
|
+
if (code) return code;
|
|
33950
|
+
}
|
|
33951
|
+
}
|
|
33952
|
+
current = current.cause;
|
|
33953
|
+
}
|
|
33954
|
+
return null;
|
|
33955
|
+
}
|
|
33956
|
+
function networkErrorReason(err) {
|
|
33957
|
+
let current = err;
|
|
33958
|
+
let reason = null;
|
|
33959
|
+
for (let depth = 0; depth < 5 && isRecord4(current); depth++) {
|
|
33960
|
+
const message = current.message;
|
|
33961
|
+
if (typeof message === "string" && message.length > 0 && message !== "fetch failed") reason = message;
|
|
33962
|
+
current = current.cause;
|
|
33963
|
+
}
|
|
33964
|
+
return reason;
|
|
33965
|
+
}
|
|
33966
|
+
function classifyNetworkError(code) {
|
|
33967
|
+
if (code === null) return "other";
|
|
33968
|
+
if (DNS_CODES.has(code)) return "dns";
|
|
33969
|
+
if (code === "ECONNREFUSED") return "refused";
|
|
33970
|
+
if (TIMEOUT_CODES.has(code)) return "timeout";
|
|
33971
|
+
if (RESET_CODES.has(code)) return "reset";
|
|
33972
|
+
if (/CERT|TLS|SSL|SELF_SIGNED|UNABLE_TO_VERIFY|UNABLE_TO_GET_ISSUER/.test(code)) return "tls";
|
|
33973
|
+
return "other";
|
|
33974
|
+
}
|
|
33975
|
+
function hostOf(baseUrl) {
|
|
33976
|
+
try {
|
|
33977
|
+
return new URL(baseUrl).host;
|
|
33978
|
+
} catch {
|
|
33979
|
+
return baseUrl;
|
|
33980
|
+
}
|
|
33981
|
+
}
|
|
33982
|
+
function describeNetworkFailure(baseUrl, err) {
|
|
33983
|
+
const host = hostOf(baseUrl);
|
|
33984
|
+
const code = networkErrorCode(err);
|
|
33985
|
+
const kind = classifyNetworkError(code);
|
|
33986
|
+
const urlHint = `The API URL in use is ${baseUrl}. Check AVSB_API_URL, or the URL \`avsb login --url\` saved.`;
|
|
33987
|
+
const networkHint = "Check your network connection, VPN or proxy.";
|
|
33988
|
+
switch (kind) {
|
|
33989
|
+
case "dns":
|
|
33990
|
+
return new CliError("cli_network", `Cannot find ${host}: the name does not resolve (DNS lookup failed).`, [urlHint, networkHint]);
|
|
33991
|
+
case "refused":
|
|
33992
|
+
return new CliError("cli_network", `Nothing is listening at ${host}: the connection was refused.`, [urlHint]);
|
|
33993
|
+
case "timeout":
|
|
33994
|
+
return new CliError("cli_network", `${host} did not answer in time (the connection timed out).`, [networkHint]);
|
|
33995
|
+
case "reset":
|
|
33996
|
+
return new CliError("cli_network", `${host} closed the connection before answering.`, [networkHint]);
|
|
33997
|
+
case "tls":
|
|
33998
|
+
return new CliError("cli_network", `Could not make a secure connection to ${host} (${code}).`, [
|
|
33999
|
+
"A proxy or antivirus that inspects HTTPS can cause this. Check its certificate settings."
|
|
34000
|
+
]);
|
|
34001
|
+
case "other": {
|
|
34002
|
+
const reason = code ?? networkErrorReason(err) ?? "no answer";
|
|
34003
|
+
return new CliError("cli_network", `Could not reach ${host} (${reason}).`, [urlHint, networkHint]);
|
|
34004
|
+
}
|
|
34005
|
+
}
|
|
34006
|
+
}
|
|
34007
|
+
function invalidBaseUrl(baseUrl) {
|
|
34008
|
+
let protocol = "";
|
|
34009
|
+
try {
|
|
34010
|
+
protocol = new URL(baseUrl).protocol;
|
|
34011
|
+
} catch {
|
|
34012
|
+
protocol = "";
|
|
34013
|
+
}
|
|
34014
|
+
if (protocol === "http:" || protocol === "https:") return null;
|
|
34015
|
+
return new CliError("cli_usage", `"${baseUrl}" is not a valid API URL.`, [
|
|
34016
|
+
"Use a full address such as https://app.avsb.cloud, in AVSB_API_URL or `avsb login --url`."
|
|
34017
|
+
]);
|
|
34018
|
+
}
|
|
34019
|
+
|
|
33827
34020
|
// src/lib/apiTransport.ts
|
|
33828
34021
|
var API_VERSION = "v1";
|
|
33829
34022
|
var API_VERSION_HEADER = "AvsB-API-Version";
|
|
33830
34023
|
var REQUEST_ID_HEADER = "X-Request-Id";
|
|
34024
|
+
function hostOf2(baseUrl) {
|
|
34025
|
+
try {
|
|
34026
|
+
return new URL(baseUrl).host;
|
|
34027
|
+
} catch {
|
|
34028
|
+
return void 0;
|
|
34029
|
+
}
|
|
34030
|
+
}
|
|
33831
34031
|
var ApiTransport = class {
|
|
33832
34032
|
baseUrl;
|
|
33833
34033
|
token;
|
|
@@ -33858,18 +34058,25 @@ var ApiTransport = class {
|
|
|
33858
34058
|
* body parsing. It reports what came back and judges nothing, so both the
|
|
33859
34059
|
* plain and the conditional paths below share exactly one implementation.
|
|
33860
34060
|
*/
|
|
33861
|
-
async send(
|
|
34061
|
+
async send(path23, options) {
|
|
33862
34062
|
this.assertUsableToken();
|
|
33863
|
-
const
|
|
33864
|
-
|
|
33865
|
-
|
|
33866
|
-
|
|
33867
|
-
|
|
33868
|
-
|
|
33869
|
-
|
|
33870
|
-
|
|
33871
|
-
|
|
33872
|
-
|
|
34063
|
+
const badUrl = invalidBaseUrl(this.baseUrl);
|
|
34064
|
+
if (badUrl) throw badUrl;
|
|
34065
|
+
const url = `${this.baseUrl}${path23}`;
|
|
34066
|
+
let res;
|
|
34067
|
+
try {
|
|
34068
|
+
res = await fetch(url, {
|
|
34069
|
+
...options,
|
|
34070
|
+
headers: {
|
|
34071
|
+
"Content-Type": "application/json",
|
|
34072
|
+
Authorization: `Bearer ${this.token}`,
|
|
34073
|
+
[API_VERSION_HEADER]: API_VERSION,
|
|
34074
|
+
...options.headers
|
|
34075
|
+
}
|
|
34076
|
+
});
|
|
34077
|
+
} catch (err) {
|
|
34078
|
+
throw describeNetworkFailure(this.baseUrl, err);
|
|
34079
|
+
}
|
|
33873
34080
|
let body = null;
|
|
33874
34081
|
const text = await res.text();
|
|
33875
34082
|
if (text.length > 0) {
|
|
@@ -33886,7 +34093,8 @@ var ApiTransport = class {
|
|
|
33886
34093
|
status: res.status,
|
|
33887
34094
|
body,
|
|
33888
34095
|
requestId: res.headers.get(REQUEST_ID_HEADER),
|
|
33889
|
-
retryAfter: res.headers.get("Retry-After")
|
|
34096
|
+
retryAfter: res.headers.get("Retry-After"),
|
|
34097
|
+
host: hostOf2(this.baseUrl)
|
|
33890
34098
|
});
|
|
33891
34099
|
}
|
|
33892
34100
|
/**
|
|
@@ -33894,8 +34102,8 @@ var ApiTransport = class {
|
|
|
33894
34102
|
* named methods. Returns the body and the ETag; `request` unwraps it for the
|
|
33895
34103
|
* common case.
|
|
33896
34104
|
*/
|
|
33897
|
-
async requestWithMeta(
|
|
33898
|
-
const { res, body } = await this.send(
|
|
34105
|
+
async requestWithMeta(path23, options = {}) {
|
|
34106
|
+
const { res, body } = await this.send(path23, options);
|
|
33899
34107
|
if (!res.ok) throw this.refusal(res, body);
|
|
33900
34108
|
return { body, etag: res.headers.get("ETag") };
|
|
33901
34109
|
}
|
|
@@ -33907,9 +34115,9 @@ var ApiTransport = class {
|
|
|
33907
34115
|
* back to comparing what it generated. Nothing here assumes the platform
|
|
33908
34116
|
* supports conditional reads.
|
|
33909
34117
|
*/
|
|
33910
|
-
async requestIfNoneMatch(
|
|
34118
|
+
async requestIfNoneMatch(path23, etag) {
|
|
33911
34119
|
const { res, body } = await this.send(
|
|
33912
|
-
|
|
34120
|
+
path23,
|
|
33913
34121
|
etag ? { headers: { "If-None-Match": etag } } : {}
|
|
33914
34122
|
);
|
|
33915
34123
|
if (res.status === 304) {
|
|
@@ -33918,8 +34126,8 @@ var ApiTransport = class {
|
|
|
33918
34126
|
if (!res.ok) throw this.refusal(res, body);
|
|
33919
34127
|
return { status: "ok", body, etag: res.headers.get("ETag") };
|
|
33920
34128
|
}
|
|
33921
|
-
async request(
|
|
33922
|
-
const result = await this.requestWithMeta(
|
|
34129
|
+
async request(path23, options = {}) {
|
|
34130
|
+
const result = await this.requestWithMeta(path23, options);
|
|
33923
34131
|
return result.body;
|
|
33924
34132
|
}
|
|
33925
34133
|
};
|
|
@@ -33947,6 +34155,11 @@ var ApiClient = class extends ApiTransport {
|
|
|
33947
34155
|
body: JSON.stringify(data)
|
|
33948
34156
|
});
|
|
33949
34157
|
}
|
|
34158
|
+
/**
|
|
34159
|
+
* Publish an experiment's pending changes, as the dashboard's Publish
|
|
34160
|
+
* changes button does. `warning` is set when the change is saved but the
|
|
34161
|
+
* live datafile could not be written yet (the platform retries it).
|
|
34162
|
+
*/
|
|
33950
34163
|
async publishChanges(shortId) {
|
|
33951
34164
|
return this.request(`/api/cli/experiments/${shortId}/publish`, {
|
|
33952
34165
|
method: "POST"
|
|
@@ -33961,11 +34174,6 @@ var ApiClient = class extends ApiTransport {
|
|
|
33961
34174
|
body: JSON.stringify(data)
|
|
33962
34175
|
});
|
|
33963
34176
|
}
|
|
33964
|
-
async publishProject(shortId) {
|
|
33965
|
-
return this.request(`/api/cli/projects/${shortId}/publish`, {
|
|
33966
|
-
method: "POST"
|
|
33967
|
-
});
|
|
33968
|
-
}
|
|
33969
34177
|
/**
|
|
33970
34178
|
* Fetch a project's published datafile for a given environment.
|
|
33971
34179
|
*
|
|
@@ -33983,8 +34191,8 @@ var ApiClient = class extends ApiTransport {
|
|
|
33983
34191
|
if (opts.type) params.set("type", opts.type);
|
|
33984
34192
|
if (opts.includeArchived) params.set("includeArchived", "true");
|
|
33985
34193
|
const qs = params.toString();
|
|
33986
|
-
const
|
|
33987
|
-
return this.request(
|
|
34194
|
+
const path23 = `/api/cli/projects/${projectShortId}/metrics${qs ? `?${qs}` : ""}`;
|
|
34195
|
+
return this.request(path23);
|
|
33988
34196
|
}
|
|
33989
34197
|
async createMetric(projectShortId, body) {
|
|
33990
34198
|
return this.request(
|
|
@@ -33995,14 +34203,9 @@ var ApiClient = class extends ApiTransport {
|
|
|
33995
34203
|
}
|
|
33996
34204
|
);
|
|
33997
34205
|
}
|
|
33998
|
-
async getMetric(projectShortId, metricId) {
|
|
33999
|
-
return this.request(
|
|
34000
|
-
`/api/cli/projects/${projectShortId}/metrics/${metricId}`
|
|
34001
|
-
);
|
|
34002
|
-
}
|
|
34003
34206
|
async updateMetric(projectShortId, metricId, patch) {
|
|
34004
34207
|
return this.request(
|
|
34005
|
-
`/api/cli/projects/${projectShortId}/metrics/${metricId}`,
|
|
34208
|
+
`/api/cli/projects/${projectShortId}/metrics/${encodeURIComponent(metricId)}`,
|
|
34006
34209
|
{
|
|
34007
34210
|
method: "PATCH",
|
|
34008
34211
|
body: JSON.stringify(patch)
|
|
@@ -34011,19 +34214,19 @@ var ApiClient = class extends ApiTransport {
|
|
|
34011
34214
|
}
|
|
34012
34215
|
async archiveMetric(projectShortId, metricId) {
|
|
34013
34216
|
return this.request(
|
|
34014
|
-
`/api/cli/projects/${projectShortId}/metrics/${metricId}/archive`,
|
|
34217
|
+
`/api/cli/projects/${projectShortId}/metrics/${encodeURIComponent(metricId)}/archive`,
|
|
34015
34218
|
{ method: "POST" }
|
|
34016
34219
|
);
|
|
34017
34220
|
}
|
|
34018
34221
|
async unarchiveMetric(projectShortId, metricId) {
|
|
34019
34222
|
return this.request(
|
|
34020
|
-
`/api/cli/projects/${projectShortId}/metrics/${metricId}/unarchive`,
|
|
34223
|
+
`/api/cli/projects/${projectShortId}/metrics/${encodeURIComponent(metricId)}/unarchive`,
|
|
34021
34224
|
{ method: "POST" }
|
|
34022
34225
|
);
|
|
34023
34226
|
}
|
|
34024
34227
|
async deleteMetric(projectShortId, metricId) {
|
|
34025
34228
|
return this.request(
|
|
34026
|
-
`/api/cli/projects/${projectShortId}/metrics/${metricId}`,
|
|
34229
|
+
`/api/cli/projects/${projectShortId}/metrics/${encodeURIComponent(metricId)}`,
|
|
34027
34230
|
{ method: "DELETE" }
|
|
34028
34231
|
);
|
|
34029
34232
|
}
|
|
@@ -34035,8 +34238,8 @@ var ApiClient = class extends ApiTransport {
|
|
|
34035
34238
|
if (opts.includeArchived) params.set("includeArchived", "true");
|
|
34036
34239
|
if (opts.limit != null) params.set("limit", String(opts.limit));
|
|
34037
34240
|
const qs = params.toString();
|
|
34038
|
-
const
|
|
34039
|
-
return this.request(
|
|
34241
|
+
const path23 = `/api/cli/projects/${projectShortId}/metric-bindings${qs ? `?${qs}` : ""}`;
|
|
34242
|
+
return this.request(path23);
|
|
34040
34243
|
}
|
|
34041
34244
|
async createMetricBinding(projectShortId, body) {
|
|
34042
34245
|
return this.request(
|
|
@@ -34047,14 +34250,9 @@ var ApiClient = class extends ApiTransport {
|
|
|
34047
34250
|
}
|
|
34048
34251
|
);
|
|
34049
34252
|
}
|
|
34050
|
-
async getMetricBinding(projectShortId, metricBindingId) {
|
|
34051
|
-
return this.request(
|
|
34052
|
-
`/api/cli/projects/${projectShortId}/metric-bindings/${metricBindingId}`
|
|
34053
|
-
);
|
|
34054
|
-
}
|
|
34055
34253
|
async updateMetricBinding(projectShortId, metricBindingId, patch) {
|
|
34056
34254
|
return this.request(
|
|
34057
|
-
`/api/cli/projects/${projectShortId}/metric-bindings/${metricBindingId}`,
|
|
34255
|
+
`/api/cli/projects/${projectShortId}/metric-bindings/${encodeURIComponent(metricBindingId)}`,
|
|
34058
34256
|
{
|
|
34059
34257
|
method: "PATCH",
|
|
34060
34258
|
body: JSON.stringify(patch)
|
|
@@ -34063,19 +34261,19 @@ var ApiClient = class extends ApiTransport {
|
|
|
34063
34261
|
}
|
|
34064
34262
|
async archiveMetricBinding(projectShortId, metricBindingId) {
|
|
34065
34263
|
return this.request(
|
|
34066
|
-
`/api/cli/projects/${projectShortId}/metric-bindings/${metricBindingId}/archive`,
|
|
34264
|
+
`/api/cli/projects/${projectShortId}/metric-bindings/${encodeURIComponent(metricBindingId)}/archive`,
|
|
34067
34265
|
{ method: "POST" }
|
|
34068
34266
|
);
|
|
34069
34267
|
}
|
|
34070
34268
|
async unarchiveMetricBinding(projectShortId, metricBindingId) {
|
|
34071
34269
|
return this.request(
|
|
34072
|
-
`/api/cli/projects/${projectShortId}/metric-bindings/${metricBindingId}/unarchive`,
|
|
34270
|
+
`/api/cli/projects/${projectShortId}/metric-bindings/${encodeURIComponent(metricBindingId)}/unarchive`,
|
|
34073
34271
|
{ method: "POST" }
|
|
34074
34272
|
);
|
|
34075
34273
|
}
|
|
34076
34274
|
async deleteMetricBinding(projectShortId, metricBindingId) {
|
|
34077
34275
|
return this.request(
|
|
34078
|
-
`/api/cli/projects/${projectShortId}/metric-bindings/${metricBindingId}`,
|
|
34276
|
+
`/api/cli/projects/${projectShortId}/metric-bindings/${encodeURIComponent(metricBindingId)}`,
|
|
34079
34277
|
{ method: "DELETE" }
|
|
34080
34278
|
);
|
|
34081
34279
|
}
|
|
@@ -34123,11 +34321,16 @@ var ApiClient = class extends ApiTransport {
|
|
|
34123
34321
|
* presigned PUTs reject chunked transfer encoding).
|
|
34124
34322
|
*/
|
|
34125
34323
|
async uploadNdjson(uploadUrl, body) {
|
|
34126
|
-
|
|
34127
|
-
|
|
34128
|
-
|
|
34129
|
-
|
|
34130
|
-
|
|
34324
|
+
let res;
|
|
34325
|
+
try {
|
|
34326
|
+
res = await fetch(uploadUrl, {
|
|
34327
|
+
method: "PUT",
|
|
34328
|
+
headers: { "Content-Type": "application/x-ndjson" },
|
|
34329
|
+
body
|
|
34330
|
+
});
|
|
34331
|
+
} catch (err) {
|
|
34332
|
+
throw describeNetworkFailure(new URL(uploadUrl).origin, err);
|
|
34333
|
+
}
|
|
34131
34334
|
if (!res.ok) {
|
|
34132
34335
|
throw new Error(`Upload failed: HTTP ${res.status}`);
|
|
34133
34336
|
}
|
|
@@ -34411,9 +34614,9 @@ function servedValueFor(flag) {
|
|
|
34411
34614
|
if (!variation) return null;
|
|
34412
34615
|
return { variationKey: variation.key, value: variation.value };
|
|
34413
34616
|
}
|
|
34414
|
-
function describeServedValue(flagKey, served) {
|
|
34617
|
+
function describeServedValue(flagKey, served, environmentKey = "production") {
|
|
34415
34618
|
if (!served) {
|
|
34416
|
-
return `${flagKey} is not being served in this environment yet, so your code gets the fallback you pass
|
|
34619
|
+
return `${flagKey} is not being served in this environment yet, so your code gets the fallback you pass. Turn it on with \`avsb flags toggle ${flagKey} --env ${environmentKey} --on\`.`;
|
|
34417
34620
|
}
|
|
34418
34621
|
return `${flagKey} serves ${JSON.stringify(served.value)} (variation ${served.variationKey}) when no targeting rule matches.`;
|
|
34419
34622
|
}
|
|
@@ -34426,12 +34629,46 @@ function canPrompt(ctx) {
|
|
|
34426
34629
|
if (envFlagEnabled(ctx.env.CI)) return false;
|
|
34427
34630
|
return true;
|
|
34428
34631
|
}
|
|
34429
|
-
function
|
|
34430
|
-
return
|
|
34632
|
+
function currentPromptContext(options = {}) {
|
|
34633
|
+
return {
|
|
34431
34634
|
input: options.input !== false,
|
|
34432
34635
|
quiet: isQuiet(),
|
|
34433
34636
|
isTty: process.stdin.isTTY === true,
|
|
34434
34637
|
env: process.env
|
|
34638
|
+
};
|
|
34639
|
+
}
|
|
34640
|
+
function isInteractiveRun(options = {}) {
|
|
34641
|
+
return canPrompt(currentPromptContext(options));
|
|
34642
|
+
}
|
|
34643
|
+
function refusalWithoutPrompt(refusal, ctx = currentPromptContext()) {
|
|
34644
|
+
if (canPrompt(ctx)) return null;
|
|
34645
|
+
return new CliError(refusal.code, refusal.message, refusal.hints);
|
|
34646
|
+
}
|
|
34647
|
+
var STDIN_IDLE_TIMEOUT_MS = 3e3;
|
|
34648
|
+
function readPipedStdin(stream = process.stdin, idleMs = STDIN_IDLE_TIMEOUT_MS) {
|
|
34649
|
+
return new Promise((resolve3) => {
|
|
34650
|
+
const chunks = [];
|
|
34651
|
+
let timer = null;
|
|
34652
|
+
const finish = () => {
|
|
34653
|
+
if (timer) clearTimeout(timer);
|
|
34654
|
+
stream.removeListener("data", onData);
|
|
34655
|
+
stream.removeListener("end", finish);
|
|
34656
|
+
stream.removeListener("error", finish);
|
|
34657
|
+
if ("pause" in stream && typeof stream.pause === "function") stream.pause();
|
|
34658
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
34659
|
+
};
|
|
34660
|
+
const arm = () => {
|
|
34661
|
+
if (timer) clearTimeout(timer);
|
|
34662
|
+
timer = setTimeout(finish, idleMs);
|
|
34663
|
+
};
|
|
34664
|
+
const onData = (chunk) => {
|
|
34665
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
34666
|
+
arm();
|
|
34667
|
+
};
|
|
34668
|
+
stream.on("data", onData);
|
|
34669
|
+
stream.once("end", finish);
|
|
34670
|
+
stream.once("error", finish);
|
|
34671
|
+
arm();
|
|
34435
34672
|
});
|
|
34436
34673
|
}
|
|
34437
34674
|
|
|
@@ -34514,10 +34751,40 @@ function unquote(value) {
|
|
|
34514
34751
|
return quote2 === '"' ? body.replace(/\\(["\\])/g, "$1") : body;
|
|
34515
34752
|
}
|
|
34516
34753
|
|
|
34517
|
-
// src/
|
|
34754
|
+
// src/lib/gitignore.ts
|
|
34518
34755
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
34519
34756
|
import fs3 from "node:fs";
|
|
34520
34757
|
import path4 from "node:path";
|
|
34758
|
+
var GITIGNORE_BANNER = "# A vs B: the SDK key file `avsb init` writes";
|
|
34759
|
+
function gitIgnoreState(dir, file) {
|
|
34760
|
+
const result = spawnSync2("git", ["check-ignore", "-q", "--", file], { cwd: dir, stdio: "ignore" });
|
|
34761
|
+
if (result.error) return "no-repo";
|
|
34762
|
+
if (result.status === 0) return "ignored";
|
|
34763
|
+
if (result.status === 1) return "not-ignored";
|
|
34764
|
+
return "no-repo";
|
|
34765
|
+
}
|
|
34766
|
+
function appendGitignoreEntry(existing, file) {
|
|
34767
|
+
const entry = `${GITIGNORE_BANNER}
|
|
34768
|
+
${file}
|
|
34769
|
+
`;
|
|
34770
|
+
if (existing === null || existing.length === 0) return entry;
|
|
34771
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
34772
|
+
return `${existing}${separator}${entry}`;
|
|
34773
|
+
}
|
|
34774
|
+
function ensureGitignored(dir, file, state = gitIgnoreState) {
|
|
34775
|
+
const current = state(dir, file);
|
|
34776
|
+
if (current === "ignored") return { outcome: "already-ignored", gitignorePath: null };
|
|
34777
|
+
if (current === "no-repo") return { outcome: "no-repo", gitignorePath: null };
|
|
34778
|
+
const gitignorePath = path4.join(dir, ".gitignore");
|
|
34779
|
+
const existing = fs3.existsSync(gitignorePath) ? fs3.readFileSync(gitignorePath, "utf-8") : null;
|
|
34780
|
+
fs3.writeFileSync(gitignorePath, appendGitignoreEntry(existing, file), "utf-8");
|
|
34781
|
+
return { outcome: "added", gitignorePath };
|
|
34782
|
+
}
|
|
34783
|
+
|
|
34784
|
+
// src/commands/init.write.ts
|
|
34785
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
34786
|
+
import fs4 from "node:fs";
|
|
34787
|
+
import path5 from "node:path";
|
|
34521
34788
|
|
|
34522
34789
|
// src/lib/jsonTypeInference.ts
|
|
34523
34790
|
var MAX_JSON_DEPTH = 4;
|
|
@@ -35023,14 +35290,14 @@ console.log('${input.flagKey}', flag.value, 'from', flag.source)
|
|
|
35023
35290
|
|
|
35024
35291
|
// src/commands/init.write.ts
|
|
35025
35292
|
function exampleFileExists(dir, profile, overridePath) {
|
|
35026
|
-
return
|
|
35293
|
+
return fs4.existsSync(resolveExamplePath(dir, profile, overridePath));
|
|
35027
35294
|
}
|
|
35028
35295
|
function resolveExamplePath(dir, profile, overridePath) {
|
|
35029
|
-
return
|
|
35296
|
+
return path5.resolve(dir, overridePath ?? profile.exampleFile);
|
|
35030
35297
|
}
|
|
35031
35298
|
function writeExampleFile(input) {
|
|
35032
35299
|
const filePath = resolveExamplePath(input.dir, input.profile, input.overridePath);
|
|
35033
|
-
if (
|
|
35300
|
+
if (fs4.existsSync(filePath)) {
|
|
35034
35301
|
return { filePath, wrote: false, existed: true };
|
|
35035
35302
|
}
|
|
35036
35303
|
const contents = buildSample({
|
|
@@ -35042,14 +35309,14 @@ function writeExampleFile(input) {
|
|
|
35042
35309
|
if (contents === null) {
|
|
35043
35310
|
return { filePath, wrote: false, existed: false };
|
|
35044
35311
|
}
|
|
35045
|
-
|
|
35046
|
-
|
|
35312
|
+
fs4.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
35313
|
+
fs4.writeFileSync(filePath, contents, "utf-8");
|
|
35047
35314
|
return { filePath, wrote: true, existed: false };
|
|
35048
35315
|
}
|
|
35049
35316
|
function runInstall(dir, manager, npmPackage, options) {
|
|
35050
35317
|
const { command, args } = installArgv(manager, npmPackage);
|
|
35051
35318
|
const printable = [command, ...args].join(" ");
|
|
35052
|
-
const result =
|
|
35319
|
+
const result = spawnSync3(command, args, {
|
|
35053
35320
|
cwd: dir,
|
|
35054
35321
|
// A quiet or JSON run must leave stdout carrying only the result, so the
|
|
35055
35322
|
// package manager's own output is captured and dropped instead of printed.
|
|
@@ -35079,10 +35346,10 @@ async function runCodegen(input) {
|
|
|
35079
35346
|
projectId: String(input.projectShortId),
|
|
35080
35347
|
env: input.environmentKey
|
|
35081
35348
|
});
|
|
35082
|
-
const filePath =
|
|
35349
|
+
const filePath = path5.resolve(input.dir, input.outputPath ?? DEFAULT_CODEGEN_OUTPUT);
|
|
35083
35350
|
try {
|
|
35084
|
-
|
|
35085
|
-
|
|
35351
|
+
fs4.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
35352
|
+
fs4.writeFileSync(filePath, generated, "utf-8");
|
|
35086
35353
|
} catch (err) {
|
|
35087
35354
|
throw new CliError(
|
|
35088
35355
|
"cli_io_failed",
|
|
@@ -35093,14 +35360,14 @@ async function runCodegen(input) {
|
|
|
35093
35360
|
}
|
|
35094
35361
|
function readFileOrNull(filePath) {
|
|
35095
35362
|
try {
|
|
35096
|
-
return
|
|
35363
|
+
return fs4.readFileSync(filePath, "utf-8");
|
|
35097
35364
|
} catch {
|
|
35098
35365
|
return null;
|
|
35099
35366
|
}
|
|
35100
35367
|
}
|
|
35101
35368
|
function listDirectory(dir) {
|
|
35102
35369
|
try {
|
|
35103
|
-
return
|
|
35370
|
+
return fs4.readdirSync(dir);
|
|
35104
35371
|
} catch {
|
|
35105
35372
|
return [];
|
|
35106
35373
|
}
|
|
@@ -35108,7 +35375,7 @@ function listDirectory(dir) {
|
|
|
35108
35375
|
|
|
35109
35376
|
// src/commands/init.execute.ts
|
|
35110
35377
|
async function executePlan(input) {
|
|
35111
|
-
const files = { env: null, envOutcome: null, example: null, codegen: null };
|
|
35378
|
+
const files = { env: null, envOutcome: null, example: null, codegen: null, gitignore: null };
|
|
35112
35379
|
let install = null;
|
|
35113
35380
|
const { profile, environment } = input;
|
|
35114
35381
|
if (stepFor(input.plan, "env").action === "write" && environment && profile.envFile && profile.envVar) {
|
|
@@ -35121,6 +35388,11 @@ async function executePlan(input) {
|
|
|
35121
35388
|
files.env = result.filePath;
|
|
35122
35389
|
files.envOutcome = result.outcome;
|
|
35123
35390
|
info(describeEnvWrite(result, profile.envVar));
|
|
35391
|
+
const ignored = ensureGitignored(input.dir, profile.envFile);
|
|
35392
|
+
if (ignored.outcome === "added" && ignored.gitignorePath) {
|
|
35393
|
+
files.gitignore = ignored.gitignorePath;
|
|
35394
|
+
info(`Added ${profile.envFile} to ${ignored.gitignorePath}, so git does not pick it up`);
|
|
35395
|
+
}
|
|
35124
35396
|
}
|
|
35125
35397
|
const installStep = stepFor(input.plan, "install");
|
|
35126
35398
|
if (profile.sdkPackage) {
|
|
@@ -35188,17 +35460,17 @@ async function watchForCheckIn(api, projectShortId, environment, wait) {
|
|
|
35188
35460
|
|
|
35189
35461
|
// src/lib/projectScaffold.ts
|
|
35190
35462
|
import { createHash as createHash2 } from "node:crypto";
|
|
35191
|
-
import
|
|
35192
|
-
import
|
|
35463
|
+
import fs8 from "node:fs";
|
|
35464
|
+
import path9 from "node:path";
|
|
35193
35465
|
|
|
35194
35466
|
// src/lib/scaffold.ts
|
|
35195
|
-
import
|
|
35196
|
-
import
|
|
35467
|
+
import fs7 from "node:fs";
|
|
35468
|
+
import path8 from "node:path";
|
|
35197
35469
|
|
|
35198
35470
|
// src/lib/manifest.ts
|
|
35199
35471
|
import { createHash } from "node:crypto";
|
|
35200
|
-
import
|
|
35201
|
-
import
|
|
35472
|
+
import fs5 from "node:fs";
|
|
35473
|
+
import path6 from "node:path";
|
|
35202
35474
|
var MANIFEST_FILENAME = ".avsb.json";
|
|
35203
35475
|
function parseExperimentInput(input) {
|
|
35204
35476
|
const urlMatch = input.match(/experiments\/(\d+)/);
|
|
@@ -35213,12 +35485,12 @@ function parseExperimentInput(input) {
|
|
|
35213
35485
|
function findManifest(startDir) {
|
|
35214
35486
|
let dir = startDir ?? process.cwd();
|
|
35215
35487
|
for (let i = 0; i < 10; i++) {
|
|
35216
|
-
const manifestPath =
|
|
35217
|
-
if (
|
|
35218
|
-
const raw =
|
|
35488
|
+
const manifestPath = path6.join(dir, MANIFEST_FILENAME);
|
|
35489
|
+
if (fs5.existsSync(manifestPath)) {
|
|
35490
|
+
const raw = fs5.readFileSync(manifestPath, "utf-8");
|
|
35219
35491
|
return JSON.parse(raw);
|
|
35220
35492
|
}
|
|
35221
|
-
const parent =
|
|
35493
|
+
const parent = path6.dirname(dir);
|
|
35222
35494
|
if (parent === dir) break;
|
|
35223
35495
|
dir = parent;
|
|
35224
35496
|
}
|
|
@@ -35227,17 +35499,17 @@ function findManifest(startDir) {
|
|
|
35227
35499
|
function findManifestDir(startDir) {
|
|
35228
35500
|
let dir = startDir ?? process.cwd();
|
|
35229
35501
|
for (let i = 0; i < 10; i++) {
|
|
35230
|
-
const manifestPath =
|
|
35231
|
-
if (
|
|
35232
|
-
const parent =
|
|
35502
|
+
const manifestPath = path6.join(dir, MANIFEST_FILENAME);
|
|
35503
|
+
if (fs5.existsSync(manifestPath)) return dir;
|
|
35504
|
+
const parent = path6.dirname(dir);
|
|
35233
35505
|
if (parent === dir) break;
|
|
35234
35506
|
dir = parent;
|
|
35235
35507
|
}
|
|
35236
35508
|
return null;
|
|
35237
35509
|
}
|
|
35238
35510
|
function writeManifest(dir, manifest) {
|
|
35239
|
-
const manifestPath =
|
|
35240
|
-
|
|
35511
|
+
const manifestPath = path6.join(dir, MANIFEST_FILENAME);
|
|
35512
|
+
fs5.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
35241
35513
|
}
|
|
35242
35514
|
function readManifestOrExit(startDir) {
|
|
35243
35515
|
const dir = findManifestDir(startDir);
|
|
@@ -35275,30 +35547,30 @@ function computeSyncHashes(triggersJs, variations) {
|
|
|
35275
35547
|
}
|
|
35276
35548
|
|
|
35277
35549
|
// src/lib/snippetTypeDefs.ts
|
|
35278
|
-
import
|
|
35279
|
-
import
|
|
35550
|
+
import fs6 from "node:fs";
|
|
35551
|
+
import path7 from "node:path";
|
|
35280
35552
|
import { createRequire } from "node:module";
|
|
35281
35553
|
import { fileURLToPath } from "node:url";
|
|
35282
35554
|
var SNIPPET_TYPES_PACKAGE = "@avsbhq/snippet-types";
|
|
35283
35555
|
var SNIPPET_TYPE_DEF_FILES = ["avsb.d.ts", "variation-code.d.ts"];
|
|
35284
35556
|
var MAX_WALK_UP = 24;
|
|
35285
35557
|
function snippetTypeDefCandidates(filename, lookup) {
|
|
35286
|
-
const candidates = [
|
|
35558
|
+
const candidates = [path7.join(lookup.moduleDir, "types", filename)];
|
|
35287
35559
|
const packageDir = lookup.packageDir();
|
|
35288
|
-
if (packageDir) candidates.push(
|
|
35560
|
+
if (packageDir) candidates.push(path7.join(packageDir, filename));
|
|
35289
35561
|
const isRepoRoot = lookup.isRepoRoot ?? defaultIsRepoRoot;
|
|
35290
35562
|
let dir = lookup.moduleDir;
|
|
35291
35563
|
for (let depth = 0; depth < MAX_WALK_UP; depth += 1) {
|
|
35292
|
-
candidates.push(
|
|
35564
|
+
candidates.push(path7.join(dir, "packages", "avsb-snippet-types", filename));
|
|
35293
35565
|
if (isRepoRoot(dir)) break;
|
|
35294
|
-
const parent =
|
|
35566
|
+
const parent = path7.dirname(dir);
|
|
35295
35567
|
if (parent === dir) break;
|
|
35296
35568
|
dir = parent;
|
|
35297
35569
|
}
|
|
35298
35570
|
return candidates;
|
|
35299
35571
|
}
|
|
35300
35572
|
function defaultIsRepoRoot(dir) {
|
|
35301
|
-
return
|
|
35573
|
+
return fs6.existsSync(path7.join(dir, ".git"));
|
|
35302
35574
|
}
|
|
35303
35575
|
function findSnippetTypeDefPath(filename, lookup) {
|
|
35304
35576
|
for (const candidate of snippetTypeDefCandidates(filename, lookup)) {
|
|
@@ -35316,15 +35588,15 @@ function typeDefBanner(filename) {
|
|
|
35316
35588
|
function defaultPackageDir() {
|
|
35317
35589
|
try {
|
|
35318
35590
|
const moduleRequire = createRequire(import.meta.url);
|
|
35319
|
-
return
|
|
35591
|
+
return path7.dirname(moduleRequire.resolve(`${SNIPPET_TYPES_PACKAGE}/package.json`));
|
|
35320
35592
|
} catch {
|
|
35321
35593
|
return null;
|
|
35322
35594
|
}
|
|
35323
35595
|
}
|
|
35324
35596
|
function defaultLookup() {
|
|
35325
35597
|
return {
|
|
35326
|
-
moduleDir:
|
|
35327
|
-
exists: (candidate) =>
|
|
35598
|
+
moduleDir: path7.dirname(fileURLToPath(import.meta.url)),
|
|
35599
|
+
exists: (candidate) => fs6.existsSync(candidate),
|
|
35328
35600
|
packageDir: defaultPackageDir
|
|
35329
35601
|
};
|
|
35330
35602
|
}
|
|
@@ -35341,7 +35613,7 @@ function readSnippetTypeDefs(lookup = defaultLookup()) {
|
|
|
35341
35613
|
]
|
|
35342
35614
|
);
|
|
35343
35615
|
}
|
|
35344
|
-
return { filename, contents: typeDefBanner(filename) +
|
|
35616
|
+
return { filename, contents: typeDefBanner(filename) + fs6.readFileSync(found, "utf-8") };
|
|
35345
35617
|
});
|
|
35346
35618
|
}
|
|
35347
35619
|
|
|
@@ -35366,7 +35638,7 @@ function writeTypeDefinitions(dir) {
|
|
|
35366
35638
|
try {
|
|
35367
35639
|
const written = [];
|
|
35368
35640
|
for (const file of readSnippetTypeDefs()) {
|
|
35369
|
-
|
|
35641
|
+
fs7.writeFileSync(path8.join(dir, file.filename), file.contents);
|
|
35370
35642
|
written.push(file.filename);
|
|
35371
35643
|
}
|
|
35372
35644
|
return written;
|
|
@@ -35382,24 +35654,24 @@ function scaffoldExperiment(options) {
|
|
|
35382
35654
|
const { data, script, style, apiBaseUrl, targetDir } = options;
|
|
35383
35655
|
const { experiment, project, org } = data;
|
|
35384
35656
|
const folderName = toKebabCase(experiment.name);
|
|
35385
|
-
const baseDir = targetDir ??
|
|
35386
|
-
if (
|
|
35657
|
+
const baseDir = targetDir ?? path8.join(process.cwd(), folderName);
|
|
35658
|
+
if (fs7.existsSync(baseDir)) {
|
|
35387
35659
|
throw new Error(`Directory already exists: ${baseDir}`);
|
|
35388
35660
|
}
|
|
35389
|
-
|
|
35661
|
+
fs7.mkdirSync(baseDir, { recursive: true });
|
|
35390
35662
|
const files = [];
|
|
35391
35663
|
const manifestVariations = [];
|
|
35392
|
-
const
|
|
35393
|
-
|
|
35394
|
-
files.push(
|
|
35664
|
+
const triggerFile2 = `trigger.${script}`;
|
|
35665
|
+
fs7.writeFileSync(path8.join(baseDir, triggerFile2), experiment.triggersJs);
|
|
35666
|
+
files.push(triggerFile2);
|
|
35395
35667
|
for (const variation of experiment.variations) {
|
|
35396
35668
|
const folder = variationFolderName(variation.name);
|
|
35397
|
-
const varDir =
|
|
35398
|
-
|
|
35669
|
+
const varDir = path8.join(baseDir, folder);
|
|
35670
|
+
fs7.mkdirSync(varDir, { recursive: true });
|
|
35399
35671
|
const scriptFile = `${folder}.${script}`;
|
|
35400
35672
|
const styleFile = `${folder}.${style}`;
|
|
35401
|
-
|
|
35402
|
-
|
|
35673
|
+
fs7.writeFileSync(path8.join(varDir, scriptFile), variation.controlJs);
|
|
35674
|
+
fs7.writeFileSync(path8.join(varDir, styleFile), variation.controlCss);
|
|
35403
35675
|
files.push(`${folder}/${scriptFile}`);
|
|
35404
35676
|
files.push(`${folder}/${styleFile}`);
|
|
35405
35677
|
manifestVariations.push({
|
|
@@ -35438,7 +35710,7 @@ function scaffoldExperiment(options) {
|
|
|
35438
35710
|
writeManifest(baseDir, manifest);
|
|
35439
35711
|
files.push(".avsb.json");
|
|
35440
35712
|
if (script === "ts") {
|
|
35441
|
-
|
|
35713
|
+
fs7.writeFileSync(path8.join(baseDir, "tsconfig.json"), TSCONFIG_TEMPLATE);
|
|
35442
35714
|
files.push("tsconfig.json");
|
|
35443
35715
|
files.push(...writeTypeDefinitions(baseDir));
|
|
35444
35716
|
}
|
|
@@ -35454,10 +35726,10 @@ function refreshTypeDefinitions(dir, script) {
|
|
|
35454
35726
|
}
|
|
35455
35727
|
const changed = [];
|
|
35456
35728
|
for (const file of current) {
|
|
35457
|
-
const target =
|
|
35458
|
-
const onDisk =
|
|
35729
|
+
const target = path8.join(dir, file.filename);
|
|
35730
|
+
const onDisk = fs7.existsSync(target) ? fs7.readFileSync(target, "utf-8") : null;
|
|
35459
35731
|
if (onDisk === file.contents) continue;
|
|
35460
|
-
|
|
35732
|
+
fs7.writeFileSync(target, file.contents);
|
|
35461
35733
|
changed.push(file.filename);
|
|
35462
35734
|
}
|
|
35463
35735
|
return changed;
|
|
@@ -35467,26 +35739,26 @@ function updateLocalFiles(dir, manifest, data) {
|
|
|
35467
35739
|
const { script, style } = manifest.languages;
|
|
35468
35740
|
const updated = [];
|
|
35469
35741
|
updated.push(...refreshTypeDefinitions(dir, script));
|
|
35470
|
-
const
|
|
35471
|
-
const triggerPath =
|
|
35472
|
-
const currentTrigger =
|
|
35742
|
+
const triggerFile2 = `trigger.${script}`;
|
|
35743
|
+
const triggerPath = path8.join(dir, triggerFile2);
|
|
35744
|
+
const currentTrigger = fs7.existsSync(triggerPath) ? fs7.readFileSync(triggerPath, "utf-8") : "";
|
|
35473
35745
|
if (currentTrigger !== experiment.triggersJs) {
|
|
35474
|
-
|
|
35475
|
-
updated.push(
|
|
35746
|
+
fs7.writeFileSync(triggerPath, experiment.triggersJs);
|
|
35747
|
+
updated.push(triggerFile2);
|
|
35476
35748
|
}
|
|
35477
35749
|
for (const variation of experiment.variations) {
|
|
35478
35750
|
const mv = manifest.variations.find((v) => v.id === variation.id);
|
|
35479
35751
|
if (!mv) continue;
|
|
35480
|
-
const jsPath =
|
|
35481
|
-
const cssPath =
|
|
35482
|
-
const currentJs =
|
|
35483
|
-
const currentCss =
|
|
35752
|
+
const jsPath = path8.join(dir, mv.folder, mv.scriptFile);
|
|
35753
|
+
const cssPath = path8.join(dir, mv.folder, mv.styleFile);
|
|
35754
|
+
const currentJs = fs7.existsSync(jsPath) ? fs7.readFileSync(jsPath, "utf-8") : "";
|
|
35755
|
+
const currentCss = fs7.existsSync(cssPath) ? fs7.readFileSync(cssPath, "utf-8") : "";
|
|
35484
35756
|
if (currentJs !== variation.controlJs) {
|
|
35485
|
-
|
|
35757
|
+
fs7.writeFileSync(jsPath, variation.controlJs);
|
|
35486
35758
|
updated.push(`${mv.folder}/${mv.scriptFile}`);
|
|
35487
35759
|
}
|
|
35488
35760
|
if (currentCss !== variation.controlCss) {
|
|
35489
|
-
|
|
35761
|
+
fs7.writeFileSync(cssPath, variation.controlCss);
|
|
35490
35762
|
updated.push(`${mv.folder}/${mv.styleFile}`);
|
|
35491
35763
|
}
|
|
35492
35764
|
}
|
|
@@ -35519,27 +35791,27 @@ function diffLocalFiles(dir, manifest, data) {
|
|
|
35519
35791
|
const { experiment } = data;
|
|
35520
35792
|
const { script } = manifest.languages;
|
|
35521
35793
|
const changed = [];
|
|
35522
|
-
const
|
|
35523
|
-
const triggerPath =
|
|
35524
|
-
if (
|
|
35525
|
-
const localContent =
|
|
35794
|
+
const triggerFile2 = `trigger.${script}`;
|
|
35795
|
+
const triggerPath = path8.join(dir, triggerFile2);
|
|
35796
|
+
if (fs7.existsSync(triggerPath)) {
|
|
35797
|
+
const localContent = fs7.readFileSync(triggerPath, "utf-8");
|
|
35526
35798
|
if (localContent !== experiment.triggersJs) {
|
|
35527
|
-
changed.push(
|
|
35799
|
+
changed.push(triggerFile2);
|
|
35528
35800
|
}
|
|
35529
35801
|
}
|
|
35530
35802
|
for (const variation of experiment.variations) {
|
|
35531
35803
|
const mv = manifest.variations.find((v) => v.id === variation.id);
|
|
35532
35804
|
if (!mv) continue;
|
|
35533
|
-
const jsPath =
|
|
35534
|
-
const cssPath =
|
|
35535
|
-
if (
|
|
35536
|
-
const localJs =
|
|
35805
|
+
const jsPath = path8.join(dir, mv.folder, mv.scriptFile);
|
|
35806
|
+
const cssPath = path8.join(dir, mv.folder, mv.styleFile);
|
|
35807
|
+
if (fs7.existsSync(jsPath)) {
|
|
35808
|
+
const localJs = fs7.readFileSync(jsPath, "utf-8");
|
|
35537
35809
|
if (localJs !== variation.controlJs) {
|
|
35538
35810
|
changed.push(`${mv.folder}/${mv.scriptFile}`);
|
|
35539
35811
|
}
|
|
35540
35812
|
}
|
|
35541
|
-
if (
|
|
35542
|
-
const localCss =
|
|
35813
|
+
if (fs7.existsSync(cssPath)) {
|
|
35814
|
+
const localCss = fs7.readFileSync(cssPath, "utf-8");
|
|
35543
35815
|
if (localCss !== variation.controlCss) {
|
|
35544
35816
|
changed.push(`${mv.folder}/${mv.styleFile}`);
|
|
35545
35817
|
}
|
|
@@ -35580,21 +35852,21 @@ function parseProjectInput(input) {
|
|
|
35580
35852
|
return num;
|
|
35581
35853
|
}
|
|
35582
35854
|
function writeProjectManifest(dir, manifest) {
|
|
35583
|
-
const manifestPath =
|
|
35584
|
-
|
|
35855
|
+
const manifestPath = path9.join(dir, PROJECT_MANIFEST_FILENAME);
|
|
35856
|
+
fs8.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
35585
35857
|
}
|
|
35586
35858
|
function readProjectManifest(dir) {
|
|
35587
|
-
const manifestPath =
|
|
35588
|
-
if (!
|
|
35589
|
-
const raw =
|
|
35859
|
+
const manifestPath = path9.join(dir, PROJECT_MANIFEST_FILENAME);
|
|
35860
|
+
if (!fs8.existsSync(manifestPath)) return null;
|
|
35861
|
+
const raw = fs8.readFileSync(manifestPath, "utf-8");
|
|
35590
35862
|
return JSON.parse(raw);
|
|
35591
35863
|
}
|
|
35592
35864
|
function findProjectManifestDir(startDir) {
|
|
35593
35865
|
let dir = startDir ?? process.cwd();
|
|
35594
35866
|
for (let i = 0; i < 10; i++) {
|
|
35595
|
-
const manifestPath =
|
|
35596
|
-
if (
|
|
35597
|
-
const parent =
|
|
35867
|
+
const manifestPath = path9.join(dir, PROJECT_MANIFEST_FILENAME);
|
|
35868
|
+
if (fs8.existsSync(manifestPath)) return dir;
|
|
35869
|
+
const parent = path9.dirname(dir);
|
|
35598
35870
|
if (parent === dir) break;
|
|
35599
35871
|
dir = parent;
|
|
35600
35872
|
}
|
|
@@ -35614,20 +35886,29 @@ function readProjectManifestOrExit(startDir) {
|
|
|
35614
35886
|
const manifest = readProjectManifest(dir);
|
|
35615
35887
|
return { manifest, dir };
|
|
35616
35888
|
}
|
|
35889
|
+
function resolveProjectPullTarget(cwd, shortId) {
|
|
35890
|
+
const enclosing = findProjectManifestDir(cwd);
|
|
35891
|
+
if (enclosing && readProjectManifest(enclosing)?.projectShortId === shortId) {
|
|
35892
|
+
return { kind: "existing", dir: enclosing };
|
|
35893
|
+
}
|
|
35894
|
+
const beside = path9.join(cwd, `project-${shortId}`);
|
|
35895
|
+
if (!fs8.existsSync(beside)) return { kind: "fresh", dir: beside };
|
|
35896
|
+
return readProjectManifest(beside)?.projectShortId === shortId ? { kind: "existing", dir: beside } : { kind: "blocked", dir: beside };
|
|
35897
|
+
}
|
|
35617
35898
|
function scaffoldProject(options) {
|
|
35618
35899
|
const { data, apiBaseUrl, targetDir } = options;
|
|
35619
35900
|
const { project } = data;
|
|
35620
35901
|
const lang = project.projectJsLang === "TYPESCRIPT" ? "ts" : "js";
|
|
35621
35902
|
const folderName = `project-${project.shortId}`;
|
|
35622
|
-
const baseDir = targetDir ??
|
|
35623
|
-
if (
|
|
35903
|
+
const baseDir = targetDir ?? path9.join(process.cwd(), folderName);
|
|
35904
|
+
if (fs8.existsSync(baseDir)) {
|
|
35624
35905
|
throw new Error(`Directory already exists: ${baseDir}`);
|
|
35625
35906
|
}
|
|
35626
|
-
|
|
35907
|
+
fs8.mkdirSync(baseDir, { recursive: true });
|
|
35627
35908
|
const files = [];
|
|
35628
35909
|
const scriptFile = `project.${lang}`;
|
|
35629
35910
|
const content = lang === "ts" ? project.editorProjectTs ?? project.editorProjectJs ?? "" : project.editorProjectJs ?? "";
|
|
35630
|
-
|
|
35911
|
+
fs8.writeFileSync(path9.join(baseDir, scriptFile), content);
|
|
35631
35912
|
files.push(scriptFile);
|
|
35632
35913
|
const manifest = {
|
|
35633
35914
|
version: 1,
|
|
@@ -35644,7 +35925,7 @@ function scaffoldProject(options) {
|
|
|
35644
35925
|
writeProjectManifest(baseDir, manifest);
|
|
35645
35926
|
files.push(".avsb-project.json");
|
|
35646
35927
|
if (lang === "ts") {
|
|
35647
|
-
|
|
35928
|
+
fs8.writeFileSync(path9.join(baseDir, "tsconfig.json"), PROJECT_TSCONFIG);
|
|
35648
35929
|
files.push("tsconfig.json");
|
|
35649
35930
|
files.push(...writeTypeDefinitions(baseDir));
|
|
35650
35931
|
}
|
|
@@ -35655,11 +35936,11 @@ function updateProjectFile(dir, manifest, data) {
|
|
|
35655
35936
|
const updated = [];
|
|
35656
35937
|
updated.push(...refreshTypeDefinitions(dir, manifest.language));
|
|
35657
35938
|
const scriptFile = `project.${manifest.language}`;
|
|
35658
|
-
const scriptPath =
|
|
35659
|
-
const current =
|
|
35939
|
+
const scriptPath = path9.join(dir, scriptFile);
|
|
35940
|
+
const current = fs8.existsSync(scriptPath) ? fs8.readFileSync(scriptPath, "utf-8") : "";
|
|
35660
35941
|
const remote = manifest.language === "ts" ? project.editorProjectTs ?? project.editorProjectJs ?? "" : project.editorProjectJs ?? "";
|
|
35661
35942
|
if (current !== remote) {
|
|
35662
|
-
|
|
35943
|
+
fs8.writeFileSync(scriptPath, remote);
|
|
35663
35944
|
updated.push(scriptFile);
|
|
35664
35945
|
}
|
|
35665
35946
|
manifest.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -35878,28 +36159,47 @@ function renderInitSummary(input) {
|
|
|
35878
36159
|
}
|
|
35879
36160
|
function describeDetection(input) {
|
|
35880
36161
|
const manager = `installs with ${input.packageManager}`;
|
|
36162
|
+
const variable = input.profile.envVar && input.profile.envVarReason ? `, key in ${input.profile.envVar}: ${input.profile.envVarReason}` : "";
|
|
35881
36163
|
if (input.detection.matchedDependency) {
|
|
35882
|
-
return `${input.profile.label} (found ${input.detection.matchedDependency} in package.json, ${manager})`;
|
|
36164
|
+
return `${input.profile.label} (found ${input.detection.matchedDependency} in package.json${variable}, ${manager})`;
|
|
35883
36165
|
}
|
|
35884
36166
|
if (input.profile.id === "none") {
|
|
35885
36167
|
return "no package.json found here";
|
|
35886
36168
|
}
|
|
35887
36169
|
return `${input.profile.label} (${manager})`;
|
|
35888
36170
|
}
|
|
35889
|
-
function
|
|
35890
|
-
const
|
|
36171
|
+
function fileReport(files) {
|
|
36172
|
+
const envWritten = files.envOutcome === "created" || files.envOutcome === "appended";
|
|
36173
|
+
const written = [envWritten ? files.env : null, files.example, files.codegen, files.gitignore].filter(
|
|
35891
36174
|
(file) => file !== null
|
|
35892
36175
|
);
|
|
36176
|
+
const leftAlone = !envWritten && files.env !== null ? [files.env] : [];
|
|
36177
|
+
return { written, leftAlone };
|
|
36178
|
+
}
|
|
36179
|
+
function renderSteps(input) {
|
|
36180
|
+
const { written, leftAlone } = fileReport(input.files);
|
|
35893
36181
|
if (written.length > 0) {
|
|
35894
36182
|
heading("Written");
|
|
35895
36183
|
fileTree(written);
|
|
35896
36184
|
line();
|
|
35897
36185
|
}
|
|
36186
|
+
if (leftAlone.length > 0) {
|
|
36187
|
+
heading("Left alone");
|
|
36188
|
+
fileTree(leftAlone);
|
|
36189
|
+
line();
|
|
36190
|
+
}
|
|
35898
36191
|
const skipped = input.plan.filter((step) => step.action === "skip");
|
|
35899
36192
|
for (const step of skipped) dim(` Skipped ${step.id}: ${step.detail}`);
|
|
35900
36193
|
if (skipped.length > 0) line();
|
|
35901
36194
|
}
|
|
35902
36195
|
function renderWhatIsLeft(input) {
|
|
36196
|
+
const todo = whatIsLeftLines(input);
|
|
36197
|
+
if (todo.length === 0) return;
|
|
36198
|
+
line();
|
|
36199
|
+
heading("Next");
|
|
36200
|
+
for (const line2 of todo) dim(` ${line2}`);
|
|
36201
|
+
}
|
|
36202
|
+
function whatIsLeftLines(input) {
|
|
35903
36203
|
const todo = [];
|
|
35904
36204
|
if (input.install && !input.install.ran) {
|
|
35905
36205
|
todo.push(`Install the SDK: ${input.install.command}`);
|
|
@@ -35911,17 +36211,15 @@ function renderWhatIsLeft(input) {
|
|
|
35911
36211
|
todo.push(input.profile.caveat);
|
|
35912
36212
|
}
|
|
35913
36213
|
if (input.flagKey && !input.flagExists) {
|
|
36214
|
+
const envKey = input.environment?.key ?? "production";
|
|
35914
36215
|
todo.push(
|
|
35915
|
-
`The example reads "${input.flagKey}", which does not exist yet. Create it with \`avsb flags create ${input.flagKey}\`.`
|
|
36216
|
+
`The example reads "${input.flagKey}", which does not exist yet. Create it with \`avsb flags create ${input.flagKey}\`, then turn it on with \`avsb flags toggle ${input.flagKey} --env ${envKey} --on\`.`
|
|
35916
36217
|
);
|
|
35917
36218
|
}
|
|
35918
36219
|
if (input.servedLine) {
|
|
35919
36220
|
todo.push(input.servedLine);
|
|
35920
36221
|
}
|
|
35921
|
-
|
|
35922
|
-
line();
|
|
35923
|
-
heading("Next");
|
|
35924
|
-
for (const line2 of todo) dim(` ${line2}`);
|
|
36222
|
+
return todo;
|
|
35925
36223
|
}
|
|
35926
36224
|
function renderAssistantPrompt(input) {
|
|
35927
36225
|
if (!input.environment && input.project.type === "FEATURE_FLAG") return;
|
|
@@ -35978,11 +36276,11 @@ var initCommand = withOutputOptions(
|
|
|
35978
36276
|
).action(async (opts) => {
|
|
35979
36277
|
beginCommand("init", opts);
|
|
35980
36278
|
try {
|
|
35981
|
-
const dir =
|
|
36279
|
+
const dir = path10.resolve(opts.dir ?? ".");
|
|
35982
36280
|
parseWaitSeconds(opts.wait);
|
|
35983
36281
|
assertHeartbeatFlagsAgree(opts);
|
|
35984
36282
|
const interactive = isInteractiveRun({ input: opts.input });
|
|
35985
|
-
const pkg2 = parsePackageJson(readFileOrNull(
|
|
36283
|
+
const pkg2 = parsePackageJson(readFileOrNull(path10.join(dir, "package.json")) ?? "");
|
|
35986
36284
|
const detection = resolveDetection(pkg2, opts.framework);
|
|
35987
36285
|
const profile = buildFrameworkProfile(detection);
|
|
35988
36286
|
const packageManager = detectPackageManager(pkg2, listDirectory(dir));
|
|
@@ -36056,7 +36354,9 @@ var initCommand = withOutputOptions(
|
|
|
36056
36354
|
id: profile.id,
|
|
36057
36355
|
label: profile.label,
|
|
36058
36356
|
detectedFrom: detection.matchedDependency,
|
|
36059
|
-
packageManager
|
|
36357
|
+
packageManager,
|
|
36358
|
+
bundler: detection.bundler?.id ?? null,
|
|
36359
|
+
envVar: profile.envVar
|
|
36060
36360
|
},
|
|
36061
36361
|
flag: sample ? { key: sample.flagKey, exists: sample.flag !== null, served } : null,
|
|
36062
36362
|
steps: plan,
|
|
@@ -36079,12 +36379,14 @@ var initCommand = withOutputOptions(
|
|
|
36079
36379
|
install: outcome.install,
|
|
36080
36380
|
flagKey: sample?.flagKey ?? null,
|
|
36081
36381
|
flagExists: sample !== null && sample.flag !== null,
|
|
36082
|
-
servedLine: sample?.flag ? describeServedValue(sample.flagKey, served) : null,
|
|
36382
|
+
servedLine: sample?.flag ? describeServedValue(sample.flagKey, served, opts.env) : null,
|
|
36083
36383
|
heartbeat,
|
|
36084
36384
|
proof,
|
|
36085
36385
|
timeoutGuidance: heartbeat && !heartbeat.seen && environment ? heartbeatTimeoutGuidance({
|
|
36086
36386
|
sdkKey: environment.sdkKey,
|
|
36087
36387
|
environmentName: environment.name,
|
|
36388
|
+
environmentKey: environment.key,
|
|
36389
|
+
flagKey: sample?.flagKey ?? null,
|
|
36088
36390
|
previousSeenAt: heartbeat.previousSeenAt,
|
|
36089
36391
|
waitedMs: heartbeat.elapsedMs,
|
|
36090
36392
|
cdnHost: new URL(DEFAULT_CDN_BASE_URL).host,
|
|
@@ -36134,8 +36436,8 @@ async function decideCodegen(opts, interactive, hasSdkKey) {
|
|
|
36134
36436
|
}
|
|
36135
36437
|
|
|
36136
36438
|
// src/commands/agents-md.ts
|
|
36137
|
-
import
|
|
36138
|
-
import
|
|
36439
|
+
import fs9 from "node:fs";
|
|
36440
|
+
import path11 from "node:path";
|
|
36139
36441
|
|
|
36140
36442
|
// src/lib/agentsMd.ts
|
|
36141
36443
|
var AGENTS_MARKER_START = "<!-- avsb:start -->";
|
|
@@ -36285,8 +36587,8 @@ var agentsMdInstallCommand = withOutputOptions(
|
|
|
36285
36587
|
).action((opts) => {
|
|
36286
36588
|
beginCommand("agents-md install", opts);
|
|
36287
36589
|
try {
|
|
36288
|
-
const dir =
|
|
36289
|
-
const pkg2 = parsePackageJson(readFileOrNull(
|
|
36590
|
+
const dir = path11.resolve(opts.dir ?? ".");
|
|
36591
|
+
const pkg2 = parsePackageJson(readFileOrNull(path11.join(dir, "package.json")) ?? "");
|
|
36290
36592
|
const profile = buildFrameworkProfile(detectFramework(pkg2));
|
|
36291
36593
|
const section = renderAgentsSection({
|
|
36292
36594
|
projectShortId: readProjectShortId(opts.project),
|
|
@@ -36310,12 +36612,12 @@ var agentsMdInstallCommand = withOutputOptions(
|
|
|
36310
36612
|
}
|
|
36311
36613
|
});
|
|
36312
36614
|
function writeTarget(dir, file, section) {
|
|
36313
|
-
const filePath =
|
|
36615
|
+
const filePath = path11.join(dir, file);
|
|
36314
36616
|
const existing = readFileOrNull(filePath);
|
|
36315
36617
|
const applied = applyAgentsSection(existing, section);
|
|
36316
36618
|
if (applied.outcome !== "unchanged") {
|
|
36317
36619
|
try {
|
|
36318
|
-
|
|
36620
|
+
fs9.writeFileSync(filePath, applied.content, "utf-8");
|
|
36319
36621
|
} catch (err) {
|
|
36320
36622
|
throw new CliError(
|
|
36321
36623
|
"cli_io_failed",
|
|
@@ -36351,7 +36653,8 @@ var loginCommand = new Command("login").description("Save a personal access toke
|
|
|
36351
36653
|
`Replacing the saved login in ${getConfigPath()}: ${existing.detail}.`
|
|
36352
36654
|
);
|
|
36353
36655
|
}
|
|
36354
|
-
|
|
36656
|
+
const interactive = canPrompt(currentPromptContext({ input: options.input }));
|
|
36657
|
+
if (existingConfig && interactive && !explicitToken && !envToken) {
|
|
36355
36658
|
const active = existingConfig.logins.find(
|
|
36356
36659
|
(login) => login.orgId === existingConfig.activeOrgId
|
|
36357
36660
|
);
|
|
@@ -36373,7 +36676,12 @@ var loginCommand = new Command("login").description("Save a personal access toke
|
|
|
36373
36676
|
const token = await resolveToken({
|
|
36374
36677
|
explicitToken,
|
|
36375
36678
|
envToken,
|
|
36376
|
-
|
|
36679
|
+
source: tokenSourceFor({
|
|
36680
|
+
explicitToken,
|
|
36681
|
+
envToken,
|
|
36682
|
+
input: options.input,
|
|
36683
|
+
isTty: process.stdin.isTTY === true
|
|
36684
|
+
})
|
|
36377
36685
|
});
|
|
36378
36686
|
const spinner = ora("Verifying token...").start();
|
|
36379
36687
|
const client = new ApiClient({ token, apiBaseUrl }, { useEnvOverrides: false });
|
|
@@ -36381,8 +36689,9 @@ var loginCommand = new Command("login").description("Save a personal access toke
|
|
|
36381
36689
|
try {
|
|
36382
36690
|
verified = await client.verifyAuth();
|
|
36383
36691
|
} catch (err) {
|
|
36384
|
-
|
|
36385
|
-
|
|
36692
|
+
const rejected = err instanceof ApiRequestError && err.status === 401;
|
|
36693
|
+
spinner.fail(rejected ? "Token rejected" : "Could not check the token");
|
|
36694
|
+
if (rejected) {
|
|
36386
36695
|
dim(` ${WHERE_TO_GET_A_TOKEN}`);
|
|
36387
36696
|
}
|
|
36388
36697
|
failCommand(err);
|
|
@@ -36417,13 +36726,22 @@ var loginCommand = new Command("login").description("Save a personal access toke
|
|
|
36417
36726
|
failCommand(err);
|
|
36418
36727
|
}
|
|
36419
36728
|
});
|
|
36729
|
+
function tokenSourceFor(input) {
|
|
36730
|
+
if (input.explicitToken) return "flag";
|
|
36731
|
+
if (input.envToken) return "env";
|
|
36732
|
+
if (!input.input) return "refuse";
|
|
36733
|
+
return input.isTty ? "prompt" : "stdin";
|
|
36734
|
+
}
|
|
36735
|
+
var NO_TOKEN_HINTS = [
|
|
36736
|
+
`Pass --token <token>, set ${TOKEN_ENV_VAR}, or pipe it in: echo "$TOKEN" | avsb login`
|
|
36737
|
+
];
|
|
36420
36738
|
async function resolveToken(input) {
|
|
36421
|
-
if (input.explicitToken) return input.explicitToken;
|
|
36422
|
-
if (input.envToken) {
|
|
36739
|
+
if (input.source === "flag" && input.explicitToken) return input.explicitToken;
|
|
36740
|
+
if (input.source === "env" && input.envToken) {
|
|
36423
36741
|
info(`Using the token from ${TOKEN_ENV_VAR}.`);
|
|
36424
36742
|
return input.envToken;
|
|
36425
36743
|
}
|
|
36426
|
-
if (
|
|
36744
|
+
if (input.source === "refuse") {
|
|
36427
36745
|
failCommand(
|
|
36428
36746
|
new CliError(
|
|
36429
36747
|
"cli_usage",
|
|
@@ -36431,6 +36749,13 @@ async function resolveToken(input) {
|
|
|
36431
36749
|
)
|
|
36432
36750
|
);
|
|
36433
36751
|
}
|
|
36752
|
+
if (input.source === "stdin") {
|
|
36753
|
+
const piped = (await readPipedStdin()).trim();
|
|
36754
|
+
if (piped.length === 0) {
|
|
36755
|
+
failCommand(new CliError("cli_usage", "No token supplied, and there is no terminal to ask in.", NO_TOKEN_HINTS));
|
|
36756
|
+
}
|
|
36757
|
+
return piped;
|
|
36758
|
+
}
|
|
36434
36759
|
const { token } = await esm_default12.prompt([
|
|
36435
36760
|
{
|
|
36436
36761
|
type: "password",
|
|
@@ -36467,12 +36792,19 @@ var logoutCommand = new Command("logout").description("Delete the saved logins o
|
|
|
36467
36792
|
});
|
|
36468
36793
|
|
|
36469
36794
|
// src/commands/whoami.ts
|
|
36795
|
+
function roleName(role) {
|
|
36796
|
+
if (typeof role === "string") return role;
|
|
36797
|
+
if (typeof role === "object" && role !== null && "name" in role && typeof role.name === "string") {
|
|
36798
|
+
return role.name;
|
|
36799
|
+
}
|
|
36800
|
+
return "unknown";
|
|
36801
|
+
}
|
|
36470
36802
|
function buildWhoamiRows(verified, session, configPath) {
|
|
36471
36803
|
return [
|
|
36472
36804
|
{ label: "Email", value: verified.email },
|
|
36473
36805
|
{ label: "Organization", value: verified.orgName },
|
|
36474
36806
|
{ label: "Org ID", value: verified.orgId },
|
|
36475
|
-
{ label: "Role", value: verified.role },
|
|
36807
|
+
{ label: "Role", value: roleName(verified.role) },
|
|
36476
36808
|
{ label: "API URL", value: session.apiBaseUrl },
|
|
36477
36809
|
{ label: "Token from", value: describeTokenSource(session, configPath) }
|
|
36478
36810
|
];
|
|
@@ -36498,7 +36830,7 @@ var whoamiCommand = withOutputOptions(
|
|
|
36498
36830
|
email: verified.data.email,
|
|
36499
36831
|
orgId: verified.data.orgId,
|
|
36500
36832
|
orgName: verified.data.orgName,
|
|
36501
|
-
role: verified.data.role,
|
|
36833
|
+
role: roleName(verified.data.role),
|
|
36502
36834
|
apiBaseUrl: session.apiBaseUrl,
|
|
36503
36835
|
tokenSource: session.tokenSource,
|
|
36504
36836
|
configPath: getConfigPath()
|
|
@@ -36508,7 +36840,7 @@ var whoamiCommand = withOutputOptions(
|
|
|
36508
36840
|
line();
|
|
36509
36841
|
heading("Signed in");
|
|
36510
36842
|
for (const row of buildWhoamiRows(verified.data, session, getConfigPath())) {
|
|
36511
|
-
|
|
36843
|
+
field(row.label, row.value);
|
|
36512
36844
|
}
|
|
36513
36845
|
line();
|
|
36514
36846
|
});
|
|
@@ -36525,12 +36857,14 @@ function configForSession(session) {
|
|
|
36525
36857
|
apiBaseUrl: session.apiBaseUrl
|
|
36526
36858
|
};
|
|
36527
36859
|
}
|
|
36860
|
+
function describeCandidates(candidates) {
|
|
36861
|
+
return candidates.map((c) => c.shortId != null ? `${c.shortId} (${c.name})` : c.name).join(", ");
|
|
36862
|
+
}
|
|
36528
36863
|
function describeSwitchFailure(candidates, needle) {
|
|
36529
36864
|
if (candidates.length === 0) {
|
|
36530
36865
|
return `No organization matches "${needle}". This token cannot reach any organization.`;
|
|
36531
36866
|
}
|
|
36532
|
-
|
|
36533
|
-
return `No organization matches "${needle}". Available: ${known}.`;
|
|
36867
|
+
return `No organization matches "${needle}". Available: ${describeCandidates(candidates)}.`;
|
|
36534
36868
|
}
|
|
36535
36869
|
var orgCommand = withOutputOptions(
|
|
36536
36870
|
new Command("org").description("Show, list, and switch the active organization")
|
|
@@ -36548,13 +36882,13 @@ var orgCommand = withOutputOptions(
|
|
|
36548
36882
|
}
|
|
36549
36883
|
heading("Current organization");
|
|
36550
36884
|
if (session.orgName) {
|
|
36551
|
-
|
|
36552
|
-
|
|
36885
|
+
field("Name", session.orgName);
|
|
36886
|
+
field("ID", session.orgId);
|
|
36553
36887
|
} else {
|
|
36554
|
-
|
|
36888
|
+
field("Name", "not saved on this machine");
|
|
36555
36889
|
}
|
|
36556
|
-
|
|
36557
|
-
|
|
36890
|
+
field("API URL", session.apiBaseUrl);
|
|
36891
|
+
field("Token from", describeTokenSource(session, getConfigPath()));
|
|
36558
36892
|
if (session.tokenSource === "env") {
|
|
36559
36893
|
dim(
|
|
36560
36894
|
` ${TOKEN_ENV_VAR} is set, so commands use that token. Run \`avsb whoami\` to see which account and organization it reaches.`
|
|
@@ -36564,8 +36898,8 @@ var orgCommand = withOutputOptions(
|
|
|
36564
36898
|
orgCommand.addCommand(
|
|
36565
36899
|
withOutputOptions(
|
|
36566
36900
|
new Command("list").description("List the organizations this token can reach")
|
|
36567
|
-
).action(async (
|
|
36568
|
-
beginCommand("org list",
|
|
36901
|
+
).action(async (_opts, command) => {
|
|
36902
|
+
beginCommand("org list", command.optsWithGlobals());
|
|
36569
36903
|
try {
|
|
36570
36904
|
const session = ensureLoggedIn();
|
|
36571
36905
|
const config = configForSession(session);
|
|
@@ -36629,12 +36963,16 @@ orgCommand.command("switch").description("Choose which organization commands use
|
|
|
36629
36963
|
failCommand(err);
|
|
36630
36964
|
}
|
|
36631
36965
|
});
|
|
36632
|
-
async function promptForOrg(candidates,
|
|
36633
|
-
|
|
36634
|
-
|
|
36635
|
-
|
|
36636
|
-
|
|
36637
|
-
|
|
36966
|
+
async function promptForOrg(candidates, input) {
|
|
36967
|
+
const refusal = refusalWithoutPrompt(
|
|
36968
|
+
{
|
|
36969
|
+
code: "cli_usage",
|
|
36970
|
+
message: "Name an organization: `avsb org switch <short id | id | name>`.",
|
|
36971
|
+
hints: [`Available: ${describeCandidates(candidates)}.`]
|
|
36972
|
+
},
|
|
36973
|
+
currentPromptContext({ input })
|
|
36974
|
+
);
|
|
36975
|
+
if (refusal) failCommand(refusal);
|
|
36638
36976
|
const { chosen } = await esm_default12.prompt([
|
|
36639
36977
|
{
|
|
36640
36978
|
type: "list",
|
|
@@ -36750,6 +37088,15 @@ var pullCommand = new Command("pull").description("Pull latest experiment data f
|
|
|
36750
37088
|
dim(` ${file}`);
|
|
36751
37089
|
}
|
|
36752
37090
|
line();
|
|
37091
|
+
const refusal = refusalWithoutPrompt({
|
|
37092
|
+
code: "cli_conflict",
|
|
37093
|
+
message: "Pulling would overwrite these local changes, and this run cannot ask first.",
|
|
37094
|
+
hints: [
|
|
37095
|
+
"Run `avsb pull --force` to overwrite them,",
|
|
37096
|
+
"or `avsb push` first to keep them on the platform."
|
|
37097
|
+
]
|
|
37098
|
+
});
|
|
37099
|
+
if (refusal) failCommand(refusal);
|
|
36753
37100
|
const { proceed } = await esm_default12.prompt([
|
|
36754
37101
|
{
|
|
36755
37102
|
type: "confirm",
|
|
@@ -36785,14 +37132,16 @@ var pullCommand = new Command("pull").description("Pull latest experiment data f
|
|
|
36785
37132
|
|
|
36786
37133
|
// src/commands/push.ts
|
|
36787
37134
|
import { createHash as createHash3 } from "node:crypto";
|
|
36788
|
-
|
|
36789
|
-
|
|
37135
|
+
|
|
37136
|
+
// src/commands/push.payload.ts
|
|
37137
|
+
import fs11 from "node:fs";
|
|
37138
|
+
import path13 from "node:path";
|
|
36790
37139
|
|
|
36791
37140
|
// src/lib/compiler.ts
|
|
36792
37141
|
import { transform } from "esbuild";
|
|
36793
37142
|
import * as sass from "sass";
|
|
36794
|
-
import
|
|
36795
|
-
import
|
|
37143
|
+
import fs10 from "node:fs";
|
|
37144
|
+
import path12 from "node:path";
|
|
36796
37145
|
async function compileTypeScript(source) {
|
|
36797
37146
|
const result = await transform(source, {
|
|
36798
37147
|
loader: "ts",
|
|
@@ -36803,7 +37152,7 @@ async function compileTypeScript(source) {
|
|
|
36803
37152
|
function compileScss(filePath) {
|
|
36804
37153
|
const result = sass.compile(filePath, {
|
|
36805
37154
|
style: "expanded",
|
|
36806
|
-
loadPaths: [
|
|
37155
|
+
loadPaths: [path12.dirname(filePath)]
|
|
36807
37156
|
});
|
|
36808
37157
|
return result.css;
|
|
36809
37158
|
}
|
|
@@ -36879,8 +37228,8 @@ function parseEsbuildErrors(err, fallbackFile) {
|
|
|
36879
37228
|
];
|
|
36880
37229
|
}
|
|
36881
37230
|
async function compileFile(filePath) {
|
|
36882
|
-
const ext =
|
|
36883
|
-
const source =
|
|
37231
|
+
const ext = path12.extname(filePath);
|
|
37232
|
+
const source = fs10.readFileSync(filePath, "utf-8");
|
|
36884
37233
|
switch (ext) {
|
|
36885
37234
|
case ".ts":
|
|
36886
37235
|
return compileTypeScript(source);
|
|
@@ -36895,21 +37244,21 @@ async function compileFile(filePath) {
|
|
|
36895
37244
|
}
|
|
36896
37245
|
async function compileExperiment(dir, manifest) {
|
|
36897
37246
|
const { script } = manifest.languages;
|
|
36898
|
-
const triggerPath =
|
|
37247
|
+
const triggerPath = path12.join(dir, `trigger.${script}`);
|
|
36899
37248
|
let triggersJs = "";
|
|
36900
|
-
if (
|
|
37249
|
+
if (fs10.existsSync(triggerPath)) {
|
|
36901
37250
|
triggersJs = await compileFile(triggerPath);
|
|
36902
37251
|
}
|
|
36903
37252
|
const variations = [];
|
|
36904
37253
|
for (const v of manifest.variations) {
|
|
36905
|
-
const jsPath =
|
|
36906
|
-
const cssPath =
|
|
37254
|
+
const jsPath = path12.join(dir, v.folder, v.scriptFile);
|
|
37255
|
+
const cssPath = path12.join(dir, v.folder, v.styleFile);
|
|
36907
37256
|
let js = "";
|
|
36908
37257
|
let css = "";
|
|
36909
|
-
if (
|
|
37258
|
+
if (fs10.existsSync(jsPath)) {
|
|
36910
37259
|
js = await compileFile(jsPath);
|
|
36911
37260
|
}
|
|
36912
|
-
if (
|
|
37261
|
+
if (fs10.existsSync(cssPath)) {
|
|
36913
37262
|
css = await compileFile(cssPath);
|
|
36914
37263
|
}
|
|
36915
37264
|
variations.push({
|
|
@@ -36928,14 +37277,91 @@ async function compileExperiment(dir, manifest) {
|
|
|
36928
37277
|
};
|
|
36929
37278
|
}
|
|
36930
37279
|
function detectChangeType(filePath) {
|
|
36931
|
-
const basename3 =
|
|
36932
|
-
const ext =
|
|
37280
|
+
const basename3 = path12.basename(filePath, path12.extname(filePath));
|
|
37281
|
+
const ext = path12.extname(filePath);
|
|
36933
37282
|
if (basename3 === "trigger") return "trigger";
|
|
36934
37283
|
if (ext === ".scss" || ext === ".css") return "css-only";
|
|
36935
37284
|
if (ext === ".ts" || ext === ".js") return "js-only";
|
|
36936
37285
|
return "both";
|
|
36937
37286
|
}
|
|
36938
37287
|
|
|
37288
|
+
// src/commands/push.payload.ts
|
|
37289
|
+
function triggerFile(manifest) {
|
|
37290
|
+
return `trigger.${manifest.languages.script}`;
|
|
37291
|
+
}
|
|
37292
|
+
function variationFile(manifest, index, kind) {
|
|
37293
|
+
const variation = manifest.variations[index];
|
|
37294
|
+
if (!variation) return void 0;
|
|
37295
|
+
return path13.posix.join(variation.folder, kind === "script" ? variation.scriptFile : variation.styleFile);
|
|
37296
|
+
}
|
|
37297
|
+
function labelForPushPath(manifest, bodyPath) {
|
|
37298
|
+
const [head, index, field2] = bodyPath;
|
|
37299
|
+
if (head === "triggersJs") return triggerFile(manifest);
|
|
37300
|
+
if (head !== "variations" || typeof index !== "number") return void 0;
|
|
37301
|
+
if (field2 === "controlJs") return variationFile(manifest, index, "script");
|
|
37302
|
+
if (field2 === "controlCss") return variationFile(manifest, index, "style");
|
|
37303
|
+
return manifest.variations[index]?.folder;
|
|
37304
|
+
}
|
|
37305
|
+
function readSource(dir, relativePath) {
|
|
37306
|
+
const absolute = path13.join(dir, relativePath);
|
|
37307
|
+
if (!fs11.existsSync(absolute)) {
|
|
37308
|
+
return {
|
|
37309
|
+
missing: {
|
|
37310
|
+
file: relativePath,
|
|
37311
|
+
line: 0,
|
|
37312
|
+
column: 0,
|
|
37313
|
+
message: "File not found. Restore it, or run `avsb pull --force` to get the platform copy back."
|
|
37314
|
+
}
|
|
37315
|
+
};
|
|
37316
|
+
}
|
|
37317
|
+
return { source: fs11.readFileSync(absolute, "utf-8") };
|
|
37318
|
+
}
|
|
37319
|
+
async function buildPushPayload(dir, manifest) {
|
|
37320
|
+
const scriptLang = manifest.languages.script === "ts" ? "TYPESCRIPT" : "JAVASCRIPT";
|
|
37321
|
+
const styleLang = manifest.languages.style === "scss" ? "SCSS" : "CSS";
|
|
37322
|
+
const triggerPath = triggerFile(manifest);
|
|
37323
|
+
const trigger = readSource(dir, triggerPath);
|
|
37324
|
+
if ("missing" in trigger) return { ok: false, summary: `${triggerPath} is missing`, errors: [trigger.missing] };
|
|
37325
|
+
const triggerCompile = await compileScript(trigger.source, scriptLang, triggerPath);
|
|
37326
|
+
if (!triggerCompile.success) {
|
|
37327
|
+
return { ok: false, summary: "Trigger compilation failed", errors: triggerCompile.errors };
|
|
37328
|
+
}
|
|
37329
|
+
const payload = {
|
|
37330
|
+
triggersJs: { source: trigger.source, compiled: triggerCompile.code },
|
|
37331
|
+
variations: []
|
|
37332
|
+
};
|
|
37333
|
+
const sources = { triggersJs: trigger.source, variations: [] };
|
|
37334
|
+
for (const [index, variation] of manifest.variations.entries()) {
|
|
37335
|
+
const scriptPath = variationFile(manifest, index, "script") ?? variation.scriptFile;
|
|
37336
|
+
const stylePath = variationFile(manifest, index, "style") ?? variation.styleFile;
|
|
37337
|
+
const script = readSource(dir, scriptPath);
|
|
37338
|
+
if ("missing" in script) return { ok: false, summary: `${scriptPath} is missing`, errors: [script.missing] };
|
|
37339
|
+
const style = readSource(dir, stylePath);
|
|
37340
|
+
if ("missing" in style) return { ok: false, summary: `${stylePath} is missing`, errors: [style.missing] };
|
|
37341
|
+
const jsCompile = await compileScript(script.source, scriptLang, scriptPath);
|
|
37342
|
+
if (!jsCompile.success) {
|
|
37343
|
+
return { ok: false, summary: `Variation "${variation.name}" script compilation failed`, errors: jsCompile.errors };
|
|
37344
|
+
}
|
|
37345
|
+
const cssCompile = compileStyle(style.source, styleLang, stylePath);
|
|
37346
|
+
if (!cssCompile.success) {
|
|
37347
|
+
return { ok: false, summary: `Variation "${variation.name}" style compilation failed`, errors: cssCompile.errors };
|
|
37348
|
+
}
|
|
37349
|
+
payload.variations.push({
|
|
37350
|
+
id: variation.id,
|
|
37351
|
+
controlJs: { source: script.source, compiled: jsCompile.code },
|
|
37352
|
+
controlCss: { source: style.source, compiled: cssCompile.code }
|
|
37353
|
+
});
|
|
37354
|
+
sources.variations.push({ id: variation.id, controlJs: script.source, controlCss: style.source });
|
|
37355
|
+
}
|
|
37356
|
+
return { ok: true, payload, sources };
|
|
37357
|
+
}
|
|
37358
|
+
function publishPromptFor(status) {
|
|
37359
|
+
if (status === "PAUSED") {
|
|
37360
|
+
return "The experiment is paused. Publish the changes now? (Visitors see them when it resumes.)";
|
|
37361
|
+
}
|
|
37362
|
+
return "The experiment is running. Publish the changes now?";
|
|
37363
|
+
}
|
|
37364
|
+
|
|
36939
37365
|
// src/commands/push.ts
|
|
36940
37366
|
function sha2563(content) {
|
|
36941
37367
|
return createHash3("sha256").update(content).digest("hex");
|
|
@@ -36960,35 +37386,60 @@ function detectRemoteChanges(remoteExp, syncedHashes) {
|
|
|
36960
37386
|
}
|
|
36961
37387
|
return changed;
|
|
36962
37388
|
}
|
|
36963
|
-
|
|
37389
|
+
async function confirmOverwrite() {
|
|
37390
|
+
if (!isInteractiveRun()) {
|
|
37391
|
+
failCommand(
|
|
37392
|
+
new CliError(
|
|
37393
|
+
"cli_conflict",
|
|
37394
|
+
"The platform copy changed since your last pull, and this run cannot ask before overwriting it.",
|
|
37395
|
+
[
|
|
37396
|
+
"Run `avsb pull` to fetch the remote changes first (it overwrites your local files),",
|
|
37397
|
+
"or `avsb push --force` to overwrite the remote changes."
|
|
37398
|
+
]
|
|
37399
|
+
)
|
|
37400
|
+
);
|
|
37401
|
+
}
|
|
37402
|
+
const { proceed } = await esm_default12.prompt([
|
|
37403
|
+
{ type: "confirm", name: "proceed", message: "Pushing will overwrite remote changes. Continue?", default: false }
|
|
37404
|
+
]);
|
|
37405
|
+
return proceed;
|
|
37406
|
+
}
|
|
37407
|
+
async function shouldPublish(options, status) {
|
|
37408
|
+
if (options.publish !== void 0) return options.publish;
|
|
37409
|
+
if (!isInteractiveRun()) return false;
|
|
37410
|
+
const { publish } = await esm_default12.prompt([
|
|
37411
|
+
{ type: "confirm", name: "publish", message: publishPromptFor(status), default: false }
|
|
37412
|
+
]);
|
|
37413
|
+
return publish;
|
|
37414
|
+
}
|
|
37415
|
+
var pushCommand = new Command("push").description("Compile and push local changes to the platform").option("-f, --force", "Skip conflict check and push without confirmation").option("--publish", "Publish the changes right away when the experiment is running or paused").option("--no-publish", "Leave the changes pending without asking").action(async (options) => {
|
|
37416
|
+
let manifest = null;
|
|
36964
37417
|
try {
|
|
36965
37418
|
const config = ensureLoggedIn();
|
|
36966
|
-
const
|
|
37419
|
+
const read = readManifestOrExit();
|
|
37420
|
+
manifest = read.manifest;
|
|
37421
|
+
const { dir } = read;
|
|
36967
37422
|
let spinner = ora("Compiling experiment...").start();
|
|
36968
|
-
await
|
|
37423
|
+
const built = await buildPushPayload(dir, manifest);
|
|
37424
|
+
if (!built.ok) {
|
|
37425
|
+
spinner.fail(built.summary);
|
|
37426
|
+
for (const e of built.errors) {
|
|
37427
|
+
error(` ${e.line > 0 ? `${e.file}:${e.line}:${e.column}` : e.file}: ${e.message}`);
|
|
37428
|
+
}
|
|
37429
|
+
process.exit(EXIT.failure);
|
|
37430
|
+
}
|
|
36969
37431
|
const client = new ApiClient(config);
|
|
36970
37432
|
if (!options.force && manifest.syncedHashes) {
|
|
36971
37433
|
spinner.text = "Checking for remote changes...";
|
|
36972
37434
|
const remote = await client.getExperiment(manifest.experimentShortId);
|
|
36973
|
-
const
|
|
36974
|
-
const remoteChanges = detectRemoteChanges(remoteExp, manifest.syncedHashes);
|
|
37435
|
+
const remoteChanges = detectRemoteChanges(remote.data.experiment, manifest.syncedHashes);
|
|
36975
37436
|
if (remoteChanges.length > 0) {
|
|
36976
37437
|
spinner.warn("Remote has changes you haven't pulled");
|
|
36977
37438
|
line();
|
|
36978
37439
|
heading("Files changed on remote:");
|
|
36979
|
-
for (const file of remoteChanges) {
|
|
36980
|
-
dim(` ${file}`);
|
|
36981
|
-
}
|
|
37440
|
+
for (const file of remoteChanges) dim(` ${file}`);
|
|
36982
37441
|
line();
|
|
36983
|
-
|
|
36984
|
-
{
|
|
36985
|
-
type: "confirm",
|
|
36986
|
-
name: "proceed",
|
|
36987
|
-
message: "Pushing will overwrite remote changes. Continue?",
|
|
36988
|
-
default: false
|
|
36989
|
-
}
|
|
36990
|
-
]);
|
|
36991
|
-
if (!proceed) {
|
|
37442
|
+
if (!await confirmOverwrite()) {
|
|
36992
37443
|
info("Push aborted. Run `avsb pull` to fetch remote changes first.");
|
|
36993
37444
|
warn("Note: pulling will overwrite your local files.");
|
|
36994
37445
|
process.exit(EXIT.success);
|
|
@@ -37000,78 +37451,37 @@ var pushCommand = new Command("push").description("Compile and push local change
|
|
|
37000
37451
|
} else {
|
|
37001
37452
|
spinner.text = "Pushing to platform...";
|
|
37002
37453
|
}
|
|
37003
|
-
|
|
37004
|
-
|
|
37005
|
-
|
|
37006
|
-
|
|
37007
|
-
"
|
|
37008
|
-
|
|
37009
|
-
const triggerCompile = await compileScript(rawTrigger, scriptLang, `trigger.${manifest.languages.script}`);
|
|
37010
|
-
if (!triggerCompile.success) {
|
|
37011
|
-
spinner.fail("Trigger compilation failed");
|
|
37012
|
-
for (const e of triggerCompile.errors) {
|
|
37013
|
-
error(` trigger.${manifest.languages.script}:${e.line}:${e.column}: ${e.message}`);
|
|
37014
|
-
}
|
|
37015
|
-
process.exit(EXIT.failure);
|
|
37016
|
-
}
|
|
37017
|
-
const rawVariations = [];
|
|
37018
|
-
const compiledVariations = [];
|
|
37019
|
-
for (const v of manifest.variations) {
|
|
37020
|
-
const rawJs = fs10.readFileSync(path12.join(dir, v.folder, v.scriptFile), "utf-8");
|
|
37021
|
-
const rawCss = fs10.readFileSync(path12.join(dir, v.folder, v.styleFile), "utf-8");
|
|
37022
|
-
const jsCompile = await compileScript(rawJs, scriptLang, `${v.name}.${manifest.languages.script}`);
|
|
37023
|
-
const cssCompile = compileStyle(rawCss, styleLang, `${v.name}.${manifest.languages.style}`);
|
|
37024
|
-
if (!jsCompile.success) {
|
|
37025
|
-
spinner.fail(`Variation "${v.name}" JS compilation failed`);
|
|
37026
|
-
for (const e of jsCompile.errors) error(` ${e.file}:${e.line}:${e.column}: ${e.message}`);
|
|
37027
|
-
process.exit(EXIT.failure);
|
|
37028
|
-
}
|
|
37029
|
-
if (!cssCompile.success) {
|
|
37030
|
-
spinner.fail(`Variation "${v.name}" CSS compilation failed`);
|
|
37031
|
-
for (const e of cssCompile.errors) error(` ${e.file}:${e.line}:${e.column}: ${e.message}`);
|
|
37032
|
-
process.exit(EXIT.failure);
|
|
37033
|
-
}
|
|
37034
|
-
rawVariations.push({ id: v.id, controlJs: rawJs, controlCss: rawCss });
|
|
37035
|
-
compiledVariations.push({
|
|
37036
|
-
id: v.id,
|
|
37037
|
-
controlJs: { source: rawJs, compiled: jsCompile.code },
|
|
37038
|
-
controlCss: { source: rawCss, compiled: cssCompile.code }
|
|
37039
|
-
});
|
|
37454
|
+
let response;
|
|
37455
|
+
try {
|
|
37456
|
+
response = await client.pushExperiment(manifest.experimentShortId, built.payload);
|
|
37457
|
+
} catch (err) {
|
|
37458
|
+
spinner.fail("Push refused");
|
|
37459
|
+
throw err;
|
|
37040
37460
|
}
|
|
37041
|
-
|
|
37042
|
-
triggersJs: { source: rawTrigger, compiled: triggerCompile.code },
|
|
37043
|
-
variations: compiledVariations
|
|
37044
|
-
});
|
|
37045
|
-
manifest.syncedHashes = computeSyncHashes(rawTrigger, rawVariations);
|
|
37461
|
+
manifest.syncedHashes = computeSyncHashes(built.sources.triggersJs, built.sources.variations);
|
|
37046
37462
|
writeManifest(dir, manifest);
|
|
37047
37463
|
spinner.succeed("Pushed successfully");
|
|
37048
37464
|
line();
|
|
37049
37465
|
if (response.data.hasPendingChanges) {
|
|
37050
|
-
|
|
37051
|
-
{
|
|
37052
|
-
type: "confirm",
|
|
37053
|
-
name: "publish",
|
|
37054
|
-
message: "Experiment is running. Publish changes now?",
|
|
37055
|
-
default: false
|
|
37056
|
-
}
|
|
37057
|
-
]);
|
|
37058
|
-
if (publish) {
|
|
37466
|
+
if (await shouldPublish(options, response.data.status)) {
|
|
37059
37467
|
const publishSpinner = ora("Publishing changes...").start();
|
|
37060
|
-
await client.publishChanges(manifest.experimentShortId);
|
|
37468
|
+
const published = await client.publishChanges(manifest.experimentShortId);
|
|
37061
37469
|
publishSpinner.succeed("Changes published");
|
|
37470
|
+
if (published.data.warning) warn(published.data.warning.message);
|
|
37062
37471
|
} else {
|
|
37063
|
-
info("Changes saved as pending. Publish from the
|
|
37472
|
+
info("Changes saved as pending. Publish them with `avsb push --publish`, or from the dashboard.");
|
|
37064
37473
|
}
|
|
37065
37474
|
}
|
|
37066
37475
|
line();
|
|
37067
37476
|
success("Push complete");
|
|
37068
37477
|
} catch (err) {
|
|
37069
|
-
|
|
37478
|
+
const current = manifest;
|
|
37479
|
+
failCommand(err, current ? { labelForPath: (bodyPath) => labelForPushPath(current, bodyPath) } : {});
|
|
37070
37480
|
}
|
|
37071
37481
|
});
|
|
37072
37482
|
|
|
37073
37483
|
// src/commands/dev.ts
|
|
37074
|
-
import
|
|
37484
|
+
import path15 from "node:path";
|
|
37075
37485
|
|
|
37076
37486
|
// node_modules/chokidar/esm/index.js
|
|
37077
37487
|
import { stat as statcb } from "fs";
|
|
@@ -37149,7 +37559,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
37149
37559
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
37150
37560
|
const statMethod = opts.lstat ? lstat : stat;
|
|
37151
37561
|
if (wantBigintFsStats) {
|
|
37152
|
-
this._stat = (
|
|
37562
|
+
this._stat = (path23) => statMethod(path23, { bigint: true });
|
|
37153
37563
|
} else {
|
|
37154
37564
|
this._stat = statMethod;
|
|
37155
37565
|
}
|
|
@@ -37174,8 +37584,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
37174
37584
|
const par = this.parent;
|
|
37175
37585
|
const fil = par && par.files;
|
|
37176
37586
|
if (fil && fil.length > 0) {
|
|
37177
|
-
const { path:
|
|
37178
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
37587
|
+
const { path: path23, depth } = par;
|
|
37588
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path23));
|
|
37179
37589
|
const awaited = await Promise.all(slice);
|
|
37180
37590
|
for (const entry of awaited) {
|
|
37181
37591
|
if (!entry)
|
|
@@ -37215,20 +37625,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
37215
37625
|
this.reading = false;
|
|
37216
37626
|
}
|
|
37217
37627
|
}
|
|
37218
|
-
async _exploreDir(
|
|
37628
|
+
async _exploreDir(path23, depth) {
|
|
37219
37629
|
let files;
|
|
37220
37630
|
try {
|
|
37221
|
-
files = await readdir(
|
|
37631
|
+
files = await readdir(path23, this._rdOptions);
|
|
37222
37632
|
} catch (error2) {
|
|
37223
37633
|
this._onError(error2);
|
|
37224
37634
|
}
|
|
37225
|
-
return { files, depth, path:
|
|
37635
|
+
return { files, depth, path: path23 };
|
|
37226
37636
|
}
|
|
37227
|
-
async _formatEntry(dirent,
|
|
37637
|
+
async _formatEntry(dirent, path23) {
|
|
37228
37638
|
let entry;
|
|
37229
37639
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
37230
37640
|
try {
|
|
37231
|
-
const fullPath = presolve(pjoin(
|
|
37641
|
+
const fullPath = presolve(pjoin(path23, basename3));
|
|
37232
37642
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
37233
37643
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
37234
37644
|
} catch (err) {
|
|
@@ -37628,16 +38038,16 @@ var delFromSet = (main3, prop, item) => {
|
|
|
37628
38038
|
};
|
|
37629
38039
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
37630
38040
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
37631
|
-
function createFsWatchInstance(
|
|
38041
|
+
function createFsWatchInstance(path23, options, listener, errHandler, emitRaw) {
|
|
37632
38042
|
const handleEvent = (rawEvent, evPath) => {
|
|
37633
|
-
listener(
|
|
37634
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
37635
|
-
if (evPath &&
|
|
37636
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
38043
|
+
listener(path23);
|
|
38044
|
+
emitRaw(rawEvent, evPath, { watchedPath: path23 });
|
|
38045
|
+
if (evPath && path23 !== evPath) {
|
|
38046
|
+
fsWatchBroadcast(sysPath.resolve(path23, evPath), KEY_LISTENERS, sysPath.join(path23, evPath));
|
|
37637
38047
|
}
|
|
37638
38048
|
};
|
|
37639
38049
|
try {
|
|
37640
|
-
return fs_watch(
|
|
38050
|
+
return fs_watch(path23, {
|
|
37641
38051
|
persistent: options.persistent
|
|
37642
38052
|
}, handleEvent);
|
|
37643
38053
|
} catch (error2) {
|
|
@@ -37653,12 +38063,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
37653
38063
|
listener(val1, val2, val3);
|
|
37654
38064
|
});
|
|
37655
38065
|
};
|
|
37656
|
-
var setFsWatchListener = (
|
|
38066
|
+
var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
37657
38067
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
37658
38068
|
let cont = FsWatchInstances.get(fullPath);
|
|
37659
38069
|
let watcher;
|
|
37660
38070
|
if (!options.persistent) {
|
|
37661
|
-
watcher = createFsWatchInstance(
|
|
38071
|
+
watcher = createFsWatchInstance(path23, options, listener, errHandler, rawEmitter);
|
|
37662
38072
|
if (!watcher)
|
|
37663
38073
|
return;
|
|
37664
38074
|
return watcher.close.bind(watcher);
|
|
@@ -37669,7 +38079,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
37669
38079
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
37670
38080
|
} else {
|
|
37671
38081
|
watcher = createFsWatchInstance(
|
|
37672
|
-
|
|
38082
|
+
path23,
|
|
37673
38083
|
options,
|
|
37674
38084
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
37675
38085
|
errHandler,
|
|
@@ -37684,7 +38094,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
37684
38094
|
cont.watcherUnusable = true;
|
|
37685
38095
|
if (isWindows && error2.code === "EPERM") {
|
|
37686
38096
|
try {
|
|
37687
|
-
const fd = await open(
|
|
38097
|
+
const fd = await open(path23, "r");
|
|
37688
38098
|
await fd.close();
|
|
37689
38099
|
broadcastErr(error2);
|
|
37690
38100
|
} catch (err) {
|
|
@@ -37715,7 +38125,7 @@ var setFsWatchListener = (path22, fullPath, options, handlers) => {
|
|
|
37715
38125
|
};
|
|
37716
38126
|
};
|
|
37717
38127
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
37718
|
-
var setFsWatchFileListener = (
|
|
38128
|
+
var setFsWatchFileListener = (path23, fullPath, options, handlers) => {
|
|
37719
38129
|
const { listener, rawEmitter } = handlers;
|
|
37720
38130
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
37721
38131
|
const copts = cont && cont.options;
|
|
@@ -37737,7 +38147,7 @@ var setFsWatchFileListener = (path22, fullPath, options, handlers) => {
|
|
|
37737
38147
|
});
|
|
37738
38148
|
const currmtime = curr.mtimeMs;
|
|
37739
38149
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
37740
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
38150
|
+
foreach(cont.listeners, (listener2) => listener2(path23, curr));
|
|
37741
38151
|
}
|
|
37742
38152
|
})
|
|
37743
38153
|
};
|
|
@@ -37765,13 +38175,13 @@ var NodeFsHandler = class {
|
|
|
37765
38175
|
* @param listener on fs change
|
|
37766
38176
|
* @returns closer for the watcher instance
|
|
37767
38177
|
*/
|
|
37768
|
-
_watchWithNodeFs(
|
|
38178
|
+
_watchWithNodeFs(path23, listener) {
|
|
37769
38179
|
const opts = this.fsw.options;
|
|
37770
|
-
const directory = sysPath.dirname(
|
|
37771
|
-
const basename3 = sysPath.basename(
|
|
38180
|
+
const directory = sysPath.dirname(path23);
|
|
38181
|
+
const basename3 = sysPath.basename(path23);
|
|
37772
38182
|
const parent = this.fsw._getWatchedDir(directory);
|
|
37773
38183
|
parent.add(basename3);
|
|
37774
|
-
const absolutePath = sysPath.resolve(
|
|
38184
|
+
const absolutePath = sysPath.resolve(path23);
|
|
37775
38185
|
const options = {
|
|
37776
38186
|
persistent: opts.persistent
|
|
37777
38187
|
};
|
|
@@ -37781,12 +38191,12 @@ var NodeFsHandler = class {
|
|
|
37781
38191
|
if (opts.usePolling) {
|
|
37782
38192
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
37783
38193
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
37784
|
-
closer = setFsWatchFileListener(
|
|
38194
|
+
closer = setFsWatchFileListener(path23, absolutePath, options, {
|
|
37785
38195
|
listener,
|
|
37786
38196
|
rawEmitter: this.fsw._emitRaw
|
|
37787
38197
|
});
|
|
37788
38198
|
} else {
|
|
37789
|
-
closer = setFsWatchListener(
|
|
38199
|
+
closer = setFsWatchListener(path23, absolutePath, options, {
|
|
37790
38200
|
listener,
|
|
37791
38201
|
errHandler: this._boundHandleError,
|
|
37792
38202
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -37808,7 +38218,7 @@ var NodeFsHandler = class {
|
|
|
37808
38218
|
let prevStats = stats;
|
|
37809
38219
|
if (parent.has(basename3))
|
|
37810
38220
|
return;
|
|
37811
|
-
const listener = async (
|
|
38221
|
+
const listener = async (path23, newStats) => {
|
|
37812
38222
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
37813
38223
|
return;
|
|
37814
38224
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -37822,11 +38232,11 @@ var NodeFsHandler = class {
|
|
|
37822
38232
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
37823
38233
|
}
|
|
37824
38234
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
37825
|
-
this.fsw._closeFile(
|
|
38235
|
+
this.fsw._closeFile(path23);
|
|
37826
38236
|
prevStats = newStats2;
|
|
37827
38237
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
37828
38238
|
if (closer2)
|
|
37829
|
-
this.fsw._addPathCloser(
|
|
38239
|
+
this.fsw._addPathCloser(path23, closer2);
|
|
37830
38240
|
} else {
|
|
37831
38241
|
prevStats = newStats2;
|
|
37832
38242
|
}
|
|
@@ -37858,7 +38268,7 @@ var NodeFsHandler = class {
|
|
|
37858
38268
|
* @param item basename of this item
|
|
37859
38269
|
* @returns true if no more processing is needed for this entry.
|
|
37860
38270
|
*/
|
|
37861
|
-
async _handleSymlink(entry, directory,
|
|
38271
|
+
async _handleSymlink(entry, directory, path23, item) {
|
|
37862
38272
|
if (this.fsw.closed) {
|
|
37863
38273
|
return;
|
|
37864
38274
|
}
|
|
@@ -37868,7 +38278,7 @@ var NodeFsHandler = class {
|
|
|
37868
38278
|
this.fsw._incrReadyCount();
|
|
37869
38279
|
let linkPath;
|
|
37870
38280
|
try {
|
|
37871
|
-
linkPath = await fsrealpath(
|
|
38281
|
+
linkPath = await fsrealpath(path23);
|
|
37872
38282
|
} catch (e) {
|
|
37873
38283
|
this.fsw._emitReady();
|
|
37874
38284
|
return true;
|
|
@@ -37878,12 +38288,12 @@ var NodeFsHandler = class {
|
|
|
37878
38288
|
if (dir.has(item)) {
|
|
37879
38289
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
37880
38290
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
37881
|
-
this.fsw._emit(EV.CHANGE,
|
|
38291
|
+
this.fsw._emit(EV.CHANGE, path23, entry.stats);
|
|
37882
38292
|
}
|
|
37883
38293
|
} else {
|
|
37884
38294
|
dir.add(item);
|
|
37885
38295
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
37886
|
-
this.fsw._emit(EV.ADD,
|
|
38296
|
+
this.fsw._emit(EV.ADD, path23, entry.stats);
|
|
37887
38297
|
}
|
|
37888
38298
|
this.fsw._emitReady();
|
|
37889
38299
|
return true;
|
|
@@ -37912,9 +38322,9 @@ var NodeFsHandler = class {
|
|
|
37912
38322
|
return;
|
|
37913
38323
|
}
|
|
37914
38324
|
const item = entry.path;
|
|
37915
|
-
let
|
|
38325
|
+
let path23 = sysPath.join(directory, item);
|
|
37916
38326
|
current.add(item);
|
|
37917
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
38327
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path23, item)) {
|
|
37918
38328
|
return;
|
|
37919
38329
|
}
|
|
37920
38330
|
if (this.fsw.closed) {
|
|
@@ -37923,8 +38333,8 @@ var NodeFsHandler = class {
|
|
|
37923
38333
|
}
|
|
37924
38334
|
if (item === target || !target && !previous.has(item)) {
|
|
37925
38335
|
this.fsw._incrReadyCount();
|
|
37926
|
-
|
|
37927
|
-
this._addToNodeFs(
|
|
38336
|
+
path23 = sysPath.join(dir, sysPath.relative(dir, path23));
|
|
38337
|
+
this._addToNodeFs(path23, initialAdd, wh, depth + 1);
|
|
37928
38338
|
}
|
|
37929
38339
|
}).on(EV.ERROR, this._boundHandleError);
|
|
37930
38340
|
return new Promise((resolve3, reject) => {
|
|
@@ -37993,13 +38403,13 @@ var NodeFsHandler = class {
|
|
|
37993
38403
|
* @param depth Child path actually targeted for watch
|
|
37994
38404
|
* @param target Child path actually targeted for watch
|
|
37995
38405
|
*/
|
|
37996
|
-
async _addToNodeFs(
|
|
38406
|
+
async _addToNodeFs(path23, initialAdd, priorWh, depth, target) {
|
|
37997
38407
|
const ready = this.fsw._emitReady;
|
|
37998
|
-
if (this.fsw._isIgnored(
|
|
38408
|
+
if (this.fsw._isIgnored(path23) || this.fsw.closed) {
|
|
37999
38409
|
ready();
|
|
38000
38410
|
return false;
|
|
38001
38411
|
}
|
|
38002
|
-
const wh = this.fsw._getWatchHelpers(
|
|
38412
|
+
const wh = this.fsw._getWatchHelpers(path23);
|
|
38003
38413
|
if (priorWh) {
|
|
38004
38414
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
38005
38415
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -38015,8 +38425,8 @@ var NodeFsHandler = class {
|
|
|
38015
38425
|
const follow = this.fsw.options.followSymlinks;
|
|
38016
38426
|
let closer;
|
|
38017
38427
|
if (stats.isDirectory()) {
|
|
38018
|
-
const absPath = sysPath.resolve(
|
|
38019
|
-
const targetPath = follow ? await fsrealpath(
|
|
38428
|
+
const absPath = sysPath.resolve(path23);
|
|
38429
|
+
const targetPath = follow ? await fsrealpath(path23) : path23;
|
|
38020
38430
|
if (this.fsw.closed)
|
|
38021
38431
|
return;
|
|
38022
38432
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -38026,29 +38436,29 @@ var NodeFsHandler = class {
|
|
|
38026
38436
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
38027
38437
|
}
|
|
38028
38438
|
} else if (stats.isSymbolicLink()) {
|
|
38029
|
-
const targetPath = follow ? await fsrealpath(
|
|
38439
|
+
const targetPath = follow ? await fsrealpath(path23) : path23;
|
|
38030
38440
|
if (this.fsw.closed)
|
|
38031
38441
|
return;
|
|
38032
38442
|
const parent = sysPath.dirname(wh.watchPath);
|
|
38033
38443
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
38034
38444
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
38035
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
38445
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path23, wh, targetPath);
|
|
38036
38446
|
if (this.fsw.closed)
|
|
38037
38447
|
return;
|
|
38038
38448
|
if (targetPath !== void 0) {
|
|
38039
|
-
this.fsw._symlinkPaths.set(sysPath.resolve(
|
|
38449
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path23), targetPath);
|
|
38040
38450
|
}
|
|
38041
38451
|
} else {
|
|
38042
38452
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
38043
38453
|
}
|
|
38044
38454
|
ready();
|
|
38045
38455
|
if (closer)
|
|
38046
|
-
this.fsw._addPathCloser(
|
|
38456
|
+
this.fsw._addPathCloser(path23, closer);
|
|
38047
38457
|
return false;
|
|
38048
38458
|
} catch (error2) {
|
|
38049
38459
|
if (this.fsw._handleError(error2)) {
|
|
38050
38460
|
ready();
|
|
38051
|
-
return
|
|
38461
|
+
return path23;
|
|
38052
38462
|
}
|
|
38053
38463
|
}
|
|
38054
38464
|
}
|
|
@@ -38091,26 +38501,26 @@ function createPattern(matcher) {
|
|
|
38091
38501
|
}
|
|
38092
38502
|
return () => false;
|
|
38093
38503
|
}
|
|
38094
|
-
function normalizePath(
|
|
38095
|
-
if (typeof
|
|
38504
|
+
function normalizePath(path23) {
|
|
38505
|
+
if (typeof path23 !== "string")
|
|
38096
38506
|
throw new Error("string expected");
|
|
38097
|
-
|
|
38098
|
-
|
|
38507
|
+
path23 = sysPath2.normalize(path23);
|
|
38508
|
+
path23 = path23.replace(/\\/g, "/");
|
|
38099
38509
|
let prepend = false;
|
|
38100
|
-
if (
|
|
38510
|
+
if (path23.startsWith("//"))
|
|
38101
38511
|
prepend = true;
|
|
38102
38512
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
38103
|
-
while (
|
|
38104
|
-
|
|
38513
|
+
while (path23.match(DOUBLE_SLASH_RE2))
|
|
38514
|
+
path23 = path23.replace(DOUBLE_SLASH_RE2, "/");
|
|
38105
38515
|
if (prepend)
|
|
38106
|
-
|
|
38107
|
-
return
|
|
38516
|
+
path23 = "/" + path23;
|
|
38517
|
+
return path23;
|
|
38108
38518
|
}
|
|
38109
38519
|
function matchPatterns(patterns, testString, stats) {
|
|
38110
|
-
const
|
|
38520
|
+
const path23 = normalizePath(testString);
|
|
38111
38521
|
for (let index = 0; index < patterns.length; index++) {
|
|
38112
38522
|
const pattern = patterns[index];
|
|
38113
|
-
if (pattern(
|
|
38523
|
+
if (pattern(path23, stats)) {
|
|
38114
38524
|
return true;
|
|
38115
38525
|
}
|
|
38116
38526
|
}
|
|
@@ -38150,19 +38560,19 @@ var toUnix = (string) => {
|
|
|
38150
38560
|
}
|
|
38151
38561
|
return str;
|
|
38152
38562
|
};
|
|
38153
|
-
var normalizePathToUnix = (
|
|
38154
|
-
var normalizeIgnored = (cwd = "") => (
|
|
38155
|
-
if (typeof
|
|
38156
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
38563
|
+
var normalizePathToUnix = (path23) => toUnix(sysPath2.normalize(toUnix(path23)));
|
|
38564
|
+
var normalizeIgnored = (cwd = "") => (path23) => {
|
|
38565
|
+
if (typeof path23 === "string") {
|
|
38566
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path23) ? path23 : sysPath2.join(cwd, path23));
|
|
38157
38567
|
} else {
|
|
38158
|
-
return
|
|
38568
|
+
return path23;
|
|
38159
38569
|
}
|
|
38160
38570
|
};
|
|
38161
|
-
var getAbsolutePath = (
|
|
38162
|
-
if (sysPath2.isAbsolute(
|
|
38163
|
-
return
|
|
38571
|
+
var getAbsolutePath = (path23, cwd) => {
|
|
38572
|
+
if (sysPath2.isAbsolute(path23)) {
|
|
38573
|
+
return path23;
|
|
38164
38574
|
}
|
|
38165
|
-
return sysPath2.join(cwd,
|
|
38575
|
+
return sysPath2.join(cwd, path23);
|
|
38166
38576
|
};
|
|
38167
38577
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
38168
38578
|
var DirEntry = class {
|
|
@@ -38217,10 +38627,10 @@ var DirEntry = class {
|
|
|
38217
38627
|
var STAT_METHOD_F = "stat";
|
|
38218
38628
|
var STAT_METHOD_L = "lstat";
|
|
38219
38629
|
var WatchHelper = class {
|
|
38220
|
-
constructor(
|
|
38630
|
+
constructor(path23, follow, fsw) {
|
|
38221
38631
|
this.fsw = fsw;
|
|
38222
|
-
const watchPath =
|
|
38223
|
-
this.path =
|
|
38632
|
+
const watchPath = path23;
|
|
38633
|
+
this.path = path23 = path23.replace(REPLACER_RE, "");
|
|
38224
38634
|
this.watchPath = watchPath;
|
|
38225
38635
|
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
38226
38636
|
this.dirParts = [];
|
|
@@ -38342,20 +38752,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38342
38752
|
this._closePromise = void 0;
|
|
38343
38753
|
let paths = unifyPaths(paths_);
|
|
38344
38754
|
if (cwd) {
|
|
38345
|
-
paths = paths.map((
|
|
38346
|
-
const absPath = getAbsolutePath(
|
|
38755
|
+
paths = paths.map((path23) => {
|
|
38756
|
+
const absPath = getAbsolutePath(path23, cwd);
|
|
38347
38757
|
return absPath;
|
|
38348
38758
|
});
|
|
38349
38759
|
}
|
|
38350
|
-
paths.forEach((
|
|
38351
|
-
this._removeIgnoredPath(
|
|
38760
|
+
paths.forEach((path23) => {
|
|
38761
|
+
this._removeIgnoredPath(path23);
|
|
38352
38762
|
});
|
|
38353
38763
|
this._userIgnored = void 0;
|
|
38354
38764
|
if (!this._readyCount)
|
|
38355
38765
|
this._readyCount = 0;
|
|
38356
38766
|
this._readyCount += paths.length;
|
|
38357
|
-
Promise.all(paths.map(async (
|
|
38358
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
38767
|
+
Promise.all(paths.map(async (path23) => {
|
|
38768
|
+
const res = await this._nodeFsHandler._addToNodeFs(path23, !_internal, void 0, 0, _origAdd);
|
|
38359
38769
|
if (res)
|
|
38360
38770
|
this._emitReady();
|
|
38361
38771
|
return res;
|
|
@@ -38377,17 +38787,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38377
38787
|
return this;
|
|
38378
38788
|
const paths = unifyPaths(paths_);
|
|
38379
38789
|
const { cwd } = this.options;
|
|
38380
|
-
paths.forEach((
|
|
38381
|
-
if (!sysPath2.isAbsolute(
|
|
38790
|
+
paths.forEach((path23) => {
|
|
38791
|
+
if (!sysPath2.isAbsolute(path23) && !this._closers.has(path23)) {
|
|
38382
38792
|
if (cwd)
|
|
38383
|
-
|
|
38384
|
-
|
|
38793
|
+
path23 = sysPath2.join(cwd, path23);
|
|
38794
|
+
path23 = sysPath2.resolve(path23);
|
|
38385
38795
|
}
|
|
38386
|
-
this._closePath(
|
|
38387
|
-
this._addIgnoredPath(
|
|
38388
|
-
if (this._watched.has(
|
|
38796
|
+
this._closePath(path23);
|
|
38797
|
+
this._addIgnoredPath(path23);
|
|
38798
|
+
if (this._watched.has(path23)) {
|
|
38389
38799
|
this._addIgnoredPath({
|
|
38390
|
-
path:
|
|
38800
|
+
path: path23,
|
|
38391
38801
|
recursive: true
|
|
38392
38802
|
});
|
|
38393
38803
|
}
|
|
@@ -38451,38 +38861,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38451
38861
|
* @param stats arguments to be passed with event
|
|
38452
38862
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
38453
38863
|
*/
|
|
38454
|
-
async _emit(event,
|
|
38864
|
+
async _emit(event, path23, stats) {
|
|
38455
38865
|
if (this.closed)
|
|
38456
38866
|
return;
|
|
38457
38867
|
const opts = this.options;
|
|
38458
38868
|
if (isWindows)
|
|
38459
|
-
|
|
38869
|
+
path23 = sysPath2.normalize(path23);
|
|
38460
38870
|
if (opts.cwd)
|
|
38461
|
-
|
|
38462
|
-
const args = [
|
|
38871
|
+
path23 = sysPath2.relative(opts.cwd, path23);
|
|
38872
|
+
const args = [path23];
|
|
38463
38873
|
if (stats != null)
|
|
38464
38874
|
args.push(stats);
|
|
38465
38875
|
const awf = opts.awaitWriteFinish;
|
|
38466
38876
|
let pw;
|
|
38467
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
38877
|
+
if (awf && (pw = this._pendingWrites.get(path23))) {
|
|
38468
38878
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
38469
38879
|
return this;
|
|
38470
38880
|
}
|
|
38471
38881
|
if (opts.atomic) {
|
|
38472
38882
|
if (event === EVENTS.UNLINK) {
|
|
38473
|
-
this._pendingUnlinks.set(
|
|
38883
|
+
this._pendingUnlinks.set(path23, [event, ...args]);
|
|
38474
38884
|
setTimeout(() => {
|
|
38475
|
-
this._pendingUnlinks.forEach((entry,
|
|
38885
|
+
this._pendingUnlinks.forEach((entry, path24) => {
|
|
38476
38886
|
this.emit(...entry);
|
|
38477
38887
|
this.emit(EVENTS.ALL, ...entry);
|
|
38478
|
-
this._pendingUnlinks.delete(
|
|
38888
|
+
this._pendingUnlinks.delete(path24);
|
|
38479
38889
|
});
|
|
38480
38890
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
38481
38891
|
return this;
|
|
38482
38892
|
}
|
|
38483
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
38893
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path23)) {
|
|
38484
38894
|
event = EVENTS.CHANGE;
|
|
38485
|
-
this._pendingUnlinks.delete(
|
|
38895
|
+
this._pendingUnlinks.delete(path23);
|
|
38486
38896
|
}
|
|
38487
38897
|
}
|
|
38488
38898
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -38500,16 +38910,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38500
38910
|
this.emitWithAll(event, args);
|
|
38501
38911
|
}
|
|
38502
38912
|
};
|
|
38503
|
-
this._awaitWriteFinish(
|
|
38913
|
+
this._awaitWriteFinish(path23, awf.stabilityThreshold, event, awfEmit);
|
|
38504
38914
|
return this;
|
|
38505
38915
|
}
|
|
38506
38916
|
if (event === EVENTS.CHANGE) {
|
|
38507
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
38917
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path23, 50);
|
|
38508
38918
|
if (isThrottled)
|
|
38509
38919
|
return this;
|
|
38510
38920
|
}
|
|
38511
38921
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
38512
|
-
const fullPath = opts.cwd ? sysPath2.join(opts.cwd,
|
|
38922
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path23) : path23;
|
|
38513
38923
|
let stats2;
|
|
38514
38924
|
try {
|
|
38515
38925
|
stats2 = await stat3(fullPath);
|
|
@@ -38540,23 +38950,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38540
38950
|
* @param timeout duration of time to suppress duplicate actions
|
|
38541
38951
|
* @returns tracking object or false if action should be suppressed
|
|
38542
38952
|
*/
|
|
38543
|
-
_throttle(actionType,
|
|
38953
|
+
_throttle(actionType, path23, timeout) {
|
|
38544
38954
|
if (!this._throttled.has(actionType)) {
|
|
38545
38955
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
38546
38956
|
}
|
|
38547
38957
|
const action = this._throttled.get(actionType);
|
|
38548
38958
|
if (!action)
|
|
38549
38959
|
throw new Error("invalid throttle");
|
|
38550
|
-
const actionPath = action.get(
|
|
38960
|
+
const actionPath = action.get(path23);
|
|
38551
38961
|
if (actionPath) {
|
|
38552
38962
|
actionPath.count++;
|
|
38553
38963
|
return false;
|
|
38554
38964
|
}
|
|
38555
38965
|
let timeoutObject;
|
|
38556
38966
|
const clear = () => {
|
|
38557
|
-
const item = action.get(
|
|
38967
|
+
const item = action.get(path23);
|
|
38558
38968
|
const count = item ? item.count : 0;
|
|
38559
|
-
action.delete(
|
|
38969
|
+
action.delete(path23);
|
|
38560
38970
|
clearTimeout(timeoutObject);
|
|
38561
38971
|
if (item)
|
|
38562
38972
|
clearTimeout(item.timeoutObject);
|
|
@@ -38564,7 +38974,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38564
38974
|
};
|
|
38565
38975
|
timeoutObject = setTimeout(clear, timeout);
|
|
38566
38976
|
const thr = { timeoutObject, clear, count: 0 };
|
|
38567
|
-
action.set(
|
|
38977
|
+
action.set(path23, thr);
|
|
38568
38978
|
return thr;
|
|
38569
38979
|
}
|
|
38570
38980
|
_incrReadyCount() {
|
|
@@ -38578,44 +38988,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38578
38988
|
* @param event
|
|
38579
38989
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
38580
38990
|
*/
|
|
38581
|
-
_awaitWriteFinish(
|
|
38991
|
+
_awaitWriteFinish(path23, threshold, event, awfEmit) {
|
|
38582
38992
|
const awf = this.options.awaitWriteFinish;
|
|
38583
38993
|
if (typeof awf !== "object")
|
|
38584
38994
|
return;
|
|
38585
38995
|
const pollInterval = awf.pollInterval;
|
|
38586
38996
|
let timeoutHandler;
|
|
38587
|
-
let fullPath =
|
|
38588
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
38589
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
38997
|
+
let fullPath = path23;
|
|
38998
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path23)) {
|
|
38999
|
+
fullPath = sysPath2.join(this.options.cwd, path23);
|
|
38590
39000
|
}
|
|
38591
39001
|
const now = /* @__PURE__ */ new Date();
|
|
38592
39002
|
const writes = this._pendingWrites;
|
|
38593
39003
|
function awaitWriteFinishFn(prevStat) {
|
|
38594
39004
|
statcb(fullPath, (err, curStat) => {
|
|
38595
|
-
if (err || !writes.has(
|
|
39005
|
+
if (err || !writes.has(path23)) {
|
|
38596
39006
|
if (err && err.code !== "ENOENT")
|
|
38597
39007
|
awfEmit(err);
|
|
38598
39008
|
return;
|
|
38599
39009
|
}
|
|
38600
39010
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
38601
39011
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
38602
|
-
writes.get(
|
|
39012
|
+
writes.get(path23).lastChange = now2;
|
|
38603
39013
|
}
|
|
38604
|
-
const pw = writes.get(
|
|
39014
|
+
const pw = writes.get(path23);
|
|
38605
39015
|
const df = now2 - pw.lastChange;
|
|
38606
39016
|
if (df >= threshold) {
|
|
38607
|
-
writes.delete(
|
|
39017
|
+
writes.delete(path23);
|
|
38608
39018
|
awfEmit(void 0, curStat);
|
|
38609
39019
|
} else {
|
|
38610
39020
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
38611
39021
|
}
|
|
38612
39022
|
});
|
|
38613
39023
|
}
|
|
38614
|
-
if (!writes.has(
|
|
38615
|
-
writes.set(
|
|
39024
|
+
if (!writes.has(path23)) {
|
|
39025
|
+
writes.set(path23, {
|
|
38616
39026
|
lastChange: now,
|
|
38617
39027
|
cancelWait: () => {
|
|
38618
|
-
writes.delete(
|
|
39028
|
+
writes.delete(path23);
|
|
38619
39029
|
clearTimeout(timeoutHandler);
|
|
38620
39030
|
return event;
|
|
38621
39031
|
}
|
|
@@ -38626,8 +39036,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38626
39036
|
/**
|
|
38627
39037
|
* Determines whether user has asked to ignore this path.
|
|
38628
39038
|
*/
|
|
38629
|
-
_isIgnored(
|
|
38630
|
-
if (this.options.atomic && DOT_RE.test(
|
|
39039
|
+
_isIgnored(path23, stats) {
|
|
39040
|
+
if (this.options.atomic && DOT_RE.test(path23))
|
|
38631
39041
|
return true;
|
|
38632
39042
|
if (!this._userIgnored) {
|
|
38633
39043
|
const { cwd } = this.options;
|
|
@@ -38637,17 +39047,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38637
39047
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
38638
39048
|
this._userIgnored = anymatch(list, void 0);
|
|
38639
39049
|
}
|
|
38640
|
-
return this._userIgnored(
|
|
39050
|
+
return this._userIgnored(path23, stats);
|
|
38641
39051
|
}
|
|
38642
|
-
_isntIgnored(
|
|
38643
|
-
return !this._isIgnored(
|
|
39052
|
+
_isntIgnored(path23, stat4) {
|
|
39053
|
+
return !this._isIgnored(path23, stat4);
|
|
38644
39054
|
}
|
|
38645
39055
|
/**
|
|
38646
39056
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
38647
39057
|
* @param path file or directory pattern being watched
|
|
38648
39058
|
*/
|
|
38649
|
-
_getWatchHelpers(
|
|
38650
|
-
return new WatchHelper(
|
|
39059
|
+
_getWatchHelpers(path23) {
|
|
39060
|
+
return new WatchHelper(path23, this.options.followSymlinks, this);
|
|
38651
39061
|
}
|
|
38652
39062
|
// Directory helpers
|
|
38653
39063
|
// -----------------
|
|
@@ -38679,63 +39089,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
38679
39089
|
* @param item base path of item/directory
|
|
38680
39090
|
*/
|
|
38681
39091
|
_remove(directory, item, isDirectory) {
|
|
38682
|
-
const
|
|
38683
|
-
const fullPath = sysPath2.resolve(
|
|
38684
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
38685
|
-
if (!this._throttle("remove",
|
|
39092
|
+
const path23 = sysPath2.join(directory, item);
|
|
39093
|
+
const fullPath = sysPath2.resolve(path23);
|
|
39094
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path23) || this._watched.has(fullPath);
|
|
39095
|
+
if (!this._throttle("remove", path23, 100))
|
|
38686
39096
|
return;
|
|
38687
39097
|
if (!isDirectory && this._watched.size === 1) {
|
|
38688
39098
|
this.add(directory, item, true);
|
|
38689
39099
|
}
|
|
38690
|
-
const wp = this._getWatchedDir(
|
|
39100
|
+
const wp = this._getWatchedDir(path23);
|
|
38691
39101
|
const nestedDirectoryChildren = wp.getChildren();
|
|
38692
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
39102
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path23, nested));
|
|
38693
39103
|
const parent = this._getWatchedDir(directory);
|
|
38694
39104
|
const wasTracked = parent.has(item);
|
|
38695
39105
|
parent.remove(item);
|
|
38696
39106
|
if (this._symlinkPaths.has(fullPath)) {
|
|
38697
39107
|
this._symlinkPaths.delete(fullPath);
|
|
38698
39108
|
}
|
|
38699
|
-
let relPath =
|
|
39109
|
+
let relPath = path23;
|
|
38700
39110
|
if (this.options.cwd)
|
|
38701
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
39111
|
+
relPath = sysPath2.relative(this.options.cwd, path23);
|
|
38702
39112
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
38703
39113
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
38704
39114
|
if (event === EVENTS.ADD)
|
|
38705
39115
|
return;
|
|
38706
39116
|
}
|
|
38707
|
-
this._watched.delete(
|
|
39117
|
+
this._watched.delete(path23);
|
|
38708
39118
|
this._watched.delete(fullPath);
|
|
38709
39119
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
38710
|
-
if (wasTracked && !this._isIgnored(
|
|
38711
|
-
this._emit(eventName,
|
|
38712
|
-
this._closePath(
|
|
39120
|
+
if (wasTracked && !this._isIgnored(path23))
|
|
39121
|
+
this._emit(eventName, path23);
|
|
39122
|
+
this._closePath(path23);
|
|
38713
39123
|
}
|
|
38714
39124
|
/**
|
|
38715
39125
|
* Closes all watchers for a path
|
|
38716
39126
|
*/
|
|
38717
|
-
_closePath(
|
|
38718
|
-
this._closeFile(
|
|
38719
|
-
const dir = sysPath2.dirname(
|
|
38720
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
39127
|
+
_closePath(path23) {
|
|
39128
|
+
this._closeFile(path23);
|
|
39129
|
+
const dir = sysPath2.dirname(path23);
|
|
39130
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path23));
|
|
38721
39131
|
}
|
|
38722
39132
|
/**
|
|
38723
39133
|
* Closes only file-specific watchers
|
|
38724
39134
|
*/
|
|
38725
|
-
_closeFile(
|
|
38726
|
-
const closers = this._closers.get(
|
|
39135
|
+
_closeFile(path23) {
|
|
39136
|
+
const closers = this._closers.get(path23);
|
|
38727
39137
|
if (!closers)
|
|
38728
39138
|
return;
|
|
38729
39139
|
closers.forEach((closer) => closer());
|
|
38730
|
-
this._closers.delete(
|
|
39140
|
+
this._closers.delete(path23);
|
|
38731
39141
|
}
|
|
38732
|
-
_addPathCloser(
|
|
39142
|
+
_addPathCloser(path23, closer) {
|
|
38733
39143
|
if (!closer)
|
|
38734
39144
|
return;
|
|
38735
|
-
let list = this._closers.get(
|
|
39145
|
+
let list = this._closers.get(path23);
|
|
38736
39146
|
if (!list) {
|
|
38737
39147
|
list = [];
|
|
38738
|
-
this._closers.set(
|
|
39148
|
+
this._closers.set(path23, list);
|
|
38739
39149
|
}
|
|
38740
39150
|
list.push(closer);
|
|
38741
39151
|
}
|
|
@@ -38765,7 +39175,7 @@ function watch(paths, options = {}) {
|
|
|
38765
39175
|
var esm_default13 = { watch, FSWatcher };
|
|
38766
39176
|
|
|
38767
39177
|
// src/lib/watcher.ts
|
|
38768
|
-
import
|
|
39178
|
+
import path14 from "node:path";
|
|
38769
39179
|
function startWatcher(options) {
|
|
38770
39180
|
const { dir, extensions, onChange } = options;
|
|
38771
39181
|
const extSet = new Set(extensions);
|
|
@@ -38779,7 +39189,7 @@ function startWatcher(options) {
|
|
|
38779
39189
|
});
|
|
38780
39190
|
let debounceTimer = null;
|
|
38781
39191
|
const handleChange2 = (filePath) => {
|
|
38782
|
-
const ext =
|
|
39192
|
+
const ext = path14.extname(filePath);
|
|
38783
39193
|
if (!extSet.has(ext)) return;
|
|
38784
39194
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
38785
39195
|
debounceTimer = setTimeout(() => {
|
|
@@ -38805,29 +39215,30 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
38805
39215
|
import http from "node:http";
|
|
38806
39216
|
var PORT_START = 4400;
|
|
38807
39217
|
var PORT_END = 4409;
|
|
39218
|
+
var PING_INTERVAL_MS = 3e4;
|
|
39219
|
+
function portsBusyError(start, end) {
|
|
39220
|
+
return new CliError("cli_failed", `Ports ${start} to ${end} are all busy.`, [
|
|
39221
|
+
"Another `avsb dev` (or another program) is holding them. Stop it, then run this again.",
|
|
39222
|
+
`\`lsof -i :${start}\` shows what is listening on a port.`
|
|
39223
|
+
]);
|
|
39224
|
+
}
|
|
39225
|
+
function isAddressInUse(err) {
|
|
39226
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "EADDRINUSE";
|
|
39227
|
+
}
|
|
38808
39228
|
async function startDevServer(options) {
|
|
38809
39229
|
const { experimentName, experimentId, onExtensionMessage } = options;
|
|
38810
|
-
|
|
38811
|
-
|
|
38812
|
-
let
|
|
38813
|
-
for (let
|
|
39230
|
+
const start = options.portRange?.start ?? PORT_START;
|
|
39231
|
+
const end = options.portRange?.end ?? PORT_END;
|
|
39232
|
+
let bound = null;
|
|
39233
|
+
for (let port2 = start; port2 <= end && !bound; port2++) {
|
|
38814
39234
|
try {
|
|
38815
|
-
|
|
38816
|
-
|
|
38817
|
-
|
|
38818
|
-
resolvedPort = port;
|
|
38819
|
-
break;
|
|
38820
|
-
} catch {
|
|
38821
|
-
continue;
|
|
39235
|
+
bound = { ...await tryPort(port2, experimentName, experimentId, onExtensionMessage), port: port2 };
|
|
39236
|
+
} catch (err) {
|
|
39237
|
+
if (!isAddressInUse(err)) throw err;
|
|
38822
39238
|
}
|
|
38823
39239
|
}
|
|
38824
|
-
if (!
|
|
38825
|
-
|
|
38826
|
-
}
|
|
38827
|
-
const pingInterval = setInterval(() => {
|
|
38828
|
-
const msg = { type: "ping", timestamp: Date.now() };
|
|
38829
|
-
broadcast(JSON.stringify(msg));
|
|
38830
|
-
}, 3e4);
|
|
39240
|
+
if (!bound) throw portsBusyError(start, end);
|
|
39241
|
+
const { httpServer, wss, port } = bound;
|
|
38831
39242
|
function broadcast(data) {
|
|
38832
39243
|
wss.clients.forEach((client) => {
|
|
38833
39244
|
if (client.readyState === import_websocket.default.OPEN) {
|
|
@@ -38835,8 +39246,12 @@ async function startDevServer(options) {
|
|
|
38835
39246
|
}
|
|
38836
39247
|
});
|
|
38837
39248
|
}
|
|
39249
|
+
const pingInterval = setInterval(() => {
|
|
39250
|
+
const msg = { type: "ping", timestamp: Date.now() };
|
|
39251
|
+
broadcast(JSON.stringify(msg));
|
|
39252
|
+
}, PING_INTERVAL_MS);
|
|
38838
39253
|
return {
|
|
38839
|
-
port
|
|
39254
|
+
port,
|
|
38840
39255
|
broadcast(msg) {
|
|
38841
39256
|
broadcast(JSON.stringify(msg));
|
|
38842
39257
|
},
|
|
@@ -38879,6 +39294,16 @@ function tryPort(port, experimentName, experimentId, onExtensionMessage) {
|
|
|
38879
39294
|
);
|
|
38880
39295
|
});
|
|
38881
39296
|
const wss = new import_websocket_server.default({ server: httpServer });
|
|
39297
|
+
let settled = false;
|
|
39298
|
+
const fail2 = (err) => {
|
|
39299
|
+
if (settled) return;
|
|
39300
|
+
settled = true;
|
|
39301
|
+
wss.close();
|
|
39302
|
+
httpServer.close();
|
|
39303
|
+
reject(err);
|
|
39304
|
+
};
|
|
39305
|
+
httpServer.on("error", fail2);
|
|
39306
|
+
wss.on("error", fail2);
|
|
38882
39307
|
wss.on("connection", (ws) => {
|
|
38883
39308
|
ws.on("message", (raw) => {
|
|
38884
39309
|
try {
|
|
@@ -38888,8 +39313,11 @@ function tryPort(port, experimentName, experimentId, onExtensionMessage) {
|
|
|
38888
39313
|
}
|
|
38889
39314
|
});
|
|
38890
39315
|
});
|
|
38891
|
-
httpServer.
|
|
38892
|
-
|
|
39316
|
+
httpServer.listen(port, () => {
|
|
39317
|
+
if (settled) return;
|
|
39318
|
+
settled = true;
|
|
39319
|
+
resolve3({ httpServer, wss });
|
|
39320
|
+
});
|
|
38893
39321
|
});
|
|
38894
39322
|
}
|
|
38895
39323
|
|
|
@@ -38945,7 +39373,7 @@ var devCommand = new Command("dev").description("Start dev server with live relo
|
|
|
38945
39373
|
dir,
|
|
38946
39374
|
extensions: [".ts", ".js", ".scss", ".css"],
|
|
38947
39375
|
onChange: async (filePath) => {
|
|
38948
|
-
const relativePath =
|
|
39376
|
+
const relativePath = path15.relative(dir, filePath);
|
|
38949
39377
|
info(`File changed: ${relativePath}`);
|
|
38950
39378
|
try {
|
|
38951
39379
|
compiled = await compileExperiment(dir, manifest);
|
|
@@ -39061,15 +39489,15 @@ var statusCommand = withOutputOptions(
|
|
|
39061
39489
|
heading(manifest.experimentName);
|
|
39062
39490
|
line();
|
|
39063
39491
|
for (const row of buildStatusRows(manifest)) {
|
|
39064
|
-
|
|
39492
|
+
field(row.label, row.value);
|
|
39065
39493
|
}
|
|
39066
39494
|
line();
|
|
39067
39495
|
});
|
|
39068
39496
|
|
|
39069
39497
|
// src/commands/project.ts
|
|
39070
39498
|
import { createHash as createHash4 } from "node:crypto";
|
|
39071
|
-
import
|
|
39072
|
-
import
|
|
39499
|
+
import fs12 from "node:fs";
|
|
39500
|
+
import path16 from "node:path";
|
|
39073
39501
|
function sha2564(content) {
|
|
39074
39502
|
return createHash4("sha256").update(content).digest("hex");
|
|
39075
39503
|
}
|
|
@@ -39100,16 +39528,29 @@ var projectPullCommand = new Command("pull").description("Pull project script fr
|
|
|
39100
39528
|
label("ID", `PRJ-${project.shortId}`);
|
|
39101
39529
|
label("Language", project.projectJsLang === "TYPESCRIPT" ? "TypeScript" : "JavaScript");
|
|
39102
39530
|
line();
|
|
39103
|
-
const
|
|
39104
|
-
const dir =
|
|
39105
|
-
if (
|
|
39531
|
+
const target = resolveProjectPullTarget(process.cwd(), project.shortId);
|
|
39532
|
+
const dir = target.dir;
|
|
39533
|
+
if (target.kind === "blocked") {
|
|
39534
|
+
failCommand(
|
|
39535
|
+
new CliError("cli_usage", `${dir} already exists and is not a pulled copy of PRJ-${project.shortId}.`, [
|
|
39536
|
+
"Move or rename it, or run `avsb project pull` from another folder."
|
|
39537
|
+
])
|
|
39538
|
+
);
|
|
39539
|
+
}
|
|
39540
|
+
if (target.kind === "existing") {
|
|
39106
39541
|
const { manifest } = readProjectManifestOrExit(dir);
|
|
39107
39542
|
if (!options.force) {
|
|
39108
39543
|
const scriptFile = `project.${manifest.language}`;
|
|
39109
|
-
const scriptPath =
|
|
39110
|
-
const localContent =
|
|
39544
|
+
const scriptPath = path16.join(dir, scriptFile);
|
|
39545
|
+
const localContent = fs12.existsSync(scriptPath) ? fs12.readFileSync(scriptPath, "utf-8") : "";
|
|
39111
39546
|
const remoteContent = manifest.language === "ts" ? project.editorProjectTs ?? project.editorProjectJs ?? "" : project.editorProjectJs ?? "";
|
|
39112
39547
|
if (localContent !== remoteContent && localContent !== "") {
|
|
39548
|
+
const refusal = refusalWithoutPrompt({
|
|
39549
|
+
code: "cli_conflict",
|
|
39550
|
+
message: `Pulling would overwrite your local ${scriptFile}, and this run cannot ask first.`,
|
|
39551
|
+
hints: [`Run \`avsb project pull ${projectInput} --force\` to overwrite it.`]
|
|
39552
|
+
});
|
|
39553
|
+
if (refusal) failCommand(refusal);
|
|
39113
39554
|
const { proceed } = await esm_default12.prompt([
|
|
39114
39555
|
{
|
|
39115
39556
|
type: "confirm",
|
|
@@ -39127,7 +39568,7 @@ var projectPullCommand = new Command("pull").description("Pull project script fr
|
|
|
39127
39568
|
const updateSpinner = ora("Updating files...").start();
|
|
39128
39569
|
const updated = updateProjectFile(dir, manifest, response.data);
|
|
39129
39570
|
if (updated.length > 0) {
|
|
39130
|
-
updateSpinner.succeed(
|
|
39571
|
+
updateSpinner.succeed(`Files updated in ${path16.relative(process.cwd(), dir) || "this folder"}`);
|
|
39131
39572
|
fileTree(updated);
|
|
39132
39573
|
} else {
|
|
39133
39574
|
updateSpinner.succeed("Already up to date");
|
|
@@ -39135,12 +39576,12 @@ var projectPullCommand = new Command("pull").description("Pull project script fr
|
|
|
39135
39576
|
} else {
|
|
39136
39577
|
const scaffoldSpinner = ora("Scaffolding project files...").start();
|
|
39137
39578
|
try {
|
|
39138
|
-
const result = scaffoldProject({ data: response.data, apiBaseUrl: config.apiBaseUrl });
|
|
39579
|
+
const result = scaffoldProject({ data: response.data, apiBaseUrl: config.apiBaseUrl, targetDir: dir });
|
|
39139
39580
|
scaffoldSpinner.succeed("Files created");
|
|
39140
39581
|
line();
|
|
39141
39582
|
fileTree(result.files);
|
|
39142
39583
|
line();
|
|
39143
|
-
success(`Project cloned to ${
|
|
39584
|
+
success(`Project cloned to ${path16.relative(process.cwd(), dir) || "."}/`);
|
|
39144
39585
|
} catch (err) {
|
|
39145
39586
|
scaffoldSpinner.fail("Scaffolding failed");
|
|
39146
39587
|
failCommand(err);
|
|
@@ -39156,11 +39597,11 @@ var projectPushCommand = new Command("push").description("Compile and push proje
|
|
|
39156
39597
|
const { manifest, dir } = readProjectManifestOrExit();
|
|
39157
39598
|
const api = new ApiClient(config);
|
|
39158
39599
|
const scriptFile = `project.${manifest.language}`;
|
|
39159
|
-
const scriptPath =
|
|
39160
|
-
if (!
|
|
39600
|
+
const scriptPath = path16.join(dir, scriptFile);
|
|
39601
|
+
if (!fs12.existsSync(scriptPath)) {
|
|
39161
39602
|
failCommand(new CliError("cli_no_manifest", `File not found: ${scriptFile}`));
|
|
39162
39603
|
}
|
|
39163
|
-
const source =
|
|
39604
|
+
const source = fs12.readFileSync(scriptPath, "utf-8");
|
|
39164
39605
|
const projLang = manifest.language === "ts" ? "TYPESCRIPT" : "JAVASCRIPT";
|
|
39165
39606
|
const compileSpinner = ora(`Compiling ${scriptFile}...`).start();
|
|
39166
39607
|
const compileResult = await compileScript(source, projLang, scriptFile, { minify: true });
|
|
@@ -39182,6 +39623,18 @@ var projectPushCommand = new Command("push").description("Compile and push proje
|
|
|
39182
39623
|
if (remoteHash !== manifest.syncedHash) {
|
|
39183
39624
|
spinner.warn("Remote has changes you haven't pulled");
|
|
39184
39625
|
line();
|
|
39626
|
+
if (!isInteractiveRun()) {
|
|
39627
|
+
failCommand(
|
|
39628
|
+
new CliError(
|
|
39629
|
+
"cli_conflict",
|
|
39630
|
+
"The platform copy changed since your last pull, and this run cannot ask before overwriting it.",
|
|
39631
|
+
[
|
|
39632
|
+
"Run `avsb project pull --force` to fetch the remote script first (it overwrites your local file),",
|
|
39633
|
+
"or `avsb project push --force` to overwrite the remote changes."
|
|
39634
|
+
]
|
|
39635
|
+
)
|
|
39636
|
+
);
|
|
39637
|
+
}
|
|
39185
39638
|
const { proceed } = await esm_default12.prompt([
|
|
39186
39639
|
{
|
|
39187
39640
|
type: "confirm",
|
|
@@ -39200,12 +39653,18 @@ var projectPushCommand = new Command("push").description("Compile and push proje
|
|
|
39200
39653
|
}
|
|
39201
39654
|
}
|
|
39202
39655
|
const payload = manifest.language === "ts" ? { editorProjectTs: { source, compiled: compiledSource } } : { editorProjectJs: { source, compiled: compiledSource } };
|
|
39203
|
-
|
|
39656
|
+
try {
|
|
39657
|
+
await api.pushProject(manifest.projectShortId, payload);
|
|
39658
|
+
} catch (err) {
|
|
39659
|
+
spinner.fail("Push refused");
|
|
39660
|
+
failCommand(err, { labelForPath: (bodyPath) => bodyPath.length > 0 ? scriptFile : void 0 });
|
|
39661
|
+
}
|
|
39204
39662
|
manifest.syncedHash = sha2564(source);
|
|
39205
39663
|
manifest.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39206
39664
|
writeProjectManifest(dir, manifest);
|
|
39207
39665
|
spinner.succeed("Pushed successfully");
|
|
39208
39666
|
line();
|
|
39667
|
+
info("Saved as unpublished changes. Publish the script from the project's Snippet settings to put it live.");
|
|
39209
39668
|
success("Push complete");
|
|
39210
39669
|
} catch (err) {
|
|
39211
39670
|
failCommand(err);
|
|
@@ -39216,11 +39675,11 @@ var projectDevCommand = new Command("dev").description("Watch project script and
|
|
|
39216
39675
|
ensureLoggedIn();
|
|
39217
39676
|
const { manifest, dir } = readProjectManifestOrExit();
|
|
39218
39677
|
const scriptFile = `project.${manifest.language}`;
|
|
39219
|
-
const scriptPath =
|
|
39678
|
+
const scriptPath = path16.join(dir, scriptFile);
|
|
39220
39679
|
heading(`Dev mode: ${manifest.projectName} (project script)`);
|
|
39221
39680
|
line();
|
|
39222
39681
|
let compiledJs;
|
|
39223
|
-
const source =
|
|
39682
|
+
const source = fs12.readFileSync(scriptPath, "utf-8");
|
|
39224
39683
|
if (manifest.language === "ts") {
|
|
39225
39684
|
const spinner = ora("Compiling...").start();
|
|
39226
39685
|
try {
|
|
@@ -39259,10 +39718,10 @@ var projectDevCommand = new Command("dev").description("Watch project script and
|
|
|
39259
39718
|
dir,
|
|
39260
39719
|
extensions: [".ts", ".js"],
|
|
39261
39720
|
onChange: async (filePath) => {
|
|
39262
|
-
const relativePath =
|
|
39721
|
+
const relativePath = path16.relative(dir, filePath);
|
|
39263
39722
|
info(`File changed: ${relativePath}`);
|
|
39264
39723
|
try {
|
|
39265
|
-
const newSource =
|
|
39724
|
+
const newSource = fs12.readFileSync(filePath, "utf-8");
|
|
39266
39725
|
compiledJs = manifest.language === "ts" ? await compileTypeScript(newSource) : newSource;
|
|
39267
39726
|
const updateMsg = {
|
|
39268
39727
|
type: "project:update",
|
|
@@ -39508,6 +39967,57 @@ function isConflictError(err) {
|
|
|
39508
39967
|
if (!(err instanceof Error)) return false;
|
|
39509
39968
|
return /409|conflict|referenc/i.test(err.message);
|
|
39510
39969
|
}
|
|
39970
|
+
function isRecord5(value) {
|
|
39971
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39972
|
+
}
|
|
39973
|
+
function isCliMetric(value) {
|
|
39974
|
+
return isRecord5(value) && typeof value.id === "string" && typeof value.shortId === "number" && typeof value.name === "string" && typeof value.type === "string";
|
|
39975
|
+
}
|
|
39976
|
+
function readMetricList(data) {
|
|
39977
|
+
if (!isRecord5(data) || !Array.isArray(data.metrics)) return null;
|
|
39978
|
+
const metrics = data.metrics.filter(isCliMetric);
|
|
39979
|
+
const usage = isRecord5(data.usage) ? data.usage : {};
|
|
39980
|
+
return { metrics, usage };
|
|
39981
|
+
}
|
|
39982
|
+
function readMetric(data) {
|
|
39983
|
+
return isRecord5(data) && isCliMetric(data.metric) ? data.metric : null;
|
|
39984
|
+
}
|
|
39985
|
+
function formatMetricTable(metrics, usage) {
|
|
39986
|
+
const rows = metrics.map((metric) => ({
|
|
39987
|
+
id: metricIdLabel(metric),
|
|
39988
|
+
type: metric.type,
|
|
39989
|
+
name: truncate(metric.name, 32),
|
|
39990
|
+
definition: truncate(describeMetric(metric), 48),
|
|
39991
|
+
usage: formatUsage(usage[metric.id]),
|
|
39992
|
+
archived: metric.archivedAt != null
|
|
39993
|
+
}));
|
|
39994
|
+
const widths = {
|
|
39995
|
+
id: Math.max(2, ...rows.map((r) => r.id.length)),
|
|
39996
|
+
type: Math.max(4, ...rows.map((r) => r.type.length)),
|
|
39997
|
+
name: Math.max(4, ...rows.map((r) => r.name.length)),
|
|
39998
|
+
definition: Math.max(10, ...rows.map((r) => r.definition.length))
|
|
39999
|
+
};
|
|
40000
|
+
const heading2 = [
|
|
40001
|
+
"ID".padEnd(widths.id),
|
|
40002
|
+
"Type".padEnd(widths.type),
|
|
40003
|
+
"Name".padEnd(widths.name),
|
|
40004
|
+
"Definition".padEnd(widths.definition),
|
|
40005
|
+
"Usage"
|
|
40006
|
+
].join(" ");
|
|
40007
|
+
return {
|
|
40008
|
+
heading: heading2,
|
|
40009
|
+
rows: rows.map((row) => {
|
|
40010
|
+
const line2 = [
|
|
40011
|
+
row.id.padEnd(widths.id),
|
|
40012
|
+
row.type.padEnd(widths.type),
|
|
40013
|
+
row.name.padEnd(widths.name),
|
|
40014
|
+
row.definition.padEnd(widths.definition),
|
|
40015
|
+
row.usage
|
|
40016
|
+
].join(" ");
|
|
40017
|
+
return row.archived ? `${line2} (archived)` : line2;
|
|
40018
|
+
})
|
|
40019
|
+
};
|
|
40020
|
+
}
|
|
39511
40021
|
|
|
39512
40022
|
// src/commands/metrics.ts
|
|
39513
40023
|
var metricsListCommand = withOutputOptions(
|
|
@@ -39527,49 +40037,26 @@ var metricsListCommand = withOutputOptions(
|
|
|
39527
40037
|
spinner.fail("Failed to fetch metrics");
|
|
39528
40038
|
failCommand(err);
|
|
39529
40039
|
}
|
|
39530
|
-
const { metrics, usage } = response.data;
|
|
39531
40040
|
if (emitJsonResult(response.data)) return;
|
|
39532
|
-
|
|
40041
|
+
const list = readMetricList(response.data);
|
|
40042
|
+
if (!list) {
|
|
40043
|
+
failCommand(
|
|
40044
|
+
new CliError("cli_failed", "The platform answered in a shape this CLI does not read.", [
|
|
40045
|
+
"Upgrade the CLI with `npm install -g @avsbhq/cli`, or pass --json to see the raw answer."
|
|
40046
|
+
])
|
|
40047
|
+
);
|
|
40048
|
+
}
|
|
40049
|
+
if (list.metrics.length === 0) {
|
|
39533
40050
|
line();
|
|
39534
40051
|
info(
|
|
39535
40052
|
'No metrics yet. Create one with `avsb metrics create --type click --name "..." --selector ".cta"`'
|
|
39536
40053
|
);
|
|
39537
40054
|
return;
|
|
39538
40055
|
}
|
|
39539
|
-
const
|
|
39540
|
-
id: metricIdLabel(metric),
|
|
39541
|
-
type: metric.type,
|
|
39542
|
-
name: truncate(metric.name, 32),
|
|
39543
|
-
definition: truncate(describeMetric(metric), 48),
|
|
39544
|
-
usage: formatUsage(usage[metric.id]),
|
|
39545
|
-
archived: metric.archivedAt != null
|
|
39546
|
-
}));
|
|
39547
|
-
const widths = {
|
|
39548
|
-
id: Math.max(2, ...rows.map((r) => r.id.length)),
|
|
39549
|
-
type: Math.max(4, ...rows.map((r) => r.type.length)),
|
|
39550
|
-
name: Math.max(4, ...rows.map((r) => r.name.length)),
|
|
39551
|
-
definition: Math.max(10, ...rows.map((r) => r.definition.length))
|
|
39552
|
-
};
|
|
40056
|
+
const table = formatMetricTable(list.metrics, list.usage);
|
|
39553
40057
|
line();
|
|
39554
|
-
heading(
|
|
39555
|
-
|
|
39556
|
-
"ID".padEnd(widths.id),
|
|
39557
|
-
"Type".padEnd(widths.type),
|
|
39558
|
-
"Name".padEnd(widths.name),
|
|
39559
|
-
"Definition".padEnd(widths.definition),
|
|
39560
|
-
"Usage"
|
|
39561
|
-
].join(" ")
|
|
39562
|
-
);
|
|
39563
|
-
for (const row of rows) {
|
|
39564
|
-
const line2 = [
|
|
39565
|
-
row.id.padEnd(widths.id),
|
|
39566
|
-
row.type.padEnd(widths.type),
|
|
39567
|
-
row.name.padEnd(widths.name),
|
|
39568
|
-
row.definition.padEnd(widths.definition),
|
|
39569
|
-
row.usage
|
|
39570
|
-
].join(" ");
|
|
39571
|
-
line(row.archived ? `${line2} (archived)` : line2);
|
|
39572
|
-
}
|
|
40058
|
+
heading(table.heading);
|
|
40059
|
+
for (const row of table.rows) line(row);
|
|
39573
40060
|
line();
|
|
39574
40061
|
});
|
|
39575
40062
|
var metricsCreateCommand = new Command("create").description("Create a new metric").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option("--type <type>", "Metric type (CLICK | PAGEVIEW | CUSTOM)").option("--name <name>", "Display name").option("--description <text>", "Optional description").option("--selector <selector>", "CSS selector (CLICK only)").option("--page-url <url>", "Page URL filter (CLICK only)").option("--url-match <pattern>", "URL match pattern (PAGEVIEW only)").option("--match-mode <mode>", "URL match mode for PAGEVIEW (SIMPLE | EXACT | PATH | SUBSTRING | PATTERN | REGEX)").option("--key <key>", "Metric key (CUSTOM only)").option("--has-value", "Toggle value-field tracking on a CUSTOM metric").action(async (opts) => {
|
|
@@ -39585,21 +40072,20 @@ var metricsCreateCommand = new Command("create").description("Create a new metri
|
|
|
39585
40072
|
const body = buildCreateBody(type, opts, opts.name);
|
|
39586
40073
|
const api = new ApiClient(config);
|
|
39587
40074
|
const spinner = ora("Creating metric...").start();
|
|
40075
|
+
let response;
|
|
39588
40076
|
try {
|
|
39589
|
-
|
|
39590
|
-
const { metric } = response.data;
|
|
39591
|
-
spinner.succeed(`Metric created: ${metric.name} (${metricIdLabel(metric)})`);
|
|
39592
|
-
printMetricDetails(metric);
|
|
40077
|
+
response = await api.createMetric(shortId, body);
|
|
39593
40078
|
} catch (err) {
|
|
39594
40079
|
spinner.fail("Failed to create metric");
|
|
39595
|
-
|
|
39596
|
-
|
|
39597
|
-
|
|
39598
|
-
|
|
39599
|
-
|
|
39600
|
-
|
|
39601
|
-
process.exit(EXIT.failure);
|
|
40080
|
+
failCommand(err);
|
|
40081
|
+
}
|
|
40082
|
+
const metric = readMetric(response.data);
|
|
40083
|
+
if (!metric) {
|
|
40084
|
+
spinner.warn("Metric created. Run `avsb metrics list` to see it.");
|
|
40085
|
+
return;
|
|
39602
40086
|
}
|
|
40087
|
+
spinner.succeed(`Metric created: ${metric.name} (${metricIdLabel(metric)})`);
|
|
40088
|
+
printMetricDetails(metric);
|
|
39603
40089
|
});
|
|
39604
40090
|
var metricsUpdateCommand = new Command("update").argument("<metricId>", "Metric ID (cuid or numeric short ID)").description("Update a metric").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option("--name <name>", "New display name").option("--description <text>", "New description").option("--selector <selector>", "New CSS selector (CLICK)").option("--page-url <url>", "New page URL filter (CLICK)").option("--url-match <pattern>", "New URL match pattern (PAGEVIEW)").option("--match-mode <mode>", "New URL match mode (PAGEVIEW)").option("--key <key>", "New metric key (CUSTOM)").option("--has-value", "Enable value-field tracking (CUSTOM)").option("--no-has-value", "Disable value-field tracking (CUSTOM)").action(async (metricId, opts) => {
|
|
39605
40091
|
const config = ensureLoggedIn();
|
|
@@ -39615,49 +40101,65 @@ var metricsUpdateCommand = new Command("update").argument("<metricId>", "Metric
|
|
|
39615
40101
|
}
|
|
39616
40102
|
const api = new ApiClient(config);
|
|
39617
40103
|
const spinner = ora("Updating metric...").start();
|
|
40104
|
+
let response;
|
|
39618
40105
|
try {
|
|
39619
|
-
|
|
39620
|
-
const { metric } = response.data;
|
|
39621
|
-
spinner.succeed(`Metric updated: ${metric.name} (${metricIdLabel(metric)})`);
|
|
39622
|
-
printMetricDetails(metric);
|
|
40106
|
+
response = await api.updateMetric(shortId, metricId, patch);
|
|
39623
40107
|
} catch (err) {
|
|
39624
40108
|
spinner.fail("Failed to update metric");
|
|
39625
40109
|
failCommand(err);
|
|
39626
40110
|
}
|
|
40111
|
+
const metric = readMetric(response.data);
|
|
40112
|
+
if (!metric) {
|
|
40113
|
+
spinner.warn(`Metric ${metricId} updated.`);
|
|
40114
|
+
return;
|
|
40115
|
+
}
|
|
40116
|
+
spinner.succeed(`Metric updated: ${metric.name} (${metricIdLabel(metric)})`);
|
|
40117
|
+
printMetricDetails(metric);
|
|
39627
40118
|
});
|
|
39628
40119
|
var metricsArchiveCommand = new Command("archive").argument("<metricId>", "Metric ID").description("Archive a metric (soft-delete, preserves history)").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").action(async (metricId, opts) => {
|
|
39629
40120
|
const config = ensureLoggedIn();
|
|
39630
40121
|
const shortId = resolveProjectShortId(opts);
|
|
39631
40122
|
const api = new ApiClient(config);
|
|
39632
40123
|
const spinner = ora("Archiving metric...").start();
|
|
40124
|
+
let response;
|
|
39633
40125
|
try {
|
|
39634
|
-
|
|
39635
|
-
const { metric } = response.data;
|
|
39636
|
-
spinner.succeed(`Metric archived: ${metric.name} (${metricIdLabel(metric)})`);
|
|
40126
|
+
response = await api.archiveMetric(shortId, metricId);
|
|
39637
40127
|
} catch (err) {
|
|
39638
40128
|
spinner.fail("Failed to archive metric");
|
|
39639
40129
|
failCommand(err);
|
|
39640
40130
|
}
|
|
40131
|
+
const metric = readMetric(response.data);
|
|
40132
|
+
spinner.succeed(metric ? `Metric archived: ${metric.name} (${metricIdLabel(metric)})` : `Metric ${metricId} archived`);
|
|
39641
40133
|
});
|
|
39642
40134
|
var metricsUnarchiveCommand = new Command("unarchive").argument("<metricId>", "Metric ID").description("Restore an archived metric").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").action(async (metricId, opts) => {
|
|
39643
40135
|
const config = ensureLoggedIn();
|
|
39644
40136
|
const shortId = resolveProjectShortId(opts);
|
|
39645
40137
|
const api = new ApiClient(config);
|
|
39646
40138
|
const spinner = ora("Restoring metric...").start();
|
|
40139
|
+
let response;
|
|
39647
40140
|
try {
|
|
39648
|
-
|
|
39649
|
-
const { metric } = response.data;
|
|
39650
|
-
spinner.succeed(`Metric restored from archive: ${metric.name} (${metricIdLabel(metric)})`);
|
|
40141
|
+
response = await api.unarchiveMetric(shortId, metricId);
|
|
39651
40142
|
} catch (err) {
|
|
39652
40143
|
spinner.fail("Failed to unarchive metric");
|
|
39653
40144
|
failCommand(err);
|
|
39654
40145
|
}
|
|
40146
|
+
const metric = readMetric(response.data);
|
|
40147
|
+
spinner.succeed(
|
|
40148
|
+
metric ? `Metric restored from archive: ${metric.name} (${metricIdLabel(metric)})` : `Metric ${metricId} restored from archive`
|
|
40149
|
+
);
|
|
39655
40150
|
});
|
|
39656
40151
|
var metricsDeleteCommand = new Command("delete").argument("<metricId>", "Metric ID").description("Delete a metric (rejected if metric bindings reference it)").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option("-f, --force", "Skip confirmation prompt").action(async (metricId, opts) => {
|
|
39657
40152
|
const config = ensureLoggedIn();
|
|
39658
40153
|
const shortId = resolveProjectShortId(opts);
|
|
39659
40154
|
const api = new ApiClient(config);
|
|
39660
40155
|
if (!opts.force) {
|
|
40156
|
+
if (!isInteractiveRun()) {
|
|
40157
|
+
failCommand(
|
|
40158
|
+
new CliError("cli_usage", `Deleting metric ${metricId} needs a confirmation this run cannot ask for.`, [
|
|
40159
|
+
`Pass -f to delete without asking: \`avsb metrics delete ${metricId} -f\``
|
|
40160
|
+
])
|
|
40161
|
+
);
|
|
40162
|
+
}
|
|
39661
40163
|
const { proceed } = await esm_default12.prompt([
|
|
39662
40164
|
{
|
|
39663
40165
|
type: "confirm",
|
|
@@ -39677,19 +40179,12 @@ var metricsDeleteCommand = new Command("delete").argument("<metricId>", "Metric
|
|
|
39677
40179
|
spinner.succeed(`Metric deleted: ${metricId}`);
|
|
39678
40180
|
} catch (err) {
|
|
39679
40181
|
spinner.fail("Failed to delete metric");
|
|
39680
|
-
const message = err instanceof Error ? err.message : "Unknown error";
|
|
39681
40182
|
if (isConflictError(err)) {
|
|
39682
40183
|
const refs = extractBindingsReferencing(err);
|
|
39683
|
-
if (refs.length > 0) {
|
|
39684
|
-
error(`Cannot delete: referenced by ${refs.join(", ")}.`);
|
|
39685
|
-
} else {
|
|
39686
|
-
error(`Cannot delete: metric bindings still reference this metric. ${message}`);
|
|
39687
|
-
}
|
|
40184
|
+
if (refs.length > 0) error(`Referenced by ${refs.join(", ")}.`);
|
|
39688
40185
|
info(`Tip: run \`avsb metrics archive ${metricId}\` instead to hide it without losing history.`);
|
|
39689
|
-
} else {
|
|
39690
|
-
error(message);
|
|
39691
40186
|
}
|
|
39692
|
-
|
|
40187
|
+
failCommand(err);
|
|
39693
40188
|
}
|
|
39694
40189
|
});
|
|
39695
40190
|
var metricsCommand = new Command("metrics").description("Manage metric tracking definitions").addCommand(metricsListCommand).addCommand(metricsCreateCommand).addCommand(metricsUpdateCommand).addCommand(metricsArchiveCommand).addCommand(metricsUnarchiveCommand).addCommand(metricsDeleteCommand);
|
|
@@ -39904,6 +40399,40 @@ function isConflictError2(err) {
|
|
|
39904
40399
|
if (!(err instanceof Error)) return false;
|
|
39905
40400
|
return /409|conflict|attached|referenc/i.test(err.message);
|
|
39906
40401
|
}
|
|
40402
|
+
function isRecord6(value) {
|
|
40403
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40404
|
+
}
|
|
40405
|
+
function isCliMetricBinding(value) {
|
|
40406
|
+
return isRecord6(value) && typeof value.id === "string" && typeof value.shortId === "number" && typeof value.name === "string" && typeof value.measure === "string";
|
|
40407
|
+
}
|
|
40408
|
+
function readMetricBindingList(data) {
|
|
40409
|
+
if (!isRecord6(data) || !Array.isArray(data.metricBindings)) return null;
|
|
40410
|
+
return data.metricBindings.filter(isCliMetricBinding);
|
|
40411
|
+
}
|
|
40412
|
+
function readMetricBinding(data) {
|
|
40413
|
+
return isRecord6(data) && isCliMetricBinding(data.metricBinding) ? data.metricBinding : null;
|
|
40414
|
+
}
|
|
40415
|
+
function formatMetricBindingTable(bindings) {
|
|
40416
|
+
const rows = bindings.map((binding) => ({
|
|
40417
|
+
id: metricBindingIdLabel(binding),
|
|
40418
|
+
measure: measureBadge(binding.measure),
|
|
40419
|
+
name: truncate2(binding.name, 28),
|
|
40420
|
+
sentence: truncate2(renderMetricBindingSentence(binding), 64),
|
|
40421
|
+
archived: binding.archivedAt != null
|
|
40422
|
+
}));
|
|
40423
|
+
const widths = {
|
|
40424
|
+
id: Math.max(2, ...rows.map((r) => r.id.length)),
|
|
40425
|
+
measure: Math.max(7, ...rows.map((r) => r.measure.length)),
|
|
40426
|
+
name: Math.max(4, ...rows.map((r) => r.name.length))
|
|
40427
|
+
};
|
|
40428
|
+
return {
|
|
40429
|
+
heading: ["ID".padEnd(widths.id), "Measure".padEnd(widths.measure), "Name".padEnd(widths.name), "Sentence"].join(" "),
|
|
40430
|
+
rows: rows.map((row) => {
|
|
40431
|
+
const line2 = [row.id.padEnd(widths.id), row.measure.padEnd(widths.measure), row.name.padEnd(widths.name), row.sentence].join(" ");
|
|
40432
|
+
return row.archived ? `${line2} (archived)` : line2;
|
|
40433
|
+
})
|
|
40434
|
+
};
|
|
40435
|
+
}
|
|
39907
40436
|
|
|
39908
40437
|
// src/commands/metric-bindings.body.ts
|
|
39909
40438
|
function parseComposition(opts) {
|
|
@@ -39965,12 +40494,11 @@ function parseWinsorization(opts) {
|
|
|
39965
40494
|
}
|
|
39966
40495
|
return { enabled: true, upperPercentile: upper, lowerPercentile: lower };
|
|
39967
40496
|
}
|
|
39968
|
-
function buildCreateMetricBindingBody(
|
|
40497
|
+
function buildCreateMetricBindingBody(measure, opts, name) {
|
|
39969
40498
|
const direction = opts.direction ? normalizeDirection(opts.direction) : "INCREASE";
|
|
39970
40499
|
const body = {
|
|
39971
40500
|
name,
|
|
39972
40501
|
description: opts.description ?? void 0,
|
|
39973
|
-
projectId,
|
|
39974
40502
|
measure,
|
|
39975
40503
|
direction,
|
|
39976
40504
|
isLibrary: true
|
|
@@ -40081,8 +40609,15 @@ var metricBindingsListCommand = withOutputOptions(
|
|
|
40081
40609
|
spinner.fail("Failed to fetch metric bindings");
|
|
40082
40610
|
failCommand(err);
|
|
40083
40611
|
}
|
|
40084
|
-
const { metricBindings } = response.data;
|
|
40085
40612
|
if (emitJsonResult(response.data)) return;
|
|
40613
|
+
const metricBindings = readMetricBindingList(response.data);
|
|
40614
|
+
if (!metricBindings) {
|
|
40615
|
+
failCommand(
|
|
40616
|
+
new CliError("cli_failed", "The platform answered in a shape this CLI does not read.", [
|
|
40617
|
+
"Upgrade the CLI with `npm install -g @avsbhq/cli`, or pass --json to see the raw answer."
|
|
40618
|
+
])
|
|
40619
|
+
);
|
|
40620
|
+
}
|
|
40086
40621
|
if (metricBindings.length === 0) {
|
|
40087
40622
|
line();
|
|
40088
40623
|
info(
|
|
@@ -40090,45 +40625,19 @@ var metricBindingsListCommand = withOutputOptions(
|
|
|
40090
40625
|
);
|
|
40091
40626
|
return;
|
|
40092
40627
|
}
|
|
40093
|
-
const
|
|
40094
|
-
id: metricBindingIdLabel(binding),
|
|
40095
|
-
measure: measureBadge(binding.measure),
|
|
40096
|
-
name: truncate2(binding.name, 28),
|
|
40097
|
-
sentence: truncate2(renderMetricBindingSentence(binding), 64),
|
|
40098
|
-
archived: binding.archivedAt != null
|
|
40099
|
-
}));
|
|
40100
|
-
const widths = {
|
|
40101
|
-
id: Math.max(2, ...rows.map((r) => r.id.length)),
|
|
40102
|
-
measure: Math.max(7, ...rows.map((r) => r.measure.length)),
|
|
40103
|
-
name: Math.max(4, ...rows.map((r) => r.name.length))
|
|
40104
|
-
};
|
|
40628
|
+
const table = formatMetricBindingTable(metricBindings);
|
|
40105
40629
|
line();
|
|
40106
|
-
heading(
|
|
40107
|
-
|
|
40108
|
-
"ID".padEnd(widths.id),
|
|
40109
|
-
"Measure".padEnd(widths.measure),
|
|
40110
|
-
"Name".padEnd(widths.name),
|
|
40111
|
-
"Sentence"
|
|
40112
|
-
].join(" ")
|
|
40113
|
-
);
|
|
40114
|
-
for (const row of rows) {
|
|
40115
|
-
const line2 = [
|
|
40116
|
-
row.id.padEnd(widths.id),
|
|
40117
|
-
row.measure.padEnd(widths.measure),
|
|
40118
|
-
row.name.padEnd(widths.name),
|
|
40119
|
-
row.sentence
|
|
40120
|
-
].join(" ");
|
|
40121
|
-
line(row.archived ? `${line2} (archived)` : line2);
|
|
40122
|
-
}
|
|
40630
|
+
heading(table.heading);
|
|
40631
|
+
for (const row of table.rows) line(row);
|
|
40123
40632
|
line();
|
|
40124
40633
|
}
|
|
40125
40634
|
);
|
|
40126
40635
|
var metricBindingsCreateCommand = new Command("create").description("Create a new metric binding").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option(
|
|
40127
40636
|
"--measure <measure>",
|
|
40128
40637
|
"Measure (UNIQUE_CONVERSIONS_PER_VISITOR | TOTAL_EVENTS | UNIQUE_VISITORS_WHO_FIRED | TOTAL_VALUE_PER_VISITOR | TOTAL_VALUE | PERCENTILE | RATE | COMPOSITE)"
|
|
40129
|
-
).option("--direction <direction>", "Direction (INCREASE | DECREASE)", "INCREASE").option("--name <name>", "Display name").option("--description <text>", "Optional description").option("--metric <metricId>", "Source metric (required for single-metric measures)").option("--numerator-metric <metricId>", "Numerator metric (required for RATE)").option("--denominator-metric <metricId>", "Denominator metric (required for RATE)").option("--composition <json>", "COMPOSITE
|
|
40130
|
-
"--component <
|
|
40131
|
-
"Repeatable component
|
|
40638
|
+
).option("--direction <direction>", "Direction (INCREASE | DECREASE)", "INCREASE").option("--name <name>", "Display name").option("--description <text>", "Optional description").option("--metric <metricId>", "Source metric: MET-17, 17 or the long id (required for single-metric measures)").option("--numerator-metric <metricId>", "Numerator metric (required for RATE)").option("--denominator-metric <metricId>", "Denominator metric (required for RATE)").option("--composition <json>", "COMPOSITE components as a JSON array of { metricId, weight }, where metricId names a metric binding").option(
|
|
40639
|
+
"--component <bindingId:weight>",
|
|
40640
|
+
"Repeatable COMPOSITE component, a metric binding and its weight (e.g. --component MBD-8:0.5 --component MBD-9:0.5)",
|
|
40132
40641
|
(value, prev = []) => prev.concat(value),
|
|
40133
40642
|
[]
|
|
40134
40643
|
).option("--percentile <n>", "Percentile (required for PERCENTILE measure, 0 < n < 100)").option("--value-source <name>", "Numeric field on the metric (value/percentile measures)", "value").option("--winsorize-upper <pct>", "Upper winsorization percentile (enables winsorization)").option("--winsorize-lower <pct>", "Lower winsorization percentile (enables winsorization)").action(async (opts) => {
|
|
@@ -40142,29 +40651,26 @@ var metricBindingsCreateCommand = new Command("create").description("Create a ne
|
|
|
40142
40651
|
const shortId = resolveProjectShortId2(opts);
|
|
40143
40652
|
const measure = normalizeMeasure(opts.measure);
|
|
40144
40653
|
const api = new ApiClient(config);
|
|
40145
|
-
const
|
|
40146
|
-
const projectId = projectResponse.data.project.id;
|
|
40147
|
-
const body = buildCreateMetricBindingBody(projectId, measure, opts, opts.name);
|
|
40654
|
+
const body = buildCreateMetricBindingBody(measure, opts, opts.name);
|
|
40148
40655
|
const spinner = ora("Creating metric binding...").start();
|
|
40656
|
+
let response;
|
|
40149
40657
|
try {
|
|
40150
|
-
|
|
40151
|
-
const { metricBinding } = response.data;
|
|
40152
|
-
spinner.succeed(`Metric binding created: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40153
|
-
printMetricBindingDetails(metricBinding);
|
|
40658
|
+
response = await api.createMetricBinding(shortId, body);
|
|
40154
40659
|
} catch (err) {
|
|
40155
40660
|
spinner.fail("Failed to create metric binding");
|
|
40156
|
-
|
|
40157
|
-
|
|
40158
|
-
|
|
40159
|
-
|
|
40160
|
-
|
|
40161
|
-
|
|
40162
|
-
process.exit(EXIT.failure);
|
|
40661
|
+
failCommand(err);
|
|
40662
|
+
}
|
|
40663
|
+
const metricBinding = readMetricBinding(response.data);
|
|
40664
|
+
if (!metricBinding) {
|
|
40665
|
+
spinner.warn("Metric binding created. Run `avsb metric-bindings list` to see it.");
|
|
40666
|
+
return;
|
|
40163
40667
|
}
|
|
40668
|
+
spinner.succeed(`Metric binding created: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40669
|
+
printMetricBindingDetails(metricBinding);
|
|
40164
40670
|
});
|
|
40165
40671
|
var metricBindingsUpdateCommand = new Command("update").argument("<metricBindingId>", "Metric binding ID (cuid or numeric short ID)").description("Update a metric binding").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option("--name <name>", "New display name").option("--description <text>", "New description").option("--direction <direction>", "New direction (INCREASE | DECREASE)").option("--metric <metricId>", "New source metric").option("--numerator-metric <metricId>", "New numerator metric (RATE)").option("--denominator-metric <metricId>", "New denominator metric (RATE)").option("--composition <json>", "New COMPOSITE composition as JSON array of { metricId,weight}").option(
|
|
40166
|
-
"--component <
|
|
40167
|
-
"Repeatable component (replaces composition)",
|
|
40672
|
+
"--component <bindingId:weight>",
|
|
40673
|
+
"Repeatable COMPOSITE component, a metric binding and its weight (replaces the composition)",
|
|
40168
40674
|
(value, prev = []) => prev.concat(value),
|
|
40169
40675
|
[]
|
|
40170
40676
|
).option("--percentile <n>", "New percentile (PERCENTILE measure)").option("--value-source <name>", "New value source field name").option("--winsorize-upper <pct>", "New upper winsorization percentile").option("--winsorize-lower <pct>", "New lower winsorization percentile").option("--library", "Mark as a library metric binding").option("--no-library", "Mark as a non-library metric binding").action(async (metricBindingId, opts) => {
|
|
@@ -40172,54 +40678,73 @@ var metricBindingsUpdateCommand = new Command("update").argument("<metricBinding
|
|
|
40172
40678
|
const shortId = resolveProjectShortId2(opts);
|
|
40173
40679
|
const patch = buildUpdateMetricBindingPatch(opts);
|
|
40174
40680
|
if (Object.keys(patch).length === 0) {
|
|
40175
|
-
|
|
40176
|
-
|
|
40681
|
+
failCommand(
|
|
40682
|
+
new CliError("cli_usage", "No update fields provided. Pass at least one of --name, --description, --direction, etc.")
|
|
40683
|
+
);
|
|
40177
40684
|
}
|
|
40178
40685
|
const api = new ApiClient(config);
|
|
40179
40686
|
const spinner = ora("Updating metric binding...").start();
|
|
40687
|
+
let response;
|
|
40180
40688
|
try {
|
|
40181
|
-
|
|
40182
|
-
const { metricBinding } = response.data;
|
|
40183
|
-
spinner.succeed(`Metric binding updated: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40184
|
-
printMetricBindingDetails(metricBinding);
|
|
40689
|
+
response = await api.updateMetricBinding(shortId, metricBindingId, patch);
|
|
40185
40690
|
} catch (err) {
|
|
40186
40691
|
spinner.fail("Failed to update metric binding");
|
|
40187
40692
|
failCommand(err);
|
|
40188
40693
|
}
|
|
40694
|
+
const metricBinding = readMetricBinding(response.data);
|
|
40695
|
+
if (!metricBinding) {
|
|
40696
|
+
spinner.warn(`Metric binding ${metricBindingId} updated.`);
|
|
40697
|
+
return;
|
|
40698
|
+
}
|
|
40699
|
+
spinner.succeed(`Metric binding updated: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40700
|
+
printMetricBindingDetails(metricBinding);
|
|
40189
40701
|
});
|
|
40190
40702
|
var metricBindingsArchiveCommand = new Command("archive").argument("<metricBindingId>", "Metric binding ID").description("Archive a metric binding (soft-delete, preserves history)").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").action(async (metricBindingId, opts) => {
|
|
40191
40703
|
const config = ensureLoggedIn();
|
|
40192
40704
|
const shortId = resolveProjectShortId2(opts);
|
|
40193
40705
|
const api = new ApiClient(config);
|
|
40194
40706
|
const spinner = ora("Archiving metric binding...").start();
|
|
40707
|
+
let response;
|
|
40195
40708
|
try {
|
|
40196
|
-
|
|
40197
|
-
const { metricBinding } = response.data;
|
|
40198
|
-
spinner.succeed(`Metric binding archived: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40709
|
+
response = await api.archiveMetricBinding(shortId, metricBindingId);
|
|
40199
40710
|
} catch (err) {
|
|
40200
40711
|
spinner.fail("Failed to archive metric binding");
|
|
40201
40712
|
failCommand(err);
|
|
40202
40713
|
}
|
|
40714
|
+
const metricBinding = readMetricBinding(response.data);
|
|
40715
|
+
spinner.succeed(
|
|
40716
|
+
metricBinding ? `Metric binding archived: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})` : `Metric binding ${metricBindingId} archived`
|
|
40717
|
+
);
|
|
40203
40718
|
});
|
|
40204
40719
|
var metricBindingsUnarchiveCommand = new Command("unarchive").argument("<metricBindingId>", "Metric binding ID").description("Restore an archived metric binding").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").action(async (metricBindingId, opts) => {
|
|
40205
40720
|
const config = ensureLoggedIn();
|
|
40206
40721
|
const shortId = resolveProjectShortId2(opts);
|
|
40207
40722
|
const api = new ApiClient(config);
|
|
40208
40723
|
const spinner = ora("Restoring metric binding...").start();
|
|
40724
|
+
let response;
|
|
40209
40725
|
try {
|
|
40210
|
-
|
|
40211
|
-
const { metricBinding } = response.data;
|
|
40212
|
-
spinner.succeed(`Metric binding restored from archive: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})`);
|
|
40726
|
+
response = await api.unarchiveMetricBinding(shortId, metricBindingId);
|
|
40213
40727
|
} catch (err) {
|
|
40214
40728
|
spinner.fail("Failed to unarchive metric binding");
|
|
40215
40729
|
failCommand(err);
|
|
40216
40730
|
}
|
|
40731
|
+
const metricBinding = readMetricBinding(response.data);
|
|
40732
|
+
spinner.succeed(
|
|
40733
|
+
metricBinding ? `Metric binding restored from archive: ${metricBinding.name} (${metricBindingIdLabel(metricBinding)})` : `Metric binding ${metricBindingId} restored from archive`
|
|
40734
|
+
);
|
|
40217
40735
|
});
|
|
40218
40736
|
var metricBindingsDeleteCommand = new Command("delete").argument("<metricBindingId>", "Metric binding ID").description("Delete a metric binding (rejected if experiments still attach it)").option("--project <project>", "Project ID (PRJ-42 or numeric); defaults to local manifest").option("-f, --force", "Skip confirmation prompt").action(async (metricBindingId, opts) => {
|
|
40219
40737
|
const config = ensureLoggedIn();
|
|
40220
40738
|
const shortId = resolveProjectShortId2(opts);
|
|
40221
40739
|
const api = new ApiClient(config);
|
|
40222
40740
|
if (!opts.force) {
|
|
40741
|
+
if (!isInteractiveRun()) {
|
|
40742
|
+
failCommand(
|
|
40743
|
+
new CliError("cli_usage", `Deleting metric binding ${metricBindingId} needs a confirmation this run cannot ask for.`, [
|
|
40744
|
+
`Pass -f to delete without asking: \`avsb metric-bindings delete ${metricBindingId} -f\``
|
|
40745
|
+
])
|
|
40746
|
+
);
|
|
40747
|
+
}
|
|
40223
40748
|
const { proceed } = await esm_default12.prompt([
|
|
40224
40749
|
{
|
|
40225
40750
|
type: "confirm",
|
|
@@ -40239,28 +40764,21 @@ var metricBindingsDeleteCommand = new Command("delete").argument("<metricBinding
|
|
|
40239
40764
|
spinner.succeed(`Metric binding deleted: ${metricBindingId}`);
|
|
40240
40765
|
} catch (err) {
|
|
40241
40766
|
spinner.fail("Failed to delete metric binding");
|
|
40242
|
-
const message = err instanceof Error ? err.message : "Unknown error";
|
|
40243
40767
|
if (isConflictError2(err)) {
|
|
40244
40768
|
const refs = extractExperimentsReferencing(err);
|
|
40245
|
-
if (refs.length > 0) {
|
|
40246
|
-
error(`Cannot delete: referenced by ${refs.join(", ")}.`);
|
|
40247
|
-
} else {
|
|
40248
|
-
error(`Cannot delete: experiments still attach this metric binding. ${message}`);
|
|
40249
|
-
}
|
|
40769
|
+
if (refs.length > 0) error(`Attached to ${refs.join(", ")}.`);
|
|
40250
40770
|
info(
|
|
40251
40771
|
`Tip: run \`avsb metric-bindings archive ${metricBindingId}\` instead to hide it without losing history.`
|
|
40252
40772
|
);
|
|
40253
|
-
} else {
|
|
40254
|
-
error(message);
|
|
40255
40773
|
}
|
|
40256
|
-
|
|
40774
|
+
failCommand(err);
|
|
40257
40775
|
}
|
|
40258
40776
|
});
|
|
40259
40777
|
var metricBindingsCommand = new Command("metric-bindings").description("Manage saved/library metric bindings").addCommand(metricBindingsListCommand).addCommand(metricBindingsCreateCommand).addCommand(metricBindingsUpdateCommand).addCommand(metricBindingsArchiveCommand).addCommand(metricBindingsUnarchiveCommand).addCommand(metricBindingsDeleteCommand);
|
|
40260
40778
|
|
|
40261
40779
|
// src/commands/codegen.ts
|
|
40262
|
-
import
|
|
40263
|
-
import
|
|
40780
|
+
import path17 from "node:path";
|
|
40781
|
+
import fs13 from "node:fs/promises";
|
|
40264
40782
|
|
|
40265
40783
|
// src/lib/codegenApi.ts
|
|
40266
40784
|
var CodegenApiClient = class extends ApiClient {
|
|
@@ -40317,6 +40835,9 @@ function serialiseManifest(manifest) {
|
|
|
40317
40835
|
}
|
|
40318
40836
|
|
|
40319
40837
|
// src/lib/codegenCheck.ts
|
|
40838
|
+
function withoutPublishStamp(text) {
|
|
40839
|
+
return text.replace(/^(\/\/ Source: .*\bpublishedAt=)\S+/m, "$1*").replace(/^(\s*"publishedAt": )(?:"[^"\n]*"|null)/gm, "$1*");
|
|
40840
|
+
}
|
|
40320
40841
|
function diffArtifact(artifact) {
|
|
40321
40842
|
const expectedLines = artifact.expected.split("\n");
|
|
40322
40843
|
if (artifact.actual === null) {
|
|
@@ -40331,11 +40852,13 @@ function diffArtifact(artifact) {
|
|
|
40331
40852
|
};
|
|
40332
40853
|
}
|
|
40333
40854
|
const actual = artifact.actual.replace(/\r\n/g, "\n");
|
|
40334
|
-
|
|
40855
|
+
const expectedComparable = withoutPublishStamp(artifact.expected).split("\n");
|
|
40856
|
+
const actualComparable = withoutPublishStamp(actual).split("\n");
|
|
40857
|
+
if (expectedComparable.join("\n") === actualComparable.join("\n")) return null;
|
|
40335
40858
|
const actualLines = actual.split("\n");
|
|
40336
|
-
const shared = Math.min(
|
|
40859
|
+
const shared = Math.min(expectedComparable.length, actualComparable.length);
|
|
40337
40860
|
let index = 0;
|
|
40338
|
-
while (index < shared &&
|
|
40861
|
+
while (index < shared && expectedComparable[index] === actualComparable[index]) index += 1;
|
|
40339
40862
|
return {
|
|
40340
40863
|
path: artifact.path,
|
|
40341
40864
|
reason: "different",
|
|
@@ -40489,7 +41012,7 @@ function planArtifacts(datafile, targets) {
|
|
|
40489
41012
|
}
|
|
40490
41013
|
async function readIfPresent(filePath) {
|
|
40491
41014
|
try {
|
|
40492
|
-
return await
|
|
41015
|
+
return await fs13.readFile(filePath, "utf-8");
|
|
40493
41016
|
} catch (err) {
|
|
40494
41017
|
if (isMissingFile(err)) return null;
|
|
40495
41018
|
throw err;
|
|
@@ -40503,8 +41026,8 @@ async function writeArtifacts(artifacts) {
|
|
|
40503
41026
|
for (const artifact of artifacts) {
|
|
40504
41027
|
const existing = await readIfPresent(artifact.path);
|
|
40505
41028
|
if (existing === artifact.contents) continue;
|
|
40506
|
-
await
|
|
40507
|
-
await
|
|
41029
|
+
await fs13.mkdir(path17.dirname(artifact.path), { recursive: true });
|
|
41030
|
+
await fs13.writeFile(artifact.path, artifact.contents, "utf-8");
|
|
40508
41031
|
written.push(artifact.path);
|
|
40509
41032
|
}
|
|
40510
41033
|
return written;
|
|
@@ -40526,6 +41049,7 @@ function flagCountLine(datafile) {
|
|
|
40526
41049
|
}
|
|
40527
41050
|
async function runCheck(artifacts, datafile, targets) {
|
|
40528
41051
|
const drifts = diffArtifacts(await toComparisons(artifacts));
|
|
41052
|
+
const exitCode = drifts.length === 0 ? EXIT.success : EXIT.failure;
|
|
40529
41053
|
if (emitJsonResult({
|
|
40530
41054
|
upToDate: drifts.length === 0,
|
|
40531
41055
|
checked: artifacts.map((a) => a.path),
|
|
@@ -40534,25 +41058,25 @@ async function runCheck(artifacts, datafile, targets) {
|
|
|
40534
41058
|
flagCount: datafile.flags.length,
|
|
40535
41059
|
publishedAt: datafile.publishedAt ?? null
|
|
40536
41060
|
})) {
|
|
40537
|
-
|
|
41061
|
+
return exitCode;
|
|
40538
41062
|
}
|
|
40539
41063
|
if (drifts.length === 0) {
|
|
40540
41064
|
success(describeDriftSummary(drifts));
|
|
40541
41065
|
dim(` Checked ${artifacts.length === 1 ? "1 file" : `${artifacts.length} files`} against ${flagCountLine(datafile)} in ${targets.env}.`);
|
|
40542
|
-
|
|
41066
|
+
return exitCode;
|
|
40543
41067
|
}
|
|
40544
41068
|
error(describeDriftSummary(drifts));
|
|
40545
41069
|
for (const drift of drifts) {
|
|
40546
41070
|
for (const line2 of describeDrift(drift)) errorDetail(line2);
|
|
40547
41071
|
}
|
|
40548
41072
|
errorDetail("Run the same command without --check, then commit the result.");
|
|
40549
|
-
|
|
41073
|
+
return exitCode;
|
|
40550
41074
|
}
|
|
40551
41075
|
function reportWatchEvent(event, targets) {
|
|
40552
41076
|
switch (event.kind) {
|
|
40553
41077
|
case "polled":
|
|
40554
41078
|
if (event.changed) {
|
|
40555
|
-
success(`Updated ${event.written.map((p) =>
|
|
41079
|
+
success(`Updated ${event.written.map((p) => path17.relative(process.cwd(), p)).join(", ")}`);
|
|
40556
41080
|
}
|
|
40557
41081
|
return;
|
|
40558
41082
|
case "failed":
|
|
@@ -40643,6 +41167,7 @@ var codegenCommand = withOutputOptions(
|
|
|
40643
41167
|
const project = resolveProjectRefOrExit(opts);
|
|
40644
41168
|
const config = ensureLoggedIn();
|
|
40645
41169
|
const spinner = startSpinner("Fetching datafile...");
|
|
41170
|
+
let checkExitCode = null;
|
|
40646
41171
|
try {
|
|
40647
41172
|
const client = new CodegenApiClient(config);
|
|
40648
41173
|
const first = await client.getDatafileConditional(project.shortId, opts.env, null);
|
|
@@ -40655,15 +41180,15 @@ var codegenCommand = withOutputOptions(
|
|
|
40655
41180
|
const datafile = first.body;
|
|
40656
41181
|
spinner.succeed("Datafile fetched");
|
|
40657
41182
|
const targets = {
|
|
40658
|
-
typesPath:
|
|
40659
|
-
manifestPath: opts.manifest ?
|
|
41183
|
+
typesPath: path17.resolve(opts.output),
|
|
41184
|
+
manifestPath: opts.manifest ? path17.resolve(opts.manifest) : null,
|
|
40660
41185
|
projectShortId: project.shortId,
|
|
40661
41186
|
env: opts.env,
|
|
40662
41187
|
augment: opts.augment !== false
|
|
40663
41188
|
};
|
|
40664
41189
|
const artifacts = planArtifacts(datafile, targets);
|
|
40665
41190
|
if (opts.check) {
|
|
40666
|
-
await runCheck(artifacts, datafile, targets);
|
|
41191
|
+
checkExitCode = await runCheck(artifacts, datafile, targets);
|
|
40667
41192
|
return;
|
|
40668
41193
|
}
|
|
40669
41194
|
const written = await writeArtifacts(artifacts);
|
|
@@ -40693,12 +41218,14 @@ var codegenCommand = withOutputOptions(
|
|
|
40693
41218
|
} catch (err) {
|
|
40694
41219
|
spinner.fail("Codegen failed");
|
|
40695
41220
|
failCommand(err);
|
|
41221
|
+
} finally {
|
|
41222
|
+
if (checkExitCode !== null) process.exit(checkExitCode);
|
|
40696
41223
|
}
|
|
40697
41224
|
});
|
|
40698
41225
|
|
|
40699
41226
|
// src/commands/find-refs.ts
|
|
40700
|
-
import
|
|
40701
|
-
import
|
|
41227
|
+
import fs14 from "node:fs/promises";
|
|
41228
|
+
import path19 from "node:path";
|
|
40702
41229
|
|
|
40703
41230
|
// src/lib/findRefs.ts
|
|
40704
41231
|
var FLAG_GETTERS = /* @__PURE__ */ new Map([
|
|
@@ -40861,7 +41388,7 @@ function byKeyName(a, b) {
|
|
|
40861
41388
|
}
|
|
40862
41389
|
|
|
40863
41390
|
// src/lib/ignoreRules.ts
|
|
40864
|
-
import
|
|
41391
|
+
import path18 from "node:path";
|
|
40865
41392
|
var ALWAYS_SKIPPED_DIRECTORIES = [
|
|
40866
41393
|
".git",
|
|
40867
41394
|
"node_modules",
|
|
@@ -40965,7 +41492,7 @@ function relativeToBase(relPath, base) {
|
|
|
40965
41492
|
return relPath.slice(base.length + 1);
|
|
40966
41493
|
}
|
|
40967
41494
|
function toRelativePosix(root, target) {
|
|
40968
|
-
return
|
|
41495
|
+
return path18.relative(root, target).split(path18.sep).join("/");
|
|
40969
41496
|
}
|
|
40970
41497
|
|
|
40971
41498
|
// src/commands/find-refs.ts
|
|
@@ -41021,13 +41548,13 @@ async function scanTree(root, extraRules, knownKeys, maxFiles) {
|
|
|
41021
41548
|
if (truncated) return;
|
|
41022
41549
|
let entries;
|
|
41023
41550
|
try {
|
|
41024
|
-
entries = await
|
|
41551
|
+
entries = await fs14.readdir(dir, { withFileTypes: true });
|
|
41025
41552
|
} catch {
|
|
41026
41553
|
return;
|
|
41027
41554
|
}
|
|
41028
41555
|
let rules = inherited;
|
|
41029
41556
|
if (entries.some((e) => e.isFile() && e.name === ".gitignore")) {
|
|
41030
|
-
const contents = await readTextFile(
|
|
41557
|
+
const contents = await readTextFile(path19.join(dir, ".gitignore"));
|
|
41031
41558
|
if (contents !== null) {
|
|
41032
41559
|
rules = [...inherited, ...parseIgnoreFile(contents, toRelativePosix(root, dir))];
|
|
41033
41560
|
}
|
|
@@ -41035,7 +41562,7 @@ async function scanTree(root, extraRules, knownKeys, maxFiles) {
|
|
|
41035
41562
|
for (const entry of entries) {
|
|
41036
41563
|
if (truncated) return;
|
|
41037
41564
|
if (entry.isSymbolicLink()) continue;
|
|
41038
|
-
const full =
|
|
41565
|
+
const full = path19.join(dir, entry.name);
|
|
41039
41566
|
const rel = toRelativePosix(root, full);
|
|
41040
41567
|
if (entry.isDirectory()) {
|
|
41041
41568
|
if (ALWAYS_SKIPPED_DIRECTORIES.includes(entry.name)) continue;
|
|
@@ -41044,7 +41571,7 @@ async function scanTree(root, extraRules, knownKeys, maxFiles) {
|
|
|
41044
41571
|
continue;
|
|
41045
41572
|
}
|
|
41046
41573
|
if (!entry.isFile()) continue;
|
|
41047
|
-
if (!SOURCE_EXTENSIONS.has(
|
|
41574
|
+
if (!SOURCE_EXTENSIONS.has(path19.extname(entry.name))) continue;
|
|
41048
41575
|
if (isIgnored(rel, false, rules)) continue;
|
|
41049
41576
|
if (filesScanned >= maxFiles) {
|
|
41050
41577
|
truncated = true;
|
|
@@ -41061,9 +41588,9 @@ async function scanTree(root, extraRules, knownKeys, maxFiles) {
|
|
|
41061
41588
|
}
|
|
41062
41589
|
async function readTextFile(filePath) {
|
|
41063
41590
|
try {
|
|
41064
|
-
const stat4 = await
|
|
41591
|
+
const stat4 = await fs14.stat(filePath);
|
|
41065
41592
|
if (stat4.size > MAX_FILE_BYTES) return null;
|
|
41066
|
-
const contents = await
|
|
41593
|
+
const contents = await fs14.readFile(filePath, "utf-8");
|
|
41067
41594
|
return contents.includes("\0") ? null : contents;
|
|
41068
41595
|
} catch {
|
|
41069
41596
|
return null;
|
|
@@ -41129,7 +41656,7 @@ var findRefsCommand = withOutputOptions(
|
|
|
41129
41656
|
const api = new FlagsApiClient(config);
|
|
41130
41657
|
const registry = await readRegistry(api, project.shortId);
|
|
41131
41658
|
spinner.succeed(`Read ${registry.length} flag${registry.length === 1 ? "" : "s"}`);
|
|
41132
|
-
const root =
|
|
41659
|
+
const root = path19.resolve(opts.dir);
|
|
41133
41660
|
await assertDirectory(root, opts.dir);
|
|
41134
41661
|
const extraRules = (opts.ignore ?? []).flatMap((pattern) => parseIgnoreFile(pattern));
|
|
41135
41662
|
const knownKeys = new Set(registry.map((f) => f.key));
|
|
@@ -41164,7 +41691,7 @@ function parseMaxFiles(raw) {
|
|
|
41164
41691
|
}
|
|
41165
41692
|
async function assertDirectory(root, asTyped) {
|
|
41166
41693
|
try {
|
|
41167
|
-
const stat4 = await
|
|
41694
|
+
const stat4 = await fs14.stat(root);
|
|
41168
41695
|
if (stat4.isDirectory()) return;
|
|
41169
41696
|
} catch {
|
|
41170
41697
|
}
|
|
@@ -41172,11 +41699,11 @@ async function assertDirectory(root, asTyped) {
|
|
|
41172
41699
|
}
|
|
41173
41700
|
|
|
41174
41701
|
// src/commands/datasets.ts
|
|
41175
|
-
import
|
|
41702
|
+
import fs16 from "node:fs";
|
|
41176
41703
|
|
|
41177
41704
|
// src/commands/datasets.helpers.ts
|
|
41178
|
-
import
|
|
41179
|
-
import
|
|
41705
|
+
import fs15 from "node:fs";
|
|
41706
|
+
import path20 from "node:path";
|
|
41180
41707
|
function parseVersionArg(raw) {
|
|
41181
41708
|
const normalized = raw.trim().replace(/^[vV]/, "");
|
|
41182
41709
|
if (!/^\d+$/.test(normalized)) {
|
|
@@ -41191,13 +41718,13 @@ function parseVersionArg(raw) {
|
|
|
41191
41718
|
return shortId;
|
|
41192
41719
|
}
|
|
41193
41720
|
function validateNdjsonPath(filePath) {
|
|
41194
|
-
const resolved =
|
|
41195
|
-
if (!
|
|
41721
|
+
const resolved = path20.resolve(filePath);
|
|
41722
|
+
if (!fs15.existsSync(resolved) || !fs15.statSync(resolved).isFile()) {
|
|
41196
41723
|
throw new Error(`File not found: ${resolved}`);
|
|
41197
41724
|
}
|
|
41198
|
-
if (
|
|
41725
|
+
if (path20.extname(resolved).toLowerCase() !== ".ndjson") {
|
|
41199
41726
|
throw new Error(
|
|
41200
|
-
`Expected an .ndjson file, got "${
|
|
41727
|
+
`Expected an .ndjson file, got "${path20.basename(resolved)}". Convert CSV files in the dashboard upload modal, or export NDJSON directly.`
|
|
41201
41728
|
);
|
|
41202
41729
|
}
|
|
41203
41730
|
return resolved;
|
|
@@ -41386,7 +41913,7 @@ var datasetsPushCommand = new Command("push").argument("<slug>", "Dataset slug")
|
|
|
41386
41913
|
try {
|
|
41387
41914
|
const result = await runPushFlow(api, projectShortId, {
|
|
41388
41915
|
slug,
|
|
41389
|
-
fileBody:
|
|
41916
|
+
fileBody: fs16.readFileSync(resolvedPath),
|
|
41390
41917
|
label: opts.label,
|
|
41391
41918
|
activate: opts.activate,
|
|
41392
41919
|
poll: { timeoutMs: timeoutSeconds * 1e3 },
|
|
@@ -41480,6 +42007,12 @@ var datasetsRollbackCommand = new Command("rollback").argument("<slug>", "Datase
|
|
|
41480
42007
|
fail(spinner, "Failed to resolve rollback target", err);
|
|
41481
42008
|
}
|
|
41482
42009
|
if (!opts.force) {
|
|
42010
|
+
const refusal = refusalWithoutPrompt({
|
|
42011
|
+
code: "cli_usage",
|
|
42012
|
+
message: `Rolling back "${slug}" (${summary}) needs a confirmation this run cannot ask for.`,
|
|
42013
|
+
hints: [`Pass -f to roll back without asking: \`avsb datasets rollback ${slug} -f\``]
|
|
42014
|
+
});
|
|
42015
|
+
if (refusal) failCommand(refusal);
|
|
41483
42016
|
const { proceed } = await esm_default12.prompt([
|
|
41484
42017
|
{
|
|
41485
42018
|
type: "confirm",
|
|
@@ -41755,7 +42288,7 @@ function printSecretHint() {
|
|
|
41755
42288
|
" A vs B shows a webhook signing secret only when the webhook is created and when its"
|
|
41756
42289
|
);
|
|
41757
42290
|
dim(" secret is rotated, so copy it then. If you no longer have it, rotate it:");
|
|
41758
|
-
dim(" Dashboard:
|
|
42291
|
+
dim(" Dashboard: Organization Settings > Integrations > Webhooks");
|
|
41759
42292
|
dim(" API: POST /api/v1/projects/{projectId}/webhooks/{webhookId}/secret");
|
|
41760
42293
|
dim(
|
|
41761
42294
|
" Put it in AVSB_WEBHOOK_SECRET and verify with verifyWebhookSignature from @avsbhq/node."
|
|
@@ -41777,8 +42310,7 @@ var listenCommand = new Command("listen").description("Watch this project's webh
|
|
|
41777
42310
|
if (opts.forward) {
|
|
41778
42311
|
const validated = validateForwardUrl(opts.forward);
|
|
41779
42312
|
if (!validated.ok) {
|
|
41780
|
-
|
|
41781
|
-
process.exit(EXIT.failure);
|
|
42313
|
+
failCommand(new CliError("cli_usage", validated.reason));
|
|
41782
42314
|
}
|
|
41783
42315
|
forwardUrl = validated.url;
|
|
41784
42316
|
}
|
|
@@ -42304,7 +42836,7 @@ var flagsCreateCommand = withOutputOptions(
|
|
|
42304
42836
|
var flagsUpdateCommand = withOutputOptions(
|
|
42305
42837
|
withProjectOption(
|
|
42306
42838
|
new Command("update").argument("<flag>", "Flag key, numeric short id, or id").description("Rename a flag or change its description").option("--name <name>", "New display name").option("--description <text>", 'New description. Pass "" to clear it').option("--key <key>", "New key (refused once the flag has served traffic)").option(
|
|
42307
|
-
"--if-match <
|
|
42839
|
+
"--if-match <version>",
|
|
42308
42840
|
"Only write if the flag still matches this version (from `avsb flags get`)"
|
|
42309
42841
|
).option("--force", "Write without checking the version first")
|
|
42310
42842
|
)
|
|
@@ -42608,46 +43140,127 @@ var openCommand = withOutputOptions(
|
|
|
42608
43140
|
|
|
42609
43141
|
// src/commands/completion.ts
|
|
42610
43142
|
var COMPLETION_SHELLS = ["bash", "zsh", "fish"];
|
|
42611
|
-
|
|
42612
|
-
|
|
43143
|
+
var ARGUMENT_CHOICES = {
|
|
43144
|
+
completion: COMPLETION_SHELLS,
|
|
43145
|
+
open: OPEN_TARGETS
|
|
43146
|
+
};
|
|
43147
|
+
function flagsOf(command, isRoot) {
|
|
43148
|
+
const flags = command.options.map((option) => option.long).filter((long) => typeof long === "string");
|
|
43149
|
+
flags.push("--help");
|
|
43150
|
+
if (isRoot) flags.push("--version");
|
|
43151
|
+
return [...new Set(flags)].sort();
|
|
43152
|
+
}
|
|
43153
|
+
function completionEntries(program3) {
|
|
43154
|
+
const entries = [];
|
|
43155
|
+
function visit(command, path23) {
|
|
43156
|
+
const subcommands = command.commands.map((child) => child.name()).sort();
|
|
43157
|
+
entries.push({
|
|
43158
|
+
path: path23,
|
|
43159
|
+
words: [...subcommands, ...ARGUMENT_CHOICES[path23] ?? []],
|
|
43160
|
+
flags: flagsOf(command, path23 === "")
|
|
43161
|
+
});
|
|
43162
|
+
for (const child of command.commands) {
|
|
43163
|
+
visit(child, path23 === "" ? child.name() : `${path23} ${child.name()}`);
|
|
43164
|
+
}
|
|
43165
|
+
}
|
|
43166
|
+
visit(program3, "");
|
|
43167
|
+
return entries;
|
|
42613
43168
|
}
|
|
42614
|
-
|
|
43169
|
+
var quoteBash = (value) => `"${value}"`;
|
|
43170
|
+
function bashCompletion(entries) {
|
|
43171
|
+
const known = entries.filter((entry) => entry.path !== "").map((entry) => quoteBash(entry.path));
|
|
43172
|
+
const cases = entries.map((entry) => {
|
|
43173
|
+
const offer = [...entry.words, ...entry.flags].join(" ");
|
|
43174
|
+
return ` ${quoteBash(entry.path)}) offer="${offer}" ;;`;
|
|
43175
|
+
});
|
|
42615
43176
|
return `# avsb completion for bash. Add to ~/.bashrc:
|
|
42616
43177
|
# eval "$(avsb completion bash)"
|
|
42617
43178
|
_avsb_completions() {
|
|
42618
|
-
local
|
|
42619
|
-
|
|
43179
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}" cmdpath="" word candidate i offer=""
|
|
43180
|
+
for ((i = 1; i < COMP_CWORD; i++)); do
|
|
43181
|
+
word="\${COMP_WORDS[i]}"
|
|
43182
|
+
[[ "$word" == -* ]] && continue
|
|
43183
|
+
candidate="\${cmdpath:+$cmdpath }$word"
|
|
43184
|
+
case "$candidate" in
|
|
43185
|
+
${known.length > 0 ? known.join("|") : '"__none__"'}) cmdpath="$candidate" ;;
|
|
43186
|
+
esac
|
|
43187
|
+
done
|
|
43188
|
+
case "$cmdpath" in
|
|
43189
|
+
${cases.join("\n")}
|
|
43190
|
+
esac
|
|
43191
|
+
COMPREPLY=($(compgen -W "$offer" -- "$cur"))
|
|
42620
43192
|
}
|
|
42621
43193
|
complete -F _avsb_completions avsb`;
|
|
42622
43194
|
}
|
|
42623
|
-
|
|
43195
|
+
var quoteZsh = (value) => `'${value}'`;
|
|
43196
|
+
function zshCompletion(entries) {
|
|
43197
|
+
const known = entries.filter((entry) => entry.path !== "").map((entry) => quoteZsh(entry.path));
|
|
43198
|
+
const cases = entries.map((entry) => {
|
|
43199
|
+
const offer = [...entry.words, ...entry.flags].map(quoteZsh).join(" ");
|
|
43200
|
+
return ` (${quoteZsh(entry.path)}) offer=(${offer}) ;;`;
|
|
43201
|
+
});
|
|
42624
43202
|
return `# avsb completion for zsh. Add to ~/.zshrc:
|
|
42625
43203
|
# eval "$(avsb completion zsh)"
|
|
42626
43204
|
_avsb() {
|
|
42627
|
-
local
|
|
42628
|
-
|
|
42629
|
-
|
|
43205
|
+
local cmdpath="" word candidate i
|
|
43206
|
+
local -a offer
|
|
43207
|
+
for ((i = 2; i < CURRENT; i++)); do
|
|
43208
|
+
word="\${words[i]}"
|
|
43209
|
+
[[ "$word" == -* ]] && continue
|
|
43210
|
+
candidate="\${cmdpath:+$cmdpath }$word"
|
|
43211
|
+
case "$candidate" in
|
|
43212
|
+
(${known.length > 0 ? known.join("|") : "'__none__'"}) cmdpath="$candidate" ;;
|
|
43213
|
+
esac
|
|
43214
|
+
done
|
|
43215
|
+
case "$cmdpath" in
|
|
43216
|
+
${cases.join("\n")}
|
|
43217
|
+
esac
|
|
43218
|
+
compadd -- $offer
|
|
42630
43219
|
}
|
|
42631
43220
|
compdef _avsb avsb`;
|
|
42632
43221
|
}
|
|
42633
|
-
|
|
42634
|
-
|
|
42635
|
-
|
|
42636
|
-
|
|
43222
|
+
var quoteFish = (value) => `'${value}'`;
|
|
43223
|
+
function fishCompletion(entries) {
|
|
43224
|
+
const known = entries.filter((entry) => entry.path !== "").map((entry) => quoteFish(entry.path));
|
|
43225
|
+
const lines = [];
|
|
43226
|
+
for (const entry of entries) {
|
|
43227
|
+
const condition = `'test (__avsb_path) = "${entry.path}"'`;
|
|
43228
|
+
if (entry.words.length > 0) {
|
|
43229
|
+
lines.push(`complete -c avsb -n ${condition} -a ${quoteFish(entry.words.join(" "))}`);
|
|
43230
|
+
}
|
|
43231
|
+
for (const flag of entry.flags) {
|
|
43232
|
+
lines.push(`complete -c avsb -n ${condition} -l ${flag.replace(/^--/, "")}`);
|
|
43233
|
+
}
|
|
43234
|
+
}
|
|
42637
43235
|
return [
|
|
42638
43236
|
"# avsb completion for fish. Save as:",
|
|
42639
43237
|
"# avsb completion fish > ~/.config/fish/completions/avsb.fish",
|
|
43238
|
+
`set -g __avsb_known_paths ${known.join(" ")}`,
|
|
43239
|
+
// Drops the first token (`avsb`) with `set -e` rather than a [2..-1]
|
|
43240
|
+
// slice, and always prints exactly one line, so `test (__avsb_path) = ""`
|
|
43241
|
+
// compares one value even at the top level.
|
|
43242
|
+
"function __avsb_path",
|
|
43243
|
+
" set -l tokens (commandline -opc)",
|
|
43244
|
+
" set -e tokens[1]",
|
|
43245
|
+
" set -l cmdpath",
|
|
43246
|
+
" for word in $tokens",
|
|
43247
|
+
" string match -q -- '-*' $word; and continue",
|
|
43248
|
+
" set -l candidate (string join ' ' $cmdpath $word)",
|
|
43249
|
+
" contains -- $candidate $__avsb_known_paths; and set cmdpath $cmdpath $word",
|
|
43250
|
+
" end",
|
|
43251
|
+
" echo (string join ' ' $cmdpath)",
|
|
43252
|
+
"end",
|
|
42640
43253
|
...lines
|
|
42641
43254
|
].join("\n");
|
|
42642
43255
|
}
|
|
42643
|
-
function completionScript(shell,
|
|
43256
|
+
function completionScript(shell, entries) {
|
|
42644
43257
|
switch (shell) {
|
|
42645
43258
|
case "bash":
|
|
42646
|
-
return bashCompletion(
|
|
43259
|
+
return bashCompletion(entries);
|
|
42647
43260
|
case "zsh":
|
|
42648
|
-
return zshCompletion(
|
|
43261
|
+
return zshCompletion(entries);
|
|
42649
43262
|
case "fish":
|
|
42650
|
-
return fishCompletion(
|
|
43263
|
+
return fishCompletion(entries);
|
|
42651
43264
|
}
|
|
42652
43265
|
}
|
|
42653
43266
|
function buildCompletionCommand(program3) {
|
|
@@ -42661,16 +43274,16 @@ function buildCompletionCommand(program3) {
|
|
|
42661
43274
|
])
|
|
42662
43275
|
);
|
|
42663
43276
|
}
|
|
42664
|
-
line(completionScript(requested,
|
|
43277
|
+
line(completionScript(requested, completionEntries(program3)));
|
|
42665
43278
|
});
|
|
42666
43279
|
}
|
|
42667
43280
|
|
|
42668
43281
|
// src/lib/updateNotifier.ts
|
|
42669
|
-
import
|
|
43282
|
+
import fs17 from "node:fs";
|
|
42670
43283
|
import os4 from "node:os";
|
|
42671
|
-
import
|
|
43284
|
+
import path21 from "node:path";
|
|
42672
43285
|
var REGISTRY_URL = "https://registry.npmjs.org/@avsbhq/cli/latest";
|
|
42673
|
-
var CACHE_FILE =
|
|
43286
|
+
var CACHE_FILE = path21.join(os4.homedir(), ".avsb", "update-check.json");
|
|
42674
43287
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
42675
43288
|
var FETCH_TIMEOUT_MS = 1500;
|
|
42676
43289
|
function isNewerVersion(latest, current) {
|
|
@@ -42710,8 +43323,8 @@ function updateMessage(latest, current) {
|
|
|
42710
43323
|
}
|
|
42711
43324
|
function writeCache(cache) {
|
|
42712
43325
|
try {
|
|
42713
|
-
|
|
42714
|
-
|
|
43326
|
+
fs17.mkdirSync(path21.dirname(CACHE_FILE), { recursive: true });
|
|
43327
|
+
fs17.writeFileSync(CACHE_FILE, JSON.stringify(cache));
|
|
42715
43328
|
} catch {
|
|
42716
43329
|
}
|
|
42717
43330
|
}
|
|
@@ -42735,7 +43348,7 @@ async function checkForUpdate(currentVersion, notify) {
|
|
|
42735
43348
|
}
|
|
42736
43349
|
let cache = null;
|
|
42737
43350
|
try {
|
|
42738
|
-
cache = readCache(() =>
|
|
43351
|
+
cache = readCache(() => fs17.readFileSync(CACHE_FILE, "utf-8"));
|
|
42739
43352
|
} catch {
|
|
42740
43353
|
cache = null;
|
|
42741
43354
|
}
|
|
@@ -42748,8 +43361,8 @@ async function checkForUpdate(currentVersion, notify) {
|
|
|
42748
43361
|
}
|
|
42749
43362
|
|
|
42750
43363
|
// src/cli.ts
|
|
42751
|
-
var __dirname =
|
|
42752
|
-
var pkg = JSON.parse(
|
|
43364
|
+
var __dirname = path22.dirname(fileURLToPath2(import.meta.url));
|
|
43365
|
+
var pkg = JSON.parse(fs18.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
42753
43366
|
var program2 = new Command();
|
|
42754
43367
|
program2.name("avsb").description(
|
|
42755
43368
|
"A vs B command line tool: experiments, flags, project scripts, metrics, datasets, webhooks, and typed flags"
|