@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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +523 -356
  3. 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 path28 = require("node:path");
1163
- var fs32 = require("node:fs");
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 (fs32.existsSync(executableFile)) return;
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 = path28.resolve(baseDir, baseName);
2163
- if (fs32.existsSync(localBin)) return localBin;
2164
- if (sourceExt.includes(path28.extname(baseName))) return void 0;
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) => fs32.existsSync(`${localBin}${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 = fs32.realpathSync(this._scriptPath);
2178
+ resolvedScriptPath = fs25.realpathSync(this._scriptPath);
2179
2179
  } catch {
2180
2180
  resolvedScriptPath = this._scriptPath;
2181
2181
  }
2182
- executableDir = path28.resolve(
2183
- path28.dirname(resolvedScriptPath),
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 = path28.basename(
2190
+ const legacyName = path21.basename(
2191
2191
  this._scriptPath,
2192
- path28.extname(this._scriptPath)
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(path28.extname(executableFile));
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 = path28.basename(filename, path28.extname(filename));
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(path29) {
3065
- if (path29 === void 0) return this._executableDir;
3066
- this._executableDir = path29;
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, path28, body6, requestTimeoutMs, form = false, requestOptions) {
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: path28, 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") });
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, path28, body6, headers);
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}${path28}`, { method, headers, body: form ? body6 : body6 ? JSON.stringify(body6) : void 0, signal: AbortSignal.timeout(timeoutMs) });
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, path28, body6, timeoutMs, requestOptions) => request2(method, path28, body6, timeoutMs, false, requestOptions), requestForm: (method, path28, body6) => request2(method, path28, body6, void 0, true) };
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.0",
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(path28) {
8397
- if (!path28.startsWith("/api/")) return ":route";
8398
- const parts = path28.split(/[?#]/, 1)[0].split("/");
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, path28, body6) => {
8576
- const response = await request2(method, path28, body6);
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, path28, body6) {
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
- path28,
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, path28, body6, requestTimeoutMs, options) {
8788
+ async function request(creds, method, path21, body6, requestTimeoutMs, options) {
8789
8789
  const response = await sharedClient(creds).request(
8790
8790
  method,
8791
- path28,
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, path28, body6) {
8800
- const response = await sharedClient(creds).requestForm(method, path28, body6);
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 path28 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
9440
- return request(creds, method, path28, body6);
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, path28, body6) => request(creds, method, path28, body6)
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 import_node_fs8 = __toESM(require("node:fs"));
10292
- var import_node_path6 = __toESM(require("node:path"));
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 import_node_fs7 = __toESM(require("node:fs"));
10857
- var import_node_path5 = __toESM(require("node:path"));
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 = import_node_path5.default.resolve(baseDir, reference);
10988
+ const absolutePath = import_node_path6.default.resolve(baseDir, reference);
10969
10989
  let realBaseDir;
10970
10990
  let realPath;
10971
10991
  try {
10972
- realBaseDir = import_node_fs7.default.realpathSync(baseDir);
10973
- realPath = import_node_fs7.default.realpathSync(absolutePath);
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 = import_node_path5.default.relative(realBaseDir, realPath);
10978
- if (relativeRealPath.startsWith("..") || import_node_path5.default.isAbsolute(relativeRealPath)) return null;
10979
- if (!import_node_fs7.default.statSync(realPath).isFile()) return null;
10980
- const bytes = import_node_fs7.default.readFileSync(realPath);
10981
- const extension = import_node_path5.default.extname(realPath).toLowerCase();
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: import_node_path5.default.basename(absolutePath),
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, path28) {
11142
+ function visit2(value2, parts, path21) {
11123
11143
  if (parts.length === 0) {
11124
- if (typeof value2 === "string" && value2.includes("\uFFFD")) paths.push(path28);
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 = path28 ? `${path28}.${key}` : key;
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 path28 of replacementCharacterFields(record5, fields3)) {
11144
- process.stderr.write(`apex: stored name contains U+FFFD: ${label}.${path28}
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 import_node_path6.default.resolve(process.cwd(), folder);
13722
+ return import_node_path7.default.resolve(process.cwd(), folder);
13533
13723
  }
13534
13724
  function filesystemApexDir(folder) {
13535
- return import_node_path6.default.join(folder, ".apex");
13725
+ return import_node_path7.default.join(folder, ".apex");
13536
13726
  }
13537
13727
  function filesystemSessionPath(folder) {
13538
- return import_node_path6.default.join(filesystemApexDir(folder), "experiment-filesystem.json");
13728
+ return import_node_path7.default.join(filesystemApexDir(folder), "experiment-filesystem.json");
13539
13729
  }
13540
13730
  function writeFilesystemSession(folder, session) {
13541
- import_node_fs8.default.mkdirSync(filesystemApexDir(folder), { recursive: true });
13542
- import_node_fs8.default.writeFileSync(
13543
- import_node_path6.default.join(filesystemApexDir(folder), ".gitignore"),
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
- import_node_fs8.default.writeFileSync(filesystemSessionPath(folder), `${JSON.stringify(session, null, 2)}
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 (!import_node_fs8.default.existsSync(filePath)) {
13553
- const workspaceMarker = import_node_path6.default.join(filesystemApexDir(folder), "session.json");
13554
- if (import_node_fs8.default.existsSync(workspaceMarker)) {
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(import_node_fs8.default.readFileSync(filePath, "utf8"));
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 = import_node_path6.default.join(folder, relativePath);
13598
- import_node_fs8.default.mkdirSync(import_node_path6.default.dirname(absPath), { recursive: true });
13599
- import_node_fs8.default.writeFileSync(absPath, file.content, "utf8");
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 = import_node_path6.default.join(folder, fileName);
13606
- if (!import_node_fs8.default.existsSync(absPath)) continue;
13607
- const content = import_node_fs8.default.readFileSync(absPath, "utf8");
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 = import_node_path6.default.join(folder, relativePath);
13622
- if (!import_node_fs8.default.existsSync(absPath)) continue;
13623
- const content = import_node_fs8.default.readFileSync(absPath, "utf8");
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 = import_node_path6.default.join(folder, relativePath);
13629
- if (!import_node_fs8.default.existsSync(absPath)) continue;
13630
- const content = import_node_fs8.default.readFileSync(absPath, "utf8");
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 = import_node_path6.default.join(folder, "variations");
13634
- if (import_node_fs8.default.existsSync(variationsRoot)) {
13635
- for (const variationDir of import_node_fs8.default.readdirSync(variationsRoot, { withFileTypes: true })) {
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 = import_node_path6.default.join(variationsRoot, variationDir.name);
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 = import_node_path6.default.join(base, fileName);
13640
- if (!import_node_fs8.default.existsSync(absPath)) continue;
13641
- const relativePath = validateFilesystemPath(import_node_path6.default.relative(folder, absPath));
13642
- const content = import_node_fs8.default.readFileSync(absPath, "utf8");
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
- import_node_path6.default.dirname(import_node_path6.default.join(input.folder, file.path))
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 source = value2.startsWith("@") ? value2.slice(1) : value2;
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: import_node_path6.default.basename(candidate)
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 = JSON.parse(import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), file), "utf8"));
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) => import_node_path6.default.join(folder, file.path))
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 = import_node_fs8.default.existsSync(candidate) && import_node_fs8.default.statSync(candidate).isDirectory();
15087
- const hasApexDir = isFolder && import_node_fs8.default.existsSync(import_node_path6.default.join(candidate, ".apex"));
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 = import_node_path6.default.join(candidate, name);
15137
- if (import_node_fs8.default.existsSync(file)) localHashes.set(name, sha256Text(import_node_fs8.default.readFileSync(file, "utf8")));
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
- const source = value2.startsWith("@") ? value2.slice(1) : value2;
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 = import_node_path6.default.resolve(process.cwd(), filePath);
15961
- if (!import_node_fs8.default.existsSync(absolutePath)) throw new Error(`Evidence file not found: ${filePath}`);
15962
- const bytes = import_node_fs8.default.readFileSync(absolutePath);
15963
- const extension = import_node_path6.default.extname(absolutePath).toLowerCase();
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], import_node_path6.default.basename(absolutePath), { type: mimeType }));
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 = import_node_path6.default.resolve(process.cwd(), opts.out);
16386
- import_node_fs8.default.mkdirSync(outputDir, { recursive: true });
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 import_node_fs8.default.promises.writeFile(
16445
- import_node_path6.default.join(outputDir, filename),
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 import_node_fs9 = __toESM(require("node:fs"));
16485
- var import_node_path7 = __toESM(require("node:path"));
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 = import_node_path7.default.resolve(fileArg);
16743
+ const resolved = import_node_path8.default.resolve(fileArg);
16551
16744
  let bytes;
16552
16745
  try {
16553
- bytes = import_node_fs9.default.readFileSync(resolved);
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]), import_node_path7.default.basename(resolved));
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 import_node_fs10 = __toESM(require("node:fs"));
16864
+ var import_node_fs11 = __toESM(require("node:fs"));
16672
16865
  var import_node_os7 = __toESM(require("node:os"));
16673
- var import_node_path8 = __toESM(require("node:path"));
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 = import_node_path8.default.extname(absPath).toLowerCase();
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 = import_node_fs10.default.mkdtempSync(import_node_path8.default.join(import_node_os7.default.tmpdir(), "drip-config-"));
16729
- const outFile = import_node_path8.default.join(tmpDir, "config.mjs");
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
- import_node_fs10.default.rmSync(tmpDir, { recursive: true, force: true });
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 = import_node_path8.default.resolve(configPath);
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: import_node_path8.default.dirname(absoluteConfigPath),
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, import_node_path8.default.dirname(absoluteConfigPath))
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 import_node_fs11 = __toESM(require("node:fs"));
16922
- var import_node_path9 = __toESM(require("node:path"));
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 (!import_node_fs11.default.existsSync(filePath)) return null;
16930
- return JSON.parse(import_node_fs11.default.readFileSync(filePath, "utf8"));
17122
+ if (!import_node_fs12.default.existsSync(filePath)) return null;
17123
+ return readJsonInput(`@${filePath}`);
16931
17124
  }
16932
17125
  function resolveFolder(folder) {
16933
- return import_node_path9.default.resolve(process.cwd(), folder);
17126
+ return import_node_path10.default.resolve(process.cwd(), folder);
16934
17127
  }
16935
17128
  function experimentConfigPath(folder) {
16936
- return import_node_path9.default.join(folder, "experiment.config.json");
17129
+ return import_node_path10.default.join(folder, "experiment.config.json");
16937
17130
  }
16938
17131
  function apexConfigPath(folder) {
16939
- return import_node_path9.default.join(folder, "apex.harness.json");
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 ?? import_node_path9.default.basename(import_node_path9.default.dirname(folder));
16949
- const variation = import_node_path9.default.basename(folder);
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 = import_node_path9.default.join(folder, variant.replace(/\.bundle\.js$/, ".js"));
16984
- const cssPath = import_node_path9.default.join(folder, variant.replace(/\.bundle\.js$/, ".css"));
16985
- return { jsPath, cssPath: import_node_fs11.default.existsSync(cssPath) ? cssPath : null };
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 = import_node_path9.default.join(folder, "build");
16988
- const files = import_node_fs11.default.existsSync(buildDir) ? import_node_fs11.default.readdirSync(buildDir) : [];
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: import_node_path9.default.join(buildDir, js),
16996
- cssPath: css ? import_node_path9.default.join(buildDir, css) : null
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 (!import_node_fs11.default.existsSync(jsPath)) throw new Error(`Missing JS artifact: ${jsPath}`);
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([import_node_fs11.default.readFileSync(jsPath)], { type: "application/javascript" }),
17007
- import_node_path9.default.basename(jsPath)
17199
+ new Blob([import_node_fs12.default.readFileSync(jsPath)], { type: "application/javascript" }),
17200
+ import_node_path10.default.basename(jsPath)
17008
17201
  );
17009
- if (cssPath && import_node_fs11.default.existsSync(cssPath) && import_node_fs11.default.statSync(cssPath).size > 0) {
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([import_node_fs11.default.readFileSync(cssPath)], { type: "text/css" }),
17013
- import_node_path9.default.basename(cssPath)
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: import_node_path9.default.relative(params.folder, jsPath),
17023
- cssPath: cssPath ? import_node_path9.default.relative(params.folder, cssPath) : null
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 (import_node_fs11.default.existsSync(targetPath) && !opts.force) {
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
- import_node_fs11.default.writeFileSync(targetPath, `${JSON.stringify(apex, null, 2)}
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 = import_node_fs11.default.watch(folder, { recursive: true }, (_event, filename) => {
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 import_node_fs12 = __toESM(require("node:fs"));
17179
- var import_node_path10 = __toESM(require("node:path"));
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 import_node_path10.default.resolve(process.cwd(), folder);
17431
+ return import_node_path11.default.resolve(process.cwd(), folder);
17239
17432
  }
17240
17433
  function apexDir(folder) {
17241
- return import_node_path10.default.join(folder, ".apex");
17434
+ return import_node_path11.default.join(folder, ".apex");
17242
17435
  }
17243
17436
  function sessionPath(folder) {
17244
- return import_node_path10.default.join(apexDir(folder), "session.json");
17437
+ return import_node_path11.default.join(apexDir(folder), "session.json");
17245
17438
  }
17246
17439
  function readJsonFile(filePath) {
17247
- if (!import_node_fs12.default.existsSync(filePath)) return null;
17248
- return JSON.parse(import_node_fs12.default.readFileSync(filePath, "utf8"));
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
- import_node_fs12.default.mkdirSync(import_node_path10.default.dirname(filePath), { recursive: true });
17252
- import_node_fs12.default.writeFileSync(filePath, `${JSON.stringify(value2, null, 2)}
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 = import_node_path10.default.join(apexDir(folder), "experiment-filesystem.json");
17259
- if (import_node_fs12.default.existsSync(filesystemMarker)) {
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
- import_node_fs12.default.mkdirSync(apexDir(folder), { recursive: true });
17290
- import_node_fs12.default.writeFileSync(
17291
- import_node_path10.default.join(apexDir(folder), ".gitignore"),
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 (!import_node_fs12.default.existsSync(dir)) return;
17316
- for (const entry of import_node_fs12.default.readdirSync(dir, { withFileTypes: true })) {
17317
- const absPath = import_node_path10.default.join(dir, entry.name);
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(import_node_path10.default.relative(folder, absPath));
17323
- const content = import_node_fs12.default.readFileSync(absPath, "utf8");
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(import_node_path10.default.join(folder, root));
17526
+ for (const root of roots) visit2(import_node_path11.default.join(folder, root));
17334
17527
  for (const file of topLevel) {
17335
- const absPath = import_node_path10.default.join(folder, file);
17336
- if (!import_node_fs12.default.existsSync(absPath)) continue;
17337
- const content = import_node_fs12.default.readFileSync(absPath, "utf8");
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(import_node_path10.default.join(folder, "experiment.config.json")) ?? {};
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 = import_node_path10.default.join(folder, relativePath);
17379
- import_node_fs12.default.mkdirSync(import_node_path10.default.dirname(absPath), { recursive: true });
17380
- import_node_fs12.default.writeFileSync(absPath, file.content, "utf8");
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
- import_node_path10.default.basename(js.path)
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" }), import_node_path10.default.basename(css.path));
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
- import_node_fs12.default.mkdirSync(folder, { recursive: true });
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
- import_node_fs12.default.mkdirSync(apexDir(folder), { recursive: true });
17646
- writeJsonFile(import_node_path10.default.join(apexDir(folder), "harness-session.json"), {
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 = import_node_fs12.default.watch(folder, { recursive: true }, (_event, filename) => {
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 import_node_fs13 = __toESM(require("node:fs"));
17740
- var import_node_path11 = __toESM(require("node:path"));
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 = import_node_path11.default.resolve(process.cwd(), opts.out ?? opts.output);
17851
- import_node_fs13.default.mkdirSync(import_node_path11.default.dirname(target), { recursive: true });
17852
- import_node_fs13.default.writeFileSync(target, rendered, "utf8");
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 import_node_fs14 = __toESM(require("node:fs"));
17867
- var import_node_path12 = __toESM(require("node:path"));
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 (!import_node_fs14.default.existsSync(filePath)) throw new Error(`Manifest not found: ${filePath}`);
17915
- const parsed = JSON.parse(import_node_fs14.default.readFileSync(filePath, "utf8"));
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
- import_node_fs14.default.mkdirSync(import_node_path12.default.dirname(filePath), { recursive: true });
17921
- import_node_fs14.default.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
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 import_node_path12.default.resolve(process.cwd(), input ?? DEFAULT_MANIFEST_PATH);
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 import_node_fs15 = __toESM(require("node:fs"));
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 ? import_node_fs15.default.createWriteStream(opts.out, { flags: "wx" }) : null;
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
- import_node_fs15.default.rmSync(opts.out, { force: true });
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 JSON.parse(text2);
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 = JSON.parse((0, import_node_fs18.readFileSync)(file, "utf8"));
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(path28) {
19608
- return path28.replaceAll("[]", ".0");
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(path28, patterns) {
19872
+ function score2(path21, patterns) {
19687
19873
  return patterns.reduce(
19688
- (total, pattern, index) => total + (pattern.test(path28) ? patterns.length - index : 0),
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(JSON.parse(import_node_fs19.default.readFileSync(import_node_path16.default.resolve(opts.file), "utf8")));
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 = JSON.parse(import_node_fs19.default.readFileSync(import_node_path16.default.resolve(opts.fromFile), "utf8"));
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 = JSON.parse(text2);
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 import_node_fs21 = __toESM(require("node:fs"));
23758
- var import_node_path18 = __toESM(require("node:path"));
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("@") || import_node_fs21.default.existsSync(source) ? import_node_fs21.default.readFileSync(import_node_path18.default.resolve(process.cwd(), source), "utf8") : value2;
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 source = value2.startsWith("@") ? value2.slice(1) : value2;
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 source = value2.startsWith("@") ? value2.slice(1) : value2;
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 = JSON.parse(text2);
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
- const text2 = value2.startsWith("@") || import_node_fs25.default.existsSync(source) ? import_node_fs25.default.readFileSync(import_node_path22.default.resolve(process.cwd(), source), "utf8") : value2;
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("<json>", "Request body as inline JSON, @file, or a file path (shape below)").action(async function(id, value2) {
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 import_node_fs26 = __toESM(require("node:fs"));
25195
- var import_node_path23 = __toESM(require("node:path"));
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 import_node_fs26.default.readFileSync(import_node_path23.default.resolve(process.cwd(), value2.slice(1)), "utf8");
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 = import_node_path23.default.basename(file);
25263
- const content_type = mediaTypes[import_node_path23.default.extname(file).toLowerCase()];
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 = import_node_fs26.default.statSync(file).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 = import_node_fs26.default.readFileSync(file);
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 source = raw.startsWith("@") ? raw.slice(1) : raw;
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((path28) => /email|phone|address|name/i.test(path28))
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(path28) {
25830
- return path28.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
25996
+ function hasSyntheticPlaceholder(path21) {
25997
+ return path21.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
25831
25998
  }
25832
- function ambiguousDottedPrefix(event, path28) {
25833
- const segments = path28.split(".");
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 path28 = field.path;
25843
- if (hasSyntheticPlaceholder(path28)) {
26009
+ const path21 = field.path;
26010
+ if (hasSyntheticPlaceholder(path21)) {
25844
26011
  return {
25845
26012
  eligible: false,
25846
- message: `Path ${JSON.stringify(path28)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
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(path28)} is container-typed. Choose an observed scalar path.`
26019
+ message: `Path ${JSON.stringify(path21)} is container-typed. Choose an observed scalar path.`
25853
26020
  };
25854
26021
  }
25855
- if (path28.includes("[]")) {
26022
+ if (path21.includes("[]")) {
25856
26023
  return {
25857
26024
  eligible: false,
25858
- message: `Path ${JSON.stringify(path28)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
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(path28)} does not have a stable observed type. Choose a path without a type collision.`
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(path28)} has no observed event name. Choose a path from a named event.`
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, path28);
26040
+ const ambiguousPrefix = ambiguousDottedPrefix(event, path21);
25874
26041
  if (ambiguousPrefix) {
25875
26042
  return {
25876
26043
  eligible: false,
25877
- message: `Path ${JSON.stringify(path28)} 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.`
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(path28) {
25887
- const words = path28.split(/[._-]+/).filter(Boolean).join(" ");
25888
- return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path28;
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(path28, eventName) {
25894
- return `apex discover --create-dimension ${quoteShellArgument(path28)} --event ${quoteShellArgument(eventName)}`;
26060
+ function createDimensionInvocation(path21, eventName) {
26061
+ return `apex discover --create-dimension ${quoteShellArgument(path21)} --event ${quoteShellArgument(eventName)}`;
25895
26062
  }
25896
- function resolveDimensionObservation(command, report, path28, eventName) {
26063
+ function resolveDimensionObservation(command, report, path21, eventName) {
25897
26064
  const matches = report.data_layer.flatMap(
25898
- (event) => event.fields.filter((field) => field.path === path28).map((field) => ({ event, field }))
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(path28)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
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(path28)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
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(path28)} 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.`,
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(path28)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
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(path28)} 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.`,
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 path28 of event.paths)
26144
+ for (const path21 of event.paths)
25978
26145
  lines.push(
25979
- ` URL ${path28.path}: ${path28.pageviews} pageviews, ${path28.count} pushes, ${path28.pspReturn_count} PSP returns`
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 import_node_fs28 = __toESM(require("node:fs"));
26193
- var import_node_path25 = __toESM(require("node:path"));
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
- import_node_path25.default.resolve(__dirname, ".."),
26197
- import_node_path25.default.resolve(import_node_path25.default.dirname(process.argv[1] ?? process.cwd()), ".."),
26198
- import_node_path25.default.resolve(process.cwd(), "packages/cli")
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 (import_node_fs28.default.existsSync(import_node_path25.default.join(candidate, "AGENTS.md"))) return candidate;
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: import_node_path25.default.join(root, "AGENTS.md"),
26211
- skill: import_node_path25.default.join(root, "skills", "apex-cli", "SKILL.md")
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) => !import_node_fs28.default.existsSync(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 = import_node_path25.default.resolve(targetRoot, "apex-cli");
26220
- import_node_fs28.default.mkdirSync(target, { recursive: true });
26221
- import_node_fs28.default.copyFileSync(paths.skill, import_node_path25.default.join(target, "SKILL.md"));
26222
- import_node_fs28.default.copyFileSync(paths.agents, import_node_path25.default.join(target, "AGENTS.md"));
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, path28) {
27280
- return new URL(path28, base).toString();
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 path28 of ["/cart.js"]) {
27467
+ for (const path21 of ["/cart.js"]) {
27301
27468
  try {
27302
- const secondary = await fetchText(abs(primary.finalUrl || url, path28));
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 import_node_fs29 = __toESM(require("node:fs"));
27598
- var import_node_path26 = __toESM(require("node:path"));
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 === "-" ? import_node_fs29.default.readFileSync(0, "utf8") : import_node_fs29.default.readFileSync(import_node_path26.default.resolve(file), "utf8");
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 import_node_fs31 = __toESM(require("node:fs"));
27723
- var import_node_path28 = __toESM(require("node:path"));
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 import_node_fs30 = __toESM(require("node:fs"));
27728
- var import_node_path27 = __toESM(require("node:path"));
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 import_node_path27.default.join(folder, LANDING_PAGE_CHECKOUT_MARKER);
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 (!import_node_fs30.default.existsSync(file)) return null;
27914
+ if (!import_node_fs23.default.existsSync(file)) return null;
27748
27915
  try {
27749
- return JSON.parse(import_node_fs30.default.readFileSync(file, "utf8"));
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
- import_node_fs30.default.mkdirSync(import_node_path27.default.dirname(markerPath(folder)), { recursive: true });
27768
- import_node_fs30.default.writeFileSync(import_node_path27.default.join(folder, ".apex", ".gitignore"), "landing-page.json\n", "utf8");
27769
- import_node_fs30.default.writeFileSync(markerPath(folder), `${JSON.stringify(marker, null, 2)}
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 = import_node_path27.default.resolve(process.cwd(), folderArg);
27774
- const marker = import_node_fs30.default.existsSync(folder) && import_node_fs30.default.statSync(folder).isDirectory() ? readMarker(folder) : null;
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 = import_node_path27.default.resolve(process.cwd(), target);
27787
- if (import_node_fs30.default.existsSync(folder) && import_node_fs30.default.statSync(folder).isDirectory()) {
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 import_node_fs30.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
27810
- const absolute = import_node_path27.default.join(directory, entry.name);
27811
- const relative = import_node_path27.default.relative(folder, absolute).split(import_node_path27.default.sep).join("/");
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: import_node_fs30.default.statSync(absolute).size, sha256: "" }
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 = import_node_fs30.default.readFileSync(absolute);
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 = import_node_path27.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
27844
- if (!import_node_fs30.default.existsSync(file)) {
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(import_node_fs30.default.readFileSync(file, "utf8"));
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 = import_node_path27.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
27909
- return import_node_fs30.default.existsSync(file) ? sha256(import_node_fs30.default.readFileSync(file)) : null;
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 = import_node_path27.default.resolve(folder, file);
27928
- if (absolute.startsWith(folder + import_node_path27.default.sep) && import_node_fs30.default.existsSync(absolute)) import_node_fs30.default.rmSync(absolute);
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
- import_node_fs30.default.mkdirSync(folder, { recursive: true });
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 = import_node_path27.default.resolve(folder, file.path);
27983
- if (!target.startsWith(folder + import_node_path27.default.sep))
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 }) => import_node_fs30.default.existsSync(target) && sha256(import_node_fs30.default.readFileSync(target)) !== 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
- import_node_fs30.default.mkdirSync(import_node_path27.default.dirname(file.target), { recursive: true });
28021
- import_node_fs30.default.writeFileSync(file.target, file.body);
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
- import_node_fs30.default.writeFileSync(
28031
- import_node_path27.default.join(folder, LANDING_PAGE_MANIFEST_FILE),
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 = import_node_path27.default.join(folder, LANDING_PAGE_CONTEXT_DIR);
28037
- import_node_fs30.default.mkdirSync(contextDir, { recursive: true });
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 import_node_fs30.default.readdirSync(contextDir)) {
28044
- if (contextName.test(name) && !returned.has(name)) import_node_fs30.default.rmSync(import_node_path27.default.join(contextDir, 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
- import_node_fs30.default.writeFileSync(import_node_path27.default.join(contextDir, name), `${JSON.stringify(value2, null, 2)}
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, import_node_path27.default.resolve(process.cwd(), opts.out)) : null;
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 = import_node_path27.default.resolve(process.cwd(), opts.out ?? id);
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 = import_node_path27.default.resolve(process.cwd(), target);
28304
- const fromManifest = import_node_fs30.default.existsSync(import_node_path27.default.join(folder, LANDING_PAGE_MANIFEST_FILE)) ? readManifest2(this, folder)?.experiment ?? {} : {};
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 = import_node_path28.default.resolve(process.cwd(), file);
28412
- if (!import_node_fs31.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
28413
- const fileStat = import_node_fs31.default.statSync(absolute);
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[import_node_path28.default.extname(absolute).toLowerCase()];
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: import_node_path28.default.basename(absolute),
28426
- bytes: import_node_fs31.default.readFileSync(absolute),
28592
+ name: import_node_path21.default.basename(absolute),
28593
+ bytes: import_node_fs24.default.readFileSync(absolute),
28427
28594
  type: mimeType
28428
28595
  }
28429
28596
  ]);