@minnovate/shadow-publisher 0.11.4 → 0.11.7

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.
@@ -55,7 +55,9 @@ function ulid(now = Date.now()) {
55
55
  }
56
56
  const rnd = (0, import_node_crypto.randomBytes)(16);
57
57
  let rand = "";
58
- for (let i = 0; i < 16; i++) rand += ALPHABET[rnd[i] % 32];
58
+ for (let i = 0; i < 16; i++) {
59
+ rand += ALPHABET[rnd[i] % 32];
60
+ }
59
61
  return timeChars.join("") + rand;
60
62
  }
61
63
  function newPublicationId() {
@@ -70,13 +72,19 @@ function canonicalJson(value) {
70
72
  return JSON.stringify(sortValue(value));
71
73
  }
72
74
  function sortValue(value) {
73
- if (value === null || typeof value !== "object") return value;
74
- if (Array.isArray(value)) return value.map(sortValue);
75
+ if (value === null || typeof value !== "object") {
76
+ return value;
77
+ }
78
+ if (Array.isArray(value)) {
79
+ return value.map(sortValue);
80
+ }
75
81
  const obj = value;
76
82
  const out = {};
77
83
  for (const key of Object.keys(obj).sort()) {
78
84
  const v = obj[key];
79
- if (v !== void 0) out[key] = sortValue(v);
85
+ if (v !== void 0) {
86
+ out[key] = sortValue(v);
87
+ }
80
88
  }
81
89
  return out;
82
90
  }
@@ -193,7 +201,9 @@ var PublisherError = class _PublisherError extends Error {
193
201
  }
194
202
  toBody() {
195
203
  const error2 = { code: this.code, message: this.message, retryable: this.retryable };
196
- if (this.details) error2.details = this.details;
204
+ if (this.details) {
205
+ error2.details = this.details;
206
+ }
197
207
  return { error: error2 };
198
208
  }
199
209
  static isPublisherError(e) {
@@ -2988,10 +2998,17 @@ function sanitizedGitEnv(opts = {}) {
2988
2998
  GIT_NO_LAZY_FETCH: "1"
2989
2999
  // never let a parent process's git context leak into the child
2990
3000
  };
2991
- if (process.env["GIT_EXEC_PATH"]) env["GIT_EXEC_PATH"] = process.env["GIT_EXEC_PATH"];
2992
- if (process.env["GITPUB_GIT_TEMPLATE_DIR"] !== void 0) env["GIT_TEMPLATE_DIR"] = process.env["GITPUB_GIT_TEMPLATE_DIR"];
2993
- else env["GIT_TEMPLATE_DIR"] = "";
2994
- if (opts.extra) Object.assign(env, opts.extra);
3001
+ if (process.env["GIT_EXEC_PATH"]) {
3002
+ env["GIT_EXEC_PATH"] = process.env["GIT_EXEC_PATH"];
3003
+ }
3004
+ if (process.env["GITPUB_GIT_TEMPLATE_DIR"] !== void 0) {
3005
+ env["GIT_TEMPLATE_DIR"] = process.env["GITPUB_GIT_TEMPLATE_DIR"];
3006
+ } else {
3007
+ env["GIT_TEMPLATE_DIR"] = "";
3008
+ }
3009
+ if (opts.extra) {
3010
+ Object.assign(env, opts.extra);
3011
+ }
2995
3012
  return env;
2996
3013
  }
2997
3014
  var GIT_BIN = process.env["GITPUB_GIT_BIN"] ?? "git";
@@ -3000,7 +3017,9 @@ function neutralCwd() {
3000
3017
  }
3001
3018
  function startFailure(label, error2, cwd) {
3002
3019
  const opts = { retryable: true, cause: error2 };
3003
- if (error2.code !== "ENOENT") return new PublisherError("UNAVAILABLE", `${label} could not be started: ${error2.message}`, opts);
3020
+ if (error2.code !== "ENOENT") {
3021
+ return new PublisherError("UNAVAILABLE", `${label} could not be started: ${error2.message}`, opts);
3022
+ }
3004
3023
  if (!isDirectory(cwd)) {
3005
3024
  const which = cwd === (0, import_node_os.tmpdir)() ? "the temporary directory (TMPDIR)" : "its working directory";
3006
3025
  return new PublisherError("UNAVAILABLE", `${label} could not be started: ${which} ${cwd} does not exist`, { ...opts, details: { missing: "cwd", cwd } });
@@ -3050,21 +3069,31 @@ function clearTimersOf(lc) {
3050
3069
  function notifyWaiters(lc) {
3051
3070
  const ws = [...lc.waiters];
3052
3071
  lc.waiters.clear();
3053
- for (const w of ws) w();
3072
+ for (const w of ws) {
3073
+ w();
3074
+ }
3054
3075
  }
3055
3076
  function markIgnored(child) {
3056
- if (childClosed(child)) return;
3077
+ if (childClosed(child)) {
3078
+ return;
3079
+ }
3057
3080
  const lc = lifecycleOf(child);
3058
- if (lc.ignored) return;
3081
+ if (lc.ignored) {
3082
+ return;
3083
+ }
3059
3084
  lc.ignored = true;
3060
3085
  clearTimersOf(lc);
3061
3086
  notifyWaiters(lc);
3062
3087
  }
3063
3088
  function armCloseBound(child, boundMs) {
3064
3089
  const lc = lifecycleOf(child);
3065
- if (childClosed(child) || lc.ignored) return;
3090
+ if (childClosed(child) || lc.ignored) {
3091
+ return;
3092
+ }
3066
3093
  const deadline = Date.now() + boundMs;
3067
- if (lc.boundTimer && deadline >= lc.boundDeadline) return;
3094
+ if (lc.boundTimer && deadline >= lc.boundDeadline) {
3095
+ return;
3096
+ }
3068
3097
  if (lc.boundTimer) {
3069
3098
  clearTimeout(lc.boundTimer);
3070
3099
  lc.timers.delete(lc.boundTimer);
@@ -3085,7 +3114,9 @@ function track(child, boundMs = CLEANUP_BOUND_MS) {
3085
3114
  const lc = lifecycleOf(child);
3086
3115
  lc.boundMs = boundMs;
3087
3116
  child.on("error", (e) => {
3088
- if (lc.error === null) lc.error = e;
3117
+ if (lc.error === null) {
3118
+ lc.error = e;
3119
+ }
3089
3120
  });
3090
3121
  child.once("close", () => {
3091
3122
  closedChildren.add(child);
@@ -3101,7 +3132,9 @@ var defaultSignalGroup = (pid, signal) => process.kill(-pid, signal);
3101
3132
  var spawnImpl = import_node_child_process.spawn;
3102
3133
  var signalGroupImpl = defaultSignalGroup;
3103
3134
  function spawnFailure(child) {
3104
- if (child.pid !== void 0) return null;
3135
+ if (child.pid !== void 0) {
3136
+ return null;
3137
+ }
3105
3138
  const e = lifecycles.get(child)?.error;
3106
3139
  return e instanceof Error ? e : e ? new Error(String(e)) : null;
3107
3140
  }
@@ -3109,9 +3142,13 @@ function childExited(child) {
3109
3142
  return child.exitCode !== null || child.signalCode !== null;
3110
3143
  }
3111
3144
  function closeOrIgnored(child) {
3112
- if (childClosed(child)) return Promise.resolve(true);
3145
+ if (childClosed(child)) {
3146
+ return Promise.resolve(true);
3147
+ }
3113
3148
  const lc = lifecycleOf(child);
3114
- if (lc.ignored) return Promise.resolve(false);
3149
+ if (lc.ignored) {
3150
+ return Promise.resolve(false);
3151
+ }
3115
3152
  return new Promise((resolve3) => {
3116
3153
  lc.waiters.add(() => resolve3(childClosed(child)));
3117
3154
  });
@@ -3126,16 +3163,22 @@ function childClosed(child) {
3126
3163
  }
3127
3164
  function killAllGitProcesses() {
3128
3165
  const n = liveChildren.size;
3129
- for (const child of liveChildren) killProcessGroup(child);
3166
+ for (const child of liveChildren) {
3167
+ killProcessGroup(child);
3168
+ }
3130
3169
  return n;
3131
3170
  }
3132
3171
  function killProcessGroup(child) {
3133
- if (child.pid === void 0 || childClosed(child)) return;
3172
+ if (child.pid === void 0 || childClosed(child)) {
3173
+ return;
3174
+ }
3134
3175
  signalChildGroup(child, "SIGKILL");
3135
3176
  }
3136
3177
  function signalChildGroup(child, signal) {
3137
3178
  try {
3138
- if (child.pid === void 0) throw new Error("no pid");
3179
+ if (child.pid === void 0) {
3180
+ throw new Error("no pid");
3181
+ }
3139
3182
  signalGroupImpl(child.pid, signal);
3140
3183
  } catch {
3141
3184
  try {
@@ -3145,19 +3188,29 @@ function signalChildGroup(child, signal) {
3145
3188
  }
3146
3189
  }
3147
3190
  function waitWithin(start, ms, stop) {
3148
- if (ms <= 0) return Promise.resolve({ status: "timeout" });
3149
- if (stop?.aborted) return Promise.resolve({ status: "stopped" });
3191
+ if (ms <= 0) {
3192
+ return Promise.resolve({ status: "timeout" });
3193
+ }
3194
+ if (stop?.aborted) {
3195
+ return Promise.resolve({ status: "stopped" });
3196
+ }
3150
3197
  const ac = new AbortController();
3151
3198
  return new Promise((resolve3) => {
3152
3199
  let timer = null;
3153
3200
  let done = false;
3154
3201
  const onStop = () => settle({ status: "stopped" });
3155
3202
  const settle = (r) => {
3156
- if (done) return;
3203
+ if (done) {
3204
+ return;
3205
+ }
3157
3206
  done = true;
3158
- if (timer) clearTimeout(timer);
3207
+ if (timer) {
3208
+ clearTimeout(timer);
3209
+ }
3159
3210
  stop?.removeEventListener("abort", onStop);
3160
- if (r.status !== "value" && r.status !== "error") ac.abort();
3211
+ if (r.status !== "value" && r.status !== "error") {
3212
+ ac.abort();
3213
+ }
3161
3214
  resolve3(r);
3162
3215
  };
3163
3216
  let p;
@@ -3171,7 +3224,9 @@ function waitWithin(start, ms, stop) {
3171
3224
  (value) => settle({ status: "value", value }),
3172
3225
  (error2) => settle({ status: "error", error: error2 })
3173
3226
  );
3174
- if (done) return;
3227
+ if (done) {
3228
+ return;
3229
+ }
3175
3230
  timer = setTimeout(() => settle({ status: "timeout" }), ms);
3176
3231
  timer.unref();
3177
3232
  stop?.addEventListener("abort", onStop, { once: true });
@@ -3179,8 +3234,12 @@ function waitWithin(start, ms, stop) {
3179
3234
  }
3180
3235
  function spawnTracked(args, opts) {
3181
3236
  for (const a of args) {
3182
- if (typeof a !== "string") throw new Error("git args must be strings");
3183
- if (a.includes("\0")) throw new PublisherError("INVALID_REQUEST", "NUL in git argument");
3237
+ if (typeof a !== "string") {
3238
+ throw new Error("git args must be strings");
3239
+ }
3240
+ if (a.includes("\0")) {
3241
+ throw new PublisherError("INVALID_REQUEST", "NUL in git argument");
3242
+ }
3184
3243
  }
3185
3244
  const cwd = opts.cwd ?? neutralCwd();
3186
3245
  const child = spawnImpl(GIT_BIN, args, { cwd, env: sanitizedGitEnv(opts.env), stdio: ["pipe", "pipe", "pipe"], windowsHide: true, detached: true });
@@ -3232,7 +3291,9 @@ function killedError(reason, detail, args, timeoutMs, maxOut) {
3232
3291
  }
3233
3292
  }
3234
3293
  async function runGit(args, opts = {}) {
3235
- if (opts.maxDiskBytes !== void 0 && !opts.diskDir) throw new Error("maxDiskBytes requires diskDir");
3294
+ if (opts.maxDiskBytes !== void 0 && !opts.diskDir) {
3295
+ throw new Error("maxDiskBytes requires diskDir");
3296
+ }
3236
3297
  const started = Date.now();
3237
3298
  const timeoutMs = opts.timeoutMs ?? 12e4;
3238
3299
  const maxOut = opts.maxStdoutBytes ?? 64 * 1024 * 1024;
@@ -3245,7 +3306,9 @@ async function runGit(args, opts = {}) {
3245
3306
  let killedDetail = "";
3246
3307
  let closed = false;
3247
3308
  const kill = (reason, detail = "") => {
3248
- if (killedReason || closed) return;
3309
+ if (killedReason || closed) {
3310
+ return;
3311
+ }
3249
3312
  killedReason = reason;
3250
3313
  killedDetail = detail;
3251
3314
  terminate(child);
@@ -3253,17 +3316,25 @@ async function runGit(args, opts = {}) {
3253
3316
  const timer = setTimeout(() => kill("timeout"), timeoutMs);
3254
3317
  const onAbort = () => kill("cancelled");
3255
3318
  if (opts.signal) {
3256
- if (opts.signal.aborted) onAbort();
3257
- else opts.signal.addEventListener("abort", onAbort, { once: true });
3319
+ if (opts.signal.aborted) {
3320
+ onAbort();
3321
+ } else {
3322
+ opts.signal.addEventListener("abort", onAbort, { once: true });
3323
+ }
3258
3324
  }
3259
3325
  const watchdog = startWatchdog(child, opts, kill, () => closed);
3260
3326
  child.stdout.on("data", (c) => {
3261
3327
  outBytes += c.length;
3262
- if (outBytes > maxOut) kill("output-limit");
3263
- else stdoutChunks.push(c);
3328
+ if (outBytes > maxOut) {
3329
+ kill("output-limit");
3330
+ } else {
3331
+ stdoutChunks.push(c);
3332
+ }
3264
3333
  });
3265
3334
  child.stderr.on("data", (c) => {
3266
- if (stderr.length < 64 * 1024) stderr += c.toString("utf8");
3335
+ if (stderr.length < 64 * 1024) {
3336
+ stderr += c.toString("utf8");
3337
+ }
3267
3338
  });
3268
3339
  const stdin = feedStdin(child, opts.input, () => kill("input-error"));
3269
3340
  const ended = await endedOrIgnored(child).finally(() => {
@@ -3285,14 +3356,24 @@ async function runGit(args, opts = {}) {
3285
3356
  const code = ended.code;
3286
3357
  const result = { code, stdout: Buffer.concat(stdoutChunks), stderr, durationMs };
3287
3358
  const cleanup = await reapChild(child, { boundMs: cleanupBoundMs, ...opts.groupObserver ? { observer: opts.groupObserver } : {} });
3288
- if (!reapSucceeded(cleanup)) throw cleanupFailure(`git ${args[0]}`, cleanup, killedReason ?? void 0);
3289
- if (ended.error) throw startFailure(`git ${args[0]}`, ended.error, cwd);
3290
- if (killedReason) throw killedError(killedReason, killedDetail, args, timeoutMs, maxOut);
3291
- if (code !== 0 && !opts.allowFailure) throw new GitCommandError(args, code, stderr, opts.label);
3359
+ if (!reapSucceeded(cleanup)) {
3360
+ throw cleanupFailure(`git ${args[0]}`, cleanup, killedReason ?? void 0);
3361
+ }
3362
+ if (ended.error) {
3363
+ throw startFailure(`git ${args[0]}`, ended.error, cwd);
3364
+ }
3365
+ if (killedReason) {
3366
+ throw killedError(killedReason, killedDetail, args, timeoutMs, maxOut);
3367
+ }
3368
+ if (code !== 0 && !opts.allowFailure) {
3369
+ throw new GitCommandError(args, code, stderr, opts.label);
3370
+ }
3292
3371
  return result;
3293
3372
  }
3294
3373
  function startWatchdog(child, opts, kill, isClosed) {
3295
- if (opts.maxMemoryBytes === void 0 && opts.maxDiskBytes === void 0) return { stop: () => void 0, done: Promise.resolve() };
3374
+ if (opts.maxMemoryBytes === void 0 && opts.maxDiskBytes === void 0) {
3375
+ return { stop: () => void 0, done: Promise.resolve() };
3376
+ }
3296
3377
  const sampler = opts.sampler ?? defaultResourceSampler;
3297
3378
  const interval2 = Math.max(10, opts.sampleIntervalMs ?? 500);
3298
3379
  const sampleBoundMs = opts.cleanupBoundMs ?? CLEANUP_BOUND_MS;
@@ -3311,10 +3392,17 @@ function startWatchdog(child, opts, kill, isClosed) {
3311
3392
  });
3312
3393
  const sample = async (what, start) => {
3313
3394
  const r = await waitWithin(start, sampleBoundMs, stopper.signal);
3314
- if (isClosed() || stopped) return null;
3315
- if (r.status === "value") return r.value;
3316
- if (r.status === "error") kill("monitor", `resource monitor failed: ${r.error.message}`);
3317
- else kill("monitor", `resource sample (${what}) did not complete within ${sampleBoundMs}ms`);
3395
+ if (isClosed() || stopped) {
3396
+ return null;
3397
+ }
3398
+ if (r.status === "value") {
3399
+ return r.value;
3400
+ }
3401
+ if (r.status === "error") {
3402
+ kill("monitor", `resource monitor failed: ${r.error.message}`);
3403
+ } else {
3404
+ kill("monitor", `resource sample (${what}) did not complete within ${sampleBoundMs}ms`);
3405
+ }
3318
3406
  return null;
3319
3407
  };
3320
3408
  const done = (async () => {
@@ -3325,10 +3413,14 @@ function startWatchdog(child, opts, kill, isClosed) {
3325
3413
  let tick = 0;
3326
3414
  while (!stopped) {
3327
3415
  await sleep2(interval2);
3328
- if (stopped) return;
3416
+ if (stopped) {
3417
+ return;
3418
+ }
3329
3419
  if (opts.maxMemoryBytes !== void 0) {
3330
3420
  const rss = await sample("memory", (signal) => sampler.groupRss(pgid, signal));
3331
- if (rss === null) return;
3421
+ if (rss === null) {
3422
+ return;
3423
+ }
3332
3424
  if (rss > opts.maxMemoryBytes) {
3333
3425
  kill("memory", `process group uses ${rss} bytes of memory; limit ${opts.maxMemoryBytes}`);
3334
3426
  return;
@@ -3337,7 +3429,9 @@ function startWatchdog(child, opts, kill, isClosed) {
3337
3429
  if (opts.maxDiskBytes !== void 0 && opts.diskDir && tick % 2 === 1) {
3338
3430
  const dir = opts.diskDir;
3339
3431
  const bytes = await sample("disk", (signal) => sampler.dirBytes(dir, signal));
3340
- if (bytes === null) return;
3432
+ if (bytes === null) {
3433
+ return;
3434
+ }
3341
3435
  if (bytes > opts.maxDiskBytes) {
3342
3436
  kill("disk", `scratch directory holds ${bytes} bytes; limit ${opts.maxDiskBytes}`);
3343
3437
  return;
@@ -3356,17 +3450,25 @@ async function scanProcessGroup(pgid, signal) {
3356
3450
  const members2 = [];
3357
3451
  if (process.platform === "linux") {
3358
3452
  for (const name of await (0, import_promises.readdir)("/proc")) {
3359
- if (signal?.aborted) throw new Error("process-group scan abandoned by its caller");
3360
- if (!/^\d+$/.test(name)) continue;
3453
+ if (signal?.aborted) {
3454
+ throw new Error("process-group scan abandoned by its caller");
3455
+ }
3456
+ if (!/^\d+$/.test(name)) {
3457
+ continue;
3458
+ }
3361
3459
  try {
3362
3460
  const stat4 = await (0, import_promises.readFile)(`/proc/${name}/stat`, "utf8");
3363
3461
  const rest = stat4.slice(stat4.lastIndexOf(")") + 2).split(" ");
3364
- if (Number(rest[2]) !== pgid) continue;
3462
+ if (Number(rest[2]) !== pgid) {
3463
+ continue;
3464
+ }
3365
3465
  const status = await (0, import_promises.readFile)(`/proc/${name}/status`, "utf8");
3366
3466
  members2.push({ pid: Number(name), rss: Number(/^VmRSS:\s+(\d+)/m.exec(status)?.[1] ?? 0) * 1024 });
3367
3467
  } catch (e) {
3368
3468
  const code = e.code;
3369
- if (code === "ENOENT" || code === "ESRCH") continue;
3469
+ if (code === "ENOENT" || code === "ESRCH") {
3470
+ continue;
3471
+ }
3370
3472
  throw e;
3371
3473
  }
3372
3474
  }
@@ -3380,7 +3482,9 @@ async function scanProcessGroup(pgid, signal) {
3380
3482
  });
3381
3483
  for (const line of stdout.split("\n")) {
3382
3484
  const [g, pid, rss] = line.trim().split(/\s+/);
3383
- if (Number(g) === pgid) members2.push({ pid: Number(pid), rss: Number(rss) * 1024 });
3485
+ if (Number(g) === pgid) {
3486
+ members2.push({ pid: Number(pid), rss: Number(rss) * 1024 });
3487
+ }
3384
3488
  }
3385
3489
  return members2;
3386
3490
  }
@@ -3390,7 +3494,9 @@ async function processGroupRss(pgid, signal) {
3390
3494
  return (await scanProcessGroup(pgid, signal)).reduce((sum, m) => sum + m.rss, 0);
3391
3495
  }
3392
3496
  async function processGroupMembers(pgid, signal) {
3393
- if (!groupMayHaveMembers(pgid)) return [];
3497
+ if (!groupMayHaveMembers(pgid)) {
3498
+ return [];
3499
+ }
3394
3500
  return (await scanProcessGroup(pgid, signal)).map((m) => m.pid);
3395
3501
  }
3396
3502
  function groupMayHaveMembers(pgid) {
@@ -3405,23 +3511,30 @@ async function directoryBytes(dir, signal) {
3405
3511
  let total = 0;
3406
3512
  const stack = [dir];
3407
3513
  while (stack.length) {
3408
- if (signal?.aborted) throw new Error("directory scan abandoned by its caller");
3514
+ if (signal?.aborted) {
3515
+ throw new Error("directory scan abandoned by its caller");
3516
+ }
3409
3517
  const d = stack.pop();
3410
3518
  let entries;
3411
3519
  try {
3412
3520
  entries = await (0, import_promises.readdir)(d, { withFileTypes: true });
3413
3521
  } catch (e) {
3414
- if (e.code === "ENOENT") continue;
3522
+ if (e.code === "ENOENT") {
3523
+ continue;
3524
+ }
3415
3525
  throw e;
3416
3526
  }
3417
3527
  for (const ent of entries) {
3418
3528
  const p = (0, import_node_path.join)(d, ent.name);
3419
- if (ent.isDirectory()) stack.push(p);
3420
- else if (ent.isFile()) {
3529
+ if (ent.isDirectory()) {
3530
+ stack.push(p);
3531
+ } else if (ent.isFile()) {
3421
3532
  try {
3422
3533
  total += (await (0, import_promises.lstat)(p)).size;
3423
3534
  } catch (e) {
3424
- if (e.code !== "ENOENT") throw e;
3535
+ if (e.code !== "ENOENT") {
3536
+ throw e;
3537
+ }
3425
3538
  }
3426
3539
  }
3427
3540
  }
@@ -3454,7 +3567,9 @@ function spawnGit(args, opts = {}) {
3454
3567
  child,
3455
3568
  opts,
3456
3569
  (reason) => {
3457
- if (killed || closed) return;
3570
+ if (killed || closed) {
3571
+ return;
3572
+ }
3458
3573
  killed = reason;
3459
3574
  terminate(child);
3460
3575
  },
@@ -3466,25 +3581,38 @@ function spawnGit(args, opts = {}) {
3466
3581
  }
3467
3582
  function terminate(child, opts = {}) {
3468
3583
  const lc = lifecycleOf(child);
3469
- if (childClosed(child) || lc.ignored) return;
3584
+ if (childClosed(child) || lc.ignored) {
3585
+ return;
3586
+ }
3470
3587
  const boundMs = opts.boundMs ?? lc.boundMs;
3471
3588
  armCloseBound(child, boundMs);
3472
- if (lc.terminated) return;
3589
+ if (lc.terminated) {
3590
+ return;
3591
+ }
3473
3592
  lc.terminated = true;
3474
3593
  const drain = () => {
3475
- for (const stream of [child.stdout, child.stderr]) stream?.resume();
3594
+ for (const stream of [child.stdout, child.stderr]) {
3595
+ stream?.resume();
3596
+ }
3476
3597
  };
3477
- if (childExited(child)) drain();
3478
- else child.once("exit", drain);
3598
+ if (childExited(child)) {
3599
+ drain();
3600
+ } else {
3601
+ child.once("exit", drain);
3602
+ }
3479
3603
  killProcessGroup(child);
3480
- if (!childClosed(child)) scheduleFor(child, () => killProcessGroup(child), 250, true);
3604
+ if (!childClosed(child)) {
3605
+ scheduleFor(child, () => killProcessGroup(child), 250, true);
3606
+ }
3481
3607
  }
3482
3608
  async function reapChild(child, opts = {}) {
3483
3609
  const boundMs = opts.boundMs ?? lifecycleOf(child).boundMs;
3484
3610
  const deadline = Date.now() + boundMs;
3485
3611
  let timedOut = false;
3486
3612
  if (!childClosed(child)) {
3487
- if (!lifecycleOf(child).ignored) terminate(child, { boundMs });
3613
+ if (!lifecycleOf(child).ignored) {
3614
+ terminate(child, { boundMs });
3615
+ }
3488
3616
  timedOut = !await closeOrIgnored(child);
3489
3617
  }
3490
3618
  let groupCleared = true;
@@ -3501,17 +3629,23 @@ async function reapChild(child, opts = {}) {
3501
3629
  break;
3502
3630
  }
3503
3631
  if (r.status !== "value") {
3504
- if (!observed) groupCleared = null;
3632
+ if (!observed) {
3633
+ groupCleared = null;
3634
+ }
3505
3635
  timedOut = true;
3506
3636
  break;
3507
3637
  }
3508
3638
  observed = true;
3509
3639
  survivors = r.value;
3510
3640
  groupCleared = survivors.length === 0;
3511
- if (groupCleared || !childClosed(child) || Date.now() >= deadline) break;
3641
+ if (groupCleared || !childClosed(child) || Date.now() >= deadline) {
3642
+ break;
3643
+ }
3512
3644
  await new Promise((resolve3) => setTimeout(resolve3, Math.min(25, deadline - Date.now())));
3513
3645
  }
3514
- if (groupCleared === false) timedOut = true;
3646
+ if (groupCleared === false) {
3647
+ timedOut = true;
3648
+ }
3515
3649
  }
3516
3650
  return { closed: childClosed(child), groupCleared, timedOut, survivors };
3517
3651
  }
@@ -3521,7 +3655,9 @@ function reapSucceeded(outcome) {
3521
3655
  function cleanupFailure(what, outcome, cause) {
3522
3656
  const why = !outcome.closed ? "the process did not close within the cleanup bound" : outcome.groupCleared === null ? "its process group could not be observed" : `its process group still has ${outcome.survivors.length} member(s)`;
3523
3657
  const cleanup = { closed: outcome.closed, groupCleared: outcome.groupCleared, timedOut: outcome.timedOut, survivors: outcome.survivors };
3524
- if (typeof cause === "string") cleanup["preempted"] = cause;
3658
+ if (typeof cause === "string") {
3659
+ cleanup["preempted"] = cause;
3660
+ }
3525
3661
  return new PublisherError("UNAVAILABLE", `${what}: ${why}`, {
3526
3662
  retryable: true,
3527
3663
  details: { cleanup },
@@ -3530,10 +3666,14 @@ function cleanupFailure(what, outcome, cause) {
3530
3666
  }
3531
3667
  var cachedVersion = null;
3532
3668
  async function gitVersion() {
3533
- if (cachedVersion) return cachedVersion;
3669
+ if (cachedVersion) {
3670
+ return cachedVersion;
3671
+ }
3534
3672
  const { stdout } = await execFileP(GIT_BIN, ["--version"], { env: sanitizedGitEnv(), timeout: 3e4 });
3535
3673
  const m = /git version (\d+\.\d+\.\d+)/.exec(stdout);
3536
- if (!m) throw new PublisherError("GIT", `cannot parse git version: ${stdout.trim()}`);
3674
+ if (!m) {
3675
+ throw new PublisherError("GIT", `cannot parse git version: ${stdout.trim()}`);
3676
+ }
3537
3677
  cachedVersion = m[1];
3538
3678
  return cachedVersion;
3539
3679
  }
@@ -3542,7 +3682,9 @@ function compareVersions(a, b) {
3542
3682
  const pb = b.split(".").map(Number);
3543
3683
  for (let i = 0; i < 3; i++) {
3544
3684
  const d = (pa[i] ?? 0) - (pb[i] ?? 0);
3545
- if (d !== 0) return d;
3685
+ if (d !== 0) {
3686
+ return d;
3687
+ }
3546
3688
  }
3547
3689
  return 0;
3548
3690
  }
@@ -3562,7 +3704,9 @@ async function sha256File(path) {
3562
3704
  }).on("error", reject).on("end", () => resolve3());
3563
3705
  });
3564
3706
  const st = await (0, import_promises2.stat)(path);
3565
- if (st.size !== size) throw new Error("file changed while hashing");
3707
+ if (st.size !== size) {
3708
+ throw new Error("file changed while hashing");
3709
+ }
3566
3710
  return { sha256: h.digest("hex"), size };
3567
3711
  }
3568
3712
 
@@ -3570,32 +3714,47 @@ async function sha256File(path) {
3570
3714
  var import_node_fs3 = require("node:fs");
3571
3715
  async function indexPackFromFile(dir, packPath, opts = {}) {
3572
3716
  const args = ["index-pack", "--stdin", ...opts.keep ?? true ? ["--keep=gitpub"] : []];
3573
- if (opts.strict ?? true) args.push("--strict", "--fsck-objects");
3574
- if (opts.maxInputBytes) args.push(`--max-input-size=${opts.maxInputBytes}`);
3717
+ if (opts.strict ?? true) {
3718
+ args.push("--strict", "--fsck-objects");
3719
+ }
3720
+ if (opts.maxInputBytes) {
3721
+ args.push(`--max-input-size=${opts.maxInputBytes}`);
3722
+ }
3575
3723
  const run = { cwd: dir, input: (0, import_node_fs3.createReadStream)(packPath), label: "index-pack", ...opts };
3576
3724
  const res = await runGit(args, { ...run, allowFailure: true });
3577
3725
  if (res.code !== 0) {
3578
3726
  const msg = res.stderr.trim().split("\n").pop() ?? "index-pack failed";
3579
3727
  const missing = /did not receive expected object|not a valid|missing/i.test(res.stderr);
3580
3728
  const environment = /No space left|Permission denied|Input\/output error|cannot open|unable to (?:open|read|create|write)|Cannot allocate memory|Too many open files|Killed/i.test(res.stderr);
3581
- if (environment) throw new PublisherError("UNAVAILABLE", `index-pack failed: ${sanitizeGitMessage(msg)}`, { retryable: true });
3729
+ if (environment) {
3730
+ throw new PublisherError("UNAVAILABLE", `index-pack failed: ${sanitizeGitMessage(msg)}`, { retryable: true });
3731
+ }
3582
3732
  throw new PublisherError(missing ? "MISSING_OBJECTS" : "PACK_INVALID", sanitizeGitMessage(msg));
3583
3733
  }
3584
3734
  const m = /^(?:pack|keep)\t([0-9a-f]{40,64})/m.exec(res.stdout.toString("utf8"));
3585
- if (!m) throw new PublisherError("PACK_INVALID", "index-pack produced no pack name");
3735
+ if (!m) {
3736
+ throw new PublisherError("PACK_INVALID", "index-pack produced no pack name");
3737
+ }
3586
3738
  return { packName: m[1] };
3587
3739
  }
3588
3740
  function sanitizeGitMessage(msg) {
3589
3741
  return msg.replace(/\/[^\s:]+/g, "<path>").replace(/https?:\/\/\S+/g, "<url>").slice(0, 500);
3590
3742
  }
3591
3743
  async function refTransaction(dir, updates, opts = {}) {
3592
- if (updates.length === 0) return;
3744
+ if (updates.length === 0) {
3745
+ return;
3746
+ }
3593
3747
  const lines = ["start"];
3594
3748
  for (const u of updates) {
3595
- if (u.ref.includes("\n") || u.ref.includes("\0")) throw new PublisherError("INVALID_REF_NAME", "newline in ref");
3749
+ if (u.ref.includes("\n") || u.ref.includes("\0")) {
3750
+ throw new PublisherError("INVALID_REF_NAME", "newline in ref");
3751
+ }
3596
3752
  const oldPart = u.old === void 0 ? "" : ` ${u.old ?? "0".repeat(40)}`;
3597
- if (u.new === null) lines.push(`delete ${u.ref}${oldPart}`);
3598
- else lines.push(`update ${u.ref} ${u.new}${oldPart}`);
3753
+ if (u.new === null) {
3754
+ lines.push(`delete ${u.ref}${oldPart}`);
3755
+ } else {
3756
+ lines.push(`update ${u.ref} ${u.new}${oldPart}`);
3757
+ }
3599
3758
  }
3600
3759
  lines.push("prepare", "commit", "");
3601
3760
  await runGit(["update-ref", "--stdin"], { cwd: dir, input: lines.join("\n"), label: "update-ref", ...opts });
@@ -3606,14 +3765,18 @@ async function batchCheck(dir, oids, opts = {}) {
3606
3765
  const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname) %(objecttype) %(objectsize)"], run);
3607
3766
  const out = [];
3608
3767
  for (const line of stdout.toString("utf8").split("\n")) {
3609
- if (!line) continue;
3768
+ if (!line) {
3769
+ continue;
3770
+ }
3610
3771
  const [oid, type, size] = line.split(" ");
3611
3772
  out.push({ oid, type: type ?? "missing", size: type === "missing" ? 0 : Number(size) });
3612
3773
  }
3613
3774
  return out;
3614
3775
  }
3615
3776
  async function resolveRev(dir, rev, peel) {
3616
- if (rev.startsWith("-")) return null;
3777
+ if (rev.startsWith("-")) {
3778
+ return null;
3779
+ }
3617
3780
  const r = await runGit(["rev-parse", "--verify", "-q", peel ? `${rev}^{}` : rev], { cwd: dir, allowFailure: true });
3618
3781
  return r.code === 0 ? r.stdout.toString("utf8").trim() : null;
3619
3782
  }
@@ -3622,14 +3785,19 @@ async function resolveRevs(dir, revs, opts = {}) {
3622
3785
  (r) => r.startsWith("-") ? null : r.includes("\0") ? new PublisherError("INVALID_REQUEST", "NUL in git argument") : null
3623
3786
  );
3624
3787
  const ask4 = revs.flatMap((r, i) => out[i] === null && !r.startsWith("-") ? [i] : []);
3625
- if (ask4.length === 0) return out;
3788
+ if (ask4.length === 0) {
3789
+ return out;
3790
+ }
3626
3791
  const { stdout } = await runGit(["cat-file", "--batch-check=%(objectname)", "-Z"], { cwd: dir, input: ask4.map((i) => revs[i]).join("\0") + "\0", ...opts });
3627
3792
  const records = stdout.toString("utf8").split("\0");
3628
3793
  const misses = [];
3629
3794
  for (const [j, i] of ask4.entries()) {
3630
3795
  const record2 = records[j] ?? "";
3631
- if (record2 && !/ (missing|ambiguous)$/.test(record2)) out[i] = record2;
3632
- else if (!revs[i].includes("^{")) misses.push(i);
3796
+ if (record2 && !/ (missing|ambiguous)$/.test(record2)) {
3797
+ out[i] = record2;
3798
+ } else if (!revs[i].includes("^{")) {
3799
+ misses.push(i);
3800
+ }
3633
3801
  }
3634
3802
  await Promise.all(misses.map(async (i) => out[i] = await resolveRev(dir, revs[i], false)));
3635
3803
  return out;
@@ -3677,10 +3845,18 @@ async function packObjectsToFile(repoDir, extraArgs, stdinLines, opts) {
3677
3845
  }
3678
3846
  }
3679
3847
  const code = ended.code;
3680
- if (ended.error) throw startFailure("git pack-objects", ended.error, repoDir);
3681
- if (opts.signal?.aborted) throw new PublisherError("CANCELLED", "pack creation cancelled");
3682
- if (timedOut) throw new PublisherError("GIT", `pack-objects timed out after ${timeoutMs}ms`, { retryable: true });
3683
- if (code !== 0) throw new PublisherError("GIT", `pack-objects failed: ${stderr.trim().split("\n").pop() ?? ""}`);
3848
+ if (ended.error) {
3849
+ throw startFailure("git pack-objects", ended.error, repoDir);
3850
+ }
3851
+ if (opts.signal?.aborted) {
3852
+ throw new PublisherError("CANCELLED", "pack creation cancelled");
3853
+ }
3854
+ if (timedOut) {
3855
+ throw new PublisherError("GIT", `pack-objects timed out after ${timeoutMs}ms`, { retryable: true });
3856
+ }
3857
+ if (code !== 0) {
3858
+ throw new PublisherError("GIT", `pack-objects failed: ${stderr.trim().split("\n").pop() ?? ""}`);
3859
+ }
3684
3860
  } catch (e) {
3685
3861
  failure = e;
3686
3862
  } finally {
@@ -3689,8 +3865,12 @@ async function packObjectsToFile(repoDir, extraArgs, stdinLines, opts) {
3689
3865
  out.close();
3690
3866
  }
3691
3867
  const cleanup = await reapChild(child, opts.groupObserver ? { observer: opts.groupObserver } : {});
3692
- if (!reapSucceeded(cleanup)) throw cleanupFailure("git pack-objects", cleanup, failure ?? void 0);
3693
- if (failure) throw failure;
3868
+ if (!reapSucceeded(cleanup)) {
3869
+ throw cleanupFailure("git pack-objects", cleanup, failure ?? void 0);
3870
+ }
3871
+ if (failure) {
3872
+ throw failure;
3873
+ }
3694
3874
  const { sha256, size } = await sha256File(opts.outPath);
3695
3875
  return { sha256, size, objectCount: packObjectCount(await readPackHeader(opts.outPath)) };
3696
3876
  }
@@ -3715,21 +3895,31 @@ async function readPackHeader(path) {
3715
3895
  }
3716
3896
  }
3717
3897
  function packObjectCount(header) {
3718
- if (header.length < 12 || header.toString("latin1", 0, 4) !== "PACK") throw new PublisherError("PACK_INVALID", "not a pack file");
3898
+ if (header.length < 12 || header.toString("latin1", 0, 4) !== "PACK") {
3899
+ throw new PublisherError("PACK_INVALID", "not a pack file");
3900
+ }
3719
3901
  const version2 = header.readUInt32BE(4);
3720
- if (version2 !== 2 && version2 !== 3) throw new PublisherError("PACK_INVALID", `unsupported pack version ${version2}`);
3902
+ if (version2 !== 2 && version2 !== 3) {
3903
+ throw new PublisherError("PACK_INVALID", `unsupported pack version ${version2}`);
3904
+ }
3721
3905
  return header.readUInt32BE(8);
3722
3906
  }
3723
3907
 
3724
3908
  // ../git-core/src/bundle.ts
3725
3909
  async function bundleUnbundleObjects(repoDir, bundlePath, opts = {}) {
3726
3910
  const r = await runGit(["bundle", "unbundle", bundlePath], { cwd: repoDir, allowFailure: true, ...opts });
3727
- if (r.code !== 0) throw new PublisherError("PACK_INVALID", `bundle unbundle failed: ${sanitizeGitMessage(r.stderr.trim().split("\n").pop() ?? "")}`);
3911
+ if (r.code !== 0) {
3912
+ throw new PublisherError("PACK_INVALID", `bundle unbundle failed: ${sanitizeGitMessage(r.stderr.trim().split("\n").pop() ?? "")}`);
3913
+ }
3728
3914
  const refs = {};
3729
3915
  for (const line of r.stdout.toString("utf8").split("\n")) {
3730
- if (!line) continue;
3916
+ if (!line) {
3917
+ continue;
3918
+ }
3731
3919
  const [oid, name] = line.split(" ");
3732
- if (name && name !== "HEAD") refs[name] = oid;
3920
+ if (name && name !== "HEAD") {
3921
+ refs[name] = oid;
3922
+ }
3733
3923
  }
3734
3924
  return refs;
3735
3925
  }
@@ -3750,12 +3940,19 @@ async function getConfig(dir, key) {
3750
3940
  async function getConfigSection(dir, section) {
3751
3941
  const r = await runGit(["config", "--local", "-z", "--get-regexp", `^${section.replace(/\./g, "\\.")}\\.`], { cwd: dir, allowFailure: true });
3752
3942
  const out = /* @__PURE__ */ new Map();
3753
- if (r.code !== 0) return out;
3943
+ if (r.code !== 0) {
3944
+ return out;
3945
+ }
3754
3946
  for (const record2 of r.stdout.toString("utf8").split("\0")) {
3755
- if (!record2) continue;
3947
+ if (!record2) {
3948
+ continue;
3949
+ }
3756
3950
  const nl = record2.indexOf("\n");
3757
- if (nl < 0) out.set(record2, "");
3758
- else out.set(record2.slice(0, nl), record2.slice(nl + 1).trim());
3951
+ if (nl < 0) {
3952
+ out.set(record2, "");
3953
+ } else {
3954
+ out.set(record2.slice(0, nl), record2.slice(nl + 1).trim());
3955
+ }
3759
3956
  }
3760
3957
  return out;
3761
3958
  }
@@ -3766,14 +3963,19 @@ async function remoteUrl(dir, name) {
3766
3963
  return getConfig(dir, `remote.${name}.url`);
3767
3964
  }
3768
3965
  async function ensureRemote(dir, name, url2) {
3769
- if (await remoteUrl(dir, name) !== null) return "exists";
3966
+ if (await remoteUrl(dir, name) !== null) {
3967
+ return "exists";
3968
+ }
3770
3969
  await runGit(["remote", "add", name, url2], { cwd: dir });
3771
3970
  await runGit(["config", "--local", `remote.${name}.pushurl`, "no_push://publisher-uses-the-publish-command"], { cwd: dir });
3772
3971
  return "added";
3773
3972
  }
3774
3973
  async function updateTrackingRef(dir, ref, oid) {
3775
- if (oid === null) await runGit(["update-ref", "-d", ref], { cwd: dir, allowFailure: true });
3776
- else await runGit(["update-ref", ref, oid], { cwd: dir });
3974
+ if (oid === null) {
3975
+ await runGit(["update-ref", "-d", ref], { cwd: dir, allowFailure: true });
3976
+ } else {
3977
+ await runGit(["update-ref", ref, oid], { cwd: dir });
3978
+ }
3777
3979
  }
3778
3980
  async function currentBranch(dir) {
3779
3981
  const r = await runGit(["symbolic-ref", "-q", "HEAD"], { cwd: dir, allowFailure: true });
@@ -3787,7 +3989,9 @@ async function localRefs(dir, prefix = "refs/") {
3787
3989
  const r = await runGit(["for-each-ref", "--format=%(objectname) %(refname)", prefix], { cwd: dir });
3788
3990
  const out = {};
3789
3991
  for (const line of r.stdout.toString("utf8").split("\n")) {
3790
- if (!line) continue;
3992
+ if (!line) {
3993
+ continue;
3994
+ }
3791
3995
  const sp = line.indexOf(" ");
3792
3996
  out[line.slice(sp + 1)] = line.slice(0, sp);
3793
3997
  }
@@ -3803,11 +4007,15 @@ async function statusPorcelain(dir) {
3803
4007
  const parts = r.stdout.toString("utf8").split("\0");
3804
4008
  for (let i = 0; i < parts.length; i++) {
3805
4009
  const p = parts[i];
3806
- if (!p) continue;
4010
+ if (!p) {
4011
+ continue;
4012
+ }
3807
4013
  const code = p.slice(0, 2);
3808
4014
  const path = p.slice(3);
3809
4015
  out.push({ code, path });
3810
- if (code[0] === "R" || code[0] === "C") i++;
4016
+ if (code[0] === "R" || code[0] === "C") {
4017
+ i++;
4018
+ }
3811
4019
  }
3812
4020
  return out;
3813
4021
  }
@@ -3815,13 +4023,19 @@ async function initWorkRepo(dir, branch = "main") {
3815
4023
  await runGit(["init", "-q", `--initial-branch=${branch}`, dir]);
3816
4024
  }
3817
4025
  async function commitPaths(dir, paths, opts) {
3818
- if (paths.length === 0) throw new PublisherError("LOCAL_STATE", "no files selected to commit");
4026
+ if (paths.length === 0) {
4027
+ throw new PublisherError("LOCAL_STATE", "no files selected to commit");
4028
+ }
3819
4029
  const run = { cwd: dir, env: authorEnv(opts) };
3820
- if (opts.signal) run.signal = opts.signal;
4030
+ if (opts.signal) {
4031
+ run.signal = opts.signal;
4032
+ }
3821
4033
  await runGit(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], { ...run, input: paths.join("\0") + "\0" });
3822
4034
  await runGit(["commit", "-q", "--no-verify", "-m", opts.message], run);
3823
4035
  const head = await headCommit(dir);
3824
- if (!head) throw new PublisherError("GIT", "commit did not produce HEAD");
4036
+ if (!head) {
4037
+ throw new PublisherError("GIT", "commit did not produce HEAD");
4038
+ }
3825
4039
  return head;
3826
4040
  }
3827
4041
  async function mergeCheck(dir, ours, theirs) {
@@ -3829,25 +4043,37 @@ async function mergeCheck(dir, ours, theirs) {
3829
4043
  const mergeBase = base.code === 0 ? base.stdout.toString("utf8").trim() : null;
3830
4044
  const oursOid = (await runGit(["rev-parse", "--verify", `${ours}^{commit}`], { cwd: dir })).stdout.toString("utf8").trim();
3831
4045
  const theirsOid = (await runGit(["rev-parse", "--verify", `${theirs}^{commit}`], { cwd: dir })).stdout.toString("utf8").trim();
3832
- if (mergeBase === theirsOid) return { ok: true, tree: null, conflicts: [], mergeBase, fastForward: "ours" };
3833
- if (mergeBase === oursOid) return { ok: true, tree: null, conflicts: [], mergeBase, fastForward: "theirs" };
4046
+ if (mergeBase === theirsOid) {
4047
+ return { ok: true, tree: null, conflicts: [], mergeBase, fastForward: "ours" };
4048
+ }
4049
+ if (mergeBase === oursOid) {
4050
+ return { ok: true, tree: null, conflicts: [], mergeBase, fastForward: "theirs" };
4051
+ }
3834
4052
  const r = await runGit(["merge-tree", "--write-tree", "-z", "--name-only", "--no-messages", ours, theirs], { cwd: dir, allowFailure: true });
3835
- if (r.code !== 0 && r.code !== 1) throw new PublisherError("GIT", `merge-tree failed: ${sanitizeGitMessage(r.stderr.trim())}`);
4053
+ if (r.code !== 0 && r.code !== 1) {
4054
+ throw new PublisherError("GIT", `merge-tree failed: ${sanitizeGitMessage(r.stderr.trim())}`);
4055
+ }
3836
4056
  const out = r.stdout.toString("utf8");
3837
4057
  const parts = out.split("\0");
3838
4058
  const tree = parts[0] ?? null;
3839
- if (r.code === 0) return { ok: true, tree, conflicts: [], mergeBase, fastForward: null };
4059
+ if (r.code === 0) {
4060
+ return { ok: true, tree, conflicts: [], mergeBase, fastForward: null };
4061
+ }
3840
4062
  const conflicts = [];
3841
4063
  for (let i = 1; i < parts.length; i++) {
3842
4064
  const p = parts[i];
3843
- if (p === "") break;
4065
+ if (p === "") {
4066
+ break;
4067
+ }
3844
4068
  conflicts.push({ path: p, kind: "content" });
3845
4069
  }
3846
4070
  return { ok: false, tree, conflicts, mergeBase, fastForward: null };
3847
4071
  }
3848
4072
  async function commitTree(dir, tree, parents, message, opts = {}) {
3849
4073
  const args = ["commit-tree", tree, "-m", message];
3850
- for (const p of parents) args.push("-p", p);
4074
+ for (const p of parents) {
4075
+ args.push("-p", p);
4076
+ }
3851
4077
  const r = await runGit(args, { cwd: dir, env: authorEnv(opts) });
3852
4078
  return r.stdout.toString("utf8").trim();
3853
4079
  }
@@ -3864,7 +4090,9 @@ async function readAssociation(repoDir) {
3864
4090
  function associationFrom(section) {
3865
4091
  const endpoint = section.get("publisher.endpoint");
3866
4092
  const repoId = section.get("publisher.repoid");
3867
- if (!endpoint || !repoId) return null;
4093
+ if (!endpoint || !repoId) {
4094
+ return null;
4095
+ }
3868
4096
  return { endpoint, repoId, remote: section.get("publisher.remote") ?? "publisher" };
3869
4097
  }
3870
4098
  async function writeAssociation(repoDir, a) {
@@ -3883,8 +4111,12 @@ async function mergeRemote(opts) {
3883
4111
  const { repoDir, ours, theirs } = opts;
3884
4112
  const oursOid = await revParse(repoDir, ours);
3885
4113
  const theirsOid = await revParse(repoDir, theirs);
3886
- if (!oursOid) throw new PublisherError("LOCAL_STATE", `unknown local ref ${ours}`);
3887
- if (!theirsOid) throw new PublisherError("LOCAL_STATE", `unknown ref ${theirs}; fetch first`);
4114
+ if (!oursOid) {
4115
+ throw new PublisherError("LOCAL_STATE", `unknown local ref ${ours}`);
4116
+ }
4117
+ if (!theirsOid) {
4118
+ throw new PublisherError("LOCAL_STATE", `unknown ref ${theirs}; fetch first`);
4119
+ }
3888
4120
  const check = await mergeCheck(repoDir, oursOid, theirsOid);
3889
4121
  const base = { ok: check.ok, applied: false, fastForward: check.fastForward, mergeBase: check.mergeBase, mergeCommit: null, conflicts: check.conflicts, hint: "" };
3890
4122
  if (!check.ok) {
@@ -3893,16 +4125,21 @@ async function mergeRemote(opts) {
3893
4125
  hint: `conflicts in ${check.conflicts.map((c) => c.path).join(", ")}; resolve them with git merge ${theirs} in a working copy, or keep both versions and publish later`
3894
4126
  };
3895
4127
  }
3896
- if (check.fastForward === "ours") return { ...base, hint: "local branch already contains the fetched history" };
3897
- if (!opts.apply)
4128
+ if (check.fastForward === "ours") {
4129
+ return { ...base, hint: "local branch already contains the fetched history" };
4130
+ }
4131
+ if (!opts.apply) {
3898
4132
  return {
3899
4133
  ...base,
3900
4134
  hint: check.fastForward === "theirs" ? "fast-forward possible; run with apply to update the branch" : "clean merge possible; run with apply to create the merge commit"
3901
4135
  };
4136
+ }
3902
4137
  const checkedOut = await currentBranch(repoDir);
3903
4138
  if (checkedOut === ours) {
3904
4139
  const dirty = (await statusPorcelain(repoDir)).filter((s) => s.code !== "??");
3905
- if (dirty.length > 0) throw new PublisherError("LOCAL_STATE", "the working tree has uncommitted changes; commit or stash them before applying the merge");
4140
+ if (dirty.length > 0) {
4141
+ throw new PublisherError("LOCAL_STATE", "the working tree has uncommitted changes; commit or stash them before applying the merge");
4142
+ }
3906
4143
  if (check.fastForward === "theirs") {
3907
4144
  await runGit(["merge", "--ff-only", "--quiet", theirsOid], { cwd: repoDir });
3908
4145
  return { ...base, applied: true, mergeCommit: theirsOid, hint: "fast-forwarded" };
@@ -3946,10 +4183,14 @@ async function openPending(repoDir) {
3946
4183
  list: async () => {
3947
4184
  const out = [];
3948
4185
  for (const f of await (0, import_promises3.readdir)(dir)) {
3949
- if (!f.endsWith(".json")) continue;
4186
+ if (!f.endsWith(".json")) {
4187
+ continue;
4188
+ }
3950
4189
  try {
3951
4190
  const p = JSON.parse(await (0, import_promises3.readFile)((0, import_node_path2.join)(dir, f), "utf8"));
3952
- if (p.version === 1) out.push(p);
4191
+ if (p.version === 1) {
4192
+ out.push(p);
4193
+ }
3953
4194
  } catch {
3954
4195
  }
3955
4196
  }
@@ -3977,16 +4218,22 @@ var PARTIAL_MARKER = ".gitpub-checkout.json";
3977
4218
  async function getWorkspaceMapping(directory) {
3978
4219
  const dir = (0, import_node_path3.resolve)(directory);
3979
4220
  const section = await getConfigSection(dir, "publisher").catch(() => null);
3980
- if (!section) return null;
4221
+ if (!section) {
4222
+ return null;
4223
+ }
3981
4224
  const a = associationFrom(section);
3982
- if (!a) return null;
4225
+ if (!a) {
4226
+ return null;
4227
+ }
3983
4228
  const get = (k) => section.get(`${NS}.${k}`) ?? null;
3984
4229
  const branch = get("branch");
3985
4230
  const baseOid = get("baseoid");
3986
4231
  const baseGeneration = get("basegeneration");
3987
4232
  const checkedOutAt = get("checkedoutat");
3988
4233
  const baseAdvancedAt = get("baseadvancedat");
3989
- if (!branch || baseGeneration === null || !checkedOutAt) return null;
4234
+ if (!branch || baseGeneration === null || !checkedOutAt) {
4235
+ return null;
4236
+ }
3990
4237
  return {
3991
4238
  directory: dir,
3992
4239
  endpoint: a.endpoint,
@@ -4009,9 +4256,15 @@ async function writeMapping(dir, m) {
4009
4256
  }
4010
4257
  async function advanceWorkspaceBase(directory, next, now = /* @__PURE__ */ new Date()) {
4011
4258
  const m = await getWorkspaceMapping(directory);
4012
- if (!m) throw new PublisherError("LOCAL_STATE", "not a workspace (no publisher.workspace mapping)");
4013
- if (next.generation < m.baseGeneration) return m;
4014
- if (next.generation === m.baseGeneration && next.oid === m.baseOid) return m;
4259
+ if (!m) {
4260
+ throw new PublisherError("LOCAL_STATE", "not a workspace (no publisher.workspace mapping)");
4261
+ }
4262
+ if (next.generation < m.baseGeneration) {
4263
+ return m;
4264
+ }
4265
+ if (next.generation === m.baseGeneration && next.oid === m.baseOid) {
4266
+ return m;
4267
+ }
4015
4268
  const updated = { ...m, baseOid: next.oid, baseGeneration: next.generation, baseAdvancedAt: now.toISOString() };
4016
4269
  await setConfig(m.directory, `${NS}.baseoid`, updated.baseOid ?? "");
4017
4270
  await setConfig(m.directory, `${NS}.basegeneration`, String(updated.baseGeneration));
@@ -4019,29 +4272,43 @@ async function advanceWorkspaceBase(directory, next, now = /* @__PURE__ */ new D
4019
4272
  return updated;
4020
4273
  }
4021
4274
  async function noteAcceptedPublication(directory, outcome, now = /* @__PURE__ */ new Date()) {
4022
- if (outcome.state !== "accepted" || outcome.resultGeneration === null) return;
4275
+ if (outcome.state !== "accepted" || outcome.resultGeneration === null) {
4276
+ return;
4277
+ }
4023
4278
  const m = await getWorkspaceMapping(directory).catch(() => null);
4024
- if (!m || m.repoId !== outcome.repoId) return;
4279
+ if (!m || m.repoId !== outcome.repoId) {
4280
+ return;
4281
+ }
4025
4282
  const oid = outcome.refs?.[`refs/heads/${m.branch}`];
4026
- if (!oid) return;
4283
+ if (!oid) {
4284
+ return;
4285
+ }
4027
4286
  await advanceWorkspaceBase(directory, { oid, generation: outcome.resultGeneration }, now);
4028
4287
  }
4029
4288
  async function adoptWorkspace(client, directory, call2 = {}, now = /* @__PURE__ */ new Date()) {
4030
4289
  const dir = (0, import_node_path3.resolve)(directory);
4031
4290
  const existing = await getWorkspaceMapping(dir);
4032
- if (existing) return existing;
4291
+ if (existing) {
4292
+ return existing;
4293
+ }
4033
4294
  const section = await getConfigSection(dir, "publisher").catch(() => null);
4034
4295
  const a = section ? associationFrom(section) : null;
4035
- if (!a) throw new PublisherError("LOCAL_STATE", "this folder is not associated with a published repository; run init first");
4296
+ if (!a) {
4297
+ throw new PublisherError("LOCAL_STATE", "this folder is not associated with a published repository; run init first");
4298
+ }
4036
4299
  const branch = (await currentBranch(dir))?.replace(/^refs\/heads\//, "") ?? null;
4037
- if (!branch) throw new PublisherError("LOCAL_STATE", "HEAD is detached; check out the branch this folder publishes, then run sync again");
4300
+ if (!branch) {
4301
+ throw new PublisherError("LOCAL_STATE", "HEAD is detached; check out the branch this folder publishes, then run sync again");
4302
+ }
4038
4303
  const repo = await client.getRepository(a.repoId, call2);
4039
4304
  const remoteOid = repo.refs[`refs/heads/${branch}`] ?? null;
4040
4305
  const tip = await revParse(dir, `refs/heads/${branch}`);
4041
4306
  let baseOid = null;
4042
4307
  let baseGeneration = 0;
4043
4308
  if (remoteOid) {
4044
- if (!await objectExists(dir, remoteOid)) await client.fetch({ repoDir: dir, repoId: a.repoId, remote: a.remote }, call2);
4309
+ if (!await objectExists(dir, remoteOid)) {
4310
+ await client.fetch({ repoDir: dir, repoId: a.repoId, remote: a.remote }, call2);
4311
+ }
4045
4312
  const haveRemote = await objectExists(dir, remoteOid);
4046
4313
  if (!tip) {
4047
4314
  baseOid = haveRemote ? remoteOid : null;
@@ -4071,8 +4338,12 @@ async function isEmptyDir(p) {
4071
4338
  try {
4072
4339
  return (await (0, import_promises4.readdir)(p)).length === 0;
4073
4340
  } catch (e) {
4074
- if (e.code === "ENOENT") return null;
4075
- if (e.code === "ENOTDIR") return false;
4341
+ if (e.code === "ENOENT") {
4342
+ return null;
4343
+ }
4344
+ if (e.code === "ENOTDIR") {
4345
+ return false;
4346
+ }
4076
4347
  throw e;
4077
4348
  }
4078
4349
  }
@@ -4087,11 +4358,15 @@ async function removeStalePartials(target, repoId) {
4087
4358
  return removed;
4088
4359
  }
4089
4360
  for (const name of entries) {
4090
- if (!name.startsWith(prefix)) continue;
4361
+ if (!name.startsWith(prefix)) {
4362
+ continue;
4363
+ }
4091
4364
  const p = (0, import_node_path3.join)(parent, name);
4092
4365
  try {
4093
4366
  const marker = JSON.parse(await (0, import_promises4.readFile)((0, import_node_path3.join)(p, PARTIAL_MARKER), "utf8"));
4094
- if (marker.target !== target || marker.repoId !== repoId) continue;
4367
+ if (marker.target !== target || marker.repoId !== repoId) {
4368
+ continue;
4369
+ }
4095
4370
  } catch {
4096
4371
  continue;
4097
4372
  }
@@ -4104,14 +4379,20 @@ async function checkoutWorkspace(client, opts, call2 = {}, now = /* @__PURE__ */
4104
4379
  const target = (0, import_node_path3.resolve)(opts.directory);
4105
4380
  const remote = opts.remote ?? "publisher";
4106
4381
  const empty2 = await isEmptyDir(target);
4107
- if (empty2 === false) throw new PublisherError("LOCAL_STATE", `${target} exists and is not empty; checkout never overlays an existing folder`);
4382
+ if (empty2 === false) {
4383
+ throw new PublisherError("LOCAL_STATE", `${target} exists and is not empty; checkout never overlays an existing folder`);
4384
+ }
4108
4385
  await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(target), { recursive: true });
4109
4386
  const removed = await removeStalePartials(target, opts.repoId);
4110
- if (removed.length) call2.onProgress?.({ phase: "checkout", message: `removed ${removed.length} interrupted checkout(s)` });
4387
+ if (removed.length) {
4388
+ call2.onProgress?.({ phase: "checkout", message: `removed ${removed.length} interrupted checkout(s)` });
4389
+ }
4111
4390
  const repo = await client.getRepository(opts.repoId, call2);
4112
4391
  const branch = opts.branch ?? repo.head.symbolic.replace(/^refs\/heads\//, "");
4113
4392
  const tip = repo.refs[`refs/heads/${branch}`] ?? null;
4114
- if (!tip && Object.keys(repo.refs).length > 0) throw new PublisherError("LOCAL_STATE", `branch ${branch} does not exist on the server`);
4393
+ if (!tip && Object.keys(repo.refs).length > 0) {
4394
+ throw new PublisherError("LOCAL_STATE", `branch ${branch} does not exist on the server`);
4395
+ }
4115
4396
  const partial2 = (0, import_node_path3.join)((0, import_node_path3.dirname)(target), `.${(0, import_node_path3.basename)(target)}.partial-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}`);
4116
4397
  await (0, import_promises4.mkdir)(partial2);
4117
4398
  try {
@@ -4122,7 +4403,9 @@ async function checkoutWorkspace(client, opts, call2 = {}, now = /* @__PURE__ */
4122
4403
  await client.fetch({ repoDir: partial2, repoId: opts.repoId, remote }, call2);
4123
4404
  const tracking = `refs/remotes/${remote}/${branch}`;
4124
4405
  const have = await revParse(partial2, tracking);
4125
- if (have !== tip) throw new PublisherError("LOCAL_STATE", `fetched ${tracking} is ${have ?? "missing"}, expected ${tip}; retry the checkout`);
4406
+ if (have !== tip) {
4407
+ throw new PublisherError("LOCAL_STATE", `fetched ${tracking} is ${have ?? "missing"}, expected ${tip}; retry the checkout`);
4408
+ }
4126
4409
  await runGit(["branch", "-q", "--no-track", branch, tip], { cwd: partial2 });
4127
4410
  await runGit(["checkout", "-q", branch], { cwd: partial2 });
4128
4411
  await runGit(["branch", `--set-upstream-to=${tracking}`, branch], { cwd: partial2, allowFailure: true });
@@ -4138,7 +4421,9 @@ async function checkoutWorkspace(client, opts, call2 = {}, now = /* @__PURE__ */
4138
4421
  baseAdvancedAt: now.toISOString()
4139
4422
  });
4140
4423
  await (0, import_promises4.rm)((0, import_node_path3.join)(partial2, PARTIAL_MARKER), { force: true });
4141
- if (empty2 === true) await (0, import_promises4.rm)(target, { recursive: false, force: true }).catch(() => void 0);
4424
+ if (empty2 === true) {
4425
+ await (0, import_promises4.rm)(target, { recursive: false, force: true }).catch(() => void 0);
4426
+ }
4142
4427
  await (0, import_promises4.rename)(partial2, target);
4143
4428
  } catch (e) {
4144
4429
  await (0, import_promises4.rm)(partial2, { recursive: true, force: true }).catch(() => void 0);
@@ -4161,7 +4446,7 @@ async function inspectWorkspace(client, directory, call2 = {}) {
4161
4446
  const dir = (0, import_node_path3.resolve)(directory);
4162
4447
  const mapping = await getWorkspaceMapping(dir);
4163
4448
  const pending = await listPending(dir).catch(() => []);
4164
- if (!mapping)
4449
+ if (!mapping) {
4165
4450
  return {
4166
4451
  state: "unassociated",
4167
4452
  mapping: null,
@@ -4171,6 +4456,7 @@ async function inspectWorkspace(client, directory, call2 = {}) {
4171
4456
  conflicts: [],
4172
4457
  hint: "not a workspace yet; run sync here (an associated folder adopts itself), or checkout to create one"
4173
4458
  };
4459
+ }
4174
4460
  const branchRef = `refs/heads/${mapping.branch}`;
4175
4461
  const tip = await revParse(dir, branchRef);
4176
4462
  const head = await revParse(dir, "HEAD");
@@ -4183,7 +4469,9 @@ async function inspectWorkspace(client, directory, call2 = {}) {
4183
4469
  const repo = await client.getRepository(mapping.repoId, call2);
4184
4470
  const remoteOid = repo.refs[branchRef] ?? null;
4185
4471
  const remote = { oid: remoteOid, generation: repo.generation };
4186
- if (mapping.baseOid && tip) local.commitsAheadOfBase = await countCommits(dir, `${mapping.baseOid}..${tip}`);
4472
+ if (mapping.baseOid && tip) {
4473
+ local.commitsAheadOfBase = await countCommits(dir, `${mapping.baseOid}..${tip}`);
4474
+ }
4187
4475
  const done = (state, hint, conflicts = []) => ({ state, mapping, local, remote, pending, conflicts, hint });
4188
4476
  const work = dirty && untrackedPaths.length > 0 ? "uncommitted and untracked files" : dirty ? "uncommitted changes" : `${untrackedPaths.length} untracked file(s)`;
4189
4477
  if (checkedOut !== mapping.branch && !(checkedOut === null && head === null && tip === null)) {
@@ -4193,32 +4481,42 @@ async function inspectWorkspace(client, directory, call2 = {}) {
4193
4481
  );
4194
4482
  }
4195
4483
  if (!remoteOid) {
4196
- if (!tip) return done(hasLocalWork ? "local_changes" : "up_to_date", hasLocalWork ? `${work}; publisher sync records and publishes them` : "empty on both sides");
4484
+ if (!tip) {
4485
+ return done(hasLocalWork ? "local_changes" : "up_to_date", hasLocalWork ? `${work}; publisher sync records and publishes them` : "empty on both sides");
4486
+ }
4197
4487
  return done("local_changes", "the server has no such branch yet; publisher sync creates it");
4198
4488
  }
4199
- if (!tip) return done("remote_ahead", hasLocalWork ? `local branch is unborn and there are ${work}; move them aside, then sync` : "local branch is unborn; sync to fast-forward");
4489
+ if (!tip) {
4490
+ return done("remote_ahead", hasLocalWork ? `local branch is unborn and there are ${work}; move them aside, then sync` : "local branch is unborn; sync to fast-forward");
4491
+ }
4200
4492
  if (!await objectExists(dir, remoteOid)) {
4201
4493
  await client.fetch({ repoDir: dir, repoId: mapping.repoId, remote: mapping.remote }, call2);
4202
- if (!await objectExists(dir, remoteOid)) return done("remote_ahead", "server tip could not be fetched; retry");
4494
+ if (!await objectExists(dir, remoteOid)) {
4495
+ return done("remote_ahead", "server tip could not be fetched; retry");
4496
+ }
4203
4497
  }
4204
- if (remoteOid === tip)
4498
+ if (remoteOid === tip) {
4205
4499
  return done(hasLocalWork ? "local_changes" : "up_to_date", hasLocalWork ? `${work}; publisher sync records and publishes them` : "in sync with the server");
4206
- if (await isAncestor(dir, remoteOid, tip))
4500
+ }
4501
+ if (await isAncestor(dir, remoteOid, tip)) {
4207
4502
  return done(
4208
4503
  "local_changes",
4209
4504
  hasLocalWork ? `local commits and ${work}; publisher sync records and publishes them` : "local commits not yet published; publisher sync publishes them"
4210
4505
  );
4211
- if (await isAncestor(dir, tip, remoteOid))
4506
+ }
4507
+ if (await isAncestor(dir, tip, remoteOid)) {
4212
4508
  return done(
4213
4509
  "remote_ahead",
4214
4510
  hasLocalWork ? `server moved ahead and there are ${work}; publisher sync records them, brings the server's changes in and publishes` : "server moved ahead; publisher sync brings it in"
4215
4511
  );
4512
+ }
4216
4513
  const merge2 = await mergeRemote({ repoDir: dir, ours: branchRef, theirs: remoteOid, apply: false });
4217
- if (merge2.ok)
4514
+ if (merge2.ok) {
4218
4515
  return done(
4219
4516
  "diverged_clean_merge",
4220
4517
  hasLocalWork ? `both sides changed, merge is clean; publisher sync records the ${work}, merges and publishes` : "both sides changed, merge is clean; publisher sync merges and publishes"
4221
4518
  );
4519
+ }
4222
4520
  return done(
4223
4521
  "conflict",
4224
4522
  `both sides changed ${merge2.conflicts.map((c) => c.path).join(", ")}; resolve with git merge ${remoteOid} in the workspace, then publish`,
@@ -4229,19 +4527,27 @@ async function syncWorkspace(client, opts, call2 = {}, now = /* @__PURE__ */ new
4229
4527
  const dir = (0, import_node_path3.resolve)(opts.directory);
4230
4528
  await adoptWorkspace(client, dir, call2, now);
4231
4529
  const before = await inspectWorkspace(client, dir, call2);
4232
- if (!before.mapping) throw new PublisherError("LOCAL_STATE", "not a workspace; run checkout");
4530
+ if (!before.mapping) {
4531
+ throw new PublisherError("LOCAL_STATE", "not a workspace; run checkout");
4532
+ }
4233
4533
  const mapping = before.mapping;
4234
4534
  const branchRef = `refs/heads/${mapping.branch}`;
4235
4535
  let applied = null;
4236
- if (before.state === "detached") throw new PublisherError("LOCAL_STATE", before.hint);
4536
+ if (before.state === "detached") {
4537
+ throw new PublisherError("LOCAL_STATE", before.hint);
4538
+ }
4237
4539
  if (opts.apply && before.remote?.oid && (before.state === "remote_ahead" || before.state === "diverged_clean_merge")) {
4238
- if (before.local?.dirty) throw new PublisherError("LOCAL_STATE", "the working tree has uncommitted changes; record them first (publisher sync does)");
4540
+ if (before.local?.dirty) {
4541
+ throw new PublisherError("LOCAL_STATE", "the working tree has uncommitted changes; record them first (publisher sync does)");
4542
+ }
4239
4543
  if (!before.local?.tip) {
4240
4544
  await runGit(["checkout", "-q", "-B", mapping.branch, before.remote.oid], { cwd: dir });
4241
4545
  applied = "fast-forward";
4242
4546
  } else {
4243
4547
  const r = await mergeRemote({ repoDir: dir, ours: branchRef, theirs: before.remote.oid, apply: true, ...opts.message ? { message: opts.message } : {} });
4244
- if (!r.applied) throw new PublisherError("LOCAL_STATE", r.hint);
4548
+ if (!r.applied) {
4549
+ throw new PublisherError("LOCAL_STATE", r.hint);
4550
+ }
4245
4551
  applied = r.fastForward === "theirs" ? "fast-forward" : "merge";
4246
4552
  }
4247
4553
  }
@@ -4262,19 +4568,30 @@ var import_node_os2 = require("node:os");
4262
4568
  var import_node_path4 = require("node:path");
4263
4569
 
4264
4570
  // ../client/src/form-post.ts
4571
+ var FORM_POST_TIMEOUT_MS = 1e4;
4265
4572
  async function postForm(base, path, fields, fetchImpl = globalThis.fetch) {
4266
- let res;
4573
+ const bound = new AbortController();
4574
+ const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), FORM_POST_TIMEOUT_MS);
4267
4575
  try {
4268
- res = await fetchImpl(`${base}${path}`, {
4269
- method: "POST",
4270
- headers: { "content-type": "application/x-www-form-urlencoded" },
4271
- body: new URLSearchParams(fields).toString()
4272
- });
4273
- } catch (e) {
4274
- throw new PublisherError("UNAVAILABLE", `could not reach ${base}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true, cause: e });
4576
+ let res;
4577
+ try {
4578
+ res = await fetchImpl(`${base}${path}`, {
4579
+ method: "POST",
4580
+ headers: { "content-type": "application/x-www-form-urlencoded" },
4581
+ body: new URLSearchParams(fields).toString(),
4582
+ signal: bound.signal
4583
+ });
4584
+ } catch (e) {
4585
+ throw new PublisherError("UNAVAILABLE", `could not reach ${base}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true, cause: e });
4586
+ }
4587
+ const body = await res.json().catch(() => ({}));
4588
+ if (bound.signal.aborted) {
4589
+ throw new PublisherError("UNAVAILABLE", `could not reach ${base}: the request timed out`, { retryable: true });
4590
+ }
4591
+ return { status: res.status, body: typeof body === "object" && body !== null ? body : {} };
4592
+ } finally {
4593
+ clearTimeout(timer);
4275
4594
  }
4276
- const body = await res.json().catch(() => ({}));
4277
- return { status: res.status, body: typeof body === "object" && body !== null ? body : {} };
4278
4595
  }
4279
4596
 
4280
4597
  // ../client/src/credentials.ts
@@ -4284,7 +4601,9 @@ function credentialsPath() {
4284
4601
  }
4285
4602
  function credentialsLocation() {
4286
4603
  const explicit = process.env["GIT_PUBLISHER_CREDENTIALS"];
4287
- if (explicit) return { path: explicit, from: "GIT_PUBLISHER_CREDENTIALS" };
4604
+ if (explicit) {
4605
+ return { path: explicit, from: "GIT_PUBLISHER_CREDENTIALS" };
4606
+ }
4288
4607
  const xdg = process.env["XDG_CONFIG_HOME"];
4289
4608
  return { path: (0, import_node_path4.join)(xdg ?? (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".config"), "git-publisher", "credentials.json"), from: xdg ? "XDG_CONFIG_HOME" : "HOME" };
4290
4609
  }
@@ -4331,28 +4650,46 @@ function hasReadableName(endpoint) {
4331
4650
  } catch {
4332
4651
  return false;
4333
4652
  }
4334
- if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host) || host.includes(":")) return false;
4335
- if (/\.lambda-url\.[a-z0-9-]+\.on\.aws$/.test(host)) return false;
4653
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host) || host.includes(":")) {
4654
+ return false;
4655
+ }
4656
+ if (/\.lambda-url\.[a-z0-9-]+\.on\.aws$/.test(host)) {
4657
+ return false;
4658
+ }
4336
4659
  return Boolean(slug(host.split(".")[0] ?? ""));
4337
4660
  }
4338
4661
  function profileNameFor(endpoint, taken) {
4339
4662
  const host = hasReadableName(endpoint) ? new URL(endpoint).hostname : "";
4340
4663
  const candidates = host ? [slug(host.split(".")[0] ?? ""), slug(host)] : ["default"];
4341
- for (const c of candidates) if (c && !taken.has(c)) return c;
4664
+ for (const c of candidates) {
4665
+ if (c && !taken.has(c)) {
4666
+ return c;
4667
+ }
4668
+ }
4342
4669
  const base = candidates.filter(Boolean).pop() ?? "default";
4343
- for (let n = 2; ; n++) if (!taken.has(`${base}-${n}`)) return `${base}-${n}`;
4670
+ for (let n = 2; ; n++) {
4671
+ if (!taken.has(`${base}-${n}`)) {
4672
+ return `${base}-${n}`;
4673
+ }
4674
+ }
4344
4675
  }
4345
4676
  var empty = () => ({ version: 1, endpoints: {}, profiles: {} });
4346
4677
  function parse2(raw) {
4347
- if (!raw || typeof raw !== "object") return empty();
4678
+ if (!raw || typeof raw !== "object") {
4679
+ return empty();
4680
+ }
4348
4681
  const r = raw;
4349
- if (r.version !== 1) return empty();
4682
+ if (r.version !== 1) {
4683
+ return empty();
4684
+ }
4350
4685
  const out = {
4351
4686
  version: 1,
4352
4687
  endpoints: r.endpoints && typeof r.endpoints === "object" ? r.endpoints : {},
4353
4688
  profiles: r.profiles && typeof r.profiles === "object" ? r.profiles : {}
4354
4689
  };
4355
- if (typeof r.defaultProfile === "string") out.defaultProfile = r.defaultProfile;
4690
+ if (typeof r.defaultProfile === "string") {
4691
+ out.defaultProfile = r.defaultProfile;
4692
+ }
4356
4693
  return out;
4357
4694
  }
4358
4695
  async function readCredentials() {
@@ -4380,7 +4717,9 @@ async function writeSecretJson(path, value) {
4380
4717
  return path;
4381
4718
  }
4382
4719
  function pruneDefault(file) {
4383
- if (file.defaultProfile && !file.profiles[file.defaultProfile]) delete file.defaultProfile;
4720
+ if (file.defaultProfile && !file.profiles[file.defaultProfile]) {
4721
+ delete file.defaultProfile;
4722
+ }
4384
4723
  return file;
4385
4724
  }
4386
4725
  async function saveCredential(endpoint, token, principal, opts = {}) {
@@ -4403,7 +4742,9 @@ async function store(url2, entry, opts) {
4403
4742
  const existing = Object.entries(file.profiles).find(([, p]) => normalize(p.endpoint) === url2)?.[0];
4404
4743
  const name = opts.profile ?? existing ?? profileNameFor(url2, new Set(Object.keys(file.profiles)));
4405
4744
  file.profiles[name] = { endpoint: url2 };
4406
- if (opts.setDefault || !file.defaultProfile) file.defaultProfile = name;
4745
+ if (opts.setDefault || !file.defaultProfile) {
4746
+ file.defaultProfile = name;
4747
+ }
4407
4748
  return writeCredentials(pruneDefault(file));
4408
4749
  }
4409
4750
  async function removeCredential(endpoint, opts = {}) {
@@ -4411,14 +4752,20 @@ async function removeCredential(endpoint, opts = {}) {
4411
4752
  const url2 = normalize(endpoint);
4412
4753
  const revoked = await revokeEntry(file.endpoints[url2], opts);
4413
4754
  delete file.endpoints[url2];
4414
- for (const [name, p] of Object.entries(file.profiles)) if (normalize(p.endpoint) === url2) delete file.profiles[name];
4755
+ for (const [name, p] of Object.entries(file.profiles)) {
4756
+ if (normalize(p.endpoint) === url2) {
4757
+ delete file.profiles[name];
4758
+ }
4759
+ }
4415
4760
  await writeCredentials(pruneDefault(file));
4416
4761
  return { revoked };
4417
4762
  }
4418
4763
  async function removeProfile(name, opts = {}) {
4419
4764
  const file = await readCredentials();
4420
4765
  const entry = file.profiles[name];
4421
- if (!entry) throw new PublisherError("LOCAL_STATE", `no such profile: ${name}`, { details: { profiles: Object.keys(file.profiles) } });
4766
+ if (!entry) {
4767
+ throw new PublisherError("LOCAL_STATE", `no such profile: ${name}`, { details: { profiles: Object.keys(file.profiles) } });
4768
+ }
4422
4769
  delete file.profiles[name];
4423
4770
  const url2 = normalize(entry.endpoint);
4424
4771
  let revoked = false;
@@ -4430,7 +4777,9 @@ async function removeProfile(name, opts = {}) {
4430
4777
  return { revoked };
4431
4778
  }
4432
4779
  async function revokeEntry(entry, opts) {
4433
- if (!opts.revoke || !entry?.refreshToken || !entry.authEndpoint) return false;
4780
+ if (!opts.revoke || !entry?.refreshToken || !entry.authEndpoint) {
4781
+ return false;
4782
+ }
4434
4783
  try {
4435
4784
  return (await postForm(entry.authEndpoint, "/v1/auth/revoke", { token: entry.refreshToken }, opts.fetch)).status === 200;
4436
4785
  } catch {
@@ -4439,7 +4788,9 @@ async function revokeEntry(entry, opts) {
4439
4788
  }
4440
4789
  async function setDefaultProfile(name) {
4441
4790
  const file = await readCredentials();
4442
- if (!file.profiles[name]) throw new PublisherError("LOCAL_STATE", `no such profile: ${name}`, { details: { profiles: Object.keys(file.profiles) } });
4791
+ if (!file.profiles[name]) {
4792
+ throw new PublisherError("LOCAL_STATE", `no such profile: ${name}`, { details: { profiles: Object.keys(file.profiles) } });
4793
+ }
4443
4794
  file.defaultProfile = name;
4444
4795
  await writeCredentials(file);
4445
4796
  }
@@ -4452,14 +4803,30 @@ function resolveEndpoint(input) {
4452
4803
  const p = input.file.profiles[name];
4453
4804
  return p ? { kind: "resolved", endpoint: normalize(p.endpoint), source, profile: name } : { kind: "unknown-profile", name, profiles: names };
4454
4805
  };
4455
- if (input.endpoint) return { kind: "resolved", endpoint: normalize(input.endpoint), source: "flag" };
4456
- if (input.profile) return byName(input.profile, "profile-flag");
4457
- if (input.env["GIT_PUBLISHER_ENDPOINT"]) return { kind: "resolved", endpoint: normalize(input.env["GIT_PUBLISHER_ENDPOINT"]), source: "env-endpoint" };
4458
- if (input.env["GIT_PUBLISHER_PROFILE"]) return byName(input.env["GIT_PUBLISHER_PROFILE"], "env-profile");
4459
- if (input.association) return { kind: "resolved", endpoint: normalize(input.association), source: "association" };
4460
- if (input.file.defaultProfile && input.file.profiles[input.file.defaultProfile]) return byName(input.file.defaultProfile, "default-profile");
4461
- if (names.length === 1) return byName(names[0], "sole-profile");
4462
- if (names.length > 1) return { kind: "ambiguous", profiles: names };
4806
+ if (input.endpoint) {
4807
+ return { kind: "resolved", endpoint: normalize(input.endpoint), source: "flag" };
4808
+ }
4809
+ if (input.profile) {
4810
+ return byName(input.profile, "profile-flag");
4811
+ }
4812
+ if (input.env["GIT_PUBLISHER_ENDPOINT"]) {
4813
+ return { kind: "resolved", endpoint: normalize(input.env["GIT_PUBLISHER_ENDPOINT"]), source: "env-endpoint" };
4814
+ }
4815
+ if (input.env["GIT_PUBLISHER_PROFILE"]) {
4816
+ return byName(input.env["GIT_PUBLISHER_PROFILE"], "env-profile");
4817
+ }
4818
+ if (input.association) {
4819
+ return { kind: "resolved", endpoint: normalize(input.association), source: "association" };
4820
+ }
4821
+ if (input.file.defaultProfile && input.file.profiles[input.file.defaultProfile]) {
4822
+ return byName(input.file.defaultProfile, "default-profile");
4823
+ }
4824
+ if (names.length === 1) {
4825
+ return byName(names[0], "sole-profile");
4826
+ }
4827
+ if (names.length > 1) {
4828
+ return { kind: "ambiguous", profiles: names };
4829
+ }
4463
4830
  return { kind: "none" };
4464
4831
  }
4465
4832
  function credentialStatus(input) {
@@ -4476,16 +4843,21 @@ function credentialStatus(input) {
4476
4843
  function nextStep(status) {
4477
4844
  const profiles = status.profiles.map((p) => p.name);
4478
4845
  const r = status.resolution;
4479
- if (status.credentials === "ok") return { action: "ready", ask: null, command: null, profiles };
4480
- if (status.credentials === "unbound") return { action: "bind-endpoint", ask: null, command: null, profiles };
4481
- if (r.kind === "ambiguous" || r.kind === "unknown-profile")
4846
+ if (status.credentials === "ok") {
4847
+ return { action: "ready", ask: null, command: null, profiles };
4848
+ }
4849
+ if (status.credentials === "unbound") {
4850
+ return { action: "bind-endpoint", ask: null, command: null, profiles };
4851
+ }
4852
+ if (r.kind === "ambiguous" || r.kind === "unknown-profile") {
4482
4853
  return {
4483
4854
  action: "choose-profile",
4484
4855
  ask: "which of these places to publish to",
4485
4856
  command: "publisher profile use <name> --json",
4486
4857
  profiles
4487
4858
  };
4488
- if (r.kind === "resolved")
4859
+ }
4860
+ if (r.kind === "resolved") {
4489
4861
  return {
4490
4862
  action: "reauthenticate",
4491
4863
  // Nothing to ask: the sign-in opens a browser and asks them there.
@@ -4493,6 +4865,7 @@ function nextStep(status) {
4493
4865
  command: r.profile ? `publisher login --profile ${r.profile} --json` : `publisher login --endpoint ${r.endpoint} --json`,
4494
4866
  profiles
4495
4867
  };
4868
+ }
4496
4869
  return { action: "sign-in", ask: null, command: "publisher login --json", profiles };
4497
4870
  }
4498
4871
  function resolveLoginEndpoint(input) {
@@ -4501,7 +4874,9 @@ function resolveLoginEndpoint(input) {
4501
4874
  return endpoint ? { endpoint: normalize(endpoint) } : { discover: true };
4502
4875
  }
4503
4876
  function profileForEndpoint(file, endpoint, requested) {
4504
- if (requested && file.profiles[requested]) return requested;
4877
+ if (requested && file.profiles[requested]) {
4878
+ return requested;
4879
+ }
4505
4880
  const url2 = normalize(endpoint);
4506
4881
  return Object.keys(file.profiles).find((n) => normalize(file.profiles[n].endpoint) === url2) ?? null;
4507
4882
  }
@@ -4516,7 +4891,9 @@ async function resolveToken(endpoint, explicit, opts = {}) {
4516
4891
  { details: { endpoint: url3, endpointSource: opts.endpointSource, next: { action: "bind-endpoint", ask: null, command: null, profiles: [] } } }
4517
4892
  );
4518
4893
  }
4519
- if (given) return given;
4894
+ if (given) {
4895
+ return given;
4896
+ }
4520
4897
  const url2 = normalize(endpoint);
4521
4898
  const entry = (await readCredentials()).endpoints[url2];
4522
4899
  if (!entry) {
@@ -4527,14 +4904,20 @@ async function resolveToken(endpoint, explicit, opts = {}) {
4527
4904
  }
4528
4905
  const now = (opts.now ?? Date.now)();
4529
4906
  const lapsed = entry.expiresAt !== void 0 && Date.parse(entry.expiresAt) - REFRESH_EARLY_MS <= now;
4530
- if (entry.refreshToken && entry.authEndpoint && lapsed) return refreshCredential(url2, entry, opts);
4907
+ if (entry.refreshToken && entry.authEndpoint && lapsed) {
4908
+ return refreshCredential(url2, entry, opts);
4909
+ }
4531
4910
  return entry.token;
4532
4911
  }
4533
4912
  async function refreshStoredCredential(endpoint, opts = {}) {
4534
- if (process.env["GIT_PUBLISHER_TOKEN"]) return false;
4913
+ if (process.env["GIT_PUBLISHER_TOKEN"]) {
4914
+ return false;
4915
+ }
4535
4916
  const url2 = normalize(endpoint);
4536
4917
  const entry = (await readCredentials()).endpoints[url2];
4537
- if (!entry?.refreshToken || !entry.authEndpoint) return false;
4918
+ if (!entry?.refreshToken || !entry.authEndpoint) {
4919
+ return false;
4920
+ }
4538
4921
  await refreshCredential(url2, entry, opts);
4539
4922
  return true;
4540
4923
  }
@@ -4554,22 +4937,48 @@ async function refreshCredential(url2, entry, opts) {
4554
4937
  return access_token;
4555
4938
  }
4556
4939
  const rotated = res.status === 400 && res.body["error"] === "refresh_token_rotated";
4557
- if (!rotated && (res.status !== 400 || res.body["error"] !== "invalid_grant"))
4940
+ if (!rotated && (res.status !== 400 || res.body["error"] !== "invalid_grant")) {
4558
4941
  throw new PublisherError("UNAVAILABLE", `the credentials service answered ${res.status}`, { retryable: true });
4942
+ }
4559
4943
  const current = (await readCredentials()).endpoints[url2];
4560
- if (current?.refreshToken && current.refreshToken !== entry.refreshToken) return current.token;
4561
- if (rotated) throw new PublisherError("UNAVAILABLE", `another process is refreshing the sign-in for ${url2}; retry`, { retryable: true });
4944
+ if (current?.refreshToken && current.refreshToken !== entry.refreshToken) {
4945
+ return current.token;
4946
+ }
4947
+ if (rotated) {
4948
+ throw new PublisherError("UNAVAILABLE", `another process is refreshing the sign-in for ${url2}; retry`, { retryable: true });
4949
+ }
4562
4950
  await updateEntry(url2, ({ refreshToken: _dropped, ...rest }) => rest);
4563
4951
  throw new PublisherError("UNAUTHORIZED", `the sign-in for ${url2} has ended; run "publisher login" again`, { details: { signIn: "ended" } });
4564
4952
  }
4565
4953
  async function updateEntry(url2, change) {
4566
4954
  const file = await readCredentials();
4567
4955
  const entry = file.endpoints[url2];
4568
- if (!entry) return;
4956
+ if (!entry) {
4957
+ return;
4958
+ }
4569
4959
  file.endpoints[url2] = change(entry);
4570
4960
  await writeCredentials(file);
4571
4961
  }
4572
4962
 
4963
+ // ../client/src/portal-refusal.ts
4964
+ function portalRefusalCode(body) {
4965
+ if (!isRecord(body)) {
4966
+ return null;
4967
+ }
4968
+ const error2 = body["error"];
4969
+ if (typeof error2 === "string") {
4970
+ return error2 === "" ? null : error2;
4971
+ }
4972
+ if (!isRecord(error2)) {
4973
+ return null;
4974
+ }
4975
+ const { code, message } = error2;
4976
+ return typeof code === "string" && code !== "" && typeof message === "string" ? code : null;
4977
+ }
4978
+ function isRecord(value) {
4979
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4980
+ }
4981
+
4573
4982
  // ../client/src/application.ts
4574
4983
  var APPLICATION_CREATION = "application-creation";
4575
4984
  var ATTEMPTS = 3;
@@ -4587,26 +4996,38 @@ async function createApplication(input, opts) {
4587
4996
  const answer = await post(fetchImpl, url2, await opts.token(), body);
4588
4997
  if (answer.status === 401 && !refreshed && opts.refresh !== void 0) {
4589
4998
  refreshed = true;
4590
- if (await opts.refresh()) continue;
4999
+ if (await opts.refresh()) {
5000
+ continue;
5001
+ }
4591
5002
  }
4592
- if (answer.status === 200 || answer.status === 201) return read(answer.body, answer.status === 201);
4593
- const code = isRecord(answer.body) && typeof answer.body["error"] === "string" ? answer.body["error"] : "";
4594
- if (answer.status === 401) throw new PublisherError("UNAUTHORIZED", 'the portal did not accept this credential; run "publisher login" again');
4595
- if (answer.status === 403) throw new PublisherError("FORBIDDEN", `the portal refused to make an application here (${code || "forbidden"})`);
4596
- if (answer.status === 409 && REFUSALS[code] !== void 0)
5003
+ if (answer.status === 200 || answer.status === 201) {
5004
+ return read(answer.body, answer.status === 201);
5005
+ }
5006
+ const code = portalRefusalCode(answer.body) ?? "";
5007
+ if (answer.status === 401) {
5008
+ throw new PublisherError("UNAUTHORIZED", 'the portal did not accept this credential; run "publisher login" again');
5009
+ }
5010
+ if (answer.status === 403) {
5011
+ throw new PublisherError("FORBIDDEN", `the portal refused to make an application here (${code || "forbidden"})`);
5012
+ }
5013
+ if (answer.status === 409 && REFUSALS[code] !== void 0) {
4597
5014
  throw new PublisherError(REFUSALS[code], `the portal refused the repository (${code})`, { details: { portal: code } });
4598
- if (answer.status === 400 || answer.status === 409)
5015
+ }
5016
+ if (answer.status === 400 || answer.status === 409) {
4599
5017
  throw new PublisherError("INVALID_REQUEST", `the portal refused the request (${code || answer.status})`, { details: { portal: code } });
5018
+ }
4600
5019
  lastOutage = answer.status === 0 ? "the portal could not be reached" : `the portal answered ${answer.status}`;
4601
5020
  attempt += 1;
4602
- if (attempt < ATTEMPTS) await new Promise((resolve3) => setTimeout(resolve3, (opts.retryDelayMs ?? 1e3) * attempt));
5021
+ if (attempt < ATTEMPTS) {
5022
+ await new Promise((resolve3) => setTimeout(resolve3, (opts.retryDelayMs ?? 1e3) * attempt));
5023
+ }
4603
5024
  }
4604
5025
  throw new PublisherError("UNAVAILABLE", `could not make the application: ${lastOutage}; run init again`, { retryable: true });
4605
5026
  }
4606
5027
  function read(body, created) {
4607
- const application = isRecord(body) ? body["application"] : void 0;
4608
- const repository = RepositoryInfo.safeParse(isRecord(body) ? body["repository"] : void 0);
4609
- if (!isRecord(application) || typeof application["appId"] !== "string" || !repository.success) {
5028
+ const application = isRecord2(body) ? body["application"] : void 0;
5029
+ const repository = RepositoryInfo.safeParse(isRecord2(body) ? body["repository"] : void 0);
5030
+ if (!isRecord2(application) || typeof application["appId"] !== "string" || !repository.success) {
4610
5031
  throw new PublisherError("UNAVAILABLE", "the portal answered with an application this version cannot read");
4611
5032
  }
4612
5033
  return { appId: application["appId"], created, repository: repository.data };
@@ -4629,7 +5050,7 @@ async function post(fetchImpl, url2, token, body) {
4629
5050
  return { status: 0, body: null };
4630
5051
  }
4631
5052
  }
4632
- function isRecord(value) {
5053
+ function isRecord2(value) {
4633
5054
  return typeof value === "object" && value !== null && !Array.isArray(value);
4634
5055
  }
4635
5056
 
@@ -4666,9 +5087,13 @@ var Transport = class {
4666
5087
  } catch (e) {
4667
5088
  if (e instanceof PublisherError && e.httpStatus === 401 && !refreshed && this.opts.refresh) {
4668
5089
  refreshed = true;
4669
- if (await this.opts.refresh()) continue;
5090
+ if (await this.opts.refresh()) {
5091
+ continue;
5092
+ }
5093
+ }
5094
+ if (!(e instanceof PublisherError) || !e.retryable || attempt > retries || ro.signal?.aborted) {
5095
+ throw e;
4670
5096
  }
4671
- if (!(e instanceof PublisherError) || !e.retryable || attempt > retries || ro.signal?.aborted) throw e;
4672
5097
  const delay2 = (this.opts.retryDelayMs ?? 300) * 2 ** (attempt - 1) * (0.5 + Math.random());
4673
5098
  await sleep(Math.min(delay2, 1e4), ro.signal);
4674
5099
  }
@@ -4681,7 +5106,9 @@ var Transport = class {
4681
5106
  const timer = setTimeout(() => controller.abort(new PublisherError("NETWORK", "request timed out", { retryable: true })), this.opts.timeoutMs ?? 6e4);
4682
5107
  const onAbort = () => controller.abort(new PublisherError("CANCELLED", "cancelled"));
4683
5108
  ro.signal?.addEventListener("abort", onAbort, { once: true });
4684
- if (ro.signal?.aborted) onAbort();
5109
+ if (ro.signal?.aborted) {
5110
+ onAbort();
5111
+ }
4685
5112
  try {
4686
5113
  let res;
4687
5114
  try {
@@ -4698,7 +5125,9 @@ var Transport = class {
4698
5125
  signal: controller.signal
4699
5126
  });
4700
5127
  } catch (e) {
4701
- if (controller.signal.aborted && controller.signal.reason instanceof PublisherError) throw controller.signal.reason;
5128
+ if (controller.signal.aborted && controller.signal.reason instanceof PublisherError) {
5129
+ throw controller.signal.reason;
5130
+ }
4702
5131
  throw new PublisherError("NETWORK", `request failed: ${e.message}`, { retryable: true, cause: e });
4703
5132
  }
4704
5133
  const text = await res.text();
@@ -4710,7 +5139,9 @@ var Transport = class {
4710
5139
  parsed = null;
4711
5140
  }
4712
5141
  }
4713
- if (res.ok) return { status: res.status, body: parsed, headers: res.headers };
5142
+ if (res.ok) {
5143
+ return { status: res.status, body: parsed, headers: res.headers };
5144
+ }
4714
5145
  const err = parsed?.error;
4715
5146
  const code = err && ERROR_CODES.includes(err.code) ? err.code : fallbackCode(res.status);
4716
5147
  throw new PublisherError(code, err?.message ?? `HTTP ${res.status}`, {
@@ -4725,9 +5156,15 @@ var Transport = class {
4725
5156
  }
4726
5157
  };
4727
5158
  function fallbackCode(status) {
4728
- if (status === 401) return "UNAUTHORIZED";
4729
- if (status === 403) return "FORBIDDEN";
4730
- if (status === 429) return "UNAVAILABLE";
5159
+ if (status === 401) {
5160
+ return "UNAUTHORIZED";
5161
+ }
5162
+ if (status === 403) {
5163
+ return "FORBIDDEN";
5164
+ }
5165
+ if (status === 429) {
5166
+ return "UNAVAILABLE";
5167
+ }
4731
5168
  return status >= 500 ? "UNAVAILABLE" : "INVALID_REQUEST";
4732
5169
  }
4733
5170
  function sleep(ms, signal) {
@@ -4749,27 +5186,47 @@ var RUN_STAGES = ["received", "running", "deploying", "live"];
4749
5186
  var STAGE_WORDS = { received: "received", running: "checked", deploying: "going live", live: "live" };
4750
5187
  var STATES = ["pending", "in-progress", "done", "failed", "skipped"];
4751
5188
  function readStages(run) {
4752
- if (!isRecord2(run)) return { malformed: "a run that is not an object" };
4753
- if (!Object.hasOwn(run, "stages")) return null;
5189
+ if (!isRecord3(run)) {
5190
+ return { malformed: "a run that is not an object" };
5191
+ }
5192
+ if (!Object.hasOwn(run, "stages")) {
5193
+ return null;
5194
+ }
4754
5195
  const stages = run["stages"];
4755
- if (!Array.isArray(stages) || stages.length !== RUN_STAGES.length) return { malformed: "stages that are not exactly four" };
5196
+ if (!Array.isArray(stages) || stages.length !== RUN_STAGES.length) {
5197
+ return { malformed: "stages that are not exactly four" };
5198
+ }
4756
5199
  const out = [];
4757
5200
  for (const [index, entry] of stages.entries()) {
4758
5201
  const stage = RUN_STAGES[index];
4759
- if (!isRecord2(entry) || entry["stage"] !== stage) return { malformed: `stage ${index + 1} is not ${stage}` };
5202
+ if (!isRecord3(entry) || entry["stage"] !== stage) {
5203
+ return { malformed: `stage ${index + 1} is not ${stage}` };
5204
+ }
4760
5205
  const state = entry["state"];
4761
- if (!STATES.includes(state)) return { malformed: `stage ${stage} has an unknown state` };
5206
+ if (!STATES.includes(state)) {
5207
+ return { malformed: `stage ${stage} has an unknown state` };
5208
+ }
4762
5209
  const at = entry["at"];
4763
- if (typeof at !== "string" && at !== null) return { malformed: `stage ${stage} has an unreadable time` };
5210
+ if (typeof at !== "string" && at !== null) {
5211
+ return { malformed: `stage ${stage} has an unreadable time` };
5212
+ }
4764
5213
  out.push({ stage, word: STAGE_WORDS[stage], state, at });
4765
5214
  }
4766
5215
  return { stages: out };
4767
5216
  }
4768
5217
  function ending(stages) {
4769
- if (stages.some((s) => s.state === "pending" || s.state === "in-progress")) return null;
4770
- if (stages.some((s) => s.state === "failed")) return "failed";
4771
- if (stages[3].state === "done") return "live";
4772
- if (stages[1].state === "done" && stages[2].state === "skipped") return "not-deployed";
5218
+ if (stages.some((s) => s.state === "pending" || s.state === "in-progress")) {
5219
+ return null;
5220
+ }
5221
+ if (stages.some((s) => s.state === "failed")) {
5222
+ return "failed";
5223
+ }
5224
+ if (stages[3].state === "done") {
5225
+ return "live";
5226
+ }
5227
+ if (stages[1].state === "done" && stages[2].state === "skipped") {
5228
+ return "not-deployed";
5229
+ }
4773
5230
  return "stopped";
4774
5231
  }
4775
5232
  var POLL_INTERVAL_MS = 5e3;
@@ -4791,47 +5248,73 @@ async function followRun(input, opts) {
4791
5248
  const answer = await ask(fetchImpl, url2, await opts.token());
4792
5249
  if (answer.status === 401 && !refreshed && opts.refresh !== void 0) {
4793
5250
  refreshed = true;
4794
- if (await opts.refresh()) continue;
5251
+ if (await opts.refresh()) {
5252
+ continue;
5253
+ }
5254
+ }
5255
+ if (answer.status === 401) {
5256
+ return { kind: "unauthorized" };
5257
+ }
5258
+ if (answer.status === 404) {
5259
+ return { kind: "unavailable", reason: "no-route" };
5260
+ }
5261
+ if (answer.status === 403) {
5262
+ return { kind: "forbidden" };
4795
5263
  }
4796
- if (answer.status === 401) return { kind: "unauthorized" };
4797
- if (answer.status === 404) return { kind: "unavailable", reason: "no-route" };
4798
- if (answer.status === 403) return { kind: "forbidden" };
4799
5264
  if (answer.status === 200) {
4800
5265
  const run = soleRun(answer.body);
4801
- if ("detail" in run) return { kind: "malformed", detail: run.detail };
5266
+ if ("detail" in run) {
5267
+ return { kind: "malformed", detail: run.detail };
5268
+ }
4802
5269
  if (run.run !== null) {
4803
5270
  const read2 = readStages(run.run);
4804
- if (read2 === null) return { kind: "unavailable", reason: "no-stages" };
4805
- if ("malformed" in read2) return { kind: "malformed", detail: read2.malformed };
5271
+ if (read2 === null) {
5272
+ return { kind: "unavailable", reason: "no-stages" };
5273
+ }
5274
+ if ("malformed" in read2) {
5275
+ return { kind: "malformed", detail: read2.malformed };
5276
+ }
4806
5277
  last = read2.stages;
4807
5278
  let moved = false;
4808
5279
  for (const entry of read2.stages) {
4809
- if (entry.state === "pending" || seen.get(entry.stage) === entry.state) continue;
5280
+ if (entry.state === "pending" || seen.get(entry.stage) === entry.state) {
5281
+ continue;
5282
+ }
4810
5283
  seen.set(entry.stage, entry.state);
4811
5284
  opts.onStage?.(entry);
4812
5285
  moved = true;
4813
5286
  }
4814
- if (isRecord2(run.run) && run.run["status"] === "awaiting-approval") return { kind: "waiting", stages: read2.stages };
5287
+ if (isRecord3(run.run) && run.run["status"] === "awaiting-approval") {
5288
+ return { kind: "waiting", stages: read2.stages };
5289
+ }
4815
5290
  const end = ending(read2.stages);
4816
5291
  if (end !== null) {
4817
- const runId = isRecord2(run.run) && typeof run.run["runId"] === "string" ? run.run["runId"] : null;
5292
+ const runId = isRecord3(run.run) && typeof run.run["runId"] === "string" ? run.run["runId"] : null;
4818
5293
  return { kind: "settled", ending: end, failedAt: read2.stages.find((s) => s.state === "failed")?.word ?? null, stages: read2.stages, runId };
4819
5294
  }
4820
- if (opts.untilChange === true && moved && answered) return { kind: "moving", stages: read2.stages };
5295
+ if (opts.untilChange === true && moved && answered) {
5296
+ return { kind: "moving", stages: read2.stages };
5297
+ }
4821
5298
  }
4822
5299
  answered = true;
4823
5300
  lastError = null;
4824
5301
  } else {
4825
5302
  lastError = answer.status === 0 ? "the portal could not be reached" : `the portal answered ${answer.status}`;
4826
5303
  }
4827
- if (now() + interval2 > deadline) return { kind: "timed-out", stages: last, lastError };
5304
+ if (now() + interval2 > deadline) {
5305
+ return { kind: "timed-out", stages: last, lastError };
5306
+ }
4828
5307
  await sleep2(interval2);
4829
5308
  }
4830
5309
  }
4831
5310
  function soleRun(body) {
4832
- if (!isRecord2(body) || !Array.isArray(body["runs"])) return { detail: "an answer with no list of runs" };
5311
+ if (!isRecord3(body) || !Array.isArray(body["runs"])) {
5312
+ return { detail: "an answer with no list of runs" };
5313
+ }
4833
5314
  const runs = body["runs"];
4834
- if (runs.length > 1) return { detail: "more than the one run this publication started" };
5315
+ if (runs.length > 1) {
5316
+ return { detail: "more than the one run this publication started" };
5317
+ }
4835
5318
  return { run: runs[0] ?? null };
4836
5319
  }
4837
5320
  async function ask(fetchImpl, url2, token) {
@@ -4842,7 +5325,9 @@ async function ask(fetchImpl, url2, token) {
4842
5325
  try {
4843
5326
  body = text ? JSON.parse(text) : null;
4844
5327
  } catch {
4845
- if (res.status === 200) return { status: 200, body: void 0 };
5328
+ if (res.status === 200) {
5329
+ return { status: 200, body: void 0 };
5330
+ }
4846
5331
  }
4847
5332
  return { status: res.status, body };
4848
5333
  } catch {
@@ -4856,25 +5341,31 @@ function portalForEndpoint(endpoint) {
4856
5341
  } catch {
4857
5342
  return null;
4858
5343
  }
4859
- if (url2.protocol !== "https:" || url2.pathname.replace(/\/+$/, "") !== "/publish") return null;
5344
+ if (url2.protocol !== "https:" || url2.pathname.replace(/\/+$/, "") !== "/publish") {
5345
+ return null;
5346
+ }
4860
5347
  return url2.origin;
4861
5348
  }
4862
5349
  var RUN_PROGRESS = "run-progress";
4863
5350
  async function confirmPortal(portal, endpoint, fetchImpl = fetch) {
4864
5351
  try {
4865
5352
  const res = await fetchImpl(`${portal}/api/v1/config`, { signal: AbortSignal.timeout(1e4) });
4866
- if (!res.ok) return null;
5353
+ if (!res.ok) {
5354
+ return null;
5355
+ }
4867
5356
  const body = await res.json();
4868
5357
  const named = body?.publishingEndpoint;
4869
5358
  const bare = (u) => u.replace(/\/+$/, "");
4870
- if (typeof named !== "string" || bare(named) !== bare(endpoint)) return null;
5359
+ if (typeof named !== "string" || bare(named) !== bare(endpoint)) {
5360
+ return null;
5361
+ }
4871
5362
  const terminal = body?.terminal;
4872
5363
  return { terminal: Array.isArray(terminal) && terminal.every((name) => typeof name === "string") ? terminal : [] };
4873
5364
  } catch {
4874
5365
  return null;
4875
5366
  }
4876
5367
  }
4877
- function isRecord2(value) {
5368
+ function isRecord3(value) {
4878
5369
  return typeof value === "object" && value !== null && !Array.isArray(value);
4879
5370
  }
4880
5371
 
@@ -4897,14 +5388,21 @@ async function call(input, opts, body) {
4897
5388
  const answer = await ask2(fetchImpl, url2, await opts.token(), body);
4898
5389
  if (answer.status === 401 && !refreshed && opts.refresh !== void 0) {
4899
5390
  refreshed = true;
4900
- if (await opts.refresh()) continue;
5391
+ if (await opts.refresh()) {
5392
+ continue;
5393
+ }
5394
+ }
5395
+ if (answer.status === 401) {
5396
+ return { kind: "unauthorized" };
5397
+ }
5398
+ if (answer.status === 403) {
5399
+ return { kind: "forbidden" };
5400
+ }
5401
+ if (answer.status === 409) {
5402
+ return { kind: "moved-on" };
4901
5403
  }
4902
- if (answer.status === 401) return { kind: "unauthorized" };
4903
- if (answer.status === 403) return { kind: "forbidden" };
4904
- if (answer.status === 409) return { kind: "moved-on" };
4905
5404
  if (answer.status === 404) {
4906
- const code = isRecord3(answer.body) ? answer.body["error"] : void 0;
4907
- return code === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
5405
+ return portalRefusalCode(answer.body) === "no-such-application" ? { kind: "no-application" } : { kind: "unavailable", detail: "the portal does not serve this address" };
4908
5406
  }
4909
5407
  if (answer.status === 200) {
4910
5408
  const view = readView(answer.body);
@@ -4914,47 +5412,73 @@ async function call(input, opts, body) {
4914
5412
  }
4915
5413
  }
4916
5414
  function readView(body) {
4917
- if (!isRecord3(body)) return { detail: "an answer that is not a preparation" };
5415
+ if (!isRecord4(body)) {
5416
+ return { detail: "an answer that is not a preparation" };
5417
+ }
4918
5418
  const { id, revision, status, message, questions, publication } = body;
4919
- if (typeof id !== "string" || id === "") return { detail: "a preparation with no id" };
4920
- if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 1) return { detail: "a preparation with no revision" };
4921
- if (!STATUSES.includes(status)) return { detail: "a preparation with an unknown status" };
4922
- if (typeof message !== "string") return { detail: "a preparation with no message" };
4923
- if (!Array.isArray(questions)) return { detail: "a preparation with no list of questions" };
5419
+ if (typeof id !== "string" || id === "") {
5420
+ return { detail: "a preparation with no id" };
5421
+ }
5422
+ if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 1) {
5423
+ return { detail: "a preparation with no revision" };
5424
+ }
5425
+ if (!STATUSES.includes(status)) {
5426
+ return { detail: "a preparation with an unknown status" };
5427
+ }
5428
+ if (typeof message !== "string") {
5429
+ return { detail: "a preparation with no message" };
5430
+ }
5431
+ if (!Array.isArray(questions)) {
5432
+ return { detail: "a preparation with no list of questions" };
5433
+ }
4924
5434
  const read2 = [];
4925
5435
  for (const q of questions) {
4926
- if (!isRecord3(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
5436
+ if (!isRecord4(q) || typeof q["id"] !== "string" || typeof q["text"] !== "string" || !Array.isArray(q["options"]) || q["options"].length === 0) {
4927
5437
  return { detail: "a question that cannot be answered" };
4928
5438
  }
4929
5439
  const options = [];
4930
5440
  for (const o of q["options"]) {
4931
- if (!isRecord3(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") return { detail: "a question with an unreadable option" };
5441
+ if (!isRecord4(o) || typeof o["value"] !== "string" || typeof o["label"] !== "string") {
5442
+ return { detail: "a question with an unreadable option" };
5443
+ }
4932
5444
  options.push({ value: o["value"], label: o["label"] });
4933
5445
  }
4934
5446
  read2.push({ id: q["id"], text: q["text"], options });
4935
5447
  }
4936
5448
  let pub = null;
4937
- if (isRecord3(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
5449
+ if (isRecord4(publication) && typeof publication["status"] === "string" && typeof publication["message"] === "string") {
4938
5450
  pub = { status: publication["status"], message: publication["message"], ...typeof publication["url"] === "string" ? { url: publication["url"] } : {} };
4939
5451
  }
4940
5452
  return { id, revision, status, message, questions: read2, publication: pub, findings: readFindings(body["findings"]) };
4941
5453
  }
4942
5454
  function readFindings(value) {
4943
- if (!Array.isArray(value)) return [];
5455
+ if (!Array.isArray(value)) {
5456
+ return [];
5457
+ }
4944
5458
  const out = [];
4945
5459
  const text = (v) => typeof v === "string" && v !== "" ? v.slice(0, MAX_TEXT) : void 0;
4946
5460
  for (const f of value) {
4947
- if (out.length === MAX_FINDINGS) break;
4948
- if (!isRecord3(f)) continue;
5461
+ if (out.length === MAX_FINDINGS) {
5462
+ break;
5463
+ }
5464
+ if (!isRecord4(f)) {
5465
+ continue;
5466
+ }
4949
5467
  const kind = text(f["kind"]);
4950
5468
  const fix = text(f["fix"]);
4951
- if (kind === void 0 || fix === void 0) continue;
5469
+ if (kind === void 0 || fix === void 0) {
5470
+ continue;
5471
+ }
4952
5472
  const finding = { kind, fix };
4953
5473
  for (const field of ["url", "path", "message"]) {
4954
5474
  const value2 = text(f[field]);
4955
- if (value2 !== void 0) finding[field] = value2;
5475
+ if (value2 !== void 0) {
5476
+ finding[field] = value2;
5477
+ }
5478
+ }
5479
+ if (typeof f["status"] === "number" && Number.isInteger(f["status"])) {
5480
+ finding.status = f["status"];
4956
5481
  }
4957
- if (typeof f["status"] === "number" && Number.isInteger(f["status"])) finding.status = f["status"];
4958
5482
  out.push(finding);
4959
5483
  }
4960
5484
  return out;
@@ -4977,7 +5501,7 @@ async function ask2(fetchImpl, url2, token, body) {
4977
5501
  return { status: 0, body: null };
4978
5502
  }
4979
5503
  }
4980
- function isRecord3(value) {
5504
+ function isRecord4(value) {
4981
5505
  return typeof value === "object" && value !== null && !Array.isArray(value);
4982
5506
  }
4983
5507
 
@@ -5038,7 +5562,9 @@ async function onboardFolder(opts) {
5038
5562
  const extra = policy.exclude ?? [];
5039
5563
  const walk = async (d) => {
5040
5564
  for (const e of await (0, import_promises6.readdir)(d, { withFileTypes: true })) {
5041
- if (opts.signal?.aborted) throw new PublisherError("CANCELLED", "cancelled");
5565
+ if (opts.signal?.aborted) {
5566
+ throw new PublisherError("CANCELLED", "cancelled");
5567
+ }
5042
5568
  const full = (0, import_node_path5.join)(d, e.name);
5043
5569
  const rel = (0, import_node_path5.relative)(dir, full).split(import_node_path5.sep).join("/");
5044
5570
  if (e.isSymbolicLink()) {
@@ -5053,7 +5579,9 @@ async function onboardFolder(opts) {
5053
5579
  await walk(full);
5054
5580
  continue;
5055
5581
  }
5056
- if (!e.isFile()) continue;
5582
+ if (!e.isFile()) {
5583
+ continue;
5584
+ }
5057
5585
  if (excludedFile(rel, e.name, extra)) {
5058
5586
  excluded.push({ path: rel, reason: "excluded by policy" });
5059
5587
  continue;
@@ -5069,8 +5597,12 @@ async function onboardFolder(opts) {
5069
5597
  await walk(dir);
5070
5598
  const ignored = await checkIgnore(dir, candidates);
5071
5599
  const selected = candidates.filter((p) => !ignored.has(p)).sort();
5072
- for (const p of ignored) excluded.push({ path: p, reason: "ignored by .gitignore" });
5073
- if (selected.length === 0) return { repoDir: dir, commit: null, committed: [], excluded, initialized };
5600
+ for (const p of ignored) {
5601
+ excluded.push({ path: p, reason: "ignored by .gitignore" });
5602
+ }
5603
+ if (selected.length === 0) {
5604
+ return { repoDir: dir, commit: null, committed: [], excluded, initialized };
5605
+ }
5074
5606
  const commit = await commitPaths(dir, selected, {
5075
5607
  message: opts.message ?? "Initial publication",
5076
5608
  ...opts.authorName ? { authorName: opts.authorName } : {},
@@ -5087,7 +5619,9 @@ function excludedFile(rel, name, extra) {
5087
5619
  }
5088
5620
  async function recordChanges(opts) {
5089
5621
  const dir = opts.dir;
5090
- if (!await isGitRepo(dir)) throw new PublisherError("LOCAL_STATE", "this folder is not published yet; run publisher init first");
5622
+ if (!await isGitRepo(dir)) {
5623
+ throw new PublisherError("LOCAL_STATE", "this folder is not published yet; run publisher init first");
5624
+ }
5091
5625
  const maxBytes = opts.policy?.maxFileBytes ?? 50 * 1024 * 1024;
5092
5626
  const extra = opts.policy?.exclude ?? [];
5093
5627
  const excluded = [];
@@ -5096,16 +5630,21 @@ async function recordChanges(opts) {
5096
5630
  if (await mergeInProgress(dir)) {
5097
5631
  const marked = [];
5098
5632
  for (const { code, path } of entries) {
5099
- if (!UNMERGED.has(code)) continue;
5633
+ if (!UNMERGED.has(code)) {
5634
+ continue;
5635
+ }
5100
5636
  const text = await (0, import_promises6.readFile)((0, import_node_path5.join)(dir, path), "utf8").catch(() => "");
5101
- if (/^<{7} /m.test(text) && /^>{7} /m.test(text)) marked.push(path);
5637
+ if (/^<{7} /m.test(text) && /^>{7} /m.test(text)) {
5638
+ marked.push(path);
5639
+ }
5102
5640
  }
5103
- if (marked.length)
5641
+ if (marked.length) {
5104
5642
  throw new PublisherError(
5105
5643
  "LOCAL_STATE",
5106
5644
  `still marked as conflicting: ${marked.join(", ")}; keep one version (or both) in each, remove the <<<<<<< ======= >>>>>>> lines, then sync again`,
5107
5645
  { details: { marked } }
5108
5646
  );
5647
+ }
5109
5648
  }
5110
5649
  for (const { code, path } of entries) {
5111
5650
  if (code !== "??") {
@@ -5136,7 +5675,9 @@ async function recordChanges(opts) {
5136
5675
  selected.push(path);
5137
5676
  }
5138
5677
  const unique = dedupe(excluded);
5139
- if (selected.length === 0) return { repoDir: dir, commit: null, committed: [], excluded: unique, initialized: false };
5678
+ if (selected.length === 0) {
5679
+ return { repoDir: dir, commit: null, committed: [], excluded: unique, initialized: false };
5680
+ }
5140
5681
  const commit = await commitPaths(dir, selected.sort(), {
5141
5682
  message: opts.message ?? "Update",
5142
5683
  ...opts.authorName ? { authorName: opts.authorName } : {},
@@ -5163,9 +5704,13 @@ function matches(rel, base, pattern) {
5163
5704
  return base === pattern || rel === pattern || rel.startsWith(pattern.replace(/\/$/, "") + "/");
5164
5705
  }
5165
5706
  async function checkIgnore(dir, paths) {
5166
- if (paths.length === 0) return /* @__PURE__ */ new Set();
5707
+ if (paths.length === 0) {
5708
+ return /* @__PURE__ */ new Set();
5709
+ }
5167
5710
  const r = await runGit(["check-ignore", "--stdin", "-z"], { cwd: dir, input: paths.join("\0") + "\0", allowFailure: true });
5168
- if (r.code !== 0) return /* @__PURE__ */ new Set();
5711
+ if (r.code !== 0) {
5712
+ return /* @__PURE__ */ new Set();
5713
+ }
5169
5714
  return new Set(r.stdout.toString("utf8").split("\0").filter(Boolean));
5170
5715
  }
5171
5716
 
@@ -5185,7 +5730,9 @@ var import_promises7 = require("node:fs/promises");
5185
5730
  var import_node_fs5 = require("node:fs");
5186
5731
  var import_node_stream = require("node:stream");
5187
5732
  async function uploadPack(fetchImpl, upload, packPath, size, opts) {
5188
- if (new Date(upload.expiresAt).getTime() < Date.now() + 5e3) return "expired";
5733
+ if (new Date(upload.expiresAt).getTime() < Date.now() + 5e3) {
5734
+ return "expired";
5735
+ }
5189
5736
  let sent = 0;
5190
5737
  const file = (0, import_node_fs5.createReadStream)(packPath);
5191
5738
  const progress = new TransformStream({
@@ -5206,16 +5753,27 @@ async function uploadPack(fetchImpl, upload, packPath, size, opts) {
5206
5753
  ...opts.signal ? { signal: opts.signal } : {}
5207
5754
  });
5208
5755
  } catch (e) {
5209
- if (opts.signal?.aborted) throw new PublisherError("CANCELLED", "upload cancelled");
5756
+ if (opts.signal?.aborted) {
5757
+ throw new PublisherError("CANCELLED", "upload cancelled");
5758
+ }
5210
5759
  throw new PublisherError("NETWORK", `upload failed: ${e.message}`, { retryable: true, cause: e });
5211
5760
  }
5212
- if (res.ok) return "uploaded";
5761
+ if (res.ok) {
5762
+ return "uploaded";
5763
+ }
5213
5764
  const text = await res.text().catch(() => "");
5214
- if (res.status === 412) return "already-present";
5215
- if (res.status === 403 && /expired|Request has expired|AccessDenied/i.test(text)) return "expired";
5216
- if (res.status === 400 && /checksum|Content-MD5|BadDigest|XAmzContentSHA256Mismatch/i.test(text))
5765
+ if (res.status === 412) {
5766
+ return "already-present";
5767
+ }
5768
+ if (res.status === 403 && /expired|Request has expired|AccessDenied/i.test(text)) {
5769
+ return "expired";
5770
+ }
5771
+ if (res.status === 400 && /checksum|Content-MD5|BadDigest|XAmzContentSHA256Mismatch/i.test(text)) {
5217
5772
  throw new PublisherError("UPLOAD_MISMATCH", "storage rejected the pack checksum");
5218
- if (res.status >= 500 || res.status === 429) throw new PublisherError("NETWORK", `upload failed with HTTP ${res.status}`, { retryable: true });
5773
+ }
5774
+ if (res.status >= 500 || res.status === 429) {
5775
+ throw new PublisherError("NETWORK", `upload failed with HTTP ${res.status}`, { retryable: true });
5776
+ }
5219
5777
  throw new PublisherError("NETWORK", `upload failed with HTTP ${res.status}`);
5220
5778
  }
5221
5779
 
@@ -5233,12 +5791,19 @@ async function publish(ctx, repoId, remote, opts, call2) {
5233
5791
  try {
5234
5792
  refUpdates = await resolveUpdates(repoDir, repo, specs);
5235
5793
  } catch (e) {
5236
- if (e instanceof PublisherError && e.code === "LOCAL_STATE" && /nothing to publish/.test(e.message)) nothingNew = e;
5237
- else throw e;
5794
+ if (e instanceof PublisherError && e.code === "LOCAL_STATE" && /nothing to publish/.test(e.message)) {
5795
+ nothingNew = e;
5796
+ } else {
5797
+ throw e;
5798
+ }
5238
5799
  }
5239
5800
  for (const pending2 of pendings) {
5240
- if (pending2.repoId !== repoId) continue;
5241
- if (opts.publicationId && pending2.publicationId !== opts.publicationId) continue;
5801
+ if (pending2.repoId !== repoId) {
5802
+ continue;
5803
+ }
5804
+ if (opts.publicationId && pending2.publicationId !== opts.publicationId) {
5805
+ continue;
5806
+ }
5242
5807
  const sameIntent = !nothingNew && pending2.baseGeneration === repo.generation && canonicalJson(refUpdates) === canonicalJson(pending2.refUpdates);
5243
5808
  if (sameIntent && (pending2.pack === null || await packFileUsable(pending2))) {
5244
5809
  progress({ phase: "resolve", message: `resuming publication ${pending2.publicationId}` });
@@ -5257,13 +5822,19 @@ async function publish(ctx, repoId, remote, opts, call2) {
5257
5822
  try {
5258
5823
  remaining = await resolveUpdates(repoDir, fresh, specs);
5259
5824
  } catch (e) {
5260
- if (!(e instanceof PublisherError && e.code === "LOCAL_STATE")) throw e;
5825
+ if (!(e instanceof PublisherError && e.code === "LOCAL_STATE")) {
5826
+ throw e;
5827
+ }
5828
+ }
5829
+ if (remaining.length === 0) {
5830
+ return toOutcome(outcome, true, pending2.refUpdates);
5261
5831
  }
5262
- if (remaining.length === 0) return toOutcome(outcome, true, pending2.refUpdates);
5263
5832
  return publish(ctx, repoId, remote, opts, call2);
5264
5833
  }
5265
5834
  }
5266
- if (nothingNew) throw nothingNew;
5835
+ if (nothingNew) {
5836
+ throw nothingNew;
5837
+ }
5267
5838
  const remoteOids = new Set(Object.values(repo.refs));
5268
5839
  const wants = refUpdates.map((u) => u.new).filter((o) => o !== null);
5269
5840
  const needsPack = wants.some((o) => !remoteOids.has(o));
@@ -5272,7 +5843,11 @@ async function publish(ctx, repoId, remote, opts, call2) {
5272
5843
  if (needsPack) {
5273
5844
  progress({ phase: "pack", message: "packing objects" });
5274
5845
  const exclude = [];
5275
- for (const info of await batchCheck(repoDir, remoteOids)) if (info.type !== "missing") exclude.push(info.oid);
5846
+ for (const info of await batchCheck(repoDir, remoteOids)) {
5847
+ if (info.type !== "missing") {
5848
+ exclude.push(info.oid);
5849
+ }
5850
+ }
5276
5851
  const outPath = store2.packPath(publicationId);
5277
5852
  const built = await buildPack(repoDir, { include: wants, exclude, outPath, ...call2.signal ? { signal: call2.signal } : {} });
5278
5853
  pack = { sha256: built.sha256, size: built.size, path: outPath };
@@ -5293,7 +5868,9 @@ async function publish(ctx, repoId, remote, opts, call2) {
5293
5868
  return runPublication(ctx, repoId, remote, repoDir, store2, pending, opts, call2, false);
5294
5869
  }
5295
5870
  async function packFileUsable(p) {
5296
- if (!p.pack) return true;
5871
+ if (!p.pack) {
5872
+ return true;
5873
+ }
5297
5874
  try {
5298
5875
  const h = await sha256File(p.pack.path);
5299
5876
  return h.sha256 === p.pack.sha256 && h.size === p.pack.size;
@@ -5310,8 +5887,12 @@ async function runPublication(ctx, repoId, remote, repoDir, store2, pending, opt
5310
5887
  }
5311
5888
  var UNCONFIRMED = /* @__PURE__ */ new Set(["UNAUTHORIZED", "FORBIDDEN", "NETWORK", "UNAVAILABLE", "INTERNAL"]);
5312
5889
  function unconfirmed(e, publicationId) {
5313
- if (!(e instanceof PublisherError) || !UNCONFIRMED.has(e.code)) return e;
5314
- if (e.code === "UNAUTHORIZED" && e.details?.["signIn"] !== void 0) return e;
5890
+ if (!(e instanceof PublisherError) || !UNCONFIRMED.has(e.code)) {
5891
+ return e;
5892
+ }
5893
+ if (e.code === "UNAUTHORIZED" && e.details?.["signIn"] !== void 0) {
5894
+ return e;
5895
+ }
5315
5896
  return new PublisherError("OUTCOME_UNKNOWN", `publication ${publicationId} was not confirmed (${e.code}: ${e.message}); run status before publishing again`, {
5316
5897
  retryable: true,
5317
5898
  cause: e,
@@ -5338,9 +5919,13 @@ async function sendAndWait(ctx, repoId, remote, repoDir, store2, pending, opts,
5338
5919
  let result = await uploadPack(ctx.fetchImpl, begin.upload, pending.pack.path, pending.pack.size, call2);
5339
5920
  if (result === "expired") {
5340
5921
  begin = (await ctx.transport.request("POST", `/v1/repos/${repoId}/publications`, beginBody, call2)).body;
5341
- if (!begin.upload) throw new PublisherError("UPLOAD_MISSING", "server did not return fresh upload instructions");
5922
+ if (!begin.upload) {
5923
+ throw new PublisherError("UPLOAD_MISSING", "server did not return fresh upload instructions");
5924
+ }
5342
5925
  result = await uploadPack(ctx.fetchImpl, begin.upload, pending.pack.path, pending.pack.size, call2);
5343
- if (result === "expired") throw new PublisherError("NETWORK", "upload authorization expired twice", { retryable: true });
5926
+ if (result === "expired") {
5927
+ throw new PublisherError("NETWORK", "upload authorization expired twice", { retryable: true });
5928
+ }
5344
5929
  }
5345
5930
  }
5346
5931
  progress({ phase: "finalize", message: "finalizing" });
@@ -5362,7 +5947,9 @@ async function sendAndWait(ctx, repoId, remote, repoDir, store2, pending, opts,
5362
5947
  progress({ phase: "done", message: `published generation ${summary.resultGeneration}` });
5363
5948
  } else if (summary.state === "rejected") {
5364
5949
  await store2.remove(pending.publicationId);
5365
- if (pending.pack) await (0, import_promises7.rm)(pending.pack.path, { force: true });
5950
+ if (pending.pack) {
5951
+ await (0, import_promises7.rm)(pending.pack.path, { force: true });
5952
+ }
5366
5953
  }
5367
5954
  return toOutcome(summary, resumed, pending.refUpdates);
5368
5955
  }
@@ -5371,7 +5958,9 @@ async function waitForOutcome(ctx, repoId, publicationId, timeoutMs, call2) {
5371
5958
  let delay2 = 500;
5372
5959
  for (; ; ) {
5373
5960
  const s = await ctx.getStatus(repoId, publicationId, { ...call2, waitSeconds: MAX_WAIT_SECONDS });
5374
- if (s.state === "accepted" || s.state === "rejected") return s;
5961
+ if (s.state === "accepted" || s.state === "rejected") {
5962
+ return s;
5963
+ }
5375
5964
  if (Date.now() > deadline) {
5376
5965
  throw new PublisherError("OUTCOME_UNKNOWN", `publication ${publicationId} has not completed yet; run status later`, { retryable: true, details: { publicationId } });
5377
5966
  }
@@ -5427,7 +6016,9 @@ function toOutcome(s, resumed, updates) {
5427
6016
  }
5428
6017
  var REF_MOVED = /* @__PURE__ */ new Set(["NON_FAST_FORWARD", "EXPECTED_OLD_MISMATCH", "STALE_GENERATION"]);
5429
6018
  function publishNext(rejection, published) {
5430
- if (rejection === null || !REF_MOVED.has(rejection.code)) return null;
6019
+ if (rejection === null || !REF_MOVED.has(rejection.code)) {
6020
+ return null;
6021
+ }
5431
6022
  const named = rejection.details?.["ref"];
5432
6023
  const ref = typeof named === "string" ? named : published;
5433
6024
  const name = ref?.replace(/^refs\/heads\//, "") ?? "main";
@@ -5439,7 +6030,9 @@ function publishNext(rejection, published) {
5439
6030
  }
5440
6031
  async function defaultUpdate(repoDir) {
5441
6032
  const branch = await currentBranch(repoDir);
5442
- if (!branch) throw new PublisherError("LOCAL_STATE", "detached HEAD: specify which ref to publish");
6033
+ if (!branch) {
6034
+ throw new PublisherError("LOCAL_STATE", "detached HEAD: specify which ref to publish");
6035
+ }
5443
6036
  return { ref: branch };
5444
6037
  }
5445
6038
  async function resolveUpdates(repoDir, repo, specs) {
@@ -5451,31 +6044,48 @@ async function resolveUpdates(repoDir, repo, specs) {
5451
6044
  );
5452
6045
  let i = 0;
5453
6046
  for (const spec of specs) {
5454
- if (!spec.ref.startsWith("refs/")) throw new PublisherError("INVALID_REQUEST", `ref must be fully qualified: ${spec.ref}`);
6047
+ if (!spec.ref.startsWith("refs/")) {
6048
+ throw new PublisherError("INVALID_REQUEST", `ref must be fully qualified: ${spec.ref}`);
6049
+ }
5455
6050
  let target;
5456
- if (spec.to === null) target = null;
5457
- else {
6051
+ if (spec.to === null) {
6052
+ target = null;
6053
+ } else {
5458
6054
  const r = resolved[i++] ?? null;
5459
- if (r instanceof PublisherError) throw r;
6055
+ if (r instanceof PublisherError) {
6056
+ throw r;
6057
+ }
5460
6058
  target = r;
5461
- if (!target) throw new PublisherError("LOCAL_STATE", `cannot resolve ${spec.to ?? spec.ref} in ${repoDir}`);
6059
+ if (!target) {
6060
+ throw new PublisherError("LOCAL_STATE", `cannot resolve ${spec.to ?? spec.ref} in ${repoDir}`);
6061
+ }
5462
6062
  }
5463
6063
  const expectedOld = repo.refs[spec.ref] ?? null;
5464
- if (target === null && expectedOld === null) throw new PublisherError("INVALID_REQUEST", `${spec.ref} does not exist on the server`);
5465
- if (target !== null && target === expectedOld) continue;
6064
+ if (target === null && expectedOld === null) {
6065
+ throw new PublisherError("INVALID_REQUEST", `${spec.ref} does not exist on the server`);
6066
+ }
6067
+ if (target !== null && target === expectedOld) {
6068
+ continue;
6069
+ }
5466
6070
  out.push({ ref: spec.ref, expectedOld, new: target, force: spec.force ?? false });
5467
6071
  }
5468
- if (out.length === 0) throw new PublisherError("LOCAL_STATE", "nothing to publish: the server already has these refs");
6072
+ if (out.length === 0) {
6073
+ throw new PublisherError("LOCAL_STATE", "nothing to publish: the server already has these refs");
6074
+ }
5469
6075
  return out;
5470
6076
  }
5471
6077
  async function applyTrackingRefs(repoDir, remote, updates) {
5472
6078
  for (const u of updates) {
5473
6079
  const tracking = trackingRef(remote, u.ref);
5474
- if (tracking) await updateTrackingRef(repoDir, tracking, u.new);
6080
+ if (tracking) {
6081
+ await updateTrackingRef(repoDir, tracking, u.new);
6082
+ }
5475
6083
  }
5476
6084
  }
5477
6085
  function trackingRef(remote, ref) {
5478
- if (ref.startsWith("refs/heads/")) return `refs/remotes/${remote}/${ref.slice("refs/heads/".length)}`;
6086
+ if (ref.startsWith("refs/heads/")) {
6087
+ return `refs/remotes/${remote}/${ref.slice("refs/heads/".length)}`;
6088
+ }
5479
6089
  return null;
5480
6090
  }
5481
6091
 
@@ -5518,7 +6128,9 @@ async function fetchRepository(ctx, repoId, opts, call2) {
5518
6128
  artifacts.filter((a) => installed.has(a.sha256)),
5519
6129
  call2
5520
6130
  );
5521
- if (!await haveAll(opts.repoDir, body.refs)) throw new PublisherError("NETWORK", "the downloaded artifacts do not contain every advertised ref", { retryable: true });
6131
+ if (!await haveAll(opts.repoDir, body.refs)) {
6132
+ throw new PublisherError("NETWORK", "the downloaded artifacts do not contain every advertised ref", { retryable: true });
6133
+ }
5522
6134
  }
5523
6135
  }
5524
6136
  await writeManifest(
@@ -5531,11 +6143,15 @@ async function fetchRepository(ctx, repoId, opts, call2) {
5531
6143
  }
5532
6144
  async function haveAll(repoDir, refs) {
5533
6145
  const oids = new Set(Object.values(refs));
5534
- if (oids.size === 0) return true;
6146
+ if (oids.size === 0) {
6147
+ return true;
6148
+ }
5535
6149
  return (await batchCheck(repoDir, oids)).every((o) => o.type !== "missing");
5536
6150
  }
5537
6151
  async function install(ctx, gd, artifacts, call2) {
5538
- if (artifacts.length === 0) return 0;
6152
+ if (artifacts.length === 0) {
6153
+ return 0;
6154
+ }
5539
6155
  const progress = call2.onProgress ?? (() => void 0);
5540
6156
  const tmp = await (0, import_promises8.mkdtemp)((0, import_node_path6.join)((0, import_node_os3.tmpdir)(), "gitpub-fetch-"));
5541
6157
  let bytes = 0;
@@ -5565,12 +6181,18 @@ async function updateRefs(repoDir, remote, refs) {
5565
6181
  const t = trackingRef(remote, ref);
5566
6182
  if (t) {
5567
6183
  tracking[t] = oid;
5568
- if (before[t] !== oid) updates.push({ ref: t, new: oid });
6184
+ if (before[t] !== oid) {
6185
+ updates.push({ ref: t, new: oid });
6186
+ }
5569
6187
  } else if (ref.startsWith("refs/tags/") && !(ref in before)) {
5570
6188
  updates.push({ ref, new: oid });
5571
6189
  }
5572
6190
  }
5573
- for (const ref of Object.keys(before)) if (ref.startsWith(prefix) && !(ref in tracking)) updates.push({ ref, new: null });
6191
+ for (const ref of Object.keys(before)) {
6192
+ if (ref.startsWith(prefix) && !(ref in tracking)) {
6193
+ updates.push({ ref, new: null });
6194
+ }
6195
+ }
5574
6196
  await refTransaction(repoDir, updates);
5575
6197
  return tracking;
5576
6198
  }
@@ -5581,7 +6203,9 @@ async function download(fetchImpl, url2, dest, sha256, size, call2) {
5581
6203
  } catch (e) {
5582
6204
  throw new PublisherError("NETWORK", `download failed: ${e.message}`, { retryable: true, cause: e });
5583
6205
  }
5584
- if (!res.ok || !res.body) throw new PublisherError("NETWORK", `download failed with HTTP ${res.status}`, { retryable: res.status >= 500 });
6206
+ if (!res.ok || !res.body) {
6207
+ throw new PublisherError("NETWORK", `download failed with HTTP ${res.status}`, { retryable: res.status >= 500 });
6208
+ }
5585
6209
  const hash = (0, import_node_crypto4.createHash)("sha256");
5586
6210
  let n = 0;
5587
6211
  const counted = new TransformStream({
@@ -5593,7 +6217,9 @@ async function download(fetchImpl, url2, dest, sha256, size, call2) {
5593
6217
  }
5594
6218
  });
5595
6219
  await (0, import_promises9.pipeline)(import_node_stream2.Readable.fromWeb(res.body.pipeThrough(counted)), (0, import_node_fs6.createWriteStream)(dest));
5596
- if (n !== size || hash.digest("hex") !== sha256) throw new PublisherError("NETWORK", "downloaded artifact failed checksum verification", { retryable: true });
6220
+ if (n !== size || hash.digest("hex") !== sha256) {
6221
+ throw new PublisherError("NETWORK", "downloaded artifact failed checksum verification", { retryable: true });
6222
+ }
5597
6223
  return n;
5598
6224
  }
5599
6225
 
@@ -5603,7 +6229,9 @@ var CLIENT_VERSION = "0.1.0";
5603
6229
  async function markConflicts(dir, remote) {
5604
6230
  const who = { GIT_AUTHOR_NAME: "Publisher", GIT_AUTHOR_EMAIL: "publisher@localhost", GIT_COMMITTER_NAME: "Publisher", GIT_COMMITTER_EMAIL: "publisher@localhost" };
5605
6231
  const r = await runGit(["merge", "--no-ff", "--no-commit", "--no-verify", remote], { cwd: dir, env: { extra: who }, allowFailure: true });
5606
- if (r.code !== 0 && !await mergeInProgress(dir)) throw new PublisherError("GIT", `could not mark the conflicts: ${r.stderr.trim()}`);
6232
+ if (r.code !== 0 && !await mergeInProgress(dir)) {
6233
+ throw new PublisherError("GIT", `could not mark the conflicts: ${r.stderr.trim()}`);
6234
+ }
5607
6235
  }
5608
6236
  var Publisher = class {
5609
6237
  constructor(opts) {
@@ -5692,9 +6320,15 @@ var Publisher = class {
5692
6320
  /** One page of the repositories the caller created (ADR-0011). An authenticated route, unlike capabilities. */
5693
6321
  async listRepositories(options = {}, call2 = {}) {
5694
6322
  const q = new URLSearchParams();
5695
- if (options.state) q.set("state", options.state);
5696
- if (options.limit !== void 0) q.set("limit", String(options.limit));
5697
- if (options.cursor) q.set("cursor", options.cursor);
6323
+ if (options.state) {
6324
+ q.set("state", options.state);
6325
+ }
6326
+ if (options.limit !== void 0) {
6327
+ q.set("limit", String(options.limit));
6328
+ }
6329
+ if (options.cursor) {
6330
+ q.set("cursor", options.cursor);
6331
+ }
5698
6332
  const qs = q.toString() ? `?${q.toString()}` : "";
5699
6333
  return (await this.transport.request("GET", `/v1/repos${qs}`, void 0, call2)).body;
5700
6334
  }
@@ -5719,8 +6353,12 @@ var Publisher = class {
5719
6353
  }
5720
6354
  async resolveRepo(repoDir, repoId) {
5721
6355
  const a = await readAssociation(repoDir);
5722
- if (repoId) return { repoId, remote: a?.remote ?? "publisher" };
5723
- if (!a) throw new PublisherError("LOCAL_STATE", "this folder is not associated with a published repository; run init first");
6356
+ if (repoId) {
6357
+ return { repoId, remote: a?.remote ?? "publisher" };
6358
+ }
6359
+ if (!a) {
6360
+ throw new PublisherError("LOCAL_STATE", "this folder is not associated with a published repository; run init first");
6361
+ }
5724
6362
  if (a.endpoint !== this.endpoint) {
5725
6363
  throw new PublisherError(
5726
6364
  "LOCAL_STATE",
@@ -5772,20 +6410,27 @@ var Publisher = class {
5772
6410
  const dir = options.directory;
5773
6411
  await this.resolveRepo(dir);
5774
6412
  const branch = (await currentBranch(dir))?.replace(/^refs\/heads\//, "") ?? null;
5775
- if (!branch) throw new PublisherError("LOCAL_STATE", "HEAD is detached; check out the branch this folder publishes, then run sync again");
6413
+ if (!branch) {
6414
+ throw new PublisherError("LOCAL_STATE", "HEAD is detached; check out the branch this folder publishes, then run sync again");
6415
+ }
5776
6416
  const mapped = await getWorkspaceMapping(dir);
5777
- if (mapped && mapped.branch !== branch)
6417
+ if (mapped && mapped.branch !== branch) {
5778
6418
  throw new PublisherError("LOCAL_STATE", `branch ${branch} is checked out, but this folder publishes ${mapped.branch}; check it out, then run sync again`);
6419
+ }
5779
6420
  const recorded = await recordChanges({ dir, ...options.message ? { message: options.message } : {} });
5780
6421
  const reconciled = await syncWorkspace(this, { directory: dir, apply: true }, call2);
5781
6422
  const base = { recorded: { commit: recorded.commit, committed: recorded.committed, excluded: recorded.excluded }, reconciled };
5782
6423
  if (reconciled.after === "conflict") {
5783
6424
  const remote = reconciled.before.remote?.oid ?? null;
5784
- if (options.markConflicts && remote) await markConflicts(dir, remote);
6425
+ if (options.markConflicts && remote) {
6426
+ await markConflicts(dir, remote);
6427
+ }
5785
6428
  return { ...base, state: "conflict", published: null, marked: options.markConflicts === true && remote !== null };
5786
6429
  }
5787
6430
  const tip = await revParse(dir, `refs/heads/${branch}`);
5788
- if (tip === null || tip === (reconciled.before.remote?.oid ?? null)) return { ...base, state: "up_to_date", published: null, marked: false };
6431
+ if (tip === null || tip === (reconciled.before.remote?.oid ?? null)) {
6432
+ return { ...base, state: "up_to_date", published: null, marked: false };
6433
+ }
5789
6434
  const published = await this.publish({ repoDir: dir }, call2);
5790
6435
  return { ...base, state: published.state === "accepted" ? "published" : published.state === "rejected" ? "rejected" : "unknown", published, marked: false };
5791
6436
  }
@@ -5795,7 +6440,9 @@ var Publisher = class {
5795
6440
  const out = [];
5796
6441
  const store2 = await openPending(repoDir);
5797
6442
  for (const p of await store2.list()) {
5798
- if (p.endpoint !== this.endpoint) continue;
6443
+ if (p.endpoint !== this.endpoint) {
6444
+ continue;
6445
+ }
5799
6446
  if (!p.finalized) {
5800
6447
  out.push(await publish(this.publishContext(), p.repoId, remote, { repoDir, publicationId: p.publicationId }, call2).catch((e) => rethrowUnlessResolvable(e, p)));
5801
6448
  continue;
@@ -5808,7 +6455,9 @@ var Publisher = class {
5808
6455
  }
5809
6456
  out.push(toOutcome(s, true, p.refUpdates));
5810
6457
  }
5811
- for (const o of out) await noteAcceptedPublication(repoDir, o);
6458
+ for (const o of out) {
6459
+ await noteAcceptedPublication(repoDir, o);
6460
+ }
5812
6461
  return out;
5813
6462
  }
5814
6463
  async pendingPublications(repoDir) {
@@ -5834,8 +6483,9 @@ var Publisher = class {
5834
6483
  }
5835
6484
  };
5836
6485
  function rethrowUnlessResolvable(e, p) {
5837
- if (e instanceof PublisherError)
6486
+ if (e instanceof PublisherError) {
5838
6487
  throw new PublisherError(e.code, `publication ${p.publicationId}: ${e.message}`, { retryable: e.retryable, details: { ...e.details ?? {}, publicationId: p.publicationId } });
6488
+ }
5839
6489
  throw e;
5840
6490
  }
5841
6491
  function createPublisher(opts) {
@@ -5843,19 +6493,28 @@ function createPublisher(opts) {
5843
6493
  }
5844
6494
 
5845
6495
  // ../client/src/migrate.ts
5846
- async function fetchCapabilities(endpoint, fetchImpl = fetch, signal) {
5847
- const res = await fetchImpl(`${normalize(endpoint)}/v1/capabilities`, { method: "GET", ...signal ? { signal } : {} });
5848
- if (!res.ok) throw new Error(`answered ${res.status}`);
5849
- return await res.json();
6496
+ var CAPABILITIES_TIMEOUT_MS = 1e4;
6497
+ async function fetchCapabilities(endpoint, fetchImpl = fetch, opts = {}) {
6498
+ const bound = new AbortController();
6499
+ const timer = setTimeout(() => bound.abort(new DOMException("the capabilities request timed out", "TimeoutError")), opts.timeoutMs ?? CAPABILITIES_TIMEOUT_MS);
6500
+ const signal = opts.signal ? AbortSignal.any([opts.signal, bound.signal]) : bound.signal;
6501
+ try {
6502
+ const res = await fetchImpl(`${normalize(endpoint)}/v1/capabilities`, { method: "GET", signal });
6503
+ if (!res.ok) {
6504
+ throw new Error(`answered ${res.status}`);
6505
+ }
6506
+ return await res.json();
6507
+ } finally {
6508
+ clearTimeout(timer);
6509
+ }
5850
6510
  }
5851
6511
  async function probeEndpoint(endpoint, opts = {}) {
5852
6512
  const from = normalize(endpoint);
5853
- const signal = AbortSignal.timeout(opts.timeoutMs ?? 1e4);
5854
6513
  let advertised;
5855
6514
  try {
5856
- advertised = (await fetchCapabilities(from, opts.fetch, signal)).endpoints?.control;
6515
+ advertised = (await fetchCapabilities(from, opts.fetch, opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {})).endpoints?.control;
5857
6516
  } catch (e) {
5858
- return { kind: "unreachable", reason: signal.aborted ? "timed out" : e.message };
6517
+ return { kind: "unreachable", reason: e.name === "TimeoutError" ? "timed out" : e.message };
5859
6518
  }
5860
6519
  if (typeof advertised !== "string" || advertised === "") {
5861
6520
  return { kind: "unreachable", reason: "it advertises no control endpoint" };
@@ -5904,16 +6563,22 @@ async function startDeviceLogin(opts) {
5904
6563
  } catch (e) {
5905
6564
  throw new PublisherError("UNAVAILABLE", `could not reach ${control}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true });
5906
6565
  }
5907
- if (typeof advertised !== "string" || advertised === "") throw new PublisherError("UNAVAILABLE", "this deployment does not offer device login; obtain a token another way");
6566
+ if (typeof advertised !== "string" || advertised === "") {
6567
+ throw new PublisherError("UNAVAILABLE", "this deployment does not offer device login; obtain a token another way");
6568
+ }
5908
6569
  const base = normalize(advertised);
5909
6570
  assertCredentialEndpoint(base);
5910
6571
  const started = await postForm(base, "/v1/auth/device", { client_id: opts.clientId ?? "gitpub-cli" }, doFetch);
5911
- if (started.status !== 200) throw new PublisherError("UNAVAILABLE", `starting the login failed (${started.status})`, { retryable: true });
6572
+ if (started.status !== 200) {
6573
+ throw new PublisherError("UNAVAILABLE", `starting the login failed (${started.status})`, { retryable: true });
6574
+ }
5912
6575
  const s = started.body;
5913
6576
  const deviceCode = str(s.device_code);
5914
6577
  const userCode = str(s.user_code);
5915
6578
  const uri = str(s.verification_uri_complete) ?? str(s.verification_uri);
5916
- if (!deviceCode || !userCode || !uri) throw new PublisherError("UNAVAILABLE", "the login response was incomplete");
6579
+ if (!deviceCode || !userCode || !uri) {
6580
+ throw new PublisherError("UNAVAILABLE", "the login response was incomplete");
6581
+ }
5917
6582
  const expiresInSeconds = num(s.expires_in, 600);
5918
6583
  return {
5919
6584
  deviceCode,
@@ -5933,25 +6598,41 @@ async function pollDeviceLogin(started, opts) {
5933
6598
  let last = opts.lastPolledAt;
5934
6599
  for (; ; ) {
5935
6600
  const at = Math.max(now(), last === null ? 0 : last + intervalMs);
5936
- if (opts.until !== void 0 && at > opts.until) return null;
5937
- if (at > now()) await sleep2(at - now());
5938
- if (now() >= started.expiresAt) throw new PublisherError("EXPIRED", "the login expired before it was approved");
6601
+ if (opts.until !== void 0 && at > opts.until) {
6602
+ return null;
6603
+ }
6604
+ if (at > now()) {
6605
+ await sleep2(at - now());
6606
+ }
6607
+ if (now() >= started.expiresAt) {
6608
+ throw new PublisherError("EXPIRED", "the login expired before it was approved");
6609
+ }
5939
6610
  const polled = await postForm(started.authEndpoint, "/v1/auth/token", { grant_type: DEVICE_CODE_GRANT, device_code: started.deviceCode }, doFetch);
5940
6611
  last = now();
5941
6612
  if (polled.status === 200) {
5942
6613
  const token = str(polled.body["access_token"]);
5943
- if (!token) throw new PublisherError("UNAVAILABLE", "the login returned no credential");
6614
+ if (!token) {
6615
+ throw new PublisherError("UNAVAILABLE", "the login returned no credential");
6616
+ }
5944
6617
  return { token, refreshToken: str(polled.body["refresh_token"]), expiresInSeconds: num(polled.body["expires_in"], 900), authEndpoint: started.authEndpoint };
5945
6618
  }
5946
6619
  const error2 = str(polled.body["error"]);
5947
- if (error2 === "authorization_pending") continue;
6620
+ if (error2 === "authorization_pending") {
6621
+ continue;
6622
+ }
5948
6623
  if (error2 === "slow_down") {
5949
6624
  intervalMs += 5e3;
5950
6625
  continue;
5951
6626
  }
5952
- if (error2 === "access_denied") throw new PublisherError("FORBIDDEN", "the login was refused");
5953
- if (error2 === "expired_token") throw new PublisherError("EXPIRED", "the login expired before it was approved");
5954
- if (polled.status >= 500) continue;
6627
+ if (error2 === "access_denied") {
6628
+ throw new PublisherError("FORBIDDEN", "the login was refused");
6629
+ }
6630
+ if (error2 === "expired_token") {
6631
+ throw new PublisherError("EXPIRED", "the login expired before it was approved");
6632
+ }
6633
+ if (polled.status >= 500) {
6634
+ continue;
6635
+ }
5955
6636
  throw new PublisherError("UNAVAILABLE", `the login failed (${polled.status}${error2 ? `: ${error2}` : ""})`);
5956
6637
  }
5957
6638
  }
@@ -5979,11 +6660,17 @@ async function readPendingLogin() {
5979
6660
  } catch {
5980
6661
  return null;
5981
6662
  }
5982
- if (typeof raw !== "object" || raw === null) return null;
6663
+ if (typeof raw !== "object" || raw === null) {
6664
+ return null;
6665
+ }
5983
6666
  const r = raw;
5984
6667
  const s = r.started;
5985
- if (r.version !== 1 || typeof r.endpoint !== "string" || !s) return null;
5986
- if (typeof s.deviceCode !== "string" || typeof s.authEndpoint !== "string" || typeof s.expiresAt !== "number" || typeof s.intervalSeconds !== "number") return null;
6668
+ if (r.version !== 1 || typeof r.endpoint !== "string" || !s) {
6669
+ return null;
6670
+ }
6671
+ if (typeof s.deviceCode !== "string" || typeof s.authEndpoint !== "string" || typeof s.expiresAt !== "number" || typeof s.intervalSeconds !== "number") {
6672
+ return null;
6673
+ }
5987
6674
  return {
5988
6675
  endpoint: r.endpoint,
5989
6676
  started: s,
@@ -6012,7 +6699,9 @@ var HEARTBEAT_MS = 3e4;
6012
6699
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
6013
6700
  function took(ms) {
6014
6701
  const seconds = Math.max(0, Math.floor(ms / 1e3));
6015
- if (seconds < 60) return `${seconds} s`;
6702
+ if (seconds < 60) {
6703
+ return `${seconds} s`;
6704
+ }
6016
6705
  const minutes = Math.floor(seconds / 60);
6017
6706
  return minutes >= 10 ? `${minutes} min` : `${minutes} min ${String(seconds % 60).padStart(2, "0")} s`;
6018
6707
  }
@@ -6044,7 +6733,9 @@ function stageReporter(io) {
6044
6733
  let drawn = false;
6045
6734
  let receivedAt = null;
6046
6735
  const clear = () => {
6047
- if (drawn) io.write("\r\x1B[2K");
6736
+ if (drawn) {
6737
+ io.write("\r\x1B[2K");
6738
+ }
6048
6739
  drawn = false;
6049
6740
  };
6050
6741
  const stopTicking = () => {
@@ -6058,14 +6749,21 @@ function stageReporter(io) {
6058
6749
  };
6059
6750
  return {
6060
6751
  onStage(e) {
6061
- if (e.state === "in-progress" || ticking?.stage === e.stage) stopTicking();
6062
- else clear();
6752
+ if (e.state === "in-progress" || ticking?.stage === e.stage) {
6753
+ stopTicking();
6754
+ } else {
6755
+ clear();
6756
+ }
6063
6757
  const says = SAYS[e.stage];
6064
- if (e.stage === "received" && e.at !== null) receivedAt = since(e);
6758
+ if (e.stage === "received" && e.at !== null) {
6759
+ receivedAt = since(e);
6760
+ }
6065
6761
  if (e.state !== "in-progress") {
6066
6762
  const total = e.stage === "live" && e.state === "done" && receivedAt !== null ? ` (${took(since(e) - receivedAt)} after Shadow received it)` : "";
6067
6763
  const line = settledLine(e, total);
6068
- if (line !== null) io.write(line);
6764
+ if (line !== null) {
6765
+ io.write(line);
6766
+ }
6069
6767
  return;
6070
6768
  }
6071
6769
  io.write(` \u2026 ${e.word.padEnd(10)} ${says.doing}
@@ -6094,13 +6792,17 @@ async function followPublication(p, endpoint, o, opts) {
6094
6792
  const confirmed = derived === null ? null : await confirmPortal(derived, endpoint, opts.fetch);
6095
6793
  const portal = confirmed === null ? null : derived;
6096
6794
  const elsewhere = (reason) => ({ kind: "unavailable", reason, portal, hint: inPortal(portal, "Published.", "Follow it") });
6097
- if (portal === null || confirmed === null) return elsewhere("no-portal");
6795
+ if (portal === null || confirmed === null) {
6796
+ return elsewhere("no-portal");
6797
+ }
6098
6798
  if (!confirmed.terminal.includes(RUN_PROGRESS)) {
6099
6799
  const noRoute = { kind: "unavailable", reason: "no-route" };
6100
6800
  return { ...noRoute, portal, hint: progressHint(noRoute, portal) };
6101
6801
  }
6102
6802
  const refs = Object.entries(o.refs ?? {});
6103
- if (refs.length !== 1) return elsewhere("several-refs");
6803
+ if (refs.length !== 1) {
6804
+ return elsewhere("several-refs");
6805
+ }
6104
6806
  const [ref, commit] = refs[0];
6105
6807
  opts.log("Following it to live: received -> checked -> going live -> live. Ctrl-C stops watching; the publication is saved and carries on without you.");
6106
6808
  const outcome = await p.followRun(
@@ -6123,19 +6825,29 @@ async function followPublication(p, endpoint, o, opts) {
6123
6825
  return { ...outcome, portal, hint: progressHint(outcome, portal, answerable), ...next === void 0 ? {} : { next } };
6124
6826
  }
6125
6827
  async function failedChecks(p, portal, repoId, outcome, served) {
6126
- if (!served || outcome.kind !== "settled" || outcome.ending !== "failed" || outcome.runId === null) return null;
6127
- if (outcome.stages.find((s) => s.state === "failed")?.stage !== "running") return null;
6828
+ if (!served || outcome.kind !== "settled" || outcome.ending !== "failed" || outcome.runId === null) {
6829
+ return null;
6830
+ }
6831
+ if (outcome.stages.find((s) => s.state === "failed")?.stage !== "running") {
6832
+ return null;
6833
+ }
6128
6834
  try {
6129
6835
  const answer = await p.readPreparation({ portal, repoId });
6130
- if (answer.kind !== "view" || answer.view.id !== outcome.runId || answer.view.status !== "blocked") return null;
6836
+ if (answer.kind !== "view" || answer.view.id !== outcome.runId || answer.view.status !== "blocked") {
6837
+ return null;
6838
+ }
6131
6839
  return answer.view.findings.length === 0 ? null : answer.view.findings;
6132
6840
  } catch {
6133
6841
  return null;
6134
6842
  }
6135
6843
  }
6136
6844
  function followTimeoutMs(flag) {
6137
- if (flag === void 0) return FOLLOW_TIMEOUT_MS;
6138
- if (!/^[1-9][0-9]*$/.test(flag)) throw new PublisherError("INVALID_REQUEST", `--timeout takes a whole number of seconds, not ${JSON.stringify(flag)}`);
6845
+ if (flag === void 0) {
6846
+ return FOLLOW_TIMEOUT_MS;
6847
+ }
6848
+ if (!/^[1-9][0-9]*$/.test(flag)) {
6849
+ throw new PublisherError("INVALID_REQUEST", `--timeout takes a whole number of seconds, not ${JSON.stringify(flag)}`);
6850
+ }
6139
6851
  return Math.min(Number(flag) * 1e3, FOLLOW_TIMEOUT_MS);
6140
6852
  }
6141
6853
  function shellWord(path) {
@@ -6144,8 +6856,12 @@ function shellWord(path) {
6144
6856
  function stageEmitter(json, quiet) {
6145
6857
  const nothing = () => {
6146
6858
  };
6147
- if (json) return { onStage: (e) => process.stdout.write(stageLine(e) + "\n"), stopStages: nothing };
6148
- if (quiet) return { onStage: nothing, stopStages: nothing };
6859
+ if (json) {
6860
+ return { onStage: (e) => process.stdout.write(stageLine(e) + "\n"), stopStages: nothing };
6861
+ }
6862
+ if (quiet) {
6863
+ return { onStage: nothing, stopStages: nothing };
6864
+ }
6149
6865
  const { onStage, stop } = stageReporter({ write: (s) => process.stderr.write(s), isTTY: process.stderr.isTTY === true });
6150
6866
  return { onStage, stopStages: stop };
6151
6867
  }
@@ -6210,7 +6926,9 @@ function folderFor(name) {
6210
6926
  // src/init.ts
6211
6927
  async function repositoryForInit(p, endpoint, name, opts = {}) {
6212
6928
  const portal = portalForEndpoint(endpoint);
6213
- if (portal === null) return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
6929
+ if (portal === null) {
6930
+ return { repoId: (await p.createRepository({ name, grants: [] })).repoId, appId: null };
6931
+ }
6214
6932
  const confirmed = await confirmPortal(portal, endpoint, opts.fetch);
6215
6933
  if (confirmed === null) {
6216
6934
  throw new PublisherError("UNAVAILABLE", `could not confirm that ${portal} publishes to ${endpoint}; run init again`, { retryable: true });
@@ -6272,18 +6990,25 @@ var DEFAULT_PORTAL_URL = "https://shadow.minnovate.ai";
6272
6990
  function portalBase(env, flag) {
6273
6991
  return (flag ?? env["GIT_PUBLISHER_PORTAL"] ?? DEFAULT_PORTAL_URL).replace(/\/+$/, "");
6274
6992
  }
6993
+ var DISCOVER_TIMEOUT_MS = 1e4;
6275
6994
  async function discoverEndpoint(portal, fetchImpl = fetch) {
6276
6995
  const url2 = `${portal}/api/v1/config`;
6996
+ const bound = new AbortController();
6997
+ const timer = setTimeout(() => bound.abort(new DOMException("the request timed out", "TimeoutError")), DISCOVER_TIMEOUT_MS);
6277
6998
  let body;
6278
6999
  try {
6279
- const response = await fetchImpl(url2);
7000
+ const response = await fetchImpl(url2, { signal: bound.signal });
6280
7001
  if (!response.ok) {
6281
7002
  throw new PublisherError("UNAVAILABLE", `could not ask ${portal} where to publish (it answered ${response.status})`, { retryable: true });
6282
7003
  }
6283
7004
  body = await response.json();
6284
7005
  } catch (e) {
6285
- if (PublisherError.isPublisherError(e)) throw e;
7006
+ if (PublisherError.isPublisherError(e)) {
7007
+ throw e;
7008
+ }
6286
7009
  throw new PublisherError("NETWORK", `could not reach ${portal}: ${e instanceof Error ? e.message : String(e)}`, { retryable: true });
7010
+ } finally {
7011
+ clearTimeout(timer);
6287
7012
  }
6288
7013
  const endpoint = body?.publishingEndpoint;
6289
7014
  if (endpoint === null || endpoint === void 0) {
@@ -6305,7 +7030,9 @@ function suggestProfileName(endpoint, taken) {
6305
7030
  }
6306
7031
  function resolveChoice(answer, names) {
6307
7032
  const a = answer.trim();
6308
- if (!a) return null;
7033
+ if (!a) {
7034
+ return null;
7035
+ }
6309
7036
  if (/^\d+$/.test(a)) {
6310
7037
  const i = Number(a);
6311
7038
  return i >= 1 && i <= names.length ? names[i - 1] : null;
@@ -6332,9 +7059,13 @@ ${purpose}
6332
7059
  for (let attempt = 0; attempt < 3; attempt++) {
6333
7060
  const answer = await ask3(`
6334
7061
  Which one? [1-${names.length}, or a name] `);
6335
- if (answer === null) return null;
7062
+ if (answer === null) {
7063
+ return null;
7064
+ }
6336
7065
  const chosen = resolveChoice(answer, names);
6337
- if (chosen) return chosen;
7066
+ if (chosen) {
7067
+ return chosen;
7068
+ }
6338
7069
  process.stderr.write(`Not one of those.
6339
7070
  `);
6340
7071
  }
@@ -6349,9 +7080,13 @@ Name this connection, so you never have to type ${endpoint} again.
6349
7080
  const answer = await ask3(suggested ? `
6350
7081
  Profile name [${suggested}]: ` : `
6351
7082
  Profile name: `);
6352
- if (answer === null) return null;
7083
+ if (answer === null) {
7084
+ return null;
7085
+ }
6353
7086
  const name = answer.trim() || suggested;
6354
- if (name && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) return name;
7087
+ if (name && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
7088
+ return name;
7089
+ }
6355
7090
  process.stderr.write(`Use letters, digits, dot, dash or underscore.
6356
7091
  `);
6357
7092
  }
@@ -6439,16 +7174,22 @@ Waiting (it expires in ${Math.round(expiresInSeconds / 60)} minutes)...
6439
7174
  );
6440
7175
  }
6441
7176
  async function login(ctx) {
6442
- if (ctx.flags["continue"] === true) return continueLogin(ctx);
7177
+ if (ctx.flags["continue"] === true) {
7178
+ return continueLogin(ctx);
7179
+ }
6443
7180
  const clock = stopwatch();
6444
7181
  const target = resolveLoginEndpoint(ctx.input);
6445
7182
  const endpoint = "endpoint" in target ? target.endpoint : await discoverEndpoint(portalBase(process.env, str2(ctx.flags["portal"])));
6446
- if (!endpoint) throw new PublisherError("INVALID_REQUEST", "login requires --endpoint (or --profile naming a stored one)");
7183
+ if (!endpoint) {
7184
+ throw new PublisherError("INVALID_REQUEST", "login requires --endpoint (or --profile naming a stored one)");
7185
+ }
6447
7186
  clock.lap("discovery");
6448
7187
  const profile = str2(ctx.flags["profile"]);
6449
7188
  const setDefault = ctx.flags["set-default"] === true;
6450
7189
  const given = str2(ctx.flags["token"]) ?? process.env["GIT_PUBLISHER_TOKEN"];
6451
- if (given) return finish(ctx, { endpoint, token: given, signIn: null, profile, setDefault }, clock);
7190
+ if (given) {
7191
+ return finish(ctx, { endpoint, token: given, signIn: null, profile, setDefault }, clock);
7192
+ }
6452
7193
  if (ctx.flags["no-wait"] === true) {
6453
7194
  const started = await startDeviceLogin({ endpoint });
6454
7195
  clock.lap("start");
@@ -6481,7 +7222,9 @@ async function continueLogin(ctx) {
6481
7222
  try {
6482
7223
  signIn = await pollDeviceLogin(pending.started, { lastPolledAt: null, ...timeout === void 0 ? {} : { until: Date.now() + followTimeoutMs(timeout) } });
6483
7224
  } catch (e) {
6484
- if (e instanceof PublisherError && (e.code === "EXPIRED" || e.code === "FORBIDDEN")) await removePendingLogin();
7225
+ if (e instanceof PublisherError && (e.code === "EXPIRED" || e.code === "FORBIDDEN")) {
7226
+ await removePendingLogin();
7227
+ }
6485
7228
  throw e;
6486
7229
  }
6487
7230
  clock.lap("approval");
@@ -6586,7 +7329,9 @@ async function installSkills({ log }) {
6586
7329
  'Claude Code is not installed, or "claude" is not on PATH. Install it from https://claude.com/claude-code, then run: publisher skills install'
6587
7330
  );
6588
7331
  }
6589
- if (r.code !== 0) throw new PublisherError("LOCAL_STATE", `"claude ${args.slice(0, 3).join(" ")}" failed: ${(r.stderr || r.stdout).trim()}`);
7332
+ if (r.code !== 0) {
7333
+ throw new PublisherError("LOCAL_STATE", `"claude ${args.slice(0, 3).join(" ")}" failed: ${(r.stderr || r.stdout).trim()}`);
7334
+ }
6590
7335
  };
6591
7336
  await step("Adding the publisher to Claude Code\u2026", ["plugin", "marketplace", "add", source, "--scope", "user"]);
6592
7337
  await step("Installing its skills\u2026", ["plugin", "install", `${PLUGIN}@${MARKETPLACE}`, "--scope", "user"]);
@@ -6603,16 +7348,24 @@ async function installSkills({ log }) {
6603
7348
  // src/question.ts
6604
7349
  async function questionAt(p, endpoint, repoId, opts = {}) {
6605
7350
  const at = await portalAt(endpoint, opts);
6606
- if ("reason" in at) return at;
7351
+ if ("reason" in at) {
7352
+ return at;
7353
+ }
6607
7354
  return shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
6608
7355
  }
6609
7356
  async function answerAt(p, endpoint, repoId, specs, opts = {}) {
6610
7357
  const answers = parseSpecs(specs);
6611
7358
  const at = await portalAt(endpoint, opts);
6612
- if ("reason" in at) return at;
7359
+ if ("reason" in at) {
7360
+ return at;
7361
+ }
6613
7362
  const read2 = shown(at.portal, await p.readPreparation({ portal: at.portal, repoId }));
6614
- if (read2.kind !== "view") return read2;
6615
- if (read2.questions.length === 0) throw new PublisherError("LOCAL_STATE", `nothing is waiting for an answer (the preparation is ${read2.preparation.status})`);
7363
+ if (read2.kind !== "view") {
7364
+ return read2;
7365
+ }
7366
+ if (read2.questions.length === 0) {
7367
+ throw new PublisherError("LOCAL_STATE", `nothing is waiting for an answer (the preparation is ${read2.preparation.status})`);
7368
+ }
6616
7369
  for (const [id, value] of Object.entries(answers)) {
6617
7370
  const question = read2.questions.find((q) => q.id === id);
6618
7371
  if (question === void 0) {
@@ -6628,11 +7381,15 @@ async function answerAt(p, endpoint, repoId, specs, opts = {}) {
6628
7381
  return shown(at.portal, await p.answerPreparation({ portal: at.portal, repoId, id: read2.preparation.id, revision: read2.preparation.revision, answers }));
6629
7382
  }
6630
7383
  function parseSpecs(specs) {
6631
- if (specs.length === 0) throw new PublisherError("INVALID_REQUEST", "answer takes one or more question=value");
7384
+ if (specs.length === 0) {
7385
+ throw new PublisherError("INVALID_REQUEST", "answer takes one or more question=value");
7386
+ }
6632
7387
  const answers = {};
6633
7388
  for (const spec of specs) {
6634
7389
  const at = spec.indexOf("=");
6635
- if (at <= 0 || at === spec.length - 1) throw new PublisherError("INVALID_REQUEST", `"${spec}" is not question=value`);
7390
+ if (at <= 0 || at === spec.length - 1) {
7391
+ throw new PublisherError("INVALID_REQUEST", `"${spec}" is not question=value`);
7392
+ }
6636
7393
  answers[spec.slice(0, at)] = spec.slice(at + 1);
6637
7394
  }
6638
7395
  return answers;
@@ -6640,7 +7397,9 @@ function parseSpecs(specs) {
6640
7397
  async function portalAt(endpoint, opts) {
6641
7398
  const derived = portalForEndpoint(endpoint);
6642
7399
  const confirmed = derived === null ? null : await confirmPortal(derived, endpoint, opts.fetch);
6643
- if (derived === null || confirmed === null) return { kind: "unavailable", reason: "no-portal", portal: null, hint: "Answer it in the portal." };
7400
+ if (derived === null || confirmed === null) {
7401
+ return { kind: "unavailable", reason: "no-portal", portal: null, hint: "Answer it in the portal." };
7402
+ }
6644
7403
  if (!confirmed.terminal.includes(PREPARATION)) {
6645
7404
  return { kind: "unavailable", reason: "no-route", portal: derived, hint: `This portal cannot show the question in the terminal yet. Answer it in the portal: ${derived}.` };
6646
7405
  }
@@ -6667,7 +7426,9 @@ function shown(portal, r) {
6667
7426
  }
6668
7427
  }
6669
7428
  function hintFor(view) {
6670
- if (view.questions.length === 0) return "Nothing is waiting for an answer.";
7429
+ if (view.questions.length === 0) {
7430
+ return "Nothing is waiting for an answer.";
7431
+ }
6671
7432
  const how = view.questions.map((q) => `${q.id}=<${q.options.map((o) => o.value).join("|")}>`).join(" ");
6672
7433
  return `It is waiting for your answer. Run: publisher answer ${how}`;
6673
7434
  }
@@ -6679,7 +7440,9 @@ var import_node_os4 = require("node:os");
6679
7440
  var FALLBACK_TEMP_DIRS = ["/tmp", "/var/tmp"];
6680
7441
  async function unusable(path) {
6681
7442
  try {
6682
- if (!(await (0, import_promises12.stat)(path)).isDirectory()) return "is not a directory";
7443
+ if (!(await (0, import_promises12.stat)(path)).isDirectory()) {
7444
+ return "is not a directory";
7445
+ }
6683
7446
  } catch {
6684
7447
  return "does not exist";
6685
7448
  }
@@ -6693,7 +7456,9 @@ async function unusable(path) {
6693
7456
  async function ensureTempDir(env = process.env, fallbacks = FALLBACK_TEMP_DIRS) {
6694
7457
  const named = (0, import_node_os4.tmpdir)();
6695
7458
  const reason = await unusable(named);
6696
- if (reason === null) return { path: named, replaced: null };
7459
+ if (reason === null) {
7460
+ return { path: named, replaced: null };
7461
+ }
6697
7462
  for (const candidate of fallbacks) {
6698
7463
  if (candidate !== named && await unusable(candidate) === null) {
6699
7464
  env["TMPDIR"] = candidate;
@@ -6833,19 +7598,27 @@ async function main(argv) {
6833
7598
  const quiet = flags["quiet"] === true || json;
6834
7599
  const out = (v) => process.stdout.write((json ? JSON.stringify(v) : formatHuman(v)) + "\n");
6835
7600
  const log = (s) => {
6836
- if (!quiet) process.stderr.write(s + "\n");
7601
+ if (!quiet) {
7602
+ process.stderr.write(s + "\n");
7603
+ }
6837
7604
  };
6838
7605
  const progress = (e) => {
6839
- if (quiet) return;
7606
+ if (quiet) {
7607
+ return;
7608
+ }
6840
7609
  const pct = e.total ? ` ${Math.round((e.bytes ?? 0) / e.total * 100)}%` : "";
6841
7610
  process.stderr.write(`\r[${e.phase}] ${e.message}${pct} `);
6842
- if (e.phase === "done") process.stderr.write("\n");
7611
+ if (e.phase === "done") {
7612
+ process.stderr.write("\n");
7613
+ }
6843
7614
  };
6844
7615
  const dirArg = (i) => (0, import_node_path9.resolve)(cmd[i] ?? ".");
6845
7616
  const following = { ...stageEmitter(json, quiet), log, untilChange: flags["until-change"] === true };
6846
7617
  const resolutionInput = async (dir) => {
6847
7618
  let association;
6848
- if (dir && await isGitRepo(dir)) association = (await readAssociation(dir))?.endpoint;
7619
+ if (dir && await isGitRepo(dir)) {
7620
+ association = (await readAssociation(dir))?.endpoint;
7621
+ }
6849
7622
  return {
6850
7623
  ...optStr(flags, "endpoint"),
6851
7624
  ...optStr(flags, "profile"),
@@ -6907,7 +7680,9 @@ async function main(argv) {
6907
7680
  }
6908
7681
  if (flags["help"] !== true && GIT_COMMANDS.has(cmd[0] ?? "")) {
6909
7682
  const line = replacedLine(await ensureTempDir());
6910
- if (line !== null) process.stderr.write(line + "\n");
7683
+ if (line !== null) {
7684
+ process.stderr.write(line + "\n");
7685
+ }
6911
7686
  }
6912
7687
  switch (flags["help"] === true ? "help" : cmd[0]) {
6913
7688
  case void 0:
@@ -6962,7 +7737,9 @@ async function main(argv) {
6962
7737
  case "logout": {
6963
7738
  const profile = str2(flags["profile"]);
6964
7739
  const endpoint = str2(flags["endpoint"]) ?? process.env["GIT_PUBLISHER_ENDPOINT"];
6965
- if (!profile && !endpoint) throw new PublisherError("INVALID_REQUEST", "logout requires --endpoint or --profile");
7740
+ if (!profile && !endpoint) {
7741
+ throw new PublisherError("INVALID_REQUEST", "logout requires --endpoint or --profile");
7742
+ }
6966
7743
  const { revoked } = profile ? await removeProfile(profile, { revoke: true }) : await removeCredential(endpoint, { revoke: true });
6967
7744
  out({ ok: true, ...profile ? { profile } : { endpoint }, revoked });
6968
7745
  return 0;
@@ -6980,14 +7757,18 @@ async function main(argv) {
6980
7757
  return 0;
6981
7758
  case "use": {
6982
7759
  const name = cmd[2];
6983
- if (!name) throw new PublisherError("INVALID_REQUEST", "profile use requires a name");
7760
+ if (!name) {
7761
+ throw new PublisherError("INVALID_REQUEST", "profile use requires a name");
7762
+ }
6984
7763
  await setDefaultProfile(name);
6985
7764
  out({ ok: true, defaultProfile: name, endpoint: file.profiles[name].endpoint });
6986
7765
  return 0;
6987
7766
  }
6988
7767
  case "remove": {
6989
7768
  const name = cmd[2];
6990
- if (!name) throw new PublisherError("INVALID_REQUEST", "profile remove requires a name");
7769
+ if (!name) {
7770
+ throw new PublisherError("INVALID_REQUEST", "profile remove requires a name");
7771
+ }
6991
7772
  await removeProfile(name);
6992
7773
  out({ ok: true, removed: name });
6993
7774
  return 0;
@@ -7026,7 +7807,9 @@ async function main(argv) {
7026
7807
  const updates = refs.length ? refs.map((r) => ({ ref: r.startsWith("refs/") ? r : `refs/heads/${r}`, force })) : void 0;
7027
7808
  if (!updates && force) {
7028
7809
  const b = await currentBranch(dir);
7029
- if (!b) throw new PublisherError("LOCAL_STATE", "detached HEAD");
7810
+ if (!b) {
7811
+ throw new PublisherError("LOCAL_STATE", "detached HEAD");
7812
+ }
7030
7813
  return finishPublish2(await p.publish({ repoDir: dir, updates: [{ ref: b, force: true }], wait: flags["no-wait"] !== true }, { onProgress: progress }));
7031
7814
  }
7032
7815
  return finishPublish2(await p.publish({ repoDir: dir, ...updates ? { updates } : {}, wait: flags["no-wait"] !== true }, { onProgress: progress }));
@@ -7038,7 +7821,9 @@ async function main(argv) {
7038
7821
  if (pubId) {
7039
7822
  const a = await readAssociation(dir);
7040
7823
  const repoId = str2(flags["repo"]) ?? a?.repoId;
7041
- if (!repoId) throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7824
+ if (!repoId) {
7825
+ throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7826
+ }
7042
7827
  const s = await p.getPublicationStatus(repoId, pubId);
7043
7828
  out(s);
7044
7829
  return s.state === "accepted" ? 0 : s.state === "rejected" ? 2 : 3;
@@ -7074,7 +7859,9 @@ async function main(argv) {
7074
7859
  case "checkout": {
7075
7860
  const dir = dirArg(1);
7076
7861
  const repoId = str2(flags["repo"]);
7077
- if (!repoId) throw new PublisherError("INVALID_REQUEST", "checkout requires --repo <id>");
7862
+ if (!repoId) {
7863
+ throw new PublisherError("INVALID_REQUEST", "checkout requires --repo <id>");
7864
+ }
7078
7865
  const { p } = await client();
7079
7866
  const branch = str2(flags["branch"]);
7080
7867
  const m = await p.checkout({ directory: dir, repoId, ...branch ? { branch } : {} }, { onProgress: progress });
@@ -7140,11 +7927,15 @@ async function main(argv) {
7140
7927
  case "follow": {
7141
7928
  const dir = dirArg(1);
7142
7929
  const publicationId = str2(flags["publication"]);
7143
- if (!publicationId) throw new PublisherError("INVALID_REQUEST", "follow requires --publication <id>");
7930
+ if (!publicationId) {
7931
+ throw new PublisherError("INVALID_REQUEST", "follow requires --publication <id>");
7932
+ }
7144
7933
  const timeoutMs = followTimeoutMs(str2(flags["timeout"]));
7145
7934
  const { p, endpoint } = await client(dir);
7146
7935
  const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
7147
- if (!repoId) throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7936
+ if (!repoId) {
7937
+ throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7938
+ }
7148
7939
  const publication = await p.getPublicationStatus(repoId, publicationId);
7149
7940
  if (publication.state !== "accepted") {
7150
7941
  throw new PublisherError("LOCAL_STATE", `publication ${publicationId} is ${publication.state}, not accepted; run publisher status`);
@@ -7164,7 +7955,9 @@ async function main(argv) {
7164
7955
  const dir = dirArg(1);
7165
7956
  const { p, endpoint } = await client(dir);
7166
7957
  const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
7167
- if (!repoId) throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7958
+ if (!repoId) {
7959
+ throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7960
+ }
7168
7961
  out({ repoId, ...await questionAt(p, endpoint, repoId) });
7169
7962
  return 0;
7170
7963
  }
@@ -7173,12 +7966,16 @@ async function main(argv) {
7173
7966
  const dir = (0, import_node_path9.resolve)(".");
7174
7967
  const { p, endpoint } = await client(dir);
7175
7968
  const repoId = str2(flags["repo"]) ?? (await readAssociation(dir))?.repoId;
7176
- if (!repoId) throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7969
+ if (!repoId) {
7970
+ throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
7971
+ }
7177
7972
  out({ repoId, ...await answerAt(p, endpoint, repoId, cmd.slice(1)) });
7178
7973
  return 0;
7179
7974
  }
7180
7975
  case "skills": {
7181
- if (cmd[1] !== "install") throw new PublisherError("INVALID_REQUEST", `unknown skills command: ${cmd[1] ?? "(none)"}; expected install`);
7976
+ if (cmd[1] !== "install") {
7977
+ throw new PublisherError("INVALID_REQUEST", `unknown skills command: ${cmd[1] ?? "(none)"}; expected install`);
7978
+ }
7182
7979
  const r = await installSkills({ log });
7183
7980
  out(r);
7184
7981
  return 0;
@@ -7187,7 +7984,9 @@ async function main(argv) {
7187
7984
  const dir = dirArg(1);
7188
7985
  const ours = str2(flags["ours"]);
7189
7986
  const theirs = str2(flags["theirs"]);
7190
- if (!ours || !theirs) throw new PublisherError("INVALID_REQUEST", "merge-check requires --ours and --theirs");
7987
+ if (!ours || !theirs) {
7988
+ throw new PublisherError("INVALID_REQUEST", "merge-check requires --ours and --theirs");
7989
+ }
7191
7990
  const r = await mergeRemote({
7192
7991
  repoDir: dir,
7193
7992
  ours: ours.startsWith("refs/") ? ours : `refs/heads/${ours}`,
@@ -7203,7 +8002,9 @@ async function main(argv) {
7203
8002
  const { p } = await client(dir);
7204
8003
  const a = await readAssociation(dir);
7205
8004
  const repoId = str2(flags["repo"]) ?? a?.repoId;
7206
- if (!repoId) throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
8005
+ if (!repoId) {
8006
+ throw new PublisherError("LOCAL_STATE", "no repository association; pass --repo");
8007
+ }
7207
8008
  out(await p.getRepository(repoId));
7208
8009
  return 0;
7209
8010
  }
@@ -7215,7 +8016,9 @@ ${USAGE}`);
7215
8016
  }
7216
8017
  var GIT_COMMANDS = /* @__PURE__ */ new Set(["init", "publish", "status", "fetch", "checkout", "workspace-status", "sync", "merge-check"]);
7217
8018
  async function demand(r, input) {
7218
- if (r.kind === "resolved") return r;
8019
+ if (r.kind === "resolved") {
8020
+ return r;
8021
+ }
7219
8022
  const next = nextStep(credentialStatus(input));
7220
8023
  const details = { profiles: r.kind === "none" ? [] : r.profiles, next };
7221
8024
  const run = next.command === null ? "" : ` Run: ${next.command}`;