@drip-apex/apex-cli 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.js +523 -356
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1159,8 +1159,8 @@ var require_command = __commonJS({
|
|
|
1159
1159
|
"node_modules/commander/lib/command.js"(exports2) {
|
|
1160
1160
|
var EventEmitter = require("node:events").EventEmitter;
|
|
1161
1161
|
var childProcess2 = require("node:child_process");
|
|
1162
|
-
var
|
|
1163
|
-
var
|
|
1162
|
+
var path21 = require("node:path");
|
|
1163
|
+
var fs25 = require("node:fs");
|
|
1164
1164
|
var process16 = require("node:process");
|
|
1165
1165
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1166
1166
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2141,7 +2141,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2141
2141
|
* @param {string} subcommandName
|
|
2142
2142
|
*/
|
|
2143
2143
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2144
|
-
if (
|
|
2144
|
+
if (fs25.existsSync(executableFile)) return;
|
|
2145
2145
|
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";
|
|
2146
2146
|
const executableMissing = `'${executableFile}' does not exist
|
|
2147
2147
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2159,11 +2159,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2159
2159
|
let launchWithNode = false;
|
|
2160
2160
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2161
2161
|
function findFile(baseDir, baseName) {
|
|
2162
|
-
const localBin =
|
|
2163
|
-
if (
|
|
2164
|
-
if (sourceExt.includes(
|
|
2162
|
+
const localBin = path21.resolve(baseDir, baseName);
|
|
2163
|
+
if (fs25.existsSync(localBin)) return localBin;
|
|
2164
|
+
if (sourceExt.includes(path21.extname(baseName))) return void 0;
|
|
2165
2165
|
const foundExt = sourceExt.find(
|
|
2166
|
-
(ext) =>
|
|
2166
|
+
(ext) => fs25.existsSync(`${localBin}${ext}`)
|
|
2167
2167
|
);
|
|
2168
2168
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2169
2169
|
return void 0;
|
|
@@ -2175,21 +2175,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2175
2175
|
if (this._scriptPath) {
|
|
2176
2176
|
let resolvedScriptPath;
|
|
2177
2177
|
try {
|
|
2178
|
-
resolvedScriptPath =
|
|
2178
|
+
resolvedScriptPath = fs25.realpathSync(this._scriptPath);
|
|
2179
2179
|
} catch {
|
|
2180
2180
|
resolvedScriptPath = this._scriptPath;
|
|
2181
2181
|
}
|
|
2182
|
-
executableDir =
|
|
2183
|
-
|
|
2182
|
+
executableDir = path21.resolve(
|
|
2183
|
+
path21.dirname(resolvedScriptPath),
|
|
2184
2184
|
executableDir
|
|
2185
2185
|
);
|
|
2186
2186
|
}
|
|
2187
2187
|
if (executableDir) {
|
|
2188
2188
|
let localFile = findFile(executableDir, executableFile);
|
|
2189
2189
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2190
|
-
const legacyName =
|
|
2190
|
+
const legacyName = path21.basename(
|
|
2191
2191
|
this._scriptPath,
|
|
2192
|
-
|
|
2192
|
+
path21.extname(this._scriptPath)
|
|
2193
2193
|
);
|
|
2194
2194
|
if (legacyName !== this._name) {
|
|
2195
2195
|
localFile = findFile(
|
|
@@ -2200,7 +2200,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2200
2200
|
}
|
|
2201
2201
|
executableFile = localFile || executableFile;
|
|
2202
2202
|
}
|
|
2203
|
-
launchWithNode = sourceExt.includes(
|
|
2203
|
+
launchWithNode = sourceExt.includes(path21.extname(executableFile));
|
|
2204
2204
|
let proc;
|
|
2205
2205
|
if (process16.platform !== "win32") {
|
|
2206
2206
|
if (launchWithNode) {
|
|
@@ -3047,7 +3047,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3047
3047
|
* @return {Command}
|
|
3048
3048
|
*/
|
|
3049
3049
|
nameFromFilename(filename) {
|
|
3050
|
-
this._name =
|
|
3050
|
+
this._name = path21.basename(filename, path21.extname(filename));
|
|
3051
3051
|
return this;
|
|
3052
3052
|
}
|
|
3053
3053
|
/**
|
|
@@ -3061,9 +3061,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3061
3061
|
* @param {string} [path]
|
|
3062
3062
|
* @return {(string|null|Command)}
|
|
3063
3063
|
*/
|
|
3064
|
-
executableDir(
|
|
3065
|
-
if (
|
|
3066
|
-
this._executableDir =
|
|
3064
|
+
executableDir(path22) {
|
|
3065
|
+
if (path22 === void 0) return this._executableDir;
|
|
3066
|
+
this._executableDir = path22;
|
|
3067
3067
|
return this;
|
|
3068
3068
|
}
|
|
3069
3069
|
/**
|
|
@@ -7140,7 +7140,7 @@ function createHttpClient(creds, options) {
|
|
|
7140
7140
|
warnedAboutVersionSkew = true;
|
|
7141
7141
|
warn4(version > 1 ? "CLI is behind the server \u2014 update.\n" : "Server API is older than this CLI supports.\n");
|
|
7142
7142
|
}
|
|
7143
|
-
async function request2(method,
|
|
7143
|
+
async function request2(method, path21, body6, requestTimeoutMs, form = false, requestOptions) {
|
|
7144
7144
|
const started2 = performance.now();
|
|
7145
7145
|
const observe = (status4, requestId2, errorCode, responseHeaders) => {
|
|
7146
7146
|
const semver = (name) => {
|
|
@@ -7149,7 +7149,7 @@ function createHttpClient(creds, options) {
|
|
|
7149
7149
|
};
|
|
7150
7150
|
const serverTiming = responseHeaders?.get("server-timing");
|
|
7151
7151
|
try {
|
|
7152
|
-
options.onResponse?.({ method, path:
|
|
7152
|
+
options.onResponse?.({ method, path: path21, status: status4, requestId: requestId2, errorCode: typeof errorCode === "string" ? errorCode : void 0, durationMs: Math.max(0, Math.round(performance.now() - started2)), serverTiming: serverTiming?.slice(0, 300) || void 0, cliLatest: semver("x-apex-cli-latest"), cliMinimum: semver("x-apex-cli-minimum") });
|
|
7153
7153
|
} catch {
|
|
7154
7154
|
}
|
|
7155
7155
|
};
|
|
@@ -7165,7 +7165,7 @@ function createHttpClient(creds, options) {
|
|
|
7165
7165
|
if (creds.resolvedContext?.orgId) headers["X-Apex-Org"] = creds.resolvedContext.orgId;
|
|
7166
7166
|
if (creds.resolvedContext?.shopId) headers["X-Apex-Shop"] = creds.resolvedContext.shopId;
|
|
7167
7167
|
}
|
|
7168
|
-
const mock = options.transport?.(method,
|
|
7168
|
+
const mock = options.transport?.(method, path21, body6, headers);
|
|
7169
7169
|
if (mock !== void 0) {
|
|
7170
7170
|
const version = options.testTransportVersion?.();
|
|
7171
7171
|
if (version) inspectVersion(new Headers({ [APEX_API_VERSION_HEADER]: version }));
|
|
@@ -7174,7 +7174,7 @@ function createHttpClient(creds, options) {
|
|
|
7174
7174
|
}
|
|
7175
7175
|
let response;
|
|
7176
7176
|
try {
|
|
7177
|
-
response = await fetch(`${creds.baseUrl}${
|
|
7177
|
+
response = await fetch(`${creds.baseUrl}${path21}`, { method, headers, body: form ? body6 : body6 ? JSON.stringify(body6) : void 0, signal: AbortSignal.timeout(timeoutMs) });
|
|
7178
7178
|
} catch (error) {
|
|
7179
7179
|
const outer = error && typeof error === "object" ? error : void 0;
|
|
7180
7180
|
const nested = outer?.cause && typeof outer.cause === "object" ? outer.cause : void 0;
|
|
@@ -7195,7 +7195,7 @@ function createHttpClient(creds, options) {
|
|
|
7195
7195
|
if (!response.ok) return { ok: false, status: response.status, data: json, error: extractErrorMessage(json, response.status), errorDetails: extractErrorDetails(json), requestId, retryAfter: response.headers.get("retry-after"), rateLimit: { limit: response.headers.get("x-ratelimit-limit"), remaining: response.headers.get("x-ratelimit-remaining"), reset: response.headers.get("x-ratelimit-reset") } };
|
|
7196
7196
|
return { ok: true, status: response.status, data: json };
|
|
7197
7197
|
}
|
|
7198
|
-
return { request: (method,
|
|
7198
|
+
return { request: (method, path21, body6, timeoutMs, requestOptions) => request2(method, path21, body6, timeoutMs, false, requestOptions), requestForm: (method, path21, body6) => request2(method, path21, body6, void 0, true) };
|
|
7199
7199
|
}
|
|
7200
7200
|
|
|
7201
7201
|
// ../api-client/src/credentials.ts
|
|
@@ -7423,7 +7423,7 @@ async function checkForUpdate(current) {
|
|
|
7423
7423
|
// package.json
|
|
7424
7424
|
var package_default = {
|
|
7425
7425
|
name: "@drip-apex/apex-cli",
|
|
7426
|
-
version: "0.19.
|
|
7426
|
+
version: "0.19.1",
|
|
7427
7427
|
license: "MIT",
|
|
7428
7428
|
description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
|
|
7429
7429
|
private: false,
|
|
@@ -8393,9 +8393,9 @@ var staticRouteSegments = /* @__PURE__ */ new Set([
|
|
|
8393
8393
|
"webhooks",
|
|
8394
8394
|
"workspace"
|
|
8395
8395
|
]);
|
|
8396
|
-
function normalizeRoute(
|
|
8397
|
-
if (!
|
|
8398
|
-
const parts =
|
|
8396
|
+
function normalizeRoute(path21) {
|
|
8397
|
+
if (!path21.startsWith("/api/")) return ":route";
|
|
8398
|
+
const parts = path21.split(/[?#]/, 1)[0].split("/");
|
|
8399
8399
|
const files = parts.indexOf("files");
|
|
8400
8400
|
if (files !== -1 && parts.length > files + 1) parts.splice(files + 1, parts.length, ":id");
|
|
8401
8401
|
return parts.map((part) => !part || staticRouteSegments.has(part) ? part : ":id").join("/");
|
|
@@ -8572,8 +8572,8 @@ function pagePath(id, action80) {
|
|
|
8572
8572
|
return `/api/v1/landing-pages/${encodeURIComponent(id)}${action80 ? `/${action80}` : ""}`;
|
|
8573
8573
|
}
|
|
8574
8574
|
function landingPagesApi(request2) {
|
|
8575
|
-
const call = async (method,
|
|
8576
|
-
const response = await request2(method,
|
|
8575
|
+
const call = async (method, path21, body6) => {
|
|
8576
|
+
const response = await request2(method, path21, body6);
|
|
8577
8577
|
return { ...response, data: response.data?.data };
|
|
8578
8578
|
};
|
|
8579
8579
|
return {
|
|
@@ -8695,11 +8695,11 @@ function getUserKeyIdentity(creds) {
|
|
|
8695
8695
|
function getAccountContext(creds) {
|
|
8696
8696
|
return userRequest(creds, "GET", "/api/v1/me/context");
|
|
8697
8697
|
}
|
|
8698
|
-
function userRequest(creds, method,
|
|
8698
|
+
function userRequest(creds, method, path21, body6) {
|
|
8699
8699
|
return request(
|
|
8700
8700
|
{ ...creds, apiKey: creds.userApiKey ?? "", baseUrl: creds.userBaseUrl ?? creds.baseUrl },
|
|
8701
8701
|
method,
|
|
8702
|
-
|
|
8702
|
+
path21,
|
|
8703
8703
|
body6
|
|
8704
8704
|
);
|
|
8705
8705
|
}
|
|
@@ -8785,10 +8785,10 @@ function sharedClient(creds) {
|
|
|
8785
8785
|
}
|
|
8786
8786
|
return client;
|
|
8787
8787
|
}
|
|
8788
|
-
async function request(creds, method,
|
|
8788
|
+
async function request(creds, method, path21, body6, requestTimeoutMs, options) {
|
|
8789
8789
|
const response = await sharedClient(creds).request(
|
|
8790
8790
|
method,
|
|
8791
|
-
|
|
8791
|
+
path21,
|
|
8792
8792
|
body6,
|
|
8793
8793
|
requestTimeoutMs,
|
|
8794
8794
|
options
|
|
@@ -8796,8 +8796,8 @@ async function request(creds, method, path28, body6, requestTimeoutMs, options)
|
|
|
8796
8796
|
recordApiResponse(response);
|
|
8797
8797
|
return response;
|
|
8798
8798
|
}
|
|
8799
|
-
async function requestForm(creds, method,
|
|
8800
|
-
const response = await sharedClient(creds).requestForm(method,
|
|
8799
|
+
async function requestForm(creds, method, path21, body6) {
|
|
8800
|
+
const response = await sharedClient(creds).requestForm(method, path21, body6);
|
|
8801
8801
|
recordApiResponse(response);
|
|
8802
8802
|
return response;
|
|
8803
8803
|
}
|
|
@@ -9436,8 +9436,8 @@ function requestTargetingResource(creds, method, kind5, id, body6, limit, cursor
|
|
|
9436
9436
|
if (limit) params.set("limit", String(limit));
|
|
9437
9437
|
if (cursor) params.set("cursor", cursor);
|
|
9438
9438
|
const query = params.toString();
|
|
9439
|
-
const
|
|
9440
|
-
return request(creds, method,
|
|
9439
|
+
const path21 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
|
|
9440
|
+
return request(creds, method, path21, body6);
|
|
9441
9441
|
}
|
|
9442
9442
|
function managedPath(kind5, id, action80) {
|
|
9443
9443
|
return `/api/v1/${kind5}${id ? `/${encodeURIComponent(id)}` : ""}${action80 ? `/${action80}` : ""}`;
|
|
@@ -9473,7 +9473,7 @@ function getConfigResource(creds, family, id) {
|
|
|
9473
9473
|
}
|
|
9474
9474
|
function landingPages(creds) {
|
|
9475
9475
|
return landingPagesApi(
|
|
9476
|
-
(method,
|
|
9476
|
+
(method, path21, body6) => request(creds, method, path21, body6)
|
|
9477
9477
|
);
|
|
9478
9478
|
}
|
|
9479
9479
|
function deleteAsset(creds, id) {
|
|
@@ -10285,11 +10285,31 @@ var useCommand = new Command("use").description("Set the current Apex organizati
|
|
|
10285
10285
|
);
|
|
10286
10286
|
});
|
|
10287
10287
|
|
|
10288
|
+
// src/lib/json-input.ts
|
|
10289
|
+
var import_node_fs7 = __toESM(require("node:fs"));
|
|
10290
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
10291
|
+
function stripUtf8Bom(text2) {
|
|
10292
|
+
return text2.startsWith("\uFEFF") ? text2.slice(1) : text2;
|
|
10293
|
+
}
|
|
10294
|
+
function readTextInput(value2) {
|
|
10295
|
+
return value2.startsWith("@") ? stripUtf8Bom(import_node_fs7.default.readFileSync(import_node_path5.default.resolve(process.cwd(), value2.slice(1)), "utf8")) : value2;
|
|
10296
|
+
}
|
|
10297
|
+
function readJsonInput(value2, options = {}) {
|
|
10298
|
+
const { allowBarePath = true, allowInline = true } = options;
|
|
10299
|
+
let text2;
|
|
10300
|
+
if (value2.startsWith("@")) text2 = import_node_fs7.default.readFileSync(import_node_path5.default.resolve(value2.slice(1)), "utf8");
|
|
10301
|
+
else if (allowBarePath && import_node_fs7.default.existsSync(value2))
|
|
10302
|
+
text2 = import_node_fs7.default.readFileSync(import_node_path5.default.resolve(value2), "utf8");
|
|
10303
|
+
else if (allowInline) text2 = value2;
|
|
10304
|
+
else throw new Error("Expected a JSON file path");
|
|
10305
|
+
return JSON.parse(stripUtf8Bom(text2));
|
|
10306
|
+
}
|
|
10307
|
+
|
|
10288
10308
|
// src/commands/experiments.ts
|
|
10289
10309
|
init_source();
|
|
10290
10310
|
var import_node_crypto6 = __toESM(require("node:crypto"));
|
|
10291
|
-
var
|
|
10292
|
-
var
|
|
10311
|
+
var import_node_fs9 = __toESM(require("node:fs"));
|
|
10312
|
+
var import_node_path7 = __toESM(require("node:path"));
|
|
10293
10313
|
|
|
10294
10314
|
// ../shared-runtime/src/event-name.ts
|
|
10295
10315
|
var RESERVED_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -10853,8 +10873,8 @@ function deriveStoreUrl(patterns) {
|
|
|
10853
10873
|
|
|
10854
10874
|
// src/lib/asset-upload.ts
|
|
10855
10875
|
var import_node_crypto5 = __toESM(require("node:crypto"));
|
|
10856
|
-
var
|
|
10857
|
-
var
|
|
10876
|
+
var import_node_fs8 = __toESM(require("node:fs"));
|
|
10877
|
+
var import_node_path6 = __toESM(require("node:path"));
|
|
10858
10878
|
var INLINE_DATA_URI_MAX_CHARS = 4096;
|
|
10859
10879
|
var DATA_IMAGE_URI_PATTERN = /data:image\/[a-z0-9.+-]+(?:(?:;[^;,\s"'`]*)*;base64,[A-Za-z0-9+/=\s]+|(?:;[^,\s"'`]*)?,[^\s"'`]+)/gi;
|
|
10860
10880
|
var QUOTED_LOCAL_IMAGE_PATTERN = /(["'])([^"'\n]+?\.(?:png|jpe?g|gif|webp|svg|avif))\1/gi;
|
|
@@ -10965,23 +10985,23 @@ function createAssetReferenceRewriter(options) {
|
|
|
10965
10985
|
if (reference.startsWith("/") || reference.startsWith("data:") || reference.includes("://")) {
|
|
10966
10986
|
return null;
|
|
10967
10987
|
}
|
|
10968
|
-
const absolutePath =
|
|
10988
|
+
const absolutePath = import_node_path6.default.resolve(baseDir, reference);
|
|
10969
10989
|
let realBaseDir;
|
|
10970
10990
|
let realPath;
|
|
10971
10991
|
try {
|
|
10972
|
-
realBaseDir =
|
|
10973
|
-
realPath =
|
|
10992
|
+
realBaseDir = import_node_fs8.default.realpathSync(baseDir);
|
|
10993
|
+
realPath = import_node_fs8.default.realpathSync(absolutePath);
|
|
10974
10994
|
} catch {
|
|
10975
10995
|
return null;
|
|
10976
10996
|
}
|
|
10977
|
-
const relativeRealPath =
|
|
10978
|
-
if (relativeRealPath.startsWith("..") ||
|
|
10979
|
-
if (!
|
|
10980
|
-
const bytes =
|
|
10981
|
-
const extension =
|
|
10997
|
+
const relativeRealPath = import_node_path6.default.relative(realBaseDir, realPath);
|
|
10998
|
+
if (relativeRealPath.startsWith("..") || import_node_path6.default.isAbsolute(relativeRealPath)) return null;
|
|
10999
|
+
if (!import_node_fs8.default.statSync(realPath).isFile()) return null;
|
|
11000
|
+
const bytes = import_node_fs8.default.readFileSync(realPath);
|
|
11001
|
+
const extension = import_node_path6.default.extname(realPath).toLowerCase();
|
|
10982
11002
|
return uploadOnce(
|
|
10983
11003
|
{
|
|
10984
|
-
name:
|
|
11004
|
+
name: import_node_path6.default.basename(absolutePath),
|
|
10985
11005
|
bytes,
|
|
10986
11006
|
type: MIME_BY_EXTENSION[extension]
|
|
10987
11007
|
},
|
|
@@ -11119,9 +11139,9 @@ function replacementCharacterOptions(argv) {
|
|
|
11119
11139
|
}
|
|
11120
11140
|
function replacementCharacterFields(record5, fields3) {
|
|
11121
11141
|
const paths = [];
|
|
11122
|
-
function visit2(value2, parts,
|
|
11142
|
+
function visit2(value2, parts, path21) {
|
|
11123
11143
|
if (parts.length === 0) {
|
|
11124
|
-
if (typeof value2 === "string" && value2.includes("\uFFFD")) paths.push(
|
|
11144
|
+
if (typeof value2 === "string" && value2.includes("\uFFFD")) paths.push(path21);
|
|
11125
11145
|
return;
|
|
11126
11146
|
}
|
|
11127
11147
|
if (value2 === null || typeof value2 !== "object") return;
|
|
@@ -11129,7 +11149,7 @@ function replacementCharacterFields(record5, fields3) {
|
|
|
11129
11149
|
const array = part.endsWith("[]");
|
|
11130
11150
|
const key = array ? part.slice(0, -2) : part;
|
|
11131
11151
|
const next = value2[key];
|
|
11132
|
-
const nextPath =
|
|
11152
|
+
const nextPath = path21 ? `${path21}.${key}` : key;
|
|
11133
11153
|
if (array) {
|
|
11134
11154
|
if (Array.isArray(next)) {
|
|
11135
11155
|
next.forEach((item, index) => visit2(item, rest, `${nextPath}[${index}]`));
|
|
@@ -11140,8 +11160,8 @@ function replacementCharacterFields(record5, fields3) {
|
|
|
11140
11160
|
return paths;
|
|
11141
11161
|
}
|
|
11142
11162
|
function warnReplacementCharacters(label, record5, fields3) {
|
|
11143
|
-
for (const
|
|
11144
|
-
process.stderr.write(`apex: stored name contains U+FFFD: ${label}.${
|
|
11163
|
+
for (const path21 of replacementCharacterFields(record5, fields3)) {
|
|
11164
|
+
process.stderr.write(`apex: stored name contains U+FFFD: ${label}.${path21}
|
|
11145
11165
|
`);
|
|
11146
11166
|
}
|
|
11147
11167
|
}
|
|
@@ -13304,6 +13324,176 @@ var experiment_filesystem_schema_default = {
|
|
|
13304
13324
|
type: "object"
|
|
13305
13325
|
}
|
|
13306
13326
|
},
|
|
13327
|
+
receipt: {
|
|
13328
|
+
description: "Post-write readback returned by a successful filesystem apply: persisted lifecycle, classification, traffic, schedule, goal count, latest QA run and per-variation SHA-256 digests of stored content. Each file includes submitted_sha256 (null when not submitted) and normalizations: bom-stripped removes a leading BOM; whitespace-trimmed trims code; literal-cleared clears a code literal; blank-cleared clears blank code; json-reserialized formats JSON; author-url-mirror recomputes authorUrl; page-rules-expanded expands page rules; server-owned-fields restores owned fields; goals-normalized orders goals; server-owned replaces generated files; server-rewritten describes other changes.",
|
|
13329
|
+
properties: {
|
|
13330
|
+
counting_type: {
|
|
13331
|
+
type: "string"
|
|
13332
|
+
},
|
|
13333
|
+
delivery_type: {
|
|
13334
|
+
type: "string"
|
|
13335
|
+
},
|
|
13336
|
+
experiment_id: {
|
|
13337
|
+
type: "string"
|
|
13338
|
+
},
|
|
13339
|
+
files: {
|
|
13340
|
+
items: {
|
|
13341
|
+
properties: {
|
|
13342
|
+
normalizations: {
|
|
13343
|
+
items: {
|
|
13344
|
+
enum: [
|
|
13345
|
+
"bom-stripped",
|
|
13346
|
+
"whitespace-trimmed",
|
|
13347
|
+
"literal-cleared",
|
|
13348
|
+
"blank-cleared",
|
|
13349
|
+
"json-reserialized",
|
|
13350
|
+
"author-url-mirror",
|
|
13351
|
+
"page-rules-expanded",
|
|
13352
|
+
"server-owned-fields",
|
|
13353
|
+
"goals-normalized",
|
|
13354
|
+
"server-owned",
|
|
13355
|
+
"server-rewritten"
|
|
13356
|
+
],
|
|
13357
|
+
type: "string"
|
|
13358
|
+
},
|
|
13359
|
+
type: "array"
|
|
13360
|
+
},
|
|
13361
|
+
path: {
|
|
13362
|
+
type: "string"
|
|
13363
|
+
},
|
|
13364
|
+
sha256: {
|
|
13365
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13366
|
+
type: "string"
|
|
13367
|
+
},
|
|
13368
|
+
submitted_sha256: {
|
|
13369
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13370
|
+
type: ["string", "null"]
|
|
13371
|
+
}
|
|
13372
|
+
},
|
|
13373
|
+
required: ["path", "sha256", "submitted_sha256", "normalizations"],
|
|
13374
|
+
type: "object"
|
|
13375
|
+
},
|
|
13376
|
+
type: "array"
|
|
13377
|
+
},
|
|
13378
|
+
goal_count: {
|
|
13379
|
+
type: "integer"
|
|
13380
|
+
},
|
|
13381
|
+
qa: {
|
|
13382
|
+
properties: {
|
|
13383
|
+
approved_at: {
|
|
13384
|
+
format: "date-time",
|
|
13385
|
+
type: ["string", "null"]
|
|
13386
|
+
},
|
|
13387
|
+
run_id: {
|
|
13388
|
+
type: "string"
|
|
13389
|
+
},
|
|
13390
|
+
status: {
|
|
13391
|
+
type: "string"
|
|
13392
|
+
}
|
|
13393
|
+
},
|
|
13394
|
+
required: ["run_id", "status", "approved_at"],
|
|
13395
|
+
type: ["object", "null"]
|
|
13396
|
+
},
|
|
13397
|
+
schedule: {
|
|
13398
|
+
properties: {
|
|
13399
|
+
scheduled_end_at: {
|
|
13400
|
+
format: "date-time",
|
|
13401
|
+
type: ["string", "null"]
|
|
13402
|
+
},
|
|
13403
|
+
scheduled_start_at: {
|
|
13404
|
+
format: "date-time",
|
|
13405
|
+
type: ["string", "null"]
|
|
13406
|
+
}
|
|
13407
|
+
},
|
|
13408
|
+
required: ["scheduled_start_at", "scheduled_end_at"],
|
|
13409
|
+
type: "object"
|
|
13410
|
+
},
|
|
13411
|
+
server_owned_fields: {
|
|
13412
|
+
items: {
|
|
13413
|
+
type: "string"
|
|
13414
|
+
},
|
|
13415
|
+
type: "array"
|
|
13416
|
+
},
|
|
13417
|
+
server_owned_files: {
|
|
13418
|
+
items: {
|
|
13419
|
+
type: "string"
|
|
13420
|
+
},
|
|
13421
|
+
type: "array"
|
|
13422
|
+
},
|
|
13423
|
+
status: {
|
|
13424
|
+
type: "string"
|
|
13425
|
+
},
|
|
13426
|
+
traffic_allocation: {
|
|
13427
|
+
type: "number"
|
|
13428
|
+
},
|
|
13429
|
+
variations: {
|
|
13430
|
+
items: {
|
|
13431
|
+
properties: {
|
|
13432
|
+
digests: {
|
|
13433
|
+
properties: {
|
|
13434
|
+
custom_css: {
|
|
13435
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13436
|
+
type: ["string", "null"]
|
|
13437
|
+
},
|
|
13438
|
+
custom_js: {
|
|
13439
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13440
|
+
type: ["string", "null"]
|
|
13441
|
+
},
|
|
13442
|
+
mutations: {
|
|
13443
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13444
|
+
type: "string"
|
|
13445
|
+
},
|
|
13446
|
+
reset_js: {
|
|
13447
|
+
pattern: "^[a-f0-9]{64}$",
|
|
13448
|
+
type: ["string", "null"]
|
|
13449
|
+
}
|
|
13450
|
+
},
|
|
13451
|
+
required: ["mutations", "custom_js", "custom_css", "reset_js"],
|
|
13452
|
+
type: "object"
|
|
13453
|
+
},
|
|
13454
|
+
id: {
|
|
13455
|
+
type: "string"
|
|
13456
|
+
},
|
|
13457
|
+
is_control: {
|
|
13458
|
+
type: "boolean"
|
|
13459
|
+
},
|
|
13460
|
+
name: {
|
|
13461
|
+
type: "string"
|
|
13462
|
+
},
|
|
13463
|
+
redirect_url: {
|
|
13464
|
+
type: ["string", "null"]
|
|
13465
|
+
},
|
|
13466
|
+
slug: {
|
|
13467
|
+
type: "string"
|
|
13468
|
+
}
|
|
13469
|
+
},
|
|
13470
|
+
required: ["id", "slug", "name", "is_control", "redirect_url", "digests"],
|
|
13471
|
+
type: "object"
|
|
13472
|
+
},
|
|
13473
|
+
type: "array"
|
|
13474
|
+
},
|
|
13475
|
+
written_at: {
|
|
13476
|
+
format: "date-time",
|
|
13477
|
+
type: "string"
|
|
13478
|
+
}
|
|
13479
|
+
},
|
|
13480
|
+
required: [
|
|
13481
|
+
"files",
|
|
13482
|
+
"server_owned_files",
|
|
13483
|
+
"server_owned_fields",
|
|
13484
|
+
"experiment_id",
|
|
13485
|
+
"status",
|
|
13486
|
+
"delivery_type",
|
|
13487
|
+
"counting_type",
|
|
13488
|
+
"traffic_allocation",
|
|
13489
|
+
"schedule",
|
|
13490
|
+
"goal_count",
|
|
13491
|
+
"qa",
|
|
13492
|
+
"written_at",
|
|
13493
|
+
"variations"
|
|
13494
|
+
],
|
|
13495
|
+
type: "object"
|
|
13496
|
+
},
|
|
13307
13497
|
schemaVersion: 1,
|
|
13308
13498
|
source: "experiment-filesystem"
|
|
13309
13499
|
};
|
|
@@ -13529,29 +13719,29 @@ function sha256Text(value2) {
|
|
|
13529
13719
|
return import_node_crypto6.default.createHash("sha256").update(value2).digest("hex");
|
|
13530
13720
|
}
|
|
13531
13721
|
function folderPath(folder) {
|
|
13532
|
-
return
|
|
13722
|
+
return import_node_path7.default.resolve(process.cwd(), folder);
|
|
13533
13723
|
}
|
|
13534
13724
|
function filesystemApexDir(folder) {
|
|
13535
|
-
return
|
|
13725
|
+
return import_node_path7.default.join(folder, ".apex");
|
|
13536
13726
|
}
|
|
13537
13727
|
function filesystemSessionPath(folder) {
|
|
13538
|
-
return
|
|
13728
|
+
return import_node_path7.default.join(filesystemApexDir(folder), "experiment-filesystem.json");
|
|
13539
13729
|
}
|
|
13540
13730
|
function writeFilesystemSession(folder, session) {
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13731
|
+
import_node_fs9.default.mkdirSync(filesystemApexDir(folder), { recursive: true });
|
|
13732
|
+
import_node_fs9.default.writeFileSync(
|
|
13733
|
+
import_node_path7.default.join(filesystemApexDir(folder), ".gitignore"),
|
|
13544
13734
|
"experiment-filesystem.json\n",
|
|
13545
13735
|
"utf8"
|
|
13546
13736
|
);
|
|
13547
|
-
|
|
13737
|
+
import_node_fs9.default.writeFileSync(filesystemSessionPath(folder), `${JSON.stringify(session, null, 2)}
|
|
13548
13738
|
`, "utf8");
|
|
13549
13739
|
}
|
|
13550
13740
|
function readFilesystemSession(folder) {
|
|
13551
13741
|
const filePath = filesystemSessionPath(folder);
|
|
13552
|
-
if (!
|
|
13553
|
-
const workspaceMarker =
|
|
13554
|
-
if (
|
|
13742
|
+
if (!import_node_fs9.default.existsSync(filePath)) {
|
|
13743
|
+
const workspaceMarker = import_node_path7.default.join(filesystemApexDir(folder), "session.json");
|
|
13744
|
+
if (import_node_fs9.default.existsSync(workspaceMarker)) {
|
|
13555
13745
|
throw new CliError(
|
|
13556
13746
|
"This folder is an operator workspace (apex workspace \u2026), not an experiment filesystem \u2014 run `apex workspace status` here, or `apex experiments pull` into a different folder.",
|
|
13557
13747
|
EXIT_CODES.general,
|
|
@@ -13578,7 +13768,7 @@ function readFilesystemSession(folder) {
|
|
|
13578
13768
|
}
|
|
13579
13769
|
);
|
|
13580
13770
|
}
|
|
13581
|
-
return JSON.parse(
|
|
13771
|
+
return JSON.parse(import_node_fs9.default.readFileSync(filePath, "utf8"));
|
|
13582
13772
|
}
|
|
13583
13773
|
function validateFilesystemPath(relativePath) {
|
|
13584
13774
|
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -13594,17 +13784,17 @@ function writeExperimentFilesystemFiles(folder, files) {
|
|
|
13594
13784
|
}));
|
|
13595
13785
|
for (const file of validatedFiles) {
|
|
13596
13786
|
const relativePath = file.relativePath;
|
|
13597
|
-
const absPath =
|
|
13598
|
-
|
|
13599
|
-
|
|
13787
|
+
const absPath = import_node_path7.default.join(folder, relativePath);
|
|
13788
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(absPath), { recursive: true });
|
|
13789
|
+
import_node_fs9.default.writeFileSync(absPath, file.content, "utf8");
|
|
13600
13790
|
}
|
|
13601
13791
|
}
|
|
13602
13792
|
function listExperimentFilesystemFiles(folder, session) {
|
|
13603
13793
|
const files = [];
|
|
13604
13794
|
for (const fileName of EXPERIMENT_FILESYSTEM_PATH_SCHEMA.topLevel) {
|
|
13605
|
-
const absPath =
|
|
13606
|
-
if (!
|
|
13607
|
-
const content =
|
|
13795
|
+
const absPath = import_node_path7.default.join(folder, fileName);
|
|
13796
|
+
if (!import_node_fs9.default.existsSync(absPath)) continue;
|
|
13797
|
+
const content = import_node_fs9.default.readFileSync(absPath, "utf8");
|
|
13608
13798
|
const sha2562 = sha256Text(content);
|
|
13609
13799
|
if (SERVER_OWNED_EXPERIMENT_FILES.has(fileName)) {
|
|
13610
13800
|
if (session.files[fileName]?.sha256 !== sha2562) {
|
|
@@ -13618,28 +13808,28 @@ function listExperimentFilesystemFiles(folder, session) {
|
|
|
13618
13808
|
}
|
|
13619
13809
|
for (const fileName of EXPERIMENT_FILESYSTEM_PATH_SCHEMA.experiment) {
|
|
13620
13810
|
const relativePath = `experiment/${fileName}`;
|
|
13621
|
-
const absPath =
|
|
13622
|
-
if (!
|
|
13623
|
-
const content =
|
|
13811
|
+
const absPath = import_node_path7.default.join(folder, relativePath);
|
|
13812
|
+
if (!import_node_fs9.default.existsSync(absPath)) continue;
|
|
13813
|
+
const content = import_node_fs9.default.readFileSync(absPath, "utf8");
|
|
13624
13814
|
files.push({ path: relativePath, content, sha256: sha256Text(content) });
|
|
13625
13815
|
}
|
|
13626
13816
|
for (const fileName of EXPERIMENT_FILESYSTEM_PATH_SCHEMA.commerce) {
|
|
13627
13817
|
const relativePath = `commerce/${fileName}`;
|
|
13628
|
-
const absPath =
|
|
13629
|
-
if (!
|
|
13630
|
-
const content =
|
|
13818
|
+
const absPath = import_node_path7.default.join(folder, relativePath);
|
|
13819
|
+
if (!import_node_fs9.default.existsSync(absPath)) continue;
|
|
13820
|
+
const content = import_node_fs9.default.readFileSync(absPath, "utf8");
|
|
13631
13821
|
files.push({ path: relativePath, content, sha256: sha256Text(content) });
|
|
13632
13822
|
}
|
|
13633
|
-
const variationsRoot =
|
|
13634
|
-
if (
|
|
13635
|
-
for (const variationDir of
|
|
13823
|
+
const variationsRoot = import_node_path7.default.join(folder, "variations");
|
|
13824
|
+
if (import_node_fs9.default.existsSync(variationsRoot)) {
|
|
13825
|
+
for (const variationDir of import_node_fs9.default.readdirSync(variationsRoot, { withFileTypes: true })) {
|
|
13636
13826
|
if (!variationDir.isDirectory()) continue;
|
|
13637
|
-
const base =
|
|
13827
|
+
const base = import_node_path7.default.join(variationsRoot, variationDir.name);
|
|
13638
13828
|
for (const fileName of EXPERIMENT_FILESYSTEM_PATH_SCHEMA.variation) {
|
|
13639
|
-
const absPath =
|
|
13640
|
-
if (!
|
|
13641
|
-
const relativePath = validateFilesystemPath(
|
|
13642
|
-
const content =
|
|
13829
|
+
const absPath = import_node_path7.default.join(base, fileName);
|
|
13830
|
+
if (!import_node_fs9.default.existsSync(absPath)) continue;
|
|
13831
|
+
const relativePath = validateFilesystemPath(import_node_path7.default.relative(folder, absPath));
|
|
13832
|
+
const content = import_node_fs9.default.readFileSync(absPath, "utf8");
|
|
13643
13833
|
files.push({ path: relativePath, content, sha256: sha256Text(content) });
|
|
13644
13834
|
}
|
|
13645
13835
|
}
|
|
@@ -13681,7 +13871,7 @@ async function rewriteExperimentFilesystemAssets(input) {
|
|
|
13681
13871
|
}
|
|
13682
13872
|
const content = await rewriter.rewrite(
|
|
13683
13873
|
file.content,
|
|
13684
|
-
|
|
13874
|
+
import_node_path7.default.dirname(import_node_path7.default.join(input.folder, file.path))
|
|
13685
13875
|
);
|
|
13686
13876
|
return { ...file, content, sha256: sha256Text(content) };
|
|
13687
13877
|
})
|
|
@@ -13883,9 +14073,7 @@ async function confirmApply(message) {
|
|
|
13883
14073
|
}
|
|
13884
14074
|
function parseEvidenceInput(value2) {
|
|
13885
14075
|
if (!value2) return void 0;
|
|
13886
|
-
const
|
|
13887
|
-
const text2 = value2.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
13888
|
-
const parsed = JSON.parse(text2);
|
|
14076
|
+
const parsed = readJsonInput(value2);
|
|
13889
14077
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
13890
14078
|
throw new Error("QA evidence must be a JSON object");
|
|
13891
14079
|
}
|
|
@@ -13929,7 +14117,7 @@ async function resolveExperimentForFilesystemOrId(value2) {
|
|
|
13929
14117
|
return {
|
|
13930
14118
|
experimentId: session.experimentId,
|
|
13931
14119
|
folder: candidate,
|
|
13932
|
-
label:
|
|
14120
|
+
label: import_node_path7.default.basename(candidate)
|
|
13933
14121
|
};
|
|
13934
14122
|
}
|
|
13935
14123
|
const exp = await resolveExperiment(value2);
|
|
@@ -14385,9 +14573,10 @@ function segmentShapeError(body6) {
|
|
|
14385
14573
|
return null;
|
|
14386
14574
|
}
|
|
14387
14575
|
function readCreateJson(command, reference) {
|
|
14388
|
-
const file = reference.startsWith("@") ? reference.slice(1) : reference;
|
|
14389
14576
|
try {
|
|
14390
|
-
const parsed =
|
|
14577
|
+
const parsed = readJsonInput(reference.startsWith("@") ? reference : `@${reference}`, {
|
|
14578
|
+
allowInline: false
|
|
14579
|
+
});
|
|
14391
14580
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
14392
14581
|
throw new Error("expected an object");
|
|
14393
14582
|
const shapeError = segmentShapeError(parsed);
|
|
@@ -15066,7 +15255,7 @@ var pullFilesystem = new Command("pull").description("Pull an experiment as an e
|
|
|
15066
15255
|
schemaVersion: 1,
|
|
15067
15256
|
folder,
|
|
15068
15257
|
experimentId: exp.id,
|
|
15069
|
-
files: res.data.files.map((file) =>
|
|
15258
|
+
files: res.data.files.map((file) => import_node_path7.default.join(folder, file.path))
|
|
15070
15259
|
},
|
|
15071
15260
|
() => {
|
|
15072
15261
|
spinner.succeed(`Pulled ${exp.name}`);
|
|
@@ -15083,8 +15272,8 @@ var experimentStatus = new Command("status").description(
|
|
|
15083
15272
|
).argument("[folder-or-id]", "Pulled folder, experiment ID, slug, or test key", ".").action(async function(target) {
|
|
15084
15273
|
const creds = requireCredentials();
|
|
15085
15274
|
const candidate = folderPath(target);
|
|
15086
|
-
const isFolder =
|
|
15087
|
-
const hasApexDir = isFolder &&
|
|
15275
|
+
const isFolder = import_node_fs9.default.existsSync(candidate) && import_node_fs9.default.statSync(candidate).isDirectory();
|
|
15276
|
+
const hasApexDir = isFolder && import_node_fs9.default.existsSync(import_node_path7.default.join(candidate, ".apex"));
|
|
15088
15277
|
if (isFolder && !hasApexDir && target === ".") {
|
|
15089
15278
|
fail(this, "This folder is not a pulled experiment.", EXIT_CODES.validation, {
|
|
15090
15279
|
code: "validation_failed",
|
|
@@ -15133,8 +15322,8 @@ var experimentStatus = new Command("status").description(
|
|
|
15133
15322
|
const localFiles = listExperimentFilesystemFiles(candidate, session);
|
|
15134
15323
|
const localHashes = new Map(localFiles.map((file) => [file.path, file.sha256]));
|
|
15135
15324
|
for (const name of SERVER_OWNED_EXPERIMENT_FILES) {
|
|
15136
|
-
const file =
|
|
15137
|
-
if (
|
|
15325
|
+
const file = import_node_path7.default.join(candidate, name);
|
|
15326
|
+
if (import_node_fs9.default.existsSync(file)) localHashes.set(name, sha256Text(import_node_fs9.default.readFileSync(file, "utf8")));
|
|
15138
15327
|
}
|
|
15139
15328
|
const paths = [.../* @__PURE__ */ new Set([...Object.keys(session.files), ...localHashes.keys()])].sort();
|
|
15140
15329
|
const files = paths.map((file) => {
|
|
@@ -15629,6 +15818,12 @@ var applyFilesystem = new Command("apply").option(
|
|
|
15629
15818
|
);
|
|
15630
15819
|
}
|
|
15631
15820
|
}
|
|
15821
|
+
const normalized = receipt?.files?.filter((file) => file.normalizations?.length);
|
|
15822
|
+
if (normalized?.length) {
|
|
15823
|
+
console.log(
|
|
15824
|
+
` Normalized on store: ${normalized.map((file) => `${file.path} (${file.normalizations.join(", ")})`).join(", ")}`
|
|
15825
|
+
);
|
|
15826
|
+
}
|
|
15632
15827
|
printQaPreviewUrls(session.experimentId, previewResult);
|
|
15633
15828
|
console.log(syncMessage);
|
|
15634
15829
|
}
|
|
@@ -15884,9 +16079,7 @@ var qaReopen = new Command("reopen").description("Reopen QA and clear scheduled
|
|
|
15884
16079
|
}
|
|
15885
16080
|
});
|
|
15886
16081
|
function parseJsonValue(value2) {
|
|
15887
|
-
|
|
15888
|
-
const text2 = value2.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
15889
|
-
return JSON.parse(text2);
|
|
16082
|
+
return readJsonInput(value2);
|
|
15890
16083
|
}
|
|
15891
16084
|
function collectFile(value2, previous = []) {
|
|
15892
16085
|
return [...previous, value2];
|
|
@@ -15957,12 +16150,12 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
|
|
|
15957
16150
|
const variationId = resolveEvidenceVariation(experiment2, opts.variation);
|
|
15958
16151
|
const form = new FormData();
|
|
15959
16152
|
for (const filePath of opts.file) {
|
|
15960
|
-
const absolutePath =
|
|
15961
|
-
if (!
|
|
15962
|
-
const bytes =
|
|
15963
|
-
const extension =
|
|
16153
|
+
const absolutePath = import_node_path7.default.resolve(process.cwd(), filePath);
|
|
16154
|
+
if (!import_node_fs9.default.existsSync(absolutePath)) throw new Error(`Evidence file not found: ${filePath}`);
|
|
16155
|
+
const bytes = import_node_fs9.default.readFileSync(absolutePath);
|
|
16156
|
+
const extension = import_node_path7.default.extname(absolutePath).toLowerCase();
|
|
15964
16157
|
const mimeType = extension === ".png" ? "image/png" : extension === ".jpg" || extension === ".jpeg" ? "image/jpeg" : extension === ".webp" ? "image/webp" : "application/octet-stream";
|
|
15965
|
-
form.append("files", new File([bytes],
|
|
16158
|
+
form.append("files", new File([bytes], import_node_path7.default.basename(absolutePath), { type: mimeType }));
|
|
15966
16159
|
}
|
|
15967
16160
|
if (opts.label) form.append("label", opts.label);
|
|
15968
16161
|
const device = normalizeEvidenceDevice(opts.device);
|
|
@@ -16382,8 +16575,8 @@ var screenshots = new Command("screenshots").description(
|
|
|
16382
16575
|
}
|
|
16383
16576
|
if (job.status === "failed")
|
|
16384
16577
|
fail(this, `Screenshot job failed: ${job.error ?? "unknown error"}`);
|
|
16385
|
-
const outputDir =
|
|
16386
|
-
|
|
16578
|
+
const outputDir = import_node_path7.default.resolve(process.cwd(), opts.out);
|
|
16579
|
+
import_node_fs9.default.mkdirSync(outputDir, { recursive: true });
|
|
16387
16580
|
const rows = [];
|
|
16388
16581
|
const errors = [];
|
|
16389
16582
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
@@ -16441,8 +16634,8 @@ var screenshots = new Command("screenshots").description(
|
|
|
16441
16634
|
try {
|
|
16442
16635
|
const response = await fetch(url);
|
|
16443
16636
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
16444
|
-
await
|
|
16445
|
-
|
|
16637
|
+
await import_node_fs9.default.promises.writeFile(
|
|
16638
|
+
import_node_path7.default.join(outputDir, filename),
|
|
16446
16639
|
Buffer.from(await response.arrayBuffer())
|
|
16447
16640
|
);
|
|
16448
16641
|
workerResults.push({ index, row: [variation.name, device, filename] });
|
|
@@ -16481,8 +16674,8 @@ Examples:
|
|
|
16481
16674
|
);
|
|
16482
16675
|
|
|
16483
16676
|
// src/commands/results.ts
|
|
16484
|
-
var
|
|
16485
|
-
var
|
|
16677
|
+
var import_node_fs10 = __toESM(require("node:fs"));
|
|
16678
|
+
var import_node_path8 = __toESM(require("node:path"));
|
|
16486
16679
|
var import_promises3 = __toESM(require("node:readline/promises"));
|
|
16487
16680
|
var OUTCOMES = /* @__PURE__ */ new Set(["won", "lost", "inconclusive"]);
|
|
16488
16681
|
function collect(value2, previous) {
|
|
@@ -16547,10 +16740,10 @@ var importCommand = new Command("import").description("Upload past test results
|
|
|
16547
16740
|
const makeForm = () => {
|
|
16548
16741
|
const form = new FormData();
|
|
16549
16742
|
for (const fileArg of options.file) {
|
|
16550
|
-
const resolved =
|
|
16743
|
+
const resolved = import_node_path8.default.resolve(fileArg);
|
|
16551
16744
|
let bytes;
|
|
16552
16745
|
try {
|
|
16553
|
-
bytes =
|
|
16746
|
+
bytes = import_node_fs10.default.readFileSync(resolved);
|
|
16554
16747
|
} catch (error) {
|
|
16555
16748
|
fail(
|
|
16556
16749
|
this,
|
|
@@ -16559,7 +16752,7 @@ var importCommand = new Command("import").description("Upload past test results
|
|
|
16559
16752
|
{ code: "local_file_error" }
|
|
16560
16753
|
);
|
|
16561
16754
|
}
|
|
16562
|
-
form.append("files", new Blob([bytes]),
|
|
16755
|
+
form.append("files", new Blob([bytes]), import_node_path8.default.basename(resolved));
|
|
16563
16756
|
}
|
|
16564
16757
|
return form;
|
|
16565
16758
|
};
|
|
@@ -16668,9 +16861,9 @@ var resultsCommand = new Command("results").description("Import and manage test
|
|
|
16668
16861
|
|
|
16669
16862
|
// src/commands/push.ts
|
|
16670
16863
|
init_source();
|
|
16671
|
-
var
|
|
16864
|
+
var import_node_fs11 = __toESM(require("node:fs"));
|
|
16672
16865
|
var import_node_os7 = __toESM(require("node:os"));
|
|
16673
|
-
var
|
|
16866
|
+
var import_node_path9 = __toESM(require("node:path"));
|
|
16674
16867
|
var import_node_url2 = require("node:url");
|
|
16675
16868
|
function toApiBody(exp) {
|
|
16676
16869
|
return {
|
|
@@ -16711,7 +16904,7 @@ async function rewriteExperimentAssets(exp, rewrite, baseDir) {
|
|
|
16711
16904
|
};
|
|
16712
16905
|
}
|
|
16713
16906
|
async function importConfigModule(absPath) {
|
|
16714
|
-
const ext =
|
|
16907
|
+
const ext = import_node_path9.default.extname(absPath).toLowerCase();
|
|
16715
16908
|
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
16716
16909
|
return await import((0, import_node_url2.pathToFileURL)(absPath).href);
|
|
16717
16910
|
}
|
|
@@ -16725,8 +16918,8 @@ async function importConfigModule(absPath) {
|
|
|
16725
16918
|
` + (err instanceof Error ? err.message : String(err))
|
|
16726
16919
|
);
|
|
16727
16920
|
}
|
|
16728
|
-
const tmpDir =
|
|
16729
|
-
const outFile =
|
|
16921
|
+
const tmpDir = import_node_fs11.default.mkdtempSync(import_node_path9.default.join(import_node_os7.default.tmpdir(), "drip-config-"));
|
|
16922
|
+
const outFile = import_node_path9.default.join(tmpDir, "config.mjs");
|
|
16730
16923
|
try {
|
|
16731
16924
|
await esbuild.build({
|
|
16732
16925
|
entryPoints: [absPath],
|
|
@@ -16742,7 +16935,7 @@ async function importConfigModule(absPath) {
|
|
|
16742
16935
|
return await import(href);
|
|
16743
16936
|
} finally {
|
|
16744
16937
|
try {
|
|
16745
|
-
|
|
16938
|
+
import_node_fs11.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
16746
16939
|
} catch {
|
|
16747
16940
|
}
|
|
16748
16941
|
}
|
|
@@ -16763,7 +16956,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
|
|
|
16763
16956
|
);
|
|
16764
16957
|
}
|
|
16765
16958
|
const spinner = process.stderr.isTTY ? await createSpinner(`Loading config from ${source_default.cyan(configPath)}...`) : null;
|
|
16766
|
-
const absoluteConfigPath =
|
|
16959
|
+
const absoluteConfigPath = import_node_path9.default.resolve(configPath);
|
|
16767
16960
|
let experiments;
|
|
16768
16961
|
try {
|
|
16769
16962
|
const mod = await importConfigModule(absoluteConfigPath);
|
|
@@ -16830,7 +17023,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
|
|
|
16830
17023
|
return;
|
|
16831
17024
|
}
|
|
16832
17025
|
const assetRewriter = createAssetReferenceRewriter({
|
|
16833
|
-
baseDir:
|
|
17026
|
+
baseDir: import_node_path9.default.dirname(absoluteConfigPath),
|
|
16834
17027
|
baseUrl: creds.baseUrl,
|
|
16835
17028
|
enabled: opts.assetUpload !== false,
|
|
16836
17029
|
upload: async (file) => {
|
|
@@ -16860,7 +17053,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
|
|
|
16860
17053
|
let body6;
|
|
16861
17054
|
try {
|
|
16862
17055
|
body6 = toApiBody(
|
|
16863
|
-
await rewriteExperimentAssets(exp, assetRewriter,
|
|
17056
|
+
await rewriteExperimentAssets(exp, assetRewriter, import_node_path9.default.dirname(absoluteConfigPath))
|
|
16864
17057
|
);
|
|
16865
17058
|
} catch (error) {
|
|
16866
17059
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -16918,25 +17111,25 @@ var pushCommand = new Command("push").description("Push experiment definitions f
|
|
|
16918
17111
|
|
|
16919
17112
|
// src/commands/harness.ts
|
|
16920
17113
|
init_source();
|
|
16921
|
-
var
|
|
16922
|
-
var
|
|
17114
|
+
var import_node_fs12 = __toESM(require("node:fs"));
|
|
17115
|
+
var import_node_path10 = __toESM(require("node:path"));
|
|
16923
17116
|
var import_node_child_process6 = require("node:child_process");
|
|
16924
17117
|
function slugify(value2, fallback2 = "variation") {
|
|
16925
17118
|
const slug = value2.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
16926
17119
|
return slug || fallback2;
|
|
16927
17120
|
}
|
|
16928
17121
|
function readJson(filePath) {
|
|
16929
|
-
if (!
|
|
16930
|
-
return
|
|
17122
|
+
if (!import_node_fs12.default.existsSync(filePath)) return null;
|
|
17123
|
+
return readJsonInput(`@${filePath}`);
|
|
16931
17124
|
}
|
|
16932
17125
|
function resolveFolder(folder) {
|
|
16933
|
-
return
|
|
17126
|
+
return import_node_path10.default.resolve(process.cwd(), folder);
|
|
16934
17127
|
}
|
|
16935
17128
|
function experimentConfigPath(folder) {
|
|
16936
|
-
return
|
|
17129
|
+
return import_node_path10.default.join(folder, "experiment.config.json");
|
|
16937
17130
|
}
|
|
16938
17131
|
function apexConfigPath(folder) {
|
|
16939
|
-
return
|
|
17132
|
+
return import_node_path10.default.join(folder, "apex.harness.json");
|
|
16940
17133
|
}
|
|
16941
17134
|
function loadHarnessConfig(folder) {
|
|
16942
17135
|
const experiment2 = readJson(experimentConfigPath(folder)) ?? {};
|
|
@@ -16945,8 +17138,8 @@ function loadHarnessConfig(folder) {
|
|
|
16945
17138
|
const targetOrigin = experiment2.targetOrigin?.replace(/\/+$/, "") ?? "";
|
|
16946
17139
|
const defaultPath = experiment2.defaultPath ?? "/";
|
|
16947
17140
|
const targetUrl = targetOrigin ? new URL(defaultPath, targetOrigin).toString() : "";
|
|
16948
|
-
const id = experiment2.id ??
|
|
16949
|
-
const variation =
|
|
17141
|
+
const id = experiment2.id ?? import_node_path10.default.basename(import_node_path10.default.dirname(folder));
|
|
17142
|
+
const variation = import_node_path10.default.basename(folder);
|
|
16950
17143
|
return {
|
|
16951
17144
|
experiment: experiment2,
|
|
16952
17145
|
apex: {
|
|
@@ -16980,37 +17173,37 @@ function runAbHarness(args, cwd) {
|
|
|
16980
17173
|
function artifactPaths(folder, experiment2) {
|
|
16981
17174
|
const variant = experiment2.variant;
|
|
16982
17175
|
if (variant?.endsWith(".bundle.js")) {
|
|
16983
|
-
const jsPath =
|
|
16984
|
-
const cssPath =
|
|
16985
|
-
return { jsPath, cssPath:
|
|
17176
|
+
const jsPath = import_node_path10.default.join(folder, variant.replace(/\.bundle\.js$/, ".js"));
|
|
17177
|
+
const cssPath = import_node_path10.default.join(folder, variant.replace(/\.bundle\.js$/, ".css"));
|
|
17178
|
+
return { jsPath, cssPath: import_node_fs12.default.existsSync(cssPath) ? cssPath : null };
|
|
16986
17179
|
}
|
|
16987
|
-
const buildDir =
|
|
16988
|
-
const files =
|
|
17180
|
+
const buildDir = import_node_path10.default.join(folder, "build");
|
|
17181
|
+
const files = import_node_fs12.default.existsSync(buildDir) ? import_node_fs12.default.readdirSync(buildDir) : [];
|
|
16989
17182
|
const js = files.find(
|
|
16990
17183
|
(file) => file.endsWith(".js") && !file.endsWith(".bundle.js") && !file.endsWith(".min.js")
|
|
16991
17184
|
);
|
|
16992
17185
|
const css = files.find((file) => file.endsWith(".css") && !file.endsWith(".min.css"));
|
|
16993
17186
|
if (!js) throw new Error("Could not find harness JS artifact in build/");
|
|
16994
17187
|
return {
|
|
16995
|
-
jsPath:
|
|
16996
|
-
cssPath: css ?
|
|
17188
|
+
jsPath: import_node_path10.default.join(buildDir, js),
|
|
17189
|
+
cssPath: css ? import_node_path10.default.join(buildDir, css) : null
|
|
16997
17190
|
};
|
|
16998
17191
|
}
|
|
16999
17192
|
async function uploadVersion(params) {
|
|
17000
17193
|
const creds = requireCredentials();
|
|
17001
17194
|
const { jsPath, cssPath } = artifactPaths(params.folder, params.experiment);
|
|
17002
|
-
if (!
|
|
17195
|
+
if (!import_node_fs12.default.existsSync(jsPath)) throw new Error(`Missing JS artifact: ${jsPath}`);
|
|
17003
17196
|
const form = new FormData();
|
|
17004
17197
|
form.append(
|
|
17005
17198
|
"js",
|
|
17006
|
-
new Blob([
|
|
17007
|
-
|
|
17199
|
+
new Blob([import_node_fs12.default.readFileSync(jsPath)], { type: "application/javascript" }),
|
|
17200
|
+
import_node_path10.default.basename(jsPath)
|
|
17008
17201
|
);
|
|
17009
|
-
if (cssPath &&
|
|
17202
|
+
if (cssPath && import_node_fs12.default.existsSync(cssPath) && import_node_fs12.default.statSync(cssPath).size > 0) {
|
|
17010
17203
|
form.append(
|
|
17011
17204
|
"css",
|
|
17012
|
-
new Blob([
|
|
17013
|
-
|
|
17205
|
+
new Blob([import_node_fs12.default.readFileSync(cssPath)], { type: "text/css" }),
|
|
17206
|
+
import_node_path10.default.basename(cssPath)
|
|
17014
17207
|
);
|
|
17015
17208
|
}
|
|
17016
17209
|
form.append(
|
|
@@ -17019,8 +17212,8 @@ async function uploadVersion(params) {
|
|
|
17019
17212
|
...params.apex,
|
|
17020
17213
|
resetJs: cleanupResetJs(params.experiment),
|
|
17021
17214
|
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17022
|
-
jsPath:
|
|
17023
|
-
cssPath: cssPath ?
|
|
17215
|
+
jsPath: import_node_path10.default.relative(params.folder, jsPath),
|
|
17216
|
+
cssPath: cssPath ? import_node_path10.default.relative(params.folder, cssPath) : null
|
|
17024
17217
|
})
|
|
17025
17218
|
);
|
|
17026
17219
|
const res = await uploadHarnessVersion(creds, params.session.sessionId, form);
|
|
@@ -17062,11 +17255,11 @@ var harnessCommand = new Command("harness").description(
|
|
|
17062
17255
|
harnessCommand.command("init").argument("<folder>", "AB Test Harness experiment folder").option("--force", "Overwrite existing apex.harness.json").action(function action24(folderArg, opts) {
|
|
17063
17256
|
const folder = resolveFolder(folderArg);
|
|
17064
17257
|
const targetPath = apexConfigPath(folder);
|
|
17065
|
-
if (
|
|
17258
|
+
if (import_node_fs12.default.existsSync(targetPath) && !opts.force) {
|
|
17066
17259
|
fail(this, `${targetPath} already exists. Use --force to overwrite.`, EXIT_CODES.conflict);
|
|
17067
17260
|
}
|
|
17068
17261
|
const { apex } = loadHarnessConfig(folder);
|
|
17069
|
-
|
|
17262
|
+
import_node_fs12.default.writeFileSync(targetPath, `${JSON.stringify(apex, null, 2)}
|
|
17070
17263
|
`, "utf8");
|
|
17071
17264
|
output(
|
|
17072
17265
|
this,
|
|
@@ -17132,7 +17325,7 @@ harnessCommand.command("dev").argument("<folder>", "AB Test Harness experiment f
|
|
|
17132
17325
|
}
|
|
17133
17326
|
};
|
|
17134
17327
|
let debounce = null;
|
|
17135
|
-
const watcher =
|
|
17328
|
+
const watcher = import_node_fs12.default.watch(folder, { recursive: true }, (_event, filename) => {
|
|
17136
17329
|
const name = String(filename ?? "");
|
|
17137
17330
|
if (!name || name.startsWith("build/") || name.includes("/build/")) return;
|
|
17138
17331
|
if (debounce) clearTimeout(debounce);
|
|
@@ -17175,8 +17368,8 @@ harnessCommand.command("publish").argument("<folder>", "AB Test Harness experime
|
|
|
17175
17368
|
// src/commands/operator.ts
|
|
17176
17369
|
init_source();
|
|
17177
17370
|
var import_node_crypto7 = __toESM(require("node:crypto"));
|
|
17178
|
-
var
|
|
17179
|
-
var
|
|
17371
|
+
var import_node_fs13 = __toESM(require("node:fs"));
|
|
17372
|
+
var import_node_path11 = __toESM(require("node:path"));
|
|
17180
17373
|
var CANONICAL_FILE_RE = /^(src\/(?:index\.js|index\.scss|info\.js|mutations\.json)|experiment\.config\.json|metrics\.json|targeting\.json|dev-doc\.md|brand\.md|goals\/[a-zA-Z0-9_.-]+\.json|segments\/[a-zA-Z0-9_.-]+\.json|network\/[a-zA-Z0-9_.-]+\.recipe\.json)$/;
|
|
17181
17374
|
function workspaceIssueFile(field, evidence) {
|
|
17182
17375
|
if (field === "custom_js" || field === "reset_js") return "src/index.js";
|
|
@@ -17235,28 +17428,28 @@ function slugify2(value2, fallback2 = "operator") {
|
|
|
17235
17428
|
return slug || fallback2;
|
|
17236
17429
|
}
|
|
17237
17430
|
function folderPath2(folder) {
|
|
17238
|
-
return
|
|
17431
|
+
return import_node_path11.default.resolve(process.cwd(), folder);
|
|
17239
17432
|
}
|
|
17240
17433
|
function apexDir(folder) {
|
|
17241
|
-
return
|
|
17434
|
+
return import_node_path11.default.join(folder, ".apex");
|
|
17242
17435
|
}
|
|
17243
17436
|
function sessionPath(folder) {
|
|
17244
|
-
return
|
|
17437
|
+
return import_node_path11.default.join(apexDir(folder), "session.json");
|
|
17245
17438
|
}
|
|
17246
17439
|
function readJsonFile(filePath) {
|
|
17247
|
-
if (!
|
|
17248
|
-
return JSON.parse(
|
|
17440
|
+
if (!import_node_fs13.default.existsSync(filePath)) return null;
|
|
17441
|
+
return JSON.parse(import_node_fs13.default.readFileSync(filePath, "utf8"));
|
|
17249
17442
|
}
|
|
17250
17443
|
function writeJsonFile(filePath, value2) {
|
|
17251
|
-
|
|
17252
|
-
|
|
17444
|
+
import_node_fs13.default.mkdirSync(import_node_path11.default.dirname(filePath), { recursive: true });
|
|
17445
|
+
import_node_fs13.default.writeFileSync(filePath, `${JSON.stringify(value2, null, 2)}
|
|
17253
17446
|
`, "utf8");
|
|
17254
17447
|
}
|
|
17255
17448
|
function readSession(folder) {
|
|
17256
17449
|
const session = readJsonFile(sessionPath(folder));
|
|
17257
17450
|
if (!session) {
|
|
17258
|
-
const filesystemMarker =
|
|
17259
|
-
if (
|
|
17451
|
+
const filesystemMarker = import_node_path11.default.join(apexDir(folder), "experiment-filesystem.json");
|
|
17452
|
+
if (import_node_fs13.default.existsSync(filesystemMarker)) {
|
|
17260
17453
|
throw new CliError(
|
|
17261
17454
|
"This folder is an experiment filesystem (apex experiments \u2026), not an operator workspace \u2014 run `apex experiments plan` here, or `apex workspace checkout` into a different folder.",
|
|
17262
17455
|
EXIT_CODES.general,
|
|
@@ -17286,9 +17479,9 @@ function readSession(folder) {
|
|
|
17286
17479
|
return session;
|
|
17287
17480
|
}
|
|
17288
17481
|
function writeSession(folder, session) {
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17482
|
+
import_node_fs13.default.mkdirSync(apexDir(folder), { recursive: true });
|
|
17483
|
+
import_node_fs13.default.writeFileSync(
|
|
17484
|
+
import_node_path11.default.join(apexDir(folder), ".gitignore"),
|
|
17292
17485
|
"session.json\nharness-session.json\n",
|
|
17293
17486
|
"utf8"
|
|
17294
17487
|
);
|
|
@@ -17312,15 +17505,15 @@ function listLocalWorkspaceFiles(folder) {
|
|
|
17312
17505
|
"brand.md"
|
|
17313
17506
|
];
|
|
17314
17507
|
function visit2(dir) {
|
|
17315
|
-
if (!
|
|
17316
|
-
for (const entry of
|
|
17317
|
-
const absPath =
|
|
17508
|
+
if (!import_node_fs13.default.existsSync(dir)) return;
|
|
17509
|
+
for (const entry of import_node_fs13.default.readdirSync(dir, { withFileTypes: true })) {
|
|
17510
|
+
const absPath = import_node_path11.default.join(dir, entry.name);
|
|
17318
17511
|
if (entry.isDirectory()) {
|
|
17319
17512
|
visit2(absPath);
|
|
17320
17513
|
continue;
|
|
17321
17514
|
}
|
|
17322
|
-
const relativePath = validateCanonicalPath(
|
|
17323
|
-
const content =
|
|
17515
|
+
const relativePath = validateCanonicalPath(import_node_path11.default.relative(folder, absPath));
|
|
17516
|
+
const content = import_node_fs13.default.readFileSync(absPath, "utf8");
|
|
17324
17517
|
files.push({
|
|
17325
17518
|
path: relativePath,
|
|
17326
17519
|
absPath,
|
|
@@ -17330,11 +17523,11 @@ function listLocalWorkspaceFiles(folder) {
|
|
|
17330
17523
|
});
|
|
17331
17524
|
}
|
|
17332
17525
|
}
|
|
17333
|
-
for (const root of roots) visit2(
|
|
17526
|
+
for (const root of roots) visit2(import_node_path11.default.join(folder, root));
|
|
17334
17527
|
for (const file of topLevel) {
|
|
17335
|
-
const absPath =
|
|
17336
|
-
if (!
|
|
17337
|
-
const content =
|
|
17528
|
+
const absPath = import_node_path11.default.join(folder, file);
|
|
17529
|
+
if (!import_node_fs13.default.existsSync(absPath)) continue;
|
|
17530
|
+
const content = import_node_fs13.default.readFileSync(absPath, "utf8");
|
|
17338
17531
|
files.push({
|
|
17339
17532
|
path: file,
|
|
17340
17533
|
absPath,
|
|
@@ -17358,7 +17551,7 @@ function validateJsonFiles(files) {
|
|
|
17358
17551
|
}
|
|
17359
17552
|
}
|
|
17360
17553
|
function readExperimentConfig(folder) {
|
|
17361
|
-
return readJsonFile(
|
|
17554
|
+
return readJsonFile(import_node_path11.default.join(folder, "experiment.config.json")) ?? {};
|
|
17362
17555
|
}
|
|
17363
17556
|
function resolveVariation(exp, variationArg) {
|
|
17364
17557
|
const variations = exp.variations ?? [];
|
|
@@ -17375,9 +17568,9 @@ function resolveVariation(exp, variationArg) {
|
|
|
17375
17568
|
function writeWorkspaceFiles(folder, workspace) {
|
|
17376
17569
|
for (const file of workspace.files) {
|
|
17377
17570
|
const relativePath = validateCanonicalPath(file.path);
|
|
17378
|
-
const absPath =
|
|
17379
|
-
|
|
17380
|
-
|
|
17571
|
+
const absPath = import_node_path11.default.join(folder, relativePath);
|
|
17572
|
+
import_node_fs13.default.mkdirSync(import_node_path11.default.dirname(absPath), { recursive: true });
|
|
17573
|
+
import_node_fs13.default.writeFileSync(absPath, file.content, "utf8");
|
|
17381
17574
|
}
|
|
17382
17575
|
}
|
|
17383
17576
|
function sessionFromWorkspace(input) {
|
|
@@ -17445,10 +17638,10 @@ async function uploadCurrentArtifactVersion(folder, sessionId) {
|
|
|
17445
17638
|
form.append(
|
|
17446
17639
|
"js",
|
|
17447
17640
|
new Blob([js.content], { type: "application/javascript" }),
|
|
17448
|
-
|
|
17641
|
+
import_node_path11.default.basename(js.path)
|
|
17449
17642
|
);
|
|
17450
17643
|
if (css && css.content.trim()) {
|
|
17451
|
-
form.append("css", new Blob([css.content], { type: "text/css" }),
|
|
17644
|
+
form.append("css", new Blob([css.content], { type: "text/css" }), import_node_path11.default.basename(css.path));
|
|
17452
17645
|
}
|
|
17453
17646
|
form.append("manifest", JSON.stringify(manifest));
|
|
17454
17647
|
const res = await uploadHarnessVersion(creds, sessionId, form);
|
|
@@ -17575,7 +17768,7 @@ operatorCommand.command("checkout").argument("<experiment-id>", "Apex experiment
|
|
|
17575
17768
|
classifyStatus(workspace.status),
|
|
17576
17769
|
workspace.errorDetails
|
|
17577
17770
|
);
|
|
17578
|
-
|
|
17771
|
+
import_node_fs13.default.mkdirSync(folder, { recursive: true });
|
|
17579
17772
|
writeWorkspaceFiles(folder, workspace.data);
|
|
17580
17773
|
writeSession(
|
|
17581
17774
|
folder,
|
|
@@ -17642,8 +17835,8 @@ operatorCommand.command("dev").argument("[folder]", "Operator workspace folder",
|
|
|
17642
17835
|
const session = readSession(folder);
|
|
17643
17836
|
const harnessSession = await createExactHarnessSession(folder, session);
|
|
17644
17837
|
const version = await uploadCurrentArtifactVersion(folder, harnessSession.sessionId);
|
|
17645
|
-
|
|
17646
|
-
writeJsonFile(
|
|
17838
|
+
import_node_fs13.default.mkdirSync(apexDir(folder), { recursive: true });
|
|
17839
|
+
writeJsonFile(import_node_path11.default.join(apexDir(folder), "harness-session.json"), {
|
|
17647
17840
|
sessionId: harnessSession.sessionId,
|
|
17648
17841
|
experimentId: harnessSession.experimentId,
|
|
17649
17842
|
variationId: harnessSession.variationId,
|
|
@@ -17698,7 +17891,7 @@ operatorCommand.command("dev").argument("[folder]", "Operator workspace folder",
|
|
|
17698
17891
|
}
|
|
17699
17892
|
};
|
|
17700
17893
|
let timer = null;
|
|
17701
|
-
const watcher =
|
|
17894
|
+
const watcher = import_node_fs13.default.watch(folder, { recursive: true }, (_event, filename) => {
|
|
17702
17895
|
const name = String(filename ?? "");
|
|
17703
17896
|
if (!name || name.startsWith(".apex/") || name.includes("/.apex/")) return;
|
|
17704
17897
|
if (timer) clearTimeout(timer);
|
|
@@ -17736,8 +17929,8 @@ operatorCommand.command("publish").argument("[folder]", "Operator workspace fold
|
|
|
17736
17929
|
|
|
17737
17930
|
// src/commands/roadmap.ts
|
|
17738
17931
|
init_source();
|
|
17739
|
-
var
|
|
17740
|
-
var
|
|
17932
|
+
var import_node_fs14 = __toESM(require("node:fs"));
|
|
17933
|
+
var import_node_path12 = __toESM(require("node:path"));
|
|
17741
17934
|
var LEGACY_OUTPUT_REMOVAL_VERSION = "0.7.0";
|
|
17742
17935
|
function truncate(value2, max) {
|
|
17743
17936
|
const text2 = typeof value2 === "string" ? value2 : value2 == null ? "" : String(value2);
|
|
@@ -17847,9 +18040,9 @@ var handoff = new Command("handoff").description(
|
|
|
17847
18040
|
`;
|
|
17848
18041
|
const outputData = opts.instructions ? { roadmapItemId: id, instructions: String(payload.instructions ?? "") } : payload;
|
|
17849
18042
|
if (opts.out || opts.output) {
|
|
17850
|
-
const target =
|
|
17851
|
-
|
|
17852
|
-
|
|
18043
|
+
const target = import_node_path12.default.resolve(process.cwd(), opts.out ?? opts.output);
|
|
18044
|
+
import_node_fs14.default.mkdirSync(import_node_path12.default.dirname(target), { recursive: true });
|
|
18045
|
+
import_node_fs14.default.writeFileSync(target, rendered, "utf8");
|
|
17853
18046
|
output(this, { path: target, roadmapItemId: id }, () => printSuccess(`Wrote ${target}`));
|
|
17854
18047
|
return;
|
|
17855
18048
|
}
|
|
@@ -17863,8 +18056,8 @@ roadmapCommand.addCommand(handoff);
|
|
|
17863
18056
|
|
|
17864
18057
|
// src/commands/tracking.ts
|
|
17865
18058
|
init_source();
|
|
17866
|
-
var
|
|
17867
|
-
var
|
|
18059
|
+
var import_node_fs15 = __toESM(require("node:fs"));
|
|
18060
|
+
var import_node_path13 = __toESM(require("node:path"));
|
|
17868
18061
|
function normalizeUrl(input) {
|
|
17869
18062
|
const value2 = input.trim();
|
|
17870
18063
|
if (!value2) throw new Error("URL cannot be empty");
|
|
@@ -17911,14 +18104,14 @@ function commonCookieDomain(hostnames) {
|
|
|
17911
18104
|
}
|
|
17912
18105
|
var DEFAULT_MANIFEST_PATH = ".apex/tracking-manifest.json";
|
|
17913
18106
|
function readManifest(filePath) {
|
|
17914
|
-
if (!
|
|
17915
|
-
const parsed = JSON.parse(
|
|
18107
|
+
if (!import_node_fs15.default.existsSync(filePath)) throw new Error(`Manifest not found: ${filePath}`);
|
|
18108
|
+
const parsed = JSON.parse(import_node_fs15.default.readFileSync(filePath, "utf8"));
|
|
17916
18109
|
if (parsed.schemaVersion !== 1) throw new Error("Unsupported tracking manifest schema");
|
|
17917
18110
|
return parsed;
|
|
17918
18111
|
}
|
|
17919
18112
|
function writeManifest(filePath, manifest) {
|
|
17920
|
-
|
|
17921
|
-
|
|
18113
|
+
import_node_fs15.default.mkdirSync(import_node_path13.default.dirname(filePath), { recursive: true });
|
|
18114
|
+
import_node_fs15.default.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
|
|
17922
18115
|
`, "utf8");
|
|
17923
18116
|
}
|
|
17924
18117
|
function buildSnippet(shopId, endpoint) {
|
|
@@ -17985,7 +18178,7 @@ function buildManifest(urlArgs, opts) {
|
|
|
17985
18178
|
};
|
|
17986
18179
|
}
|
|
17987
18180
|
function manifestPath(input) {
|
|
17988
|
-
return
|
|
18181
|
+
return import_node_path13.default.resolve(process.cwd(), input ?? DEFAULT_MANIFEST_PATH);
|
|
17989
18182
|
}
|
|
17990
18183
|
function printManifestPlan(manifest) {
|
|
17991
18184
|
console.log(source_default.bold("Apex tracking ingestion plan"));
|
|
@@ -18212,7 +18405,7 @@ trackingCommand.command("latency").description("Measure forced-preview versus na
|
|
|
18212
18405
|
});
|
|
18213
18406
|
|
|
18214
18407
|
// src/commands/events.ts
|
|
18215
|
-
var
|
|
18408
|
+
var import_node_fs16 = __toESM(require("node:fs"));
|
|
18216
18409
|
var maxWindowMs = 31 * 864e5;
|
|
18217
18410
|
function correctedWindowStep(opts, nowMs) {
|
|
18218
18411
|
const fromMs = Date.parse(opts.from);
|
|
@@ -18315,7 +18508,7 @@ eventsCommand.command("export").description(
|
|
|
18315
18508
|
if (opts.jsonSummary && !opts.out) {
|
|
18316
18509
|
fail(command, "--json-summary requires --out for the event rows", EXIT_CODES.validation);
|
|
18317
18510
|
}
|
|
18318
|
-
const output2 = opts.out ?
|
|
18511
|
+
const output2 = opts.out ? import_node_fs16.default.createWriteStream(opts.out, { flags: "wx" }) : null;
|
|
18319
18512
|
let outputError = null;
|
|
18320
18513
|
output2?.on("error", (error) => {
|
|
18321
18514
|
outputError = error;
|
|
@@ -18454,7 +18647,7 @@ eventsCommand.command("export").description(
|
|
|
18454
18647
|
} catch (error) {
|
|
18455
18648
|
if (output2 && opts.out) {
|
|
18456
18649
|
output2.destroy();
|
|
18457
|
-
|
|
18650
|
+
import_node_fs16.default.rmSync(opts.out, { force: true });
|
|
18458
18651
|
const suffix = `The partial file ${opts.out} was removed.`;
|
|
18459
18652
|
if (error instanceof CliError)
|
|
18460
18653
|
throw new CliError(
|
|
@@ -18475,8 +18668,6 @@ eventsCommand.command("export").description(
|
|
|
18475
18668
|
|
|
18476
18669
|
// src/commands/goals.ts
|
|
18477
18670
|
init_source();
|
|
18478
|
-
var import_node_fs16 = __toESM(require("node:fs"));
|
|
18479
|
-
var import_node_path13 = __toESM(require("node:path"));
|
|
18480
18671
|
|
|
18481
18672
|
// ../sdk/src/safe-regex.ts
|
|
18482
18673
|
var MAX_PATTERN_LENGTH = 1024;
|
|
@@ -18657,11 +18848,10 @@ function printSuggestions(data) {
|
|
|
18657
18848
|
}
|
|
18658
18849
|
function parseCapture(command, value2) {
|
|
18659
18850
|
if (!value2) return void 0;
|
|
18660
|
-
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
18661
|
-
const text2 = value2.startsWith("@") || import_node_fs16.default.existsSync(source) ? import_node_fs16.default.readFileSync(import_node_path13.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
18662
18851
|
try {
|
|
18663
|
-
return
|
|
18852
|
+
return readJsonInput(value2);
|
|
18664
18853
|
} catch (error) {
|
|
18854
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
18665
18855
|
const detail5 = error instanceof Error ? error.message : String(error);
|
|
18666
18856
|
fail(command, `Invalid --capture JSON: ${detail5}`, EXIT_CODES.validation);
|
|
18667
18857
|
}
|
|
@@ -19436,7 +19626,7 @@ var fields = ["enabled", "css", "helperJs", "js"];
|
|
|
19436
19626
|
function readPatch(command, file) {
|
|
19437
19627
|
let input;
|
|
19438
19628
|
try {
|
|
19439
|
-
input =
|
|
19629
|
+
input = readJsonInput(`@${file}`);
|
|
19440
19630
|
} catch (error) {
|
|
19441
19631
|
fail(command, `Cannot read project code JSON: ${String(error)}`, EXIT_CODES.validation);
|
|
19442
19632
|
}
|
|
@@ -19599,13 +19789,9 @@ for (const mode of ["diff", "set"]) {
|
|
|
19599
19789
|
projectCodeCommand.addCommand(command);
|
|
19600
19790
|
}
|
|
19601
19791
|
|
|
19602
|
-
// src/commands/triggers.ts
|
|
19603
|
-
var import_node_fs19 = __toESM(require("node:fs"));
|
|
19604
|
-
var import_node_path16 = __toESM(require("node:path"));
|
|
19605
|
-
|
|
19606
19792
|
// src/trigger-proposal.ts
|
|
19607
|
-
function runtimePath(
|
|
19608
|
-
return
|
|
19793
|
+
function runtimePath(path21) {
|
|
19794
|
+
return path21.replaceAll("[]", ".0");
|
|
19609
19795
|
}
|
|
19610
19796
|
function fieldsFrom(value2, prefix = "", descriptorList = false) {
|
|
19611
19797
|
if (!value2 || typeof value2 !== "object") return [];
|
|
@@ -19683,9 +19869,9 @@ function candidatesFrom(report) {
|
|
|
19683
19869
|
}
|
|
19684
19870
|
return [root];
|
|
19685
19871
|
}
|
|
19686
|
-
function score2(
|
|
19872
|
+
function score2(path21, patterns) {
|
|
19687
19873
|
return patterns.reduce(
|
|
19688
|
-
(total, pattern, index) => total + (pattern.test(
|
|
19874
|
+
(total, pattern, index) => total + (pattern.test(path21) ? patterns.length - index : 0),
|
|
19689
19875
|
0
|
|
19690
19876
|
);
|
|
19691
19877
|
}
|
|
@@ -20110,7 +20296,7 @@ var get3 = new Command("get").description("Get data layer triggers").requiredOpt
|
|
|
20110
20296
|
var set2 = new Command("set").requiredOption("--file <json>", "JSON trigger array").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").action(async function(opts) {
|
|
20111
20297
|
let triggers;
|
|
20112
20298
|
try {
|
|
20113
|
-
triggers = strictTriggers(
|
|
20299
|
+
triggers = strictTriggers(readJsonInput(`@${opts.file}`));
|
|
20114
20300
|
} catch (error) {
|
|
20115
20301
|
fail(
|
|
20116
20302
|
this,
|
|
@@ -20137,7 +20323,7 @@ var propose = new Command("propose").requiredOption("--from-file <u2-report.json
|
|
|
20137
20323
|
let proposals;
|
|
20138
20324
|
let report;
|
|
20139
20325
|
try {
|
|
20140
|
-
report =
|
|
20326
|
+
report = readJsonInput(`@${opts.fromFile}`);
|
|
20141
20327
|
proposals = proposeTriggers(report);
|
|
20142
20328
|
} catch (error) {
|
|
20143
20329
|
fail(this, error instanceof Error ? error.message : String(error), EXIT_CODES.validation);
|
|
@@ -23578,8 +23764,6 @@ installCommand.command("steps").description("Show each Apex installation step").
|
|
|
23578
23764
|
|
|
23579
23765
|
// src/commands/flags.ts
|
|
23580
23766
|
init_source();
|
|
23581
|
-
var import_node_fs20 = __toESM(require("node:fs"));
|
|
23582
|
-
var import_node_path17 = __toESM(require("node:path"));
|
|
23583
23767
|
|
|
23584
23768
|
// src/edge-status.ts
|
|
23585
23769
|
function getEdgeDeliveryStatus(deliveryType, expectedWorstCaseSeconds = 360) {
|
|
@@ -23620,11 +23804,10 @@ var kind = "flags";
|
|
|
23620
23804
|
function body(command, value2, opts) {
|
|
23621
23805
|
let parsed = {};
|
|
23622
23806
|
if (value2) {
|
|
23623
|
-
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
23624
|
-
const text2 = value2.startsWith("@") || import_node_fs20.default.existsSync(source) ? import_node_fs20.default.readFileSync(import_node_path17.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
23625
23807
|
try {
|
|
23626
|
-
parsed =
|
|
23808
|
+
parsed = readJsonInput(value2);
|
|
23627
23809
|
} catch (e) {
|
|
23810
|
+
if (!(e instanceof SyntaxError)) throw e;
|
|
23628
23811
|
fail(
|
|
23629
23812
|
command,
|
|
23630
23813
|
`Invalid JSON: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -23754,11 +23937,11 @@ Examples:
|
|
|
23754
23937
|
);
|
|
23755
23938
|
|
|
23756
23939
|
// src/commands/surveys.ts
|
|
23757
|
-
var
|
|
23758
|
-
var
|
|
23940
|
+
var import_node_fs19 = __toESM(require("node:fs"));
|
|
23941
|
+
var import_node_path16 = __toESM(require("node:path"));
|
|
23759
23942
|
function body2(command, value2) {
|
|
23760
23943
|
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
23761
|
-
const text2 = value2.startsWith("@") ||
|
|
23944
|
+
const text2 = value2.startsWith("@") || import_node_fs19.default.existsSync(source) ? import_node_fs19.default.readFileSync(import_node_path16.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
23762
23945
|
try {
|
|
23763
23946
|
return JSON.parse(text2);
|
|
23764
23947
|
} catch (error) {
|
|
@@ -23938,17 +24121,13 @@ Examples:
|
|
|
23938
24121
|
);
|
|
23939
24122
|
|
|
23940
24123
|
// src/commands/backlog.ts
|
|
23941
|
-
var import_node_fs22 = __toESM(require("node:fs"));
|
|
23942
|
-
var import_node_path19 = __toESM(require("node:path"));
|
|
23943
24124
|
init_source();
|
|
23944
24125
|
var kind2 = "backlog";
|
|
23945
24126
|
var singular = "backlog item";
|
|
23946
24127
|
function parse(command, value2) {
|
|
23947
24128
|
if (!value2) return {};
|
|
23948
24129
|
try {
|
|
23949
|
-
const
|
|
23950
|
-
const text2 = value2.startsWith("@") || import_node_fs22.default.existsSync(source) ? import_node_fs22.default.readFileSync(import_node_path19.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
23951
|
-
const body6 = JSON.parse(text2);
|
|
24130
|
+
const body6 = readJsonInput(value2);
|
|
23952
24131
|
if (!body6 || typeof body6 !== "object" || Array.isArray(body6))
|
|
23953
24132
|
throw new Error("body must be an object");
|
|
23954
24133
|
return body6;
|
|
@@ -24224,17 +24403,13 @@ var fields2 = new Command("fields").description("Manage the shop's own idea fiel
|
|
|
24224
24403
|
var backlogCommand = new Command("backlog").description("Manage backlog").addCommand(list8).addCommand(get7).addCommand(write3("create")).addCommand(write3("update")).addCommand(archive).addCommand(restore).addCommand(remove2).addCommand(fields2);
|
|
24225
24404
|
|
|
24226
24405
|
// src/commands/signals.ts
|
|
24227
|
-
var import_node_fs23 = __toESM(require("node:fs"));
|
|
24228
|
-
var import_node_path20 = __toESM(require("node:path"));
|
|
24229
24406
|
init_source();
|
|
24230
24407
|
var kind3 = "signals";
|
|
24231
24408
|
var singular2 = "signal";
|
|
24232
24409
|
function parse2(command, value2) {
|
|
24233
24410
|
if (!value2) return {};
|
|
24234
24411
|
try {
|
|
24235
|
-
const
|
|
24236
|
-
const text2 = value2.startsWith("@") || import_node_fs23.default.existsSync(source) ? import_node_fs23.default.readFileSync(import_node_path20.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
24237
|
-
const body6 = JSON.parse(text2);
|
|
24412
|
+
const body6 = readJsonInput(value2);
|
|
24238
24413
|
if (!body6 || typeof body6 !== "object" || Array.isArray(body6))
|
|
24239
24414
|
throw new Error("body must be an object");
|
|
24240
24415
|
return body6;
|
|
@@ -24315,17 +24490,14 @@ var signalsCommand = new Command("signals").description("Manage signals").addCom
|
|
|
24315
24490
|
|
|
24316
24491
|
// src/commands/personalizations.ts
|
|
24317
24492
|
init_source();
|
|
24318
|
-
var import_node_fs24 = __toESM(require("node:fs"));
|
|
24319
|
-
var import_node_path21 = __toESM(require("node:path"));
|
|
24320
24493
|
var kind4 = "personalizations";
|
|
24321
24494
|
function body3(command, value2, opts) {
|
|
24322
24495
|
let parsed = {};
|
|
24323
24496
|
if (value2) {
|
|
24324
|
-
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
24325
|
-
const text2 = value2.startsWith("@") || import_node_fs24.default.existsSync(source) ? import_node_fs24.default.readFileSync(import_node_path21.default.resolve(process.cwd(), source), "utf8") : value2;
|
|
24326
24497
|
try {
|
|
24327
|
-
parsed =
|
|
24498
|
+
parsed = readJsonInput(value2);
|
|
24328
24499
|
} catch (e) {
|
|
24500
|
+
if (!(e instanceof SyntaxError)) throw e;
|
|
24329
24501
|
fail(
|
|
24330
24502
|
command,
|
|
24331
24503
|
`Invalid JSON: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -24404,8 +24576,6 @@ var personalizationsCommand = new Command("personalizations").description("Manag
|
|
|
24404
24576
|
|
|
24405
24577
|
// src/commands/targeting-resources.ts
|
|
24406
24578
|
init_source();
|
|
24407
|
-
var import_node_fs25 = __toESM(require("node:fs"));
|
|
24408
|
-
var import_node_path22 = __toESM(require("node:path"));
|
|
24409
24579
|
|
|
24410
24580
|
// src/generated/exclusion-group-body.schema.json
|
|
24411
24581
|
var exclusion_group_body_schema_default = {
|
|
@@ -24887,10 +25057,8 @@ function exclusionFailureDetails(kind5, action80, status4, details) {
|
|
|
24887
25057
|
}
|
|
24888
25058
|
function body4(command, value2) {
|
|
24889
25059
|
if (!value2) return {};
|
|
24890
|
-
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
24891
25060
|
try {
|
|
24892
|
-
|
|
24893
|
-
return JSON.parse(text2);
|
|
25061
|
+
return readJsonInput(value2);
|
|
24894
25062
|
} catch (error) {
|
|
24895
25063
|
fail(
|
|
24896
25064
|
command,
|
|
@@ -24973,8 +25141,8 @@ function targetingResourceCommand(kind5) {
|
|
|
24973
25141
|
}
|
|
24974
25142
|
command.argument(
|
|
24975
25143
|
"[json]",
|
|
24976
|
-
kind5 === "exclusion-groups" || kind5 === "pages" ? "Request body as inline JSON, @file, or a file path (shape below)" : "Inline JSON, @file, or file path"
|
|
24977
|
-
).option("--file <path>", "JSON body file").action(async function(...args) {
|
|
25144
|
+
kind5 === "exclusion-groups" || kind5 === "pages" ? "Request body as inline JSON, @file, or a file path (shape below; a UTF-8 BOM is ignored)" : "Inline JSON, @file, or file path (a UTF-8 BOM is ignored)"
|
|
25145
|
+
).option("--file <path>", "JSON body file (a UTF-8 BOM is ignored)").action(async function(...args) {
|
|
24978
25146
|
const opts = this.opts();
|
|
24979
25147
|
const id = action80 === "update" ? String(args[0]) : void 0;
|
|
24980
25148
|
const value2 = opts.file ? `@${opts.file}` : String(args[action80 === "update" ? 1 : 0] ?? "") || void 0;
|
|
@@ -25076,7 +25244,10 @@ function targetingResourceCommand(kind5) {
|
|
|
25076
25244
|
);
|
|
25077
25245
|
if (kind5 === "exclusion-groups") {
|
|
25078
25246
|
root.addCommand(
|
|
25079
|
-
new Command("replace-members").description("Replace exclusion group members").argument("<id>").argument(
|
|
25247
|
+
new Command("replace-members").description("Replace exclusion group members").argument("<id>").argument(
|
|
25248
|
+
"<json>",
|
|
25249
|
+
"Request body as inline JSON, @file, or a file path (shape below; a UTF-8 BOM is ignored)"
|
|
25250
|
+
).action(async function(id, value2) {
|
|
25080
25251
|
const response = await requestTargetingResource(
|
|
25081
25252
|
requireCredentials(),
|
|
25082
25253
|
"PUT",
|
|
@@ -25191,8 +25362,8 @@ var targetingCommand = new Command("targeting").description("Test experiment tar
|
|
|
25191
25362
|
// src/commands/sos.ts
|
|
25192
25363
|
var import_node_crypto8 = require("node:crypto");
|
|
25193
25364
|
init_source();
|
|
25194
|
-
var
|
|
25195
|
-
var
|
|
25365
|
+
var import_node_fs20 = __toESM(require("node:fs"));
|
|
25366
|
+
var import_node_path17 = __toESM(require("node:path"));
|
|
25196
25367
|
var SOS_TYPES = /* @__PURE__ */ new Set([
|
|
25197
25368
|
"full_handover",
|
|
25198
25369
|
"design",
|
|
@@ -25205,7 +25376,7 @@ var SOS_TYPES = /* @__PURE__ */ new Set([
|
|
|
25205
25376
|
function readText(value2) {
|
|
25206
25377
|
if (!value2) return void 0;
|
|
25207
25378
|
if (!value2.startsWith("@")) return value2;
|
|
25208
|
-
return
|
|
25379
|
+
return readTextInput(value2);
|
|
25209
25380
|
}
|
|
25210
25381
|
function printTicket(ticket, reused) {
|
|
25211
25382
|
console.log();
|
|
@@ -25259,12 +25430,12 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
|
|
|
25259
25430
|
};
|
|
25260
25431
|
if (opts.attach.length > 5) fail(this, "Attach no more than 5 files", 3);
|
|
25261
25432
|
const files = opts.attach.map((file) => {
|
|
25262
|
-
const filename =
|
|
25263
|
-
const content_type = mediaTypes[
|
|
25433
|
+
const filename = import_node_path17.default.basename(file);
|
|
25434
|
+
const content_type = mediaTypes[import_node_path17.default.extname(file).toLowerCase()];
|
|
25264
25435
|
if (!content_type) fail(this, "Unsupported attachment type", 3);
|
|
25265
25436
|
let size;
|
|
25266
25437
|
try {
|
|
25267
|
-
size =
|
|
25438
|
+
size = import_node_fs20.default.statSync(file).size;
|
|
25268
25439
|
} catch {
|
|
25269
25440
|
fail(this, `Attachment not found: ${file}`, 3);
|
|
25270
25441
|
}
|
|
@@ -25272,7 +25443,7 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
|
|
|
25272
25443
|
if (size <= 0 || size > cap) fail(this, "Attachment exceeds its size limit or is empty", 3);
|
|
25273
25444
|
let bytes;
|
|
25274
25445
|
try {
|
|
25275
|
-
bytes =
|
|
25446
|
+
bytes = import_node_fs20.default.readFileSync(file);
|
|
25276
25447
|
} catch {
|
|
25277
25448
|
fail(this, `Attachment not found: ${file}`, 3);
|
|
25278
25449
|
}
|
|
@@ -25408,14 +25579,10 @@ var markRead = new Command("mark-read").option("--channel <key>", "Mark one chan
|
|
|
25408
25579
|
var inboxCommand = new Command("inbox").description("Read and send inbox messages").addCommand(list12).addCommand(send2).addCommand(markRead);
|
|
25409
25580
|
|
|
25410
25581
|
// src/commands/shops.ts
|
|
25411
|
-
var import_node_fs27 = __toESM(require("node:fs"));
|
|
25412
|
-
var import_node_path24 = __toESM(require("node:path"));
|
|
25413
25582
|
function body5(command, raw) {
|
|
25414
25583
|
if (!raw) return {};
|
|
25415
25584
|
try {
|
|
25416
|
-
const
|
|
25417
|
-
const text2 = raw.startsWith("@") || import_node_fs27.default.existsSync(source) ? import_node_fs27.default.readFileSync(import_node_path24.default.resolve(source), "utf8") : raw;
|
|
25418
|
-
const value2 = JSON.parse(text2);
|
|
25585
|
+
const value2 = readJsonInput(raw);
|
|
25419
25586
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2))
|
|
25420
25587
|
throw new Error("body must be an object");
|
|
25421
25588
|
return value2;
|
|
@@ -25757,7 +25924,7 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
|
|
|
25757
25924
|
vtpPaths,
|
|
25758
25925
|
ecommerceFrom: [...ecommerce][0] ?? null,
|
|
25759
25926
|
serverContainerUrl: [...serverUrls][0] ?? null,
|
|
25760
|
-
piiPaths: vtpPaths.filter((
|
|
25927
|
+
piiPaths: vtpPaths.filter((path21) => /email|phone|address|name/i.test(path21))
|
|
25761
25928
|
}
|
|
25762
25929
|
};
|
|
25763
25930
|
}
|
|
@@ -25826,11 +25993,11 @@ function isContainerField(field) {
|
|
|
25826
25993
|
const types = baseFieldTypes(field);
|
|
25827
25994
|
return types.has("obj") || types.has("arr");
|
|
25828
25995
|
}
|
|
25829
|
-
function hasSyntheticPlaceholder(
|
|
25830
|
-
return
|
|
25996
|
+
function hasSyntheticPlaceholder(path21) {
|
|
25997
|
+
return path21.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
|
|
25831
25998
|
}
|
|
25832
|
-
function ambiguousDottedPrefix(event,
|
|
25833
|
-
const segments =
|
|
25999
|
+
function ambiguousDottedPrefix(event, path21) {
|
|
26000
|
+
const segments = path21.split(".");
|
|
25834
26001
|
for (let index = 1; index < segments.length; index += 1) {
|
|
25835
26002
|
const prefix = segments.slice(0, index).join(".");
|
|
25836
26003
|
const parent = event.fields.find((field) => field.path === prefix);
|
|
@@ -25839,42 +26006,42 @@ function ambiguousDottedPrefix(event, path28) {
|
|
|
25839
26006
|
return void 0;
|
|
25840
26007
|
}
|
|
25841
26008
|
function dimensionEligibility(event, field) {
|
|
25842
|
-
const
|
|
25843
|
-
if (hasSyntheticPlaceholder(
|
|
26009
|
+
const path21 = field.path;
|
|
26010
|
+
if (hasSyntheticPlaceholder(path21)) {
|
|
25844
26011
|
return {
|
|
25845
26012
|
eligible: false,
|
|
25846
|
-
message: `Path ${JSON.stringify(
|
|
26013
|
+
message: `Path ${JSON.stringify(path21)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
|
|
25847
26014
|
};
|
|
25848
26015
|
}
|
|
25849
26016
|
if (isContainerField(field)) {
|
|
25850
26017
|
return {
|
|
25851
26018
|
eligible: false,
|
|
25852
|
-
message: `Path ${JSON.stringify(
|
|
26019
|
+
message: `Path ${JSON.stringify(path21)} is container-typed. Choose an observed scalar path.`
|
|
25853
26020
|
};
|
|
25854
26021
|
}
|
|
25855
|
-
if (
|
|
26022
|
+
if (path21.includes("[]")) {
|
|
25856
26023
|
return {
|
|
25857
26024
|
eligible: false,
|
|
25858
|
-
message: `Path ${JSON.stringify(
|
|
26025
|
+
message: `Path ${JSON.stringify(path21)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
|
|
25859
26026
|
};
|
|
25860
26027
|
}
|
|
25861
26028
|
if (!isStableField(field)) {
|
|
25862
26029
|
return {
|
|
25863
26030
|
eligible: false,
|
|
25864
|
-
message: `Path ${JSON.stringify(
|
|
26031
|
+
message: `Path ${JSON.stringify(path21)} does not have a stable observed type. Choose a path without a type collision.`
|
|
25865
26032
|
};
|
|
25866
26033
|
}
|
|
25867
26034
|
if (!event.event) {
|
|
25868
26035
|
return {
|
|
25869
26036
|
eligible: false,
|
|
25870
|
-
message: `Path ${JSON.stringify(
|
|
26037
|
+
message: `Path ${JSON.stringify(path21)} has no observed event name. Choose a path from a named event.`
|
|
25871
26038
|
};
|
|
25872
26039
|
}
|
|
25873
|
-
const ambiguousPrefix = ambiguousDottedPrefix(event,
|
|
26040
|
+
const ambiguousPrefix = ambiguousDottedPrefix(event, path21);
|
|
25874
26041
|
if (ambiguousPrefix) {
|
|
25875
26042
|
return {
|
|
25876
26043
|
eligible: false,
|
|
25877
|
-
message: `Path ${JSON.stringify(
|
|
26044
|
+
message: `Path ${JSON.stringify(path21)} could be a literal key containing dots because the discovery report has no observed container at ${JSON.stringify(ambiguousPrefix)}. Apex cannot safely create a capture for it.`
|
|
25878
26045
|
};
|
|
25879
26046
|
}
|
|
25880
26047
|
return { eligible: true };
|
|
@@ -25883,24 +26050,24 @@ function isNumericField(field) {
|
|
|
25883
26050
|
const numericTypes = /* @__PURE__ */ new Set(["num", "numstr", "numstr_comma"]);
|
|
25884
26051
|
return field.types.length > 0 && field.types.every((type) => numericTypes.has(type.split(":")[0]));
|
|
25885
26052
|
}
|
|
25886
|
-
function defaultDimensionLabel(
|
|
25887
|
-
const words =
|
|
25888
|
-
return words ? `${words[0].toUpperCase()}${words.slice(1)}` :
|
|
26053
|
+
function defaultDimensionLabel(path21) {
|
|
26054
|
+
const words = path21.split(/[._-]+/).filter(Boolean).join(" ");
|
|
26055
|
+
return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path21;
|
|
25889
26056
|
}
|
|
25890
26057
|
function quoteShellArgument(value2) {
|
|
25891
26058
|
return "'" + value2.replaceAll("'", "'\\''") + "'";
|
|
25892
26059
|
}
|
|
25893
|
-
function createDimensionInvocation(
|
|
25894
|
-
return `apex discover --create-dimension ${quoteShellArgument(
|
|
26060
|
+
function createDimensionInvocation(path21, eventName) {
|
|
26061
|
+
return `apex discover --create-dimension ${quoteShellArgument(path21)} --event ${quoteShellArgument(eventName)}`;
|
|
25895
26062
|
}
|
|
25896
|
-
function resolveDimensionObservation(command, report,
|
|
26063
|
+
function resolveDimensionObservation(command, report, path21, eventName) {
|
|
25897
26064
|
const matches = report.data_layer.flatMap(
|
|
25898
|
-
(event) => event.fields.filter((field) => field.path ===
|
|
26065
|
+
(event) => event.fields.filter((field) => field.path === path21).map((field) => ({ event, field }))
|
|
25899
26066
|
);
|
|
25900
26067
|
if (!matches.length) {
|
|
25901
26068
|
fail(
|
|
25902
26069
|
command,
|
|
25903
|
-
`Path ${JSON.stringify(
|
|
26070
|
+
`Path ${JSON.stringify(path21)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
|
|
25904
26071
|
EXIT_CODES.validation
|
|
25905
26072
|
);
|
|
25906
26073
|
}
|
|
@@ -25910,7 +26077,7 @@ function resolveDimensionObservation(command, report, path28, eventName) {
|
|
|
25910
26077
|
if (!eventMatches.length) {
|
|
25911
26078
|
fail(
|
|
25912
26079
|
command,
|
|
25913
|
-
`Path ${JSON.stringify(
|
|
26080
|
+
`Path ${JSON.stringify(path21)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
|
|
25914
26081
|
EXIT_CODES.validation
|
|
25915
26082
|
);
|
|
25916
26083
|
}
|
|
@@ -25918,7 +26085,7 @@ function resolveDimensionObservation(command, report, path28, eventName) {
|
|
|
25918
26085
|
if (layers.length > 1) {
|
|
25919
26086
|
fail(
|
|
25920
26087
|
command,
|
|
25921
|
-
`Path ${JSON.stringify(
|
|
26088
|
+
`Path ${JSON.stringify(path21)} on event ${JSON.stringify(eventName)} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
|
|
25922
26089
|
EXIT_CODES.validation
|
|
25923
26090
|
);
|
|
25924
26091
|
}
|
|
@@ -25930,7 +26097,7 @@ function resolveDimensionObservation(command, report, path28, eventName) {
|
|
|
25930
26097
|
if (namedEvents.length > 1) {
|
|
25931
26098
|
fail(
|
|
25932
26099
|
command,
|
|
25933
|
-
`Path ${JSON.stringify(
|
|
26100
|
+
`Path ${JSON.stringify(path21)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
|
|
25934
26101
|
EXIT_CODES.validation
|
|
25935
26102
|
);
|
|
25936
26103
|
}
|
|
@@ -25939,7 +26106,7 @@ function resolveDimensionObservation(command, report, path28, eventName) {
|
|
|
25939
26106
|
if (layers.length > 1) {
|
|
25940
26107
|
fail(
|
|
25941
26108
|
command,
|
|
25942
|
-
`Path ${JSON.stringify(
|
|
26109
|
+
`Path ${JSON.stringify(path21)} on event ${JSON.stringify(namedEvents[0] ?? "")} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
|
|
25943
26110
|
EXIT_CODES.validation
|
|
25944
26111
|
);
|
|
25945
26112
|
}
|
|
@@ -25974,9 +26141,9 @@ function renderDiscovery(report) {
|
|
|
25974
26141
|
`
|
|
25975
26142
|
${event.event || "(unnamed event)"} [${event.dlName}] \u2014 ${event.count} observation(s)${event.matched ? " \u2014 matched" : ""}; t_ms p50=${event.t_ms_p50 ?? "n/a"}, p95=${event.t_ms_p95 ?? "n/a"}, pre-init=${event.pre_init_count}`
|
|
25976
26143
|
);
|
|
25977
|
-
for (const
|
|
26144
|
+
for (const path21 of event.paths)
|
|
25978
26145
|
lines.push(
|
|
25979
|
-
` URL ${
|
|
26146
|
+
` URL ${path21.path}: ${path21.pageviews} pageviews, ${path21.count} pushes, ${path21.pspReturn_count} PSP returns`
|
|
25980
26147
|
);
|
|
25981
26148
|
for (const field of event.fields) {
|
|
25982
26149
|
lines.push(
|
|
@@ -26189,16 +26356,16 @@ var discoverCommand = new Command("discover").description("Read back the trackin
|
|
|
26189
26356
|
});
|
|
26190
26357
|
|
|
26191
26358
|
// src/commands/skills.ts
|
|
26192
|
-
var
|
|
26193
|
-
var
|
|
26359
|
+
var import_node_fs21 = __toESM(require("node:fs"));
|
|
26360
|
+
var import_node_path18 = __toESM(require("node:path"));
|
|
26194
26361
|
function packageRoot() {
|
|
26195
26362
|
const candidates = [
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26363
|
+
import_node_path18.default.resolve(__dirname, ".."),
|
|
26364
|
+
import_node_path18.default.resolve(import_node_path18.default.dirname(process.argv[1] ?? process.cwd()), ".."),
|
|
26365
|
+
import_node_path18.default.resolve(process.cwd(), "packages/cli")
|
|
26199
26366
|
];
|
|
26200
26367
|
for (const candidate of candidates) {
|
|
26201
|
-
if (
|
|
26368
|
+
if (import_node_fs21.default.existsSync(import_node_path18.default.join(candidate, "AGENTS.md"))) return candidate;
|
|
26202
26369
|
}
|
|
26203
26370
|
throw new Error(
|
|
26204
26371
|
`Apex CLI package files not found. Looked for AGENTS.md in: ${candidates.join(", ")}`
|
|
@@ -26207,19 +26374,19 @@ function packageRoot() {
|
|
|
26207
26374
|
function skillPaths() {
|
|
26208
26375
|
const root = packageRoot();
|
|
26209
26376
|
return {
|
|
26210
|
-
agents:
|
|
26211
|
-
skill:
|
|
26377
|
+
agents: import_node_path18.default.join(root, "AGENTS.md"),
|
|
26378
|
+
skill: import_node_path18.default.join(root, "skills", "apex-cli", "SKILL.md")
|
|
26212
26379
|
};
|
|
26213
26380
|
}
|
|
26214
26381
|
function installSkill(paths, targetRoot) {
|
|
26215
|
-
const missing = [paths.skill, paths.agents].filter((filePath) => !
|
|
26382
|
+
const missing = [paths.skill, paths.agents].filter((filePath) => !import_node_fs21.default.existsSync(filePath));
|
|
26216
26383
|
if (missing.length > 0) {
|
|
26217
26384
|
throw new Error(`Apex CLI package files not found. Missing: ${missing.join(", ")}`);
|
|
26218
26385
|
}
|
|
26219
|
-
const target =
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26386
|
+
const target = import_node_path18.default.resolve(targetRoot, "apex-cli");
|
|
26387
|
+
import_node_fs21.default.mkdirSync(target, { recursive: true });
|
|
26388
|
+
import_node_fs21.default.copyFileSync(paths.skill, import_node_path18.default.join(target, "SKILL.md"));
|
|
26389
|
+
import_node_fs21.default.copyFileSync(paths.agents, import_node_path18.default.join(target, "AGENTS.md"));
|
|
26223
26390
|
return target;
|
|
26224
26391
|
}
|
|
26225
26392
|
var skillsCommand = new Command("skills").description("Print or install the Apex CLI agent skill files").option("--install", "Copy the Apex CLI skill into ./.claude/skills/apex-cli").option("--target <dir>", "Install target skills directory", ".claude/skills").option("--json", "Print JSON").action(function action73(opts) {
|
|
@@ -27276,8 +27443,8 @@ async function fetchText(url) {
|
|
|
27276
27443
|
});
|
|
27277
27444
|
return { status: res.status, text: await readResponseText(res), finalUrl: res.url };
|
|
27278
27445
|
}
|
|
27279
|
-
function abs(base,
|
|
27280
|
-
return new URL(
|
|
27446
|
+
function abs(base, path21) {
|
|
27447
|
+
return new URL(path21, base).toString();
|
|
27281
27448
|
}
|
|
27282
27449
|
function looksLikeShopifyCart(text2) {
|
|
27283
27450
|
try {
|
|
@@ -27297,9 +27464,9 @@ async function collectSignals(url) {
|
|
|
27297
27464
|
const chunks = [`__apex_primary_status_${primary.status}__`, primary.text];
|
|
27298
27465
|
const protectedHit = primary.status === 403 || primary.status === 429 || matchDetectionMarkers(primary.text, "protected").length > 0;
|
|
27299
27466
|
if (!protectedHit) {
|
|
27300
|
-
for (const
|
|
27467
|
+
for (const path21 of ["/cart.js"]) {
|
|
27301
27468
|
try {
|
|
27302
|
-
const secondary = await fetchText(abs(primary.finalUrl || url,
|
|
27469
|
+
const secondary = await fetchText(abs(primary.finalUrl || url, path21));
|
|
27303
27470
|
if (secondary.status >= 200 && secondary.status < 300 && looksLikeShopifyCart(secondary.text)) {
|
|
27304
27471
|
chunks.push("__apex_secondary_shopify_cart_js__", secondary.text.slice(0, 2e3));
|
|
27305
27472
|
}
|
|
@@ -27594,10 +27761,10 @@ var webhooksCommand = new Command("webhooks").description("Manage shop webhooks"
|
|
|
27594
27761
|
|
|
27595
27762
|
// src/commands/simulate.ts
|
|
27596
27763
|
init_source();
|
|
27597
|
-
var
|
|
27598
|
-
var
|
|
27764
|
+
var import_node_fs22 = __toESM(require("node:fs"));
|
|
27765
|
+
var import_node_path19 = __toESM(require("node:path"));
|
|
27599
27766
|
function readJson2(file) {
|
|
27600
|
-
const text2 = file === "-" ?
|
|
27767
|
+
const text2 = file === "-" ? import_node_fs22.default.readFileSync(0, "utf8") : import_node_fs22.default.readFileSync(import_node_path19.default.resolve(file), "utf8");
|
|
27601
27768
|
try {
|
|
27602
27769
|
return JSON.parse(text2);
|
|
27603
27770
|
} catch (error) {
|
|
@@ -27719,13 +27886,13 @@ var simulateCommand = new Command("simulate").description("Simulate data-layer t
|
|
|
27719
27886
|
});
|
|
27720
27887
|
|
|
27721
27888
|
// src/commands/config-resources.ts
|
|
27722
|
-
var
|
|
27723
|
-
var
|
|
27889
|
+
var import_node_fs24 = __toESM(require("node:fs"));
|
|
27890
|
+
var import_node_path21 = __toESM(require("node:path"));
|
|
27724
27891
|
|
|
27725
27892
|
// src/commands/landing-pages.ts
|
|
27726
27893
|
var import_node_crypto9 = __toESM(require("node:crypto"));
|
|
27727
|
-
var
|
|
27728
|
-
var
|
|
27894
|
+
var import_node_fs23 = __toESM(require("node:fs"));
|
|
27895
|
+
var import_node_path20 = __toESM(require("node:path"));
|
|
27729
27896
|
function apiFailure2(command, response, fallback2) {
|
|
27730
27897
|
if (!response.ok || response.data === void 0) {
|
|
27731
27898
|
fail(
|
|
@@ -27740,13 +27907,13 @@ function sha256(body6) {
|
|
|
27740
27907
|
return import_node_crypto9.default.createHash("sha256").update(body6).digest("hex");
|
|
27741
27908
|
}
|
|
27742
27909
|
function markerPath(folder) {
|
|
27743
|
-
return
|
|
27910
|
+
return import_node_path20.default.join(folder, LANDING_PAGE_CHECKOUT_MARKER);
|
|
27744
27911
|
}
|
|
27745
27912
|
function readMarker(folder) {
|
|
27746
27913
|
const file = markerPath(folder);
|
|
27747
|
-
if (!
|
|
27914
|
+
if (!import_node_fs23.default.existsSync(file)) return null;
|
|
27748
27915
|
try {
|
|
27749
|
-
return JSON.parse(
|
|
27916
|
+
return JSON.parse(import_node_fs23.default.readFileSync(file, "utf8"));
|
|
27750
27917
|
} catch (error) {
|
|
27751
27918
|
throw new CliError(
|
|
27752
27919
|
`${LANDING_PAGE_CHECKOUT_MARKER} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -27764,14 +27931,14 @@ function readMarker(folder) {
|
|
|
27764
27931
|
}
|
|
27765
27932
|
}
|
|
27766
27933
|
function writeMarker(folder, marker) {
|
|
27767
|
-
|
|
27768
|
-
|
|
27769
|
-
|
|
27934
|
+
import_node_fs23.default.mkdirSync(import_node_path20.default.dirname(markerPath(folder)), { recursive: true });
|
|
27935
|
+
import_node_fs23.default.writeFileSync(import_node_path20.default.join(folder, ".apex", ".gitignore"), "landing-page.json\n", "utf8");
|
|
27936
|
+
import_node_fs23.default.writeFileSync(markerPath(folder), `${JSON.stringify(marker, null, 2)}
|
|
27770
27937
|
`, "utf8");
|
|
27771
27938
|
}
|
|
27772
27939
|
function requireCheckout(command, folderArg) {
|
|
27773
|
-
const folder =
|
|
27774
|
-
const marker =
|
|
27940
|
+
const folder = import_node_path20.default.resolve(process.cwd(), folderArg);
|
|
27941
|
+
const marker = import_node_fs23.default.existsSync(folder) && import_node_fs23.default.statSync(folder).isDirectory() ? readMarker(folder) : null;
|
|
27775
27942
|
if (!marker) {
|
|
27776
27943
|
fail(command, `${folderArg} is not a landing-page checkout.`, EXIT_CODES.validation, {
|
|
27777
27944
|
code: "validation_failed",
|
|
@@ -27783,8 +27950,8 @@ function requireCheckout(command, folderArg) {
|
|
|
27783
27950
|
return { folder, marker };
|
|
27784
27951
|
}
|
|
27785
27952
|
function resolvePageId(target) {
|
|
27786
|
-
const folder =
|
|
27787
|
-
if (
|
|
27953
|
+
const folder = import_node_path20.default.resolve(process.cwd(), target);
|
|
27954
|
+
if (import_node_fs23.default.existsSync(folder) && import_node_fs23.default.statSync(folder).isDirectory()) {
|
|
27788
27955
|
const marker = readMarker(folder);
|
|
27789
27956
|
if (marker) return marker.landingPageId;
|
|
27790
27957
|
throw new CliError(
|
|
@@ -27806,16 +27973,16 @@ function resolvePageId(target) {
|
|
|
27806
27973
|
function readCheckoutFiles(folder) {
|
|
27807
27974
|
const files = [];
|
|
27808
27975
|
const visit2 = (directory) => {
|
|
27809
|
-
for (const entry of
|
|
27810
|
-
const absolute =
|
|
27811
|
-
const relative =
|
|
27976
|
+
for (const entry of import_node_fs23.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
27977
|
+
const absolute = import_node_path20.default.join(directory, entry.name);
|
|
27978
|
+
const relative = import_node_path20.default.relative(folder, absolute).split(import_node_path20.default.sep).join("/");
|
|
27812
27979
|
if (!isLandingPageCheckoutPath(relative)) continue;
|
|
27813
27980
|
if (entry.isDirectory()) visit2(absolute);
|
|
27814
27981
|
else if (entry.isFile()) {
|
|
27815
27982
|
try {
|
|
27816
27983
|
validateLandingPageLocalFiles([
|
|
27817
27984
|
...files,
|
|
27818
|
-
{ path: relative, bytes:
|
|
27985
|
+
{ path: relative, bytes: import_node_fs23.default.statSync(absolute).size, sha256: "" }
|
|
27819
27986
|
]);
|
|
27820
27987
|
} catch (error) {
|
|
27821
27988
|
throw new CliError(
|
|
@@ -27823,7 +27990,7 @@ function readCheckoutFiles(folder) {
|
|
|
27823
27990
|
EXIT_CODES.validation
|
|
27824
27991
|
);
|
|
27825
27992
|
}
|
|
27826
|
-
const body6 =
|
|
27993
|
+
const body6 = import_node_fs23.default.readFileSync(absolute);
|
|
27827
27994
|
files.push({ path: relative, sha256: sha256(body6), bytes: body6.byteLength, body: body6 });
|
|
27828
27995
|
}
|
|
27829
27996
|
}
|
|
@@ -27840,8 +28007,8 @@ function readCheckoutFiles(folder) {
|
|
|
27840
28007
|
return files;
|
|
27841
28008
|
}
|
|
27842
28009
|
function readManifest2(command, folder) {
|
|
27843
|
-
const file =
|
|
27844
|
-
if (!
|
|
28010
|
+
const file = import_node_path20.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
|
|
28011
|
+
if (!import_node_fs23.default.existsSync(file)) {
|
|
27845
28012
|
fail(
|
|
27846
28013
|
command,
|
|
27847
28014
|
`${LANDING_PAGE_MANIFEST_FILE} is missing from the checkout.`,
|
|
@@ -27852,7 +28019,7 @@ function readManifest2(command, folder) {
|
|
|
27852
28019
|
);
|
|
27853
28020
|
}
|
|
27854
28021
|
try {
|
|
27855
|
-
return JSON.parse(
|
|
28022
|
+
return JSON.parse(import_node_fs23.default.readFileSync(file, "utf8"));
|
|
27856
28023
|
} catch (error) {
|
|
27857
28024
|
fail(
|
|
27858
28025
|
command,
|
|
@@ -27905,8 +28072,8 @@ async function confirm2(message) {
|
|
|
27905
28072
|
return answer.trim().toLowerCase() === "y";
|
|
27906
28073
|
}
|
|
27907
28074
|
function manifestDigest(folder) {
|
|
27908
|
-
const file =
|
|
27909
|
-
return
|
|
28075
|
+
const file = import_node_path20.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
|
|
28076
|
+
return import_node_fs23.default.existsSync(file) ? sha256(import_node_fs23.default.readFileSync(file)) : null;
|
|
27910
28077
|
}
|
|
27911
28078
|
function localChanges(folder, marker) {
|
|
27912
28079
|
const local = new Map(readCheckoutFiles(folder).map((file) => [file.path, file.sha256]));
|
|
@@ -27924,8 +28091,8 @@ function removeStaleFiles(folder, previous, next, added = []) {
|
|
|
27924
28091
|
if (!previous) return;
|
|
27925
28092
|
for (const file of [...Object.keys(previous.files), ...added]) {
|
|
27926
28093
|
if (next.has(file) || !isLandingPageCheckoutPath(file)) continue;
|
|
27927
|
-
const absolute =
|
|
27928
|
-
if (absolute.startsWith(folder +
|
|
28094
|
+
const absolute = import_node_path20.default.resolve(folder, file);
|
|
28095
|
+
if (absolute.startsWith(folder + import_node_path20.default.sep) && import_node_fs23.default.existsSync(absolute)) import_node_fs23.default.rmSync(absolute);
|
|
27929
28096
|
}
|
|
27930
28097
|
}
|
|
27931
28098
|
async function pullCheckout(command, creds, id, folder, force = false) {
|
|
@@ -27965,7 +28132,7 @@ async function pullCheckout(command, creds, id, folder, force = false) {
|
|
|
27965
28132
|
const api = landingPages(creds);
|
|
27966
28133
|
const listing = await api.checkout(id);
|
|
27967
28134
|
apiFailure2(command, listing, "Failed to pull landing page");
|
|
27968
|
-
|
|
28135
|
+
import_node_fs23.default.mkdirSync(folder, { recursive: true });
|
|
27969
28136
|
const written = {};
|
|
27970
28137
|
const downloaded = [];
|
|
27971
28138
|
for (const batch of landingPageBatches(listing.data.files, (file) => file.bytes)) {
|
|
@@ -27979,8 +28146,8 @@ async function pullCheckout(command, creds, id, folder, force = false) {
|
|
|
27979
28146
|
if (missing.length)
|
|
27980
28147
|
fail(command, `The server did not return ${missing.slice(0, 5).join(", ")}. Pull again.`);
|
|
27981
28148
|
for (const file of contents.data.files) {
|
|
27982
|
-
const target =
|
|
27983
|
-
if (!target.startsWith(folder +
|
|
28149
|
+
const target = import_node_path20.default.resolve(folder, file.path);
|
|
28150
|
+
if (!target.startsWith(folder + import_node_path20.default.sep))
|
|
27984
28151
|
fail(command, `Refusing to write outside the checkout: ${file.path}`);
|
|
27985
28152
|
const download = file.download_url ? await fetch(file.download_url, { signal: AbortSignal.timeout(6e4) }) : null;
|
|
27986
28153
|
if (download && !download.ok)
|
|
@@ -27993,7 +28160,7 @@ async function pullCheckout(command, creds, id, folder, force = false) {
|
|
|
27993
28160
|
}
|
|
27994
28161
|
if (!existing && !force) {
|
|
27995
28162
|
const clobbered = downloaded.filter(
|
|
27996
|
-
({ target, sha256: digest }) =>
|
|
28163
|
+
({ target, sha256: digest }) => import_node_fs23.default.existsSync(target) && sha256(import_node_fs23.default.readFileSync(target)) !== digest
|
|
27997
28164
|
).map((file) => file.path);
|
|
27998
28165
|
if (clobbered.length) {
|
|
27999
28166
|
fail(
|
|
@@ -28017,8 +28184,8 @@ async function pullCheckout(command, creds, id, folder, force = false) {
|
|
|
28017
28184
|
}
|
|
28018
28185
|
}
|
|
28019
28186
|
for (const file of downloaded) {
|
|
28020
|
-
|
|
28021
|
-
|
|
28187
|
+
import_node_fs23.default.mkdirSync(import_node_path20.default.dirname(file.target), { recursive: true });
|
|
28188
|
+
import_node_fs23.default.writeFileSync(file.target, file.body);
|
|
28022
28189
|
written[file.path] = file.sha256;
|
|
28023
28190
|
}
|
|
28024
28191
|
removeStaleFiles(
|
|
@@ -28027,24 +28194,24 @@ async function pullCheckout(command, creds, id, folder, force = false) {
|
|
|
28027
28194
|
new Set(Object.keys(written)),
|
|
28028
28195
|
force ? readCheckoutFiles(folder).map((file) => file.path) : []
|
|
28029
28196
|
);
|
|
28030
|
-
|
|
28031
|
-
|
|
28197
|
+
import_node_fs23.default.writeFileSync(
|
|
28198
|
+
import_node_path20.default.join(folder, LANDING_PAGE_MANIFEST_FILE),
|
|
28032
28199
|
`${JSON.stringify(listing.data.manifest ?? {}, null, 2)}
|
|
28033
28200
|
`,
|
|
28034
28201
|
"utf8"
|
|
28035
28202
|
);
|
|
28036
|
-
const contextDir =
|
|
28037
|
-
|
|
28203
|
+
const contextDir = import_node_path20.default.join(folder, LANDING_PAGE_CONTEXT_DIR);
|
|
28204
|
+
import_node_fs23.default.mkdirSync(contextDir, { recursive: true });
|
|
28038
28205
|
const contextName = /^[a-z0-9_-]+\.json$/i;
|
|
28039
28206
|
const contextEntries = Object.entries(listing.data.context ?? {}).filter(
|
|
28040
28207
|
([name]) => contextName.test(name)
|
|
28041
28208
|
);
|
|
28042
28209
|
const returned = new Set(contextEntries.map(([name]) => name));
|
|
28043
|
-
for (const name of
|
|
28044
|
-
if (contextName.test(name) && !returned.has(name))
|
|
28210
|
+
for (const name of import_node_fs23.default.readdirSync(contextDir)) {
|
|
28211
|
+
if (contextName.test(name) && !returned.has(name)) import_node_fs23.default.rmSync(import_node_path20.default.join(contextDir, name));
|
|
28045
28212
|
}
|
|
28046
28213
|
for (const [name, value2] of contextEntries) {
|
|
28047
|
-
|
|
28214
|
+
import_node_fs23.default.writeFileSync(import_node_path20.default.join(contextDir, name), `${JSON.stringify(value2, null, 2)}
|
|
28048
28215
|
`, "utf8");
|
|
28049
28216
|
}
|
|
28050
28217
|
writeMarker(folder, {
|
|
@@ -28076,7 +28243,7 @@ var create9 = new Command("create").description("Create a landing page to build
|
|
|
28076
28243
|
...opts.idea ? { idea_key: opts.idea } : {}
|
|
28077
28244
|
});
|
|
28078
28245
|
apiFailure2(this, created, "Failed to create landing page");
|
|
28079
|
-
const pulled = opts.out ? await pullCheckout(this, creds, created.data.id,
|
|
28246
|
+
const pulled = opts.out ? await pullCheckout(this, creds, created.data.id, import_node_path20.default.resolve(process.cwd(), opts.out)) : null;
|
|
28080
28247
|
output(
|
|
28081
28248
|
this,
|
|
28082
28249
|
{ landing_page: created.data, ...pulled ? { folder: pulled.folder } : {} },
|
|
@@ -28088,7 +28255,7 @@ var create9 = new Command("create").description("Create a landing page to build
|
|
|
28088
28255
|
});
|
|
28089
28256
|
var pull = new Command("pull").description("Pull a landing page into a checkout folder").argument("<id>", "Landing page id").option("--out <folder>", "Target folder (default: a folder named after the id)").option("--force", "Overwrite changes in the checkout that are not applied").action(async function(id, opts) {
|
|
28090
28257
|
const creds = requireCredentials();
|
|
28091
|
-
const folder =
|
|
28258
|
+
const folder = import_node_path20.default.resolve(process.cwd(), opts.out ?? id);
|
|
28092
28259
|
const result = await pullCheckout(this, creds, id, folder, opts.force === true);
|
|
28093
28260
|
output(this, result, () => {
|
|
28094
28261
|
console.log(
|
|
@@ -28300,8 +28467,8 @@ var experiment = new Command("experiment").description("Create a draft experimen
|
|
|
28300
28467
|
"--variant <number>",
|
|
28301
28468
|
"Variant version number (default: the manifest's experiment.variant)"
|
|
28302
28469
|
).option("--name <name>", "Experiment name").option("--primary-goal <key>", "Manifest goal key to make primary (default: the first goal)").action(async function(target, opts) {
|
|
28303
|
-
const folder =
|
|
28304
|
-
const fromManifest =
|
|
28470
|
+
const folder = import_node_path20.default.resolve(process.cwd(), target);
|
|
28471
|
+
const fromManifest = import_node_fs23.default.existsSync(import_node_path20.default.join(folder, LANDING_PAGE_MANIFEST_FILE)) ? readManifest2(this, folder)?.experiment ?? {} : {};
|
|
28305
28472
|
const control = Number(opts.control ?? fromManifest.control);
|
|
28306
28473
|
const variant = Number(opts.variant ?? fromManifest.variant);
|
|
28307
28474
|
if (!Number.isInteger(control) || !Number.isInteger(variant) || control < 1 || variant < 1) {
|
|
@@ -28408,12 +28575,12 @@ function readFamily(family, label) {
|
|
|
28408
28575
|
}
|
|
28409
28576
|
var assetReads = readFamily("assets", "assets");
|
|
28410
28577
|
var upload = new Command("upload").description("Upload an asset").argument("<file>").action(async function(file) {
|
|
28411
|
-
const absolute =
|
|
28412
|
-
if (!
|
|
28413
|
-
const fileStat =
|
|
28578
|
+
const absolute = import_node_path21.default.resolve(process.cwd(), file);
|
|
28579
|
+
if (!import_node_fs24.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
|
|
28580
|
+
const fileStat = import_node_fs24.default.statSync(absolute);
|
|
28414
28581
|
if (!fileStat.isFile()) fail(this, `File not found: ${file}`, 3);
|
|
28415
28582
|
if (fileStat.size > MAX_ASSET_SIZE_BYTES) fail(this, `${file} is larger than 10 MB`, 3);
|
|
28416
|
-
const mimeType = ASSET_MIME_TYPES[
|
|
28583
|
+
const mimeType = ASSET_MIME_TYPES[import_node_path21.default.extname(absolute).toLowerCase()];
|
|
28417
28584
|
if (!mimeType)
|
|
28418
28585
|
fail(
|
|
28419
28586
|
this,
|
|
@@ -28422,8 +28589,8 @@ var upload = new Command("upload").description("Upload an asset").argument("<fil
|
|
|
28422
28589
|
);
|
|
28423
28590
|
const r = await uploadAssets(requireCredentials(), [
|
|
28424
28591
|
{
|
|
28425
|
-
name:
|
|
28426
|
-
bytes:
|
|
28592
|
+
name: import_node_path21.default.basename(absolute),
|
|
28593
|
+
bytes: import_node_fs24.default.readFileSync(absolute),
|
|
28427
28594
|
type: mimeType
|
|
28428
28595
|
}
|
|
28429
28596
|
]);
|