jj-octo 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +6 -2
  2. package/dist/jj-octo.js +628 -528
  3. package/package.json +3 -3
package/dist/jj-octo.js CHANGED
@@ -925,6 +925,32 @@ class Fail extends ReasonBase {
925
925
  }
926
926
  }
927
927
  var causeFromReasons = (reasons) => new CauseImpl(reasons);
928
+ var dedupeReasons = (self, that) => {
929
+ const buckets = new Map;
930
+ const out = [];
931
+ for (const reason of self.concat(that)) {
932
+ const hash2 = hash(reason);
933
+ const bucket = buckets.get(hash2);
934
+ if (bucket === undefined) {
935
+ buckets.set(hash2, [reason]);
936
+ } else if (bucket.some((previous) => equals(previous, reason))) {
937
+ continue;
938
+ } else {
939
+ bucket.push(reason);
940
+ }
941
+ out.push(reason);
942
+ }
943
+ return out;
944
+ };
945
+ var causeCombine = /* @__PURE__ */ dual(2, (self, that) => {
946
+ if (self.reasons.length === 0) {
947
+ return that;
948
+ } else if (that.reasons.length === 0) {
949
+ return self;
950
+ }
951
+ const newCause = new CauseImpl(dedupeReasons(self.reasons, that.reasons));
952
+ return equals(self, newCause) ? self : newCause;
953
+ });
928
954
  var causeEmpty = /* @__PURE__ */ new CauseImpl([]);
929
955
  var causeFail = (error) => new CauseImpl([new Fail(error)]);
930
956
 
@@ -1039,8 +1065,18 @@ var exitFailCause = /* @__PURE__ */ makeExit({
1039
1065
  annotated = true;
1040
1066
  }
1041
1067
  let cont = fiber.getCont(contE);
1042
- while (fiber.interruptible && fiber._interruptedCause && cont) {
1043
- cont = fiber.getCont(contE);
1068
+ const interruptedCause = fiber._interruptedCause;
1069
+ if (interruptedCause && fiber.interruptible) {
1070
+ let skippedHandler = false;
1071
+ while (cont && fiber.interruptible) {
1072
+ skippedHandler ||= identifier in cont;
1073
+ cont = fiber.getCont(contE);
1074
+ }
1075
+ if (skippedHandler) {
1076
+ cause = causeFromReasons(cause.reasons.filter((reason) => reason._tag !== "Fail"));
1077
+ }
1078
+ cause = causeCombine(cause, interruptedCause);
1079
+ annotated = true;
1044
1080
  }
1045
1081
  return cont ? cont[contE](cause, fiber, annotated ? undefined : this) : fiber.yieldWith(annotated ? exitFailCause(cause) : this);
1046
1082
  }
@@ -2365,32 +2401,6 @@ var causeFilterInterruptors = (self) => {
2365
2401
  return interruptors ? succeed2(interruptors) : fail2(self);
2366
2402
  };
2367
2403
  var hasInterruptsOnly = (self) => self.reasons.length > 0 && self.reasons.every(isInterruptReason);
2368
- var dedupeReasons = (self, that) => {
2369
- const buckets = new Map;
2370
- const out = [];
2371
- for (const reason of self.concat(that)) {
2372
- const hash2 = hash(reason);
2373
- const bucket = buckets.get(hash2);
2374
- if (bucket === undefined) {
2375
- buckets.set(hash2, [reason]);
2376
- } else if (bucket.some((previous) => equals(previous, reason))) {
2377
- continue;
2378
- } else {
2379
- bucket.push(reason);
2380
- }
2381
- out.push(reason);
2382
- }
2383
- return out;
2384
- };
2385
- var causeCombine = /* @__PURE__ */ dual(2, (self, that) => {
2386
- if (self.reasons.length === 0) {
2387
- return that;
2388
- } else if (that.reasons.length === 0) {
2389
- return self;
2390
- }
2391
- const newCause = new CauseImpl(dedupeReasons(self.reasons, that.reasons));
2392
- return equals(self, newCause) ? self : newCause;
2393
- });
2394
2404
  var causeMap = /* @__PURE__ */ dual(2, (self, f) => {
2395
2405
  let hasFail = false;
2396
2406
  const failures = self.reasons.map((failure) => {
@@ -2577,6 +2587,14 @@ var fiberVariance = {
2577
2587
  var fiberIdStore = {
2578
2588
  id: 0
2579
2589
  };
2590
+ var AsyncResource = /* @__PURE__ */ (() => {
2591
+ try {
2592
+ return globalThis.process?.getBuiltinModule?.("node:async_hooks")?.AsyncResource;
2593
+ } catch {
2594
+ return;
2595
+ }
2596
+ })();
2597
+ var captureAsyncContext = () => AsyncResource === undefined ? undefined : new AsyncResource("effect/Fiber");
2580
2598
  var getCurrentFiber = () => globalThis[currentFiberTypeId];
2581
2599
 
2582
2600
  class FiberImpl {
@@ -2594,6 +2612,7 @@ class FiberImpl {
2594
2612
  this._running = false;
2595
2613
  this._deferredInterrupt = false;
2596
2614
  this._parent = undefined;
2615
+ this._asyncContext = undefined;
2597
2616
  this.cache.runtimeMetrics?.recordFiberStart(this.context);
2598
2617
  }
2599
2618
  get [FiberTypeId]() {
@@ -2651,6 +2670,10 @@ class FiberImpl {
2651
2670
  evaluate(effect) {
2652
2671
  if (this._exit) {
2653
2672
  return;
2673
+ } else if (this._asyncContext !== undefined) {
2674
+ const asyncContext = this._asyncContext;
2675
+ this._asyncContext = undefined;
2676
+ return asyncContext.runInAsyncScope(this.evaluate, this, effect);
2654
2677
  } else if (this._yielded !== undefined) {
2655
2678
  const yielded = this._yielded;
2656
2679
  this._yielded = undefined;
@@ -2658,6 +2681,7 @@ class FiberImpl {
2658
2681
  }
2659
2682
  const exit = this.runLoop(effect);
2660
2683
  if (exit === Yield) {
2684
+ this._asyncContext = captureAsyncContext();
2661
2685
  return;
2662
2686
  }
2663
2687
  const interruptChildren = fiberMiddleware.interruptChildren && fiberMiddleware.interruptChildren(this);
@@ -2850,6 +2874,16 @@ var fiberAwaitAll = (self) => callback((resume) => {
2850
2874
  loop();
2851
2875
  return sync(() => cancel?.());
2852
2876
  });
2877
+ var fiberJoin = (self) => {
2878
+ const impl = self;
2879
+ if (impl._exit)
2880
+ return impl._exit;
2881
+ return callback((resume) => {
2882
+ if (impl._exit)
2883
+ return resume(impl._exit);
2884
+ return sync(self.addObserver(resume));
2885
+ });
2886
+ };
2853
2887
  var fiberInterrupt = (self) => withFiber((fiber) => fiberInterruptAs(self, fiber.id));
2854
2888
  var fiberInterruptAs = /* @__PURE__ */ dual((args) => hasProperty(args[0], FiberTypeId), (self, fiberId, annotations) => withFiber((parent) => {
2855
2889
  let ann = fiberStackAnnotations(parent);
@@ -2902,7 +2936,6 @@ var yieldNowWith = /* @__PURE__ */ makePrimitive({
2902
2936
  }
2903
2937
  });
2904
2938
  var yieldNow = /* @__PURE__ */ yieldNowWith(0);
2905
- var succeedNone = /* @__PURE__ */ succeed3(/* @__PURE__ */ none2());
2906
2939
  var failCauseSync = (evaluate) => suspend(() => failCause(evaluate()));
2907
2940
  var die = (defect) => exitDie(defect);
2908
2941
  var failSync = (error) => suspend(() => fail3(error()));
@@ -2925,17 +2958,17 @@ var tryPromise = (options) => {
2925
2958
  const f = typeof options === "function" ? options : options.try;
2926
2959
  const catcher = typeof options === "function" ? (cause) => new UnknownError(cause, "An error occurred in Effect.tryPromise") : options.catch;
2927
2960
  return callbackOptions(function(resume, signal) {
2928
- const failWithCatch = (cause) => {
2961
+ const failWithCatch = (cause) => suspend(() => {
2929
2962
  try {
2930
- resume(fail3(internalCall(() => catcher(cause))));
2963
+ return fail3(internalCall(() => catcher(cause)));
2931
2964
  } catch (err) {
2932
- resume(die(err));
2965
+ return die(err);
2933
2966
  }
2934
- };
2967
+ });
2935
2968
  try {
2936
- f(signal).then((a) => resume(succeed3(a)), failWithCatch);
2969
+ f(signal).then((a) => resume(succeed3(a)), (e) => resume(failWithCatch(e)));
2937
2970
  } catch (err) {
2938
- failWithCatch(err);
2971
+ resume(failWithCatch(err));
2939
2972
  }
2940
2973
  }, f.length !== 0);
2941
2974
  };
@@ -2992,7 +3025,7 @@ var asyncFinalizer = /* @__PURE__ */ makePrimitive({
2992
3025
  }
2993
3026
  },
2994
3027
  [contE](cause, _fiber) {
2995
- return hasInterrupts(cause) ? flatMap2(this[args](), () => failCause(cause)) : failCause(cause);
3028
+ return hasInterrupts(cause) ? flatMap2(combineFinalizerCause(exitFailCause(cause), this[args]()), () => failCause(cause)) : failCause(cause);
2996
3029
  }
2997
3030
  });
2998
3031
  var callback = (register) => callbackOptions(register, register.length >= 2);
@@ -3137,39 +3170,37 @@ var tapCont = function(value) {
3137
3170
  var tapEffectCont = function(value) {
3138
3171
  return new ContImpl(this.payload, succeedPayload, value);
3139
3172
  };
3140
- var asSome = (self) => map5(self, some2);
3141
3173
  var andThen = /* @__PURE__ */ dual(2, (self, f) => new ContImpl(self, isEffect(f) ? returnPayload : andThenCont, f));
3142
3174
  var tap = /* @__PURE__ */ dual(2, (self, f) => new ContImpl(self, isEffect(f) ? tapEffectCont : tapCont, f));
3143
3175
  var asVoid = (self) => new ContImpl(self, returnPayload, exitVoid);
3144
- var raceAllFirst = (all, options) => withFiber((parent) => callback((resume) => {
3145
- let done = false;
3176
+ var raceAllFirst = (all, options) => withFiber((parent) => {
3146
3177
  const fibers = new Set;
3147
- const onExit = (exit) => {
3148
- done = true;
3149
- resume(fibers.size === 0 ? exit : flatMap2(uninterruptible(fiberInterruptAll(fibers)), () => exit));
3150
- };
3151
- let i = 0;
3152
- for (const effect of all) {
3153
- if (done)
3154
- break;
3155
- const index = i++;
3156
- const fiber = forkUnsafe(parent, effect, true, true, false);
3157
- fibers.add(fiber);
3158
- fiber.addObserver((exit) => {
3159
- fibers.delete(fiber);
3160
- const isWinner = !done;
3161
- onExit(exit);
3162
- if (isWinner && options?.onWinner) {
3163
- options.onWinner({
3164
- fiber,
3165
- index,
3166
- parentFiber: parent
3167
- });
3168
- }
3169
- });
3170
- }
3171
- return fiberInterruptAll(fibers);
3172
- }));
3178
+ onExitUnsafe(parent, () => fibers.size === 0 ? undefined : fiberInterruptAll(fibers));
3179
+ return callback((resume) => {
3180
+ let done = false;
3181
+ let i = 0;
3182
+ for (const effect of all) {
3183
+ if (done)
3184
+ break;
3185
+ const index = i++;
3186
+ const fiber = forkUnsafe(parent, effect, true, true, false);
3187
+ fibers.add(fiber);
3188
+ fiber.addObserver((exit) => {
3189
+ fibers.delete(fiber);
3190
+ const isWinner = !done;
3191
+ done = true;
3192
+ resume(exit);
3193
+ if (isWinner && options?.onWinner) {
3194
+ options.onWinner({
3195
+ fiber,
3196
+ index,
3197
+ parentFiber: parent
3198
+ });
3199
+ }
3200
+ });
3201
+ }
3202
+ });
3203
+ });
3173
3204
  var raceFirst = /* @__PURE__ */ dual((args) => isEffect(args[1]), (self, that, options) => raceAllFirst([self, that], options));
3174
3205
  var flatMap2 = /* @__PURE__ */ dual(2, (self, f) => new ContImpl(self, andThenCont, f));
3175
3206
  var effectIsExit = (effect) => effect[ExitTypeId] !== undefined;
@@ -3198,6 +3229,7 @@ var exitInterrupt = (fiberId) => exitFailCause(causeInterrupt(fiberId));
3198
3229
  var exitIsSuccess = (self) => self._tag === "Success";
3199
3230
  var exitIsFailure = (self) => self._tag === "Failure";
3200
3231
  var exitFilterCause = (self) => self._tag === "Failure" ? succeed2(self.cause) : fail2(self);
3232
+ var exitHasInterrupts = (self) => self._tag === "Failure" && hasInterrupts(self.cause);
3201
3233
  var exitVoid = /* @__PURE__ */ exitSucceed(undefined);
3202
3234
  var exitMap = /* @__PURE__ */ dual(2, (self, f) => self._tag === "Success" ? exitSucceed(f(self.value)) : self);
3203
3235
  var exitMapError = /* @__PURE__ */ dual(2, (self, f) => {
@@ -3609,24 +3641,50 @@ var onErrorFilter = /* @__PURE__ */ dual(3, (self, filter, f) => onExit(self, (e
3609
3641
  return isFailure2(result) ? void_ : f(result.success, exit.cause);
3610
3642
  }));
3611
3643
  var onInterrupt = /* @__PURE__ */ dual(2, (self, finalizer) => onErrorFilter(causeFilterInterruptors, finalizer)(self));
3612
- var cached = (self) => sync(() => {
3613
- const latch = makeLatchUnsafe(false);
3614
- let started = false;
3644
+ var infiniteTTL = /* @__PURE__ */ constant(Infinity);
3645
+ var makeCachedUnsafe = (self, ttlMillis) => {
3646
+ let expiresAt = 0;
3615
3647
  let exit;
3616
- const wait = flatMap2(latch.await, () => exit);
3617
- return withFiber((fiber) => {
3618
- if (exit !== undefined)
3648
+ let current;
3649
+ const join = (fiber, run) => {
3650
+ run.awaiters++;
3651
+ onExitUnsafe(fiber, () => {
3652
+ if (--run.awaiters > 0 || current !== run)
3653
+ return;
3654
+ current = undefined;
3655
+ return fiberInterrupt(run.fiber);
3656
+ });
3657
+ return fiberJoin(run.fiber);
3658
+ };
3659
+ return [withFiber((fiber) => {
3660
+ if (current !== undefined)
3661
+ return join(fiber, current);
3662
+ if (exit !== undefined && (expiresAt === Infinity || fiber.getRef(ClockRef).currentTimeMillisUnsafe() < expiresAt)) {
3619
3663
  return exit;
3620
- if (started)
3621
- return wait;
3622
- started = true;
3623
- onExitUnsafe(fiber, (result) => sync(() => {
3624
- exit = result;
3625
- latch.openUnsafe();
3626
- }));
3627
- return self;
3628
- });
3629
- });
3664
+ }
3665
+ exit = undefined;
3666
+ const clock = fiber.getRef(ClockRef);
3667
+ const run = {
3668
+ fiber: undefined,
3669
+ awaiters: 0
3670
+ };
3671
+ current = run;
3672
+ run.fiber = forkUnsafe(fiber, onExitPrimitive(self, (exit_) => {
3673
+ if (current !== run)
3674
+ return;
3675
+ current = undefined;
3676
+ if (exitHasInterrupts(exit_))
3677
+ return;
3678
+ const duration = ttlMillis(exit_);
3679
+ expiresAt = duration === Infinity ? Infinity : clock.currentTimeMillisUnsafe() + duration;
3680
+ exit = exit_;
3681
+ }), true, true);
3682
+ return run.fiber._exit ?? join(fiber, run);
3683
+ }), sync(() => {
3684
+ exit = undefined;
3685
+ })];
3686
+ };
3687
+ var cached = (self) => sync(() => makeCachedUnsafe(self, infiniteTTL)[0]);
3630
3688
  var interrupt = /* @__PURE__ */ withFiber((fiber) => failCause(causeInterrupt(fiber.id)));
3631
3689
  var uninterruptible = (self) => withFiber((fiber) => {
3632
3690
  if (!fiber.interruptible)
@@ -3769,15 +3827,13 @@ var iterateConcurrentImpl = (options) => {
3769
3827
  let parentFiber;
3770
3828
  let fibers;
3771
3829
  let resume;
3772
- let interrupted = false;
3773
3830
  let terminal;
3774
3831
  let effect;
3775
3832
  const failDefect = (error) => {
3776
3833
  const defect = exitDie(error);
3777
3834
  terminal = defect;
3778
3835
  done = true;
3779
- interrupted = true;
3780
- return fibers && fibers.size > 0 ? flatMap2(uninterruptible(fiberInterruptAll(Array.from(fibers))), () => defect) : defect;
3836
+ return fibers && fibers.size > 0 ? flatMap2(uninterruptible(fiberInterruptAll(Array.from(fibers))), () => terminal ?? defect) : defect;
3781
3837
  };
3782
3838
  const go = () => {
3783
3839
  let paused = false;
@@ -3803,9 +3859,8 @@ var iterateConcurrentImpl = (options) => {
3803
3859
  if (result)
3804
3860
  return cb(result);
3805
3861
  return suspend(() => {
3806
- terminal = exitVoid;
3807
- interrupted = true;
3808
- return fibers ? fiberInterruptAll(fibers) : void_;
3862
+ terminal ??= exitVoid;
3863
+ return flatMap2(fibers ? fiberInterruptAll(fibers) : void_, () => terminal?._tag === "Failure" ? terminal : void_);
3809
3864
  });
3810
3865
  });
3811
3866
  } else {
@@ -3823,21 +3878,17 @@ var iterateConcurrentImpl = (options) => {
3823
3878
  fibers.delete(fiber);
3824
3879
  try {
3825
3880
  if (terminal) {
3826
- if (!interrupted && exit._tag === "Failure") {
3827
- for (const reason of exit.cause.reasons) {
3828
- if (reason._tag === "Interrupt")
3829
- continue;
3830
- else if (terminal._tag === "Failure") {
3831
- terminal.cause.reasons.push(reason);
3832
- } else {
3833
- terminal = exitFailCause(causeFromReasons([reason]));
3834
- }
3881
+ if (exit._tag === "Failure") {
3882
+ const reasons = exit.cause.reasons.filter((reason) => reason._tag !== "Interrupt");
3883
+ if (reasons.length > 0) {
3884
+ const cause = causeFromReasons(reasons);
3885
+ terminal = exitFailCause(terminal._tag === "Failure" ? causeCombine(terminal.cause, cause) : cause);
3835
3886
  }
3836
3887
  }
3837
3888
  } else {
3838
3889
  const result = step(state, item, exit, currentIndex);
3839
3890
  if (result) {
3840
- terminal = result._tag === "Failure" ? exitFailCause(causeFromReasons(result.cause.reasons.slice())) : result;
3891
+ terminal = result;
3841
3892
  go();
3842
3893
  }
3843
3894
  }
@@ -3900,6 +3951,7 @@ var forkUnsafe = (parent, effect, immediate = false, daemon = false, uninterrupt
3900
3951
  if (immediate) {
3901
3952
  child.evaluate(effect);
3902
3953
  } else {
3954
+ child._asyncContext = captureAsyncContext();
3903
3955
  parentRuntime.currentDispatcher.scheduleTask(() => child.evaluate(effect), 0);
3904
3956
  }
3905
3957
  if (!daemon && !child._exit) {
@@ -5179,7 +5231,9 @@ var systemError = (options) => new PlatformError(new SystemError(options));
5179
5231
  var badArgument = (options) => new PlatformError(new BadArgument(options));
5180
5232
 
5181
5233
  // node_modules/effect/dist/Fiber.js
5234
+ var join = fiberJoin;
5182
5235
  var interrupt3 = fiberInterrupt;
5236
+ var getCurrent = getCurrentFiber;
5183
5237
  var runIn = fiberRunIn;
5184
5238
 
5185
5239
  // node_modules/effect/dist/Latch.js
@@ -5429,8 +5483,10 @@ var releaseTakers = (self) => {
5429
5483
  return;
5430
5484
  }
5431
5485
  for (const taker of self.state.takers) {
5486
+ if (!taker.ready())
5487
+ continue;
5432
5488
  self.state.takers.delete(taker);
5433
- taker(exitVoid);
5489
+ taker.resume(exitVoid);
5434
5490
  if (self.messages.length === 0) {
5435
5491
  break;
5436
5492
  }
@@ -5464,10 +5520,14 @@ var awaitTake = (self, ready) => callback((resume) => {
5464
5520
  return resume(self.state.exit);
5465
5521
  if (ready())
5466
5522
  return resume(exitVoid);
5467
- self.state.takers.add(resume);
5523
+ const taker = {
5524
+ ready,
5525
+ resume
5526
+ };
5527
+ self.state.takers.add(taker);
5468
5528
  return sync(() => {
5469
5529
  if (self.state._tag !== "Done")
5470
- self.state.takers.delete(resume);
5530
+ self.state.takers.delete(taker);
5471
5531
  });
5472
5532
  });
5473
5533
  var offerOrWait = (self, message) => callback((resume) => offerUnsafe(self, message) ? resume(exitTrue) : waitToOffer(self, {
@@ -5545,7 +5605,7 @@ var finalize = (self, exit) => {
5545
5605
  exit
5546
5606
  };
5547
5607
  for (const taker of openState.takers) {
5548
- taker(exit);
5608
+ taker.resume(exit);
5549
5609
  }
5550
5610
  openState.takers.clear();
5551
5611
  for (const awaiter of openState.awaiters) {
@@ -5554,109 +5614,6 @@ var finalize = (self, exit) => {
5554
5614
  openState.awaiters.clear();
5555
5615
  };
5556
5616
 
5557
- // node_modules/effect/dist/Semaphore.js
5558
- var makeUnsafe5 = (permits) => new SemaphoreImpl(permits);
5559
- var waitForPermits = (self, n, effect) => callback((resume) => {
5560
- if (self.free >= n)
5561
- return resume(effect);
5562
- const observer = () => {
5563
- if (self.free < n)
5564
- return;
5565
- self.waiters.delete(observer);
5566
- resume(effect);
5567
- };
5568
- self.waiters.add(observer);
5569
- return sync(() => {
5570
- self.waiters.delete(observer);
5571
- });
5572
- });
5573
-
5574
- class SemaphoreImpl {
5575
- waiters = /* @__PURE__ */ new Set;
5576
- taken = 0;
5577
- permits;
5578
- constructor(permits) {
5579
- this.permits = permits;
5580
- }
5581
- get free() {
5582
- return this.permits - this.taken;
5583
- }
5584
- take(n) {
5585
- const take = suspend(() => {
5586
- if (this.free < n) {
5587
- return waitForPermits(this, n, take);
5588
- }
5589
- this.taken += n;
5590
- return succeed3(n);
5591
- });
5592
- return take;
5593
- }
5594
- takeIfAvailable(n) {
5595
- return suspend(() => {
5596
- if (this.free < n)
5597
- return succeed3(false);
5598
- this.taken += n;
5599
- return succeed3(true);
5600
- });
5601
- }
5602
- releaseUnsafe(fiber, n) {
5603
- this.taken -= n;
5604
- if (this.waiters.size > 0) {
5605
- fiber.currentDispatcher.scheduleTask(() => {
5606
- for (const observer of this.waiters) {
5607
- if (this.free <= 0)
5608
- break;
5609
- observer();
5610
- }
5611
- }, 0);
5612
- }
5613
- return this.free;
5614
- }
5615
- resize(permits) {
5616
- return withFiber((fiber) => {
5617
- this.permits = permits;
5618
- if (this.free < 0)
5619
- return void_;
5620
- this.releaseUnsafe(fiber, 0);
5621
- return void_;
5622
- });
5623
- }
5624
- release(n) {
5625
- return withFiber((fiber) => succeed3(this.releaseUnsafe(fiber, n)));
5626
- }
5627
- get releaseAll() {
5628
- return withFiber((fiber) => succeed3(this.releaseUnsafe(fiber, this.taken)));
5629
- }
5630
- withPermits(n) {
5631
- return (self) => uninterruptibleMask((restore) => {
5632
- const acquire = suspend(() => {
5633
- if (this.free < n) {
5634
- const wait = waitForPermits(this, n, void_);
5635
- return flatMap2(restore(wait), () => acquire);
5636
- }
5637
- this.taken += n;
5638
- return onExitPrimitive(restore(self), () => {
5639
- this.releaseUnsafe(getCurrentFiber(), n);
5640
- return;
5641
- }, true);
5642
- });
5643
- return acquire;
5644
- });
5645
- }
5646
- withPermit = /* @__PURE__ */ this.withPermits(1);
5647
- withPermitsIfAvailable(n) {
5648
- return (self) => uninterruptibleMask((restore) => {
5649
- if (this.free < n)
5650
- return succeedNone;
5651
- this.taken += n;
5652
- return onExitPrimitive(restore(asSome(self)), () => {
5653
- this.releaseUnsafe(getCurrentFiber(), n);
5654
- return;
5655
- }, true);
5656
- });
5657
- }
5658
- }
5659
-
5660
5617
  // node_modules/effect/dist/Channel.js
5661
5618
  var TypeId11 = "~effect/Channel";
5662
5619
  var isChannel = (u) => hasProperty(u, TypeId11);
@@ -5941,7 +5898,6 @@ class RcRefImpl {
5941
5898
  return pipeArguments(this, arguments);
5942
5899
  }
5943
5900
  state = stateEmpty;
5944
- semaphore = /* @__PURE__ */ makeUnsafe5(1);
5945
5901
  acquire;
5946
5902
  context;
5947
5903
  scope;
@@ -5958,9 +5914,13 @@ var make10 = (options) => withFiber2((fiber) => {
5958
5914
  const scope = get(context, Scope);
5959
5915
  const ref = new RcRefImpl(options.acquire, context, scope, options.idleTimeToLive !== undefined ? fromInputUnsafe(options.idleTimeToLive) : undefined);
5960
5916
  return as2(addFinalizerExit(scope, () => {
5961
- const close2 = ref.state._tag === "Acquired" ? close(ref.state.scope, void_2) : void_3;
5917
+ const state = ref.state;
5962
5918
  ref.state = stateClosed;
5963
- return close2;
5919
+ if (state._tag === "Acquired")
5920
+ return close(state.scope, void_2);
5921
+ if (state._tag === "Acquiring")
5922
+ return interrupt3(state.fiber);
5923
+ return void_3;
5964
5924
  }), ref);
5965
5925
  });
5966
5926
  var getState = (self, restore) => {
@@ -5973,31 +5933,57 @@ var getState = (self, restore) => {
5973
5933
  state.refCount++;
5974
5934
  return state.fiber ? as2(interrupt3(state.fiber), state) : succeed6(state);
5975
5935
  }
5936
+ case "Acquiring": {
5937
+ const state = self.state;
5938
+ state.awaiters++;
5939
+ return awaitAcquiring(self, state, restore);
5940
+ }
5976
5941
  case "Empty": {
5977
- return self.semaphore.withPermit(suspend2(() => {
5978
- if (self.state._tag !== "Empty") {
5979
- return getState(self, restore);
5942
+ const acquiring = {
5943
+ _tag: "Acquiring",
5944
+ fiber: undefined,
5945
+ awaiters: 1,
5946
+ acquired: undefined
5947
+ };
5948
+ self.state = acquiring;
5949
+ const scope = makeUnsafe3();
5950
+ const acquire = provideContext2(self.acquire, add(self.context, Scope, scope)).pipe(flatMap4((value) => {
5951
+ if (self.state !== acquiring) {
5952
+ return interrupt2;
5980
5953
  }
5981
- const scope = makeUnsafe3();
5982
- return restore(provideContext2(self.acquire, add(self.context, Scope, scope))).pipe(flatMap4((value) => {
5983
- if (self.state._tag === "Closed") {
5984
- return interrupt2;
5985
- }
5986
- const state = {
5987
- _tag: "Acquired",
5988
- value,
5989
- scope,
5990
- fiber: undefined,
5991
- refCount: 1,
5992
- invalidated: false
5993
- };
5994
- self.state = state;
5995
- return succeed6(state);
5996
- }), onExit2((exit) => isFailure3(exit) ? close(scope, exit) : void_3));
5954
+ const state = {
5955
+ _tag: "Acquired",
5956
+ value,
5957
+ scope,
5958
+ fiber: undefined,
5959
+ refCount: acquiring.awaiters,
5960
+ invalidated: false
5961
+ };
5962
+ self.state = state;
5963
+ acquiring.acquired = state;
5964
+ return succeed6(state);
5965
+ }), onExit2((exit) => {
5966
+ if (isSuccess3(exit))
5967
+ return void_3;
5968
+ if (self.state === acquiring)
5969
+ self.state = stateEmpty;
5970
+ return close(scope, exit);
5997
5971
  }));
5972
+ acquiring.fiber = runForkWith2(getCurrent().context)(acquire);
5973
+ return awaitAcquiring(self, acquiring, restore);
5998
5974
  }
5999
5975
  }
6000
5976
  };
5977
+ var awaitAcquiring = (self, acquiring, restore) => onExit2(restore(join(acquiring.fiber)), (exit) => {
5978
+ const acquired = acquiring.acquired;
5979
+ if (acquired !== undefined) {
5980
+ return isFailure3(exit) ? release(self, acquired) : void_3;
5981
+ }
5982
+ if (--acquiring.awaiters > 0 || self.state !== acquiring)
5983
+ return void_3;
5984
+ self.state = stateEmpty;
5985
+ return interrupt3(acquiring.fiber);
5986
+ });
6001
5987
  var get2 = (self_) => {
6002
5988
  const self = self_;
6003
5989
  return uninterruptibleMask2((restore) => flatMap4(scope2, (scope) => flatMap4(getState(self, restore), (state) => as2(addFinalizerExit(scope, () => release(self, state)), state.value))));
@@ -6671,13 +6657,9 @@ var posixImpl = /* @__PURE__ */ Path.of({
6671
6657
  function resolve2(ast) {
6672
6658
  return ast.checks ? ast.checks[ast.checks.length - 1].annotations : ast.annotations;
6673
6659
  }
6674
- function resolveAt(key) {
6675
- return (ast) => resolve2(ast)?.[key];
6676
- }
6677
6660
  var STRUCTURAL_ANNOTATION_KEY = "~structural";
6678
6661
  var SENTINELS_ANNOTATION_KEY = "~sentinels";
6679
6662
  var CONSTRUCTOR_ANNOTATION_KEY = "~constructor";
6680
- var resolveBrands = /* @__PURE__ */ resolveAt("brands");
6681
6663
  var getExpected = /* @__PURE__ */ memoize((ast) => {
6682
6664
  const identifier = resolve2(ast)?.identifier;
6683
6665
  if (typeof identifier === "string")
@@ -8103,7 +8085,7 @@ var candidateIndexCache = /* @__PURE__ */ new WeakMap;
8103
8085
  var emptyCandidates = /* @__PURE__ */ Object.freeze([]);
8104
8086
  var getRuntimeType = (input) => input === null ? "null" : Array.isArray(input) ? "array" : typeof input;
8105
8087
  var hasPropertySignature = (input, key) => key === "__proto__" ? Object.hasOwn(input, key) : (key in input);
8106
- function getIndex(types) {
8088
+ function getCandidateIndex(types) {
8107
8089
  let index = candidateIndexCache.get(types);
8108
8090
  if (index)
8109
8091
  return index;
@@ -8112,6 +8094,7 @@ function getIndex(types) {
8112
8094
  let otherwise;
8113
8095
  let literalCandidates;
8114
8096
  let onlyLiterals = true;
8097
+ const literalOf = [];
8115
8098
  for (let i = 0;i < types.length; i++) {
8116
8099
  const a = types[i];
8117
8100
  const encoded = toCandidate(a);
@@ -8120,10 +8103,11 @@ function getIndex(types) {
8120
8103
  if (isLiteral(encoded) || isUniqueSymbol(encoded)) {
8121
8104
  literalCandidates ??= new Map;
8122
8105
  const literal = isLiteral(encoded) ? encoded.literal : encoded.symbol;
8106
+ literalOf[i] = literal;
8123
8107
  let arr = literalCandidates.get(literal);
8124
8108
  if (!arr)
8125
8109
  literalCandidates.set(literal, arr = []);
8126
- arr.push(a);
8110
+ arr.push(i);
8127
8111
  } else {
8128
8112
  onlyLiterals = false;
8129
8113
  }
@@ -8152,23 +8136,24 @@ function getIndex(types) {
8152
8136
  }
8153
8137
  }
8154
8138
  const fallbacks = {};
8155
- const getFallback = (type) => fallbacks[type] ??= Object.freeze((otherwise?.[type] ?? emptyCandidates).map((i) => types[i]));
8139
+ const getFallback = (type) => fallbacks[type] ??= Object.freeze(otherwise?.[type] ?? emptyCandidates);
8156
8140
  if (onlyLiterals && literalCandidates) {
8157
8141
  literalCandidates.forEach(Object.freeze);
8158
8142
  index = (input) => literalCandidates.get(input) ?? emptyCandidates;
8159
8143
  } else if (bySentinel?.size === 1 && !otherwise) {
8160
8144
  const [key, [byValue]] = bySentinel.entries().next().value;
8161
- const candidates = byValue;
8145
+ const candidates = new Map;
8162
8146
  for (const [literal, indexes] of byValue) {
8163
- candidates.set(literal, Object.freeze(Array.from(indexes, (index) => types[index])));
8147
+ candidates.set(literal, Object.freeze(Array.from(indexes)));
8164
8148
  }
8149
+ const all = Object.freeze(types.map((_, i) => i));
8165
8150
  index = (input, isConstructor) => {
8166
8151
  if (isObjectKeyword(input)) {
8167
8152
  const value = hasPropertySignature(input, key) ? input[key] : undefined;
8168
8153
  if (value !== undefined)
8169
8154
  return candidates.get(value) ?? emptyCandidates;
8170
8155
  if (isConstructor)
8171
- return types;
8156
+ return all;
8172
8157
  }
8173
8158
  return emptyCandidates;
8174
8159
  };
@@ -8227,26 +8212,17 @@ function getIndex(types) {
8227
8212
  }
8228
8213
  }
8229
8214
  }
8230
- return Array.from(selected).sort((a, b) => a - b).map((i) => types[i]);
8215
+ return Array.from(selected).sort((a, b) => a - b);
8231
8216
  };
8232
8217
  } else {
8233
8218
  index = (input) => {
8234
8219
  const fallback = getFallback(getRuntimeType(input));
8235
- return literalCandidates ? fallback.filter(filterLiterals(input)) : fallback;
8220
+ return literalCandidates ? fallback.filter((i) => literalOf[i] === undefined || literalOf[i] === input) : fallback;
8236
8221
  };
8237
8222
  }
8238
8223
  candidateIndexCache.set(types, index);
8239
8224
  return index;
8240
8225
  }
8241
- function filterLiterals(input) {
8242
- return (ast) => {
8243
- const encoded = toCandidate(ast);
8244
- return encoded._tag === "Literal" ? encoded.literal === input : encoded._tag === "UniqueSymbol" ? encoded.symbol === input : true;
8245
- };
8246
- }
8247
- function getCandidates(input, types, isConstructor = false) {
8248
- return getIndex(types)(input, isConstructor);
8249
- }
8250
8226
  var Union = class extends ASTNodeImpl {
8251
8227
  _tag = "Union";
8252
8228
  types;
@@ -8260,42 +8236,25 @@ var Union = class extends ASTNodeImpl {
8260
8236
  }
8261
8237
  getParser(compile, compileField) {
8262
8238
  const ast = this;
8239
+ const isConstructor = compileField !== undefined;
8240
+ const parsers = [];
8241
+ const parser = (i) => parsers[i] ??= compile(ast.types[i]);
8242
+ let index;
8263
8243
  return (input, options) => {
8264
8244
  if (input === missing) {
8265
8245
  return missingExit;
8266
8246
  }
8267
- const candidates = getCandidates(input, ast.types, compileField !== undefined);
8247
+ const candidates = (index ??= getCandidateIndex(ast.types))(input, isConstructor);
8268
8248
  if (candidates.length === 0) {
8269
8249
  return fail6(new AnyOf(ast, [], input, options));
8270
8250
  }
8271
8251
  if (candidates.length === 1) {
8272
- const result = compile(candidates[0])(input, options);
8252
+ const result = parser(candidates[0])(input, options);
8273
8253
  if (result._tag === "Success")
8274
8254
  return result;
8275
- return effectIsExit(result) ? failSingleUnionCandidate(ast, result.cause, input, options) : catchCause2(result, (cause) => failSingleUnionCandidate(ast, cause, input, options));
8255
+ return effectIsExit(result) ? failSingleUnionCandidate(ast, result.cause, input, options) : catchSingleUnionCandidate(ast, result, input, options);
8276
8256
  }
8277
- const state = {
8278
- ast,
8279
- compile,
8280
- input,
8281
- out: undefined,
8282
- successes: ast.options?.mode === "oneOf" ? [] : undefined,
8283
- issues: undefined,
8284
- options
8285
- };
8286
- const eff = parseUnion(state, candidates);
8287
- if (!eff) {
8288
- if (state.out)
8289
- return state.out;
8290
- return fail6(new AnyOf(ast, state.issues ?? [], input, options));
8291
- }
8292
- return flatMapEager2(eff, (_) => {
8293
- if (state.out === sameExit)
8294
- return succeed6(input);
8295
- if (state.out)
8296
- return state.out;
8297
- return fail6(new AnyOf(ast, state.issues ?? [], input, options));
8298
- });
8257
+ return parseUnionCandidates(ast, parser, candidates, input, options);
8299
8258
  };
8300
8259
  }
8301
8260
  _rebuild(recur, checks, encodingChecks) {
@@ -8351,12 +8310,41 @@ function failSingleUnionCandidate(ast, cause, input, options) {
8351
8310
  return failCause2(cause);
8352
8311
  return fail5(new AnyOf(ast, [issue], input, options));
8353
8312
  }
8313
+ function catchSingleUnionCandidate(ast, result, input, options) {
8314
+ return catchCause2(result, (cause) => failSingleUnionCandidate(ast, cause, input, options));
8315
+ }
8316
+ function parseUnionCandidates(ast, parser, candidates, input, options) {
8317
+ const state = {
8318
+ ast,
8319
+ parser,
8320
+ input,
8321
+ out: undefined,
8322
+ successes: ast.options?.mode === "oneOf" ? [] : undefined,
8323
+ issues: undefined,
8324
+ options
8325
+ };
8326
+ const eff = parseUnion(state, candidates);
8327
+ if (!eff) {
8328
+ if (state.out)
8329
+ return state.out;
8330
+ return fail6(new AnyOf(ast, state.issues ?? [], input, options));
8331
+ }
8332
+ return resumeUnion(eff, state);
8333
+ }
8334
+ function resumeUnion(eff, state) {
8335
+ return flatMapEager2(eff, (_) => {
8336
+ if (state.out === sameExit)
8337
+ return succeed6(state.input);
8338
+ if (state.out)
8339
+ return state.out;
8340
+ return fail6(new AnyOf(state.ast, state.issues ?? [], state.input, state.options));
8341
+ });
8342
+ }
8354
8343
  var parseUnion = /* @__PURE__ */ iterateEager()({
8355
- onItem(s, ast) {
8356
- const parser = s.compile(ast);
8357
- return parser(s.input, s.options);
8344
+ onItem(s, i) {
8345
+ return s.parser(i)(s.input, s.options);
8358
8346
  },
8359
- step(s, candidate, exit) {
8347
+ step(s, i, exit) {
8360
8348
  if (exit._tag === "Failure") {
8361
8349
  const issue = getSchemaIssue(exit.cause);
8362
8350
  if (issue === undefined) {
@@ -8368,12 +8356,12 @@ var parseUnion = /* @__PURE__ */ iterateEager()({
8368
8356
  s.issues = [issue];
8369
8357
  } else {
8370
8358
  if (s.out && s.successes) {
8371
- s.successes.push(candidate);
8359
+ s.successes.push(s.ast.types[i]);
8372
8360
  return fail5(new OneOf(s.ast, s.successes, s.input, s.options));
8373
8361
  }
8374
8362
  s.out = exit;
8375
8363
  if (s.successes) {
8376
- s.successes.push(candidate);
8364
+ s.successes.push(s.ast.types[i]);
8377
8365
  } else {
8378
8366
  return void_2;
8379
8367
  }
@@ -8476,33 +8464,42 @@ function isPattern(regExp, annotations) {
8476
8464
  ...annotations
8477
8465
  });
8478
8466
  }
8479
- function copy(ast) {
8480
- return Object.assign(Object.create(Object.getPrototypeOf(ast)), ast);
8467
+ var bodyOwners = /* @__PURE__ */ new WeakMap;
8468
+ function copy(ast, changes) {
8469
+ const out = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, changes);
8470
+ if (Reflect.ownKeys(changes).every((key) => key === "context" || key === "encoding")) {
8471
+ bodyOwners.set(out, getContextOwner(ast));
8472
+ }
8473
+ return out;
8481
8474
  }
8482
- var contextOwners = /* @__PURE__ */ new WeakMap;
8483
8475
  function getContextOwner(ast) {
8484
- return contextOwners.get(ast) ?? ast;
8476
+ const existing = bodyOwners.get(ast);
8477
+ if (existing !== undefined)
8478
+ return existing;
8479
+ if (ast.encoding === undefined)
8480
+ return ast;
8481
+ const owner = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, {
8482
+ encoding: undefined
8483
+ });
8484
+ bodyOwners.set(ast, owner);
8485
+ return owner;
8485
8486
  }
8486
8487
  function replaceEncoding(ast, encoding) {
8487
- if (ast.encoding === encoding) {
8488
- return ast;
8489
- }
8490
- const out = copy(ast);
8491
- out.encoding = encoding;
8492
- return out;
8488
+ return ast.encoding === encoding ? ast : copy(ast, {
8489
+ encoding
8490
+ });
8493
8491
  }
8494
8492
  function replaceContext(ast, context) {
8495
8493
  if (ast.context === context) {
8496
8494
  return ast;
8497
8495
  }
8498
8496
  const owner = getContextOwner(ast);
8499
- if (owner.context === context) {
8497
+ if (owner.context === context && owner.encoding === ast.encoding) {
8500
8498
  return owner;
8501
8499
  }
8502
- const out = copy(ast);
8503
- out.context = context;
8504
- contextOwners.set(out, owner);
8505
- return out;
8500
+ return copy(ast, {
8501
+ context
8502
+ });
8506
8503
  }
8507
8504
  function getLastEncoding(ast) {
8508
8505
  return ast.encoding ? getLastEncoding(ast.encoding[ast.encoding.length - 1].to) : ast;
@@ -8512,12 +8509,12 @@ function annotate(ast, annotations) {
8512
8509
  const last = ast.checks[ast.checks.length - 1];
8513
8510
  return replaceChecks(ast, append(ast.checks.slice(0, -1), last.annotate(annotations)));
8514
8511
  }
8515
- const out = copy(ast);
8516
- out.annotations = {
8517
- ...ast.annotations,
8518
- ...annotations
8519
- };
8520
- return out;
8512
+ return copy(ast, {
8513
+ annotations: {
8514
+ ...ast.annotations,
8515
+ ...annotations
8516
+ }
8517
+ });
8521
8518
  }
8522
8519
  function replaceChecks(ast, checks) {
8523
8520
  if (ast._tag === "Suspend" && checks) {
@@ -8526,9 +8523,9 @@ function replaceChecks(ast, checks) {
8526
8523
  if (ast.checks === checks) {
8527
8524
  return ast;
8528
8525
  }
8529
- const out = copy(ast);
8530
- out.checks = checks;
8531
- return out;
8526
+ return copy(ast, {
8527
+ checks
8528
+ });
8532
8529
  }
8533
8530
  function appendChecks(ast, checks) {
8534
8531
  return replaceChecks(ast, combineChecks(ast.checks, checks));
@@ -8569,13 +8566,6 @@ function appendTransformation(from, transformation, to) {
8569
8566
  const link = new Link(from, transformation);
8570
8567
  return replaceEncoding(to, to.encoding ? [...to.encoding, link] : [link]);
8571
8568
  }
8572
- function brand(ast, brand) {
8573
- const existing = resolveBrands(ast);
8574
- const brands = existing ? [...existing, brand] : [brand];
8575
- return annotate(ast, {
8576
- brands
8577
- });
8578
- }
8579
8569
  function mapOrSame(as, f) {
8580
8570
  let out;
8581
8571
  for (let i = 0;i < as.length; i++) {
@@ -8628,19 +8618,33 @@ function extractStructuralChecks(checks) {
8628
8618
  const out = checks.flatMap(extract);
8629
8619
  return isArrayNonEmpty2(out) ? out : undefined;
8630
8620
  }
8621
+ function canPreserveEncodingChecks(ast) {
8622
+ let preserve = true;
8623
+ function visit(child) {
8624
+ preserve = preserve && !child.encoding && !isSuspend(child);
8625
+ if (preserve && "recur" in child)
8626
+ child.recur(visit);
8627
+ return child;
8628
+ }
8629
+ if ("recur" in ast)
8630
+ ast.recur(visit);
8631
+ return preserve;
8632
+ }
8631
8633
  var toType = /* @__PURE__ */ memoizeIdempotent((ast) => {
8632
- const out = ast;
8633
- const type = out.recur?.(toType) ?? out;
8634
- const encodingChecks = type.encodingChecks;
8635
- if (encodingChecks) {
8636
- const checks = type === ast ? encodingChecks : isArrays(type) || isObjects(type) || isDeclaration(type) && type.typeParameters.length > 0 ? extractStructuralChecks(encodingChecks) : undefined;
8637
- const copyOfType = copy(type);
8638
- copyOfType.encoding = undefined;
8639
- copyOfType.encodingChecks = undefined;
8640
- copyOfType.checks = combineChecks(type.checks, checks);
8641
- return copyOfType;
8642
- }
8643
- return type.encoding ? replaceEncoding(type, undefined) : type;
8634
+ const owner = getContextOwner(ast);
8635
+ if (owner !== ast) {
8636
+ const type = toType(owner);
8637
+ return type === owner && ast.encoding === undefined ? ast : replaceContext(type, ast.context);
8638
+ }
8639
+ const type = "recur" in ast ? ast.recur(toType) : ast;
8640
+ if ("encodingChecks" in type && type.encodingChecks) {
8641
+ const checks = canPreserveEncodingChecks(ast) ? type.encodingChecks : isArrays(type) || isObjects(type) || isDeclaration(type) && type.typeParameters.length > 0 ? extractStructuralChecks(type.encodingChecks) : undefined;
8642
+ return copy(type, {
8643
+ encodingChecks: undefined,
8644
+ checks: combineChecks(type.checks, checks)
8645
+ });
8646
+ }
8647
+ return type;
8644
8648
  });
8645
8649
  var toEncoded = /* @__PURE__ */ memoizeIdempotent((ast) => {
8646
8650
  return toType(flip2(ast));
@@ -8664,8 +8668,12 @@ var flip2 = /* @__PURE__ */ memoize((ast) => {
8664
8668
  if (ast.encoding) {
8665
8669
  return flipEncoding(ast, ast.encoding);
8666
8670
  }
8667
- const out = ast;
8668
- return out.flip?.(flip2) ?? out.recur?.(flip2) ?? out;
8671
+ const owner = getContextOwner(ast);
8672
+ if (owner !== ast) {
8673
+ const flipped = flip2(owner);
8674
+ return flipped === owner ? ast : replaceContext(flipped, ast.context);
8675
+ }
8676
+ return "flip" in ast ? ast.flip(flip2) : ("recur" in ast) ? ast.recur(flip2) : ast;
8669
8677
  });
8670
8678
  function containsUndefined(ast) {
8671
8679
  switch (ast._tag) {
@@ -9548,7 +9556,7 @@ var make15 = /* @__PURE__ */ gen2(function* () {
9548
9556
  if (exited) {
9549
9557
  const [code] = yield* _await(exitSignal);
9550
9558
  if (code !== 0 && isNotNull(code)) {
9551
- yield* ignore2(killProcessGroup(cmd, childProcess, cmd.options.killSignal ?? "SIGTERM"));
9559
+ yield* ignore2(terminateProcessGroup(cmd, childProcess, exitSignal, cmd.options));
9552
9560
  } else if (isReferenced && process.platform !== "win32" && cmd.options.detached !== false) {
9553
9561
  yield* ignore2(terminateProcessGroup(cmd, childProcess, exitSignal, cmd.options));
9554
9562
  }
@@ -11302,19 +11310,7 @@ function validateCanonicalObjectPropertyNames(ast) {
11302
11310
  }
11303
11311
  function makeReorder(getPriority) {
11304
11312
  return (types) => {
11305
- const indexMap = new Map;
11306
- for (let i = 0;i < types.length; i++) {
11307
- indexMap.set(toEncoded(types[i]), i);
11308
- }
11309
- const sortedTypes = [...types].sort((a, b) => {
11310
- a = toEncoded(a);
11311
- b = toEncoded(b);
11312
- const pa = getPriority(a);
11313
- const pb = getPriority(b);
11314
- if (pa !== pb)
11315
- return pa - pb;
11316
- return indexMap.get(a) - indexMap.get(b);
11317
- });
11313
+ const sortedTypes = [...types].sort((a, b) => getPriority(toEncoded(a)) - getPriority(toEncoded(b)));
11318
11314
  const orderChanged = sortedTypes.some((ast, index) => ast !== types[index]);
11319
11315
  if (!orderChanged)
11320
11316
  return types;
@@ -11597,8 +11593,8 @@ function Literals(literals) {
11597
11593
  }
11598
11594
  var NullOr = /* @__PURE__ */ lambda((self) => Union2([self, Null2]));
11599
11595
  var UndefinedOr = /* @__PURE__ */ lambda((self) => Union2([self, Undefined2]));
11600
- function brand2(identifier) {
11601
- return (schema) => make24(brand(schema.ast, identifier), {
11596
+ function brand(identifier) {
11597
+ return (schema) => make24(schema.ast, {
11602
11598
  schema,
11603
11599
  identifier
11604
11600
  });
@@ -18116,7 +18112,7 @@ var layer14 = /* @__PURE__ */ layerMergedContext(/* @__PURE__ */ succeed6(fetch)
18116
18112
  // package.json
18117
18113
  var package_default = {
18118
18114
  name: "jj-octo",
18119
- version: "0.1.0",
18115
+ version: "0.2.1",
18120
18116
  description: "A jj subcommand for working with GitHub that supports stacking",
18121
18117
  license: "MIT",
18122
18118
  repository: {
@@ -18146,11 +18142,11 @@ var package_default = {
18146
18142
  test: "bun test ./src"
18147
18143
  },
18148
18144
  devDependencies: {
18149
- "@effect/platform-bun": "4.0.0-rc.118",
18145
+ "@effect/platform-bun": "4.0.1",
18150
18146
  "@octokit/openapi-types": "29.0.1",
18151
18147
  "@tsconfig/strictest": "2.0.8",
18152
18148
  "@types/bun": "1.4.2",
18153
- effect: "4.0.0-rc.118",
18149
+ effect: "4.0.1",
18154
18150
  oxfmt: "0.71.0",
18155
18151
  oxlint: "1.86.0",
18156
18152
  "oxlint-tsgolint": "7.0.2003",
@@ -18169,123 +18165,67 @@ var withHelp = (summary, ...paragraphs) => (command) => command.pipe(withShortDe
18169
18165
 
18170
18166
  `)));
18171
18167
  var revisionFlag = (description) => String8("revision").pipe(withAlias2("r"), withDefault5("@"), withDescription2(`${description}, the working copy by default`));
18172
- // src/clients/github/openapi.ts
18173
- var responseOf = () => (schema) => schema;
18174
- var bodyOf = (body) => bodyJsonUnsafe(body);
18168
+ // src/clients/git.ts
18169
+ class GitFailed extends TaggedError2("GitFailed") {
18170
+ }
18175
18171
 
18176
- // src/clients/github/types.ts
18177
- var PullRequestNumberSchema = Number6.pipe(brand2("PullRequestNumber"));
18178
- var StackNumberSchema = Number6.pipe(brand2("StackNumber"));
18172
+ class Git extends Service()("jj-octo/Git") {
18173
+ }
18174
+ var gitLayer = ({ cwd, env } = {}) => effect(Git, gen2(function* () {
18175
+ const spawner = yield* ChildProcessSpawner;
18176
+ const fs = yield* FileSystem;
18177
+ const path = yield* Path;
18178
+ return {
18179
+ mergeText: ({ base, local, remote }) => scoped2(gen2(function* () {
18180
+ const directory = yield* fs.makeTempDirectoryScoped();
18181
+ const written = { base: `${base}
18182
+ `, github: `${remote}
18183
+ `, local: `${local}
18184
+ ` };
18185
+ yield* all2(Object.entries(written).map(([name, text]) => fs.writeFileString(path.join(directory, name), text)));
18186
+ const handle = yield* spawner.spawn(make14("git", [
18187
+ "merge-file",
18188
+ "-p",
18189
+ "-L",
18190
+ "local",
18191
+ "-L",
18192
+ "base",
18193
+ "-L",
18194
+ "GitHub",
18195
+ "local",
18196
+ "base",
18197
+ "github"
18198
+ ], { cwd: directory }));
18199
+ const [text, stderr, code] = yield* all2([
18200
+ mkString(decodeText(handle.stdout)),
18201
+ mkString(decodeText(handle.stderr)),
18202
+ handle.exitCode
18203
+ ], { concurrency: "unbounded" });
18204
+ if (code > 127) {
18205
+ return yield* new GitFailed({ message: `git merge-file failed: ${stderr.trim()}` });
18206
+ }
18207
+ return { conflicted: code > 0, text: text.trimEnd() };
18208
+ })).pipe(catchTag2("PlatformError", (error) => fail6(new GitFailed({ message: `git merge-file failed: ${error.message}` })))),
18209
+ read: (args) => scoped2(gen2(function* () {
18210
+ const handle = yield* spawner.spawn(make14("git", args, { cwd, env, extendEnv: true }));
18211
+ const [stdout, stderr, code] = yield* all2([
18212
+ mkString(decodeText(handle.stdout)),
18213
+ mkString(decodeText(handle.stderr)),
18214
+ handle.exitCode
18215
+ ], { concurrency: "unbounded" });
18216
+ return code === 0 ? stdout : yield* new GitFailed({
18217
+ message: `git ${args.join(" ")} failed${stderr.trim() === "" ? "" : `: ${stderr.trim()}`}`
18218
+ });
18219
+ })).pipe(catchTag2("PlatformError", (error) => fail6(new GitFailed({ message: `git ${args.join(" ")} failed: ${error.message}` }))))
18220
+ };
18221
+ }));
18222
+ var GitLive = gitLayer();
18179
18223
 
18180
- // src/clients/github/client.ts
18181
- class GitHubFailed extends TaggedError2("GitHubFailed") {
18224
+ // src/clients/jj.ts
18225
+ class JjFailed extends TaggedError2("JjFailed") {
18182
18226
  }
18183
18227
 
18184
- class GitHub extends Service()("jj-octo/GitHub") {
18185
- }
18186
- var CreatedPullRequest = responseOf()(Struct({ number: PullRequestNumberSchema }));
18187
- var CreatedStack = responseOf()(Struct({ number: StackNumberSchema }));
18188
- var PullRequests = responseOf()(ArraySchema(Struct({
18189
- base: Struct({ ref: String4 }),
18190
- body: NullOr(String4),
18191
- draft: optionalKey2(Boolean2),
18192
- number: PullRequestNumberSchema,
18193
- title: String4
18194
- })));
18195
- var StackedPullRequest = Struct({
18196
- number: PullRequestNumberSchema,
18197
- state: Literals(["closed", "open"])
18198
- });
18199
- var Stacks = responseOf()(ArraySchema(Struct({ number: StackNumberSchema, pull_requests: ArraySchema(StackedPullRequest) })));
18200
- var RepositoryDetails = responseOf()(Struct({ default_branch: String4 }));
18201
- var MergeRequest = Struct({
18202
- details: Struct({
18203
- message: String4,
18204
- uuid: optionalKey2(String4)
18205
- }),
18206
- status: Literals(["enqueued", "failed", "merged", "pending"])
18207
- });
18208
- var StartedMerge = responseOf()(MergeRequest);
18209
- var MergeResult = responseOf()(MergeRequest);
18210
- var token = gen2(function* () {
18211
- const fromEnvironment = yield* option2(Redacted2("GH_TOKEN").pipe(orElse(() => Redacted2("GITHUB_TOKEN"))));
18212
- if (isSome2(fromEnvironment)) {
18213
- return fromEnvironment.value;
18214
- }
18215
- const spawner = yield* ChildProcessSpawner;
18216
- const stored = (yield* spawner.string(make14("gh", ["auth", "token"]))).trim();
18217
- return stored === "" ? yield* fail6("no token") : make19(stored);
18218
- }).pipe(mapError2(() => new GitHubFailed({ message: "no GitHub token: set GH_TOKEN, or run `gh auth login`" })));
18219
- var GitHubLive = (repository) => {
18220
- const repo = (path) => `/repos/${repository.owner}/${repository.name}${path}`;
18221
- return effect(GitHub, gen2(function* () {
18222
- const bearer = yield* token;
18223
- const client = (yield* HttpClient).pipe(mapRequest((request) => request.pipe(prependUrl("https://api.github.com"), acceptJson, bearerToken(bearer), setHeaders({
18224
- "user-agent": "jj-octo",
18225
- "x-github-api-version": "2022-11-28"
18226
- }))));
18227
- const send = (request) => client.execute(request).pipe(flatMap4((response) => response.status < 400 ? succeed6(response) : response.text.pipe(orElseSucceed2(() => ""), flatMap4((text) => fail6(new GitHubFailed({
18228
- message: `${request.method} ${request.url} returned ${response.status}: ${text}`
18229
- }))))), catchTag2("HttpClientError", (error) => fail6(new GitHubFailed({ message: error.message }))));
18230
- const json = (schema) => (request) => send(request).pipe(flatMap4(schemaBodyJson(schema)), catchTags2({
18231
- HttpClientError: (error) => fail6(new GitHubFailed({ message: error.message })),
18232
- SchemaError: (error) => fail6(new GitHubFailed({
18233
- message: `unexpected response to ${request.url}: ${error.message}`
18234
- }))
18235
- }));
18236
- const stackOf = (pullRequest) => json(Stacks)(get4(repo(`/stacks?pull_request=${pullRequest}`))).pipe(map7((stacks) => map(fromNullishOr(stacks[0]), (stack) => ({
18237
- number: stack.number,
18238
- open: stack.pull_requests.filter(({ state }) => state === "open").map(({ number }) => number)
18239
- }))));
18240
- const mergeRequest = (path) => json(MergeResult)(get4(repo(path)));
18241
- return {
18242
- addToStack: (stack, pullRequests) => send(post(repo(`/stacks/${stack}/add`)).pipe(bodyOf({ pull_requests: [...pullRequests] }))).pipe(asVoid2),
18243
- createPullRequest: ({ base, body, draft, head, title }) => json(CreatedPullRequest)(post(repo("/pulls")).pipe(bodyOf({ base, body, draft, head, title }))).pipe(map7(({ number }) => number)),
18244
- createStack: (pullRequests) => json(CreatedStack)(post(repo("/stacks")).pipe(bodyOf({ pull_requests: [...pullRequests] }))).pipe(map7(({ number }) => number)),
18245
- defaultBranch: json(RepositoryDetails)(get4(repo(""))).pipe(map7(({ default_branch }) => default_branch)),
18246
- mergePullRequest: (pullRequest) => send(put(repo(`/pulls/${pullRequest}/merge`)).pipe(bodyOf({ merge_method: "squash" }))).pipe(asVoid2),
18247
- mergeStack: (pullRequest) => gen2(function* () {
18248
- const started = yield* json(StartedMerge)(put(repo(`/pulls/${pullRequest}/merge-async`)).pipe(bodyOf({
18249
- merge_action: "default",
18250
- merge_method: "squash"
18251
- })));
18252
- const { uuid } = started.details;
18253
- if (uuid === undefined) {
18254
- return yield* new GitHubFailed({
18255
- message: `merging #${pullRequest} returned no request to follow`
18256
- });
18257
- }
18258
- const settled = yield* mergeRequest(`/pulls/${pullRequest}/merge-async/${uuid}`).pipe(repeat2({
18259
- schedule: spaced("5 seconds"),
18260
- until: ({ status }) => status !== "pending"
18261
- }));
18262
- if (settled.status === "merged" || settled.status === "enqueued") {
18263
- return settled.status;
18264
- }
18265
- return yield* new GitHubFailed({
18266
- message: `merging #${pullRequest} ended ${settled.status}: ${settled.details.message}`
18267
- });
18268
- }),
18269
- openPullRequest: (branch) => json(PullRequests)(get4(repo(`/pulls?state=open&head=${repository.owner}:${encodeURIComponent(branch)}`))).pipe(map7((found) => map(fromNullishOr(found[0]), ({ base, body, draft, number, title }) => ({
18270
- base: base.ref,
18271
- body: body ?? "",
18272
- draft: draft ?? false,
18273
- number,
18274
- title
18275
- })))),
18276
- setBase: (pullRequest, base) => send(patch(repo(`/pulls/${pullRequest}`)).pipe(bodyOf({ base }))).pipe(asVoid2),
18277
- setDescription: (pullRequest, { body, title }) => send(patch(repo(`/pulls/${pullRequest}`)).pipe(bodyOf({ body, title }))).pipe(asVoid2),
18278
- stackOf,
18279
- unstack: (stack) => send(post(repo(`/stacks/${stack}/unstack`))).pipe(asVoid2)
18280
- };
18281
- }));
18282
- };
18283
-
18284
- // src/clients/jj.ts
18285
- class JjFailed extends TaggedError2("JjFailed") {
18286
- }
18287
-
18288
- class Jj extends Service()("jj-octo/Jj") {
18228
+ class Jj extends Service()("jj-octo/Jj") {
18289
18229
  }
18290
18230
  var failed = (args, detail) => new JjFailed({
18291
18231
  message: detail.trim() === "" ? `jj ${args.join(" ")} failed` : detail.trim().replace(/^Error: /u, "")
@@ -18308,27 +18248,6 @@ var jjLayer = ({ cwd, env, quiet = false } = {}) => effect(Jj, gen2(function* ()
18308
18248
  }));
18309
18249
  var JjLive = jjLayer();
18310
18250
 
18311
- // src/clients/github/repository.ts
18312
- var repositoryFromUrl = (url) => {
18313
- const match = /github\.com[:/](?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?\/?$/u.exec(url.trim());
18314
- const owner = match?.groups?.["owner"];
18315
- const name = match?.groups?.["name"];
18316
- return owner === undefined || name === undefined ? none2() : some2({ name, owner });
18317
- };
18318
-
18319
- // src/clients/github/origin.ts
18320
- var GitHubFromOrigin = unwrap(gen2(function* () {
18321
- const jj = yield* Jj;
18322
- const remotes = yield* jj.read(["git", "remote", "list"]);
18323
- const origin = remotes.split(`
18324
- `).find((line) => line.startsWith("origin "));
18325
- const repository = repositoryFromUrl(origin?.slice("origin ".length) ?? "");
18326
- if (isNone2(repository)) {
18327
- return yield* new GitHubFailed({ message: "the `origin` remote isn't a GitHub repository" });
18328
- }
18329
- return GitHubLive(repository.value);
18330
- }));
18331
-
18332
18251
  // src/cli/gh/stack/changes.ts
18333
18252
  class NotAStack extends TaggedError2("NotAStack") {
18334
18253
  }
@@ -18442,6 +18361,221 @@ var chain = (revset, revision) => gen2(function* () {
18442
18361
  var stackChanges = (revision) => chain(stackRevset(revision), revision);
18443
18362
  var downstackChanges = (revision) => chain(downstackRevset(revision), revision);
18444
18363
 
18364
+ // src/cli/gh/stack/trunk.ts
18365
+ class TrunkMismatch extends TaggedError2("TrunkMismatch") {
18366
+ }
18367
+ var trunkRevset = (branch) => `${JSON.stringify(branch)}@origin`;
18368
+ var checkTrunk = (branch) => gen2(function* () {
18369
+ if ((yield* commitsAt(`trunk() & present(${trunkRevset(branch)})`)).length === 0) {
18370
+ yield* new TrunkMismatch({
18371
+ message: `trunk() isn't ${branch}@origin, GitHub's default branch; run \`jj gh init\``
18372
+ });
18373
+ }
18374
+ });
18375
+
18376
+ // src/cli/gh/init.ts
18377
+ var colocate = gen2(function* () {
18378
+ const fs = yield* FileSystem;
18379
+ const git = yield* Git;
18380
+ const jj = yield* Jj;
18381
+ const resolve = (output) => fs.realPath(output.trim()).pipe(orElseSucceed2(() => ""));
18382
+ const root = yield* resolve(yield* git.read(["rev-parse", "--show-toplevel"]));
18383
+ const jjRoot = yield* jj.read(["root"]).pipe(flatMap4(resolve), orElseSucceed2(() => ""));
18384
+ if (jjRoot !== root) {
18385
+ yield* jj.run(["git", "init", "--colocate", root]);
18386
+ }
18387
+ });
18388
+ var copyIdentity = gen2(function* () {
18389
+ const git = yield* Git;
18390
+ const jj = yield* Jj;
18391
+ for (const key of ["user.name", "user.email"]) {
18392
+ const own = (yield* jj.read(["config", "get", key])).trim();
18393
+ const value = (yield* git.read(["config", key]).pipe(orElseSucceed2(() => ""))).trim();
18394
+ if (own === "" && value !== "") {
18395
+ yield* jj.run(["config", "set", "--repo", key, value]);
18396
+ }
18397
+ }
18398
+ });
18399
+ var defaultBranch = gen2(function* () {
18400
+ const git = yield* Git;
18401
+ const output = yield* git.read(["ls-remote", "--symref", "origin", "HEAD"]);
18402
+ const branch = /^ref: refs\/heads\/(?<branch>\S+)\tHEAD$/mu.exec(output)?.groups?.["branch"];
18403
+ if (branch === undefined) {
18404
+ return yield* new GitFailed({ message: "origin names no default branch" });
18405
+ }
18406
+ return branch;
18407
+ });
18408
+ var setTrunk = gen2(function* () {
18409
+ const jj = yield* Jj;
18410
+ const branch = yield* defaultBranch;
18411
+ if ((yield* commitsAt(`present(${trunkRevset(branch)})`)).length === 0) {
18412
+ yield* jj.run(["git", "fetch", "--remote", "origin", "--branch", `exact:${branch}`]);
18413
+ }
18414
+ if ((yield* bookmarkTargets).get(branch)?.tracked !== true) {
18415
+ yield* jj.run(["bookmark", "track", `exact:${branch}`, "--remote", "origin"]);
18416
+ }
18417
+ yield* jj.run([
18418
+ "config",
18419
+ "set",
18420
+ "--repo",
18421
+ 'revset-aliases."trunk()"',
18422
+ JSON.stringify(trunkRevset(branch))
18423
+ ]);
18424
+ return branch;
18425
+ });
18426
+ var init = gen2(function* () {
18427
+ const jj = yield* Jj;
18428
+ yield* colocate;
18429
+ yield* copyIdentity;
18430
+ const anonymous = 'mutable() & (author_name(exact:"") | author_email(exact:""))';
18431
+ if ((yield* commitsAt(anonymous)).length > 0) {
18432
+ yield* jj.run(["metaedit", "--quiet", "--update-author", "-r", anonymous]);
18433
+ }
18434
+ const branch = yield* setTrunk;
18435
+ yield* jj.run([
18436
+ "config",
18437
+ "set",
18438
+ "--repo",
18439
+ "aliases.gh",
18440
+ JSON.stringify(["util", "exec", "--", "jj-octo"])
18441
+ ]);
18442
+ yield* error(`set up jj, with trunk() as ${branch}@origin`);
18443
+ });
18444
+ var initCommand = make30("init", {}, () => asUserError(init)).pipe(withHelp("Set up a git checkout for the other commands", "Makes it a jj repository colocated with git, unless it already is one, and copies git's name and email into the repository's jj config where jj has none.", "Reads the default branch from `origin`, fetches and tracks it, and sets `trunk()` to it in the repository's config. jj's own `trunk()` guesses from branch names, so without this a default branch it doesn't guess would be the base of pull requests but not of local rebases. `submit`, `merge` and `sync` check that the two agree.", "Sets the `jj gh` alias to run `jj-octo` from `PATH`, in the repository's config. Set it again afterwards to run another build.", "Needs no GitHub token. Running it again changes nothing that is already set up. Run it again after the default branch changes on GitHub."));
18445
+
18446
+ // src/clients/github/openapi.ts
18447
+ var responseOf = () => (schema) => schema;
18448
+ var bodyOf = (body) => bodyJsonUnsafe(body);
18449
+
18450
+ // src/clients/github/types.ts
18451
+ var PullRequestNumberSchema = Number6.pipe(brand("PullRequestNumber"));
18452
+ var StackNumberSchema = Number6.pipe(brand("StackNumber"));
18453
+
18454
+ // src/clients/github/client.ts
18455
+ class GitHubFailed extends TaggedError2("GitHubFailed") {
18456
+ }
18457
+
18458
+ class GitHub extends Service()("jj-octo/GitHub") {
18459
+ }
18460
+ var CreatedPullRequest = responseOf()(Struct({ number: PullRequestNumberSchema }));
18461
+ var CreatedStack = responseOf()(Struct({ number: StackNumberSchema }));
18462
+ var PullRequests = responseOf()(ArraySchema(Struct({
18463
+ base: Struct({ ref: String4 }),
18464
+ body: NullOr(String4),
18465
+ draft: optionalKey2(Boolean2),
18466
+ number: PullRequestNumberSchema,
18467
+ title: String4
18468
+ })));
18469
+ var StackedPullRequest = Struct({
18470
+ number: PullRequestNumberSchema,
18471
+ state: Literals(["closed", "open"])
18472
+ });
18473
+ var Stacks = responseOf()(ArraySchema(Struct({ number: StackNumberSchema, pull_requests: ArraySchema(StackedPullRequest) })));
18474
+ var RepositoryDetails = responseOf()(Struct({ default_branch: String4 }));
18475
+ var MergeRequest = Struct({
18476
+ details: Struct({
18477
+ message: String4,
18478
+ uuid: optionalKey2(String4)
18479
+ }),
18480
+ status: Literals(["enqueued", "failed", "merged", "pending"])
18481
+ });
18482
+ var StartedMerge = responseOf()(MergeRequest);
18483
+ var MergeResult = responseOf()(MergeRequest);
18484
+ var token = gen2(function* () {
18485
+ const fromEnvironment = yield* option2(Redacted2("GH_TOKEN").pipe(orElse(() => Redacted2("GITHUB_TOKEN"))));
18486
+ if (isSome2(fromEnvironment)) {
18487
+ return fromEnvironment.value;
18488
+ }
18489
+ const spawner = yield* ChildProcessSpawner;
18490
+ const stored = (yield* spawner.string(make14("gh", ["auth", "token"]))).trim();
18491
+ return stored === "" ? yield* fail6("no token") : make19(stored);
18492
+ }).pipe(mapError2(() => new GitHubFailed({ message: "no GitHub token: set GH_TOKEN, or run `gh auth login`" })));
18493
+ var GitHubLive = (repository) => {
18494
+ const repo = (path) => `/repos/${repository.owner}/${repository.name}${path}`;
18495
+ return effect(GitHub, gen2(function* () {
18496
+ const bearer = yield* token;
18497
+ const client = (yield* HttpClient).pipe(mapRequest((request) => request.pipe(prependUrl("https://api.github.com"), acceptJson, bearerToken(bearer), setHeaders({
18498
+ "user-agent": "jj-octo",
18499
+ "x-github-api-version": "2022-11-28"
18500
+ }))));
18501
+ const send = (request) => client.execute(request).pipe(flatMap4((response) => response.status < 400 ? succeed6(response) : response.text.pipe(orElseSucceed2(() => ""), flatMap4((text) => fail6(new GitHubFailed({
18502
+ message: `${request.method} ${request.url} returned ${response.status}: ${text}`
18503
+ }))))), catchTag2("HttpClientError", (error) => fail6(new GitHubFailed({ message: error.message }))));
18504
+ const json = (schema) => (request) => send(request).pipe(flatMap4(schemaBodyJson(schema)), catchTags2({
18505
+ HttpClientError: (error) => fail6(new GitHubFailed({ message: error.message })),
18506
+ SchemaError: (error) => fail6(new GitHubFailed({
18507
+ message: `unexpected response to ${request.url}: ${error.message}`
18508
+ }))
18509
+ }));
18510
+ const stackOf = (pullRequest) => json(Stacks)(get4(repo(`/stacks?pull_request=${pullRequest}`))).pipe(map7((stacks) => map(fromNullishOr(stacks[0]), (stack) => ({
18511
+ number: stack.number,
18512
+ open: stack.pull_requests.filter(({ state }) => state === "open").map(({ number }) => number)
18513
+ }))));
18514
+ const mergeRequest = (path) => json(MergeResult)(get4(repo(path)));
18515
+ return {
18516
+ addToStack: (stack, pullRequests) => send(post(repo(`/stacks/${stack}/add`)).pipe(bodyOf({ pull_requests: [...pullRequests] }))).pipe(asVoid2),
18517
+ createPullRequest: ({ base, body, draft, head, title }) => json(CreatedPullRequest)(post(repo("/pulls")).pipe(bodyOf({ base, body, draft, head, title }))).pipe(map7(({ number }) => number)),
18518
+ createStack: (pullRequests) => json(CreatedStack)(post(repo("/stacks")).pipe(bodyOf({ pull_requests: [...pullRequests] }))).pipe(map7(({ number }) => number)),
18519
+ defaultBranch: json(RepositoryDetails)(get4(repo(""))).pipe(map7(({ default_branch }) => default_branch)),
18520
+ mergePullRequest: (pullRequest) => send(put(repo(`/pulls/${pullRequest}/merge`)).pipe(bodyOf({ merge_method: "squash" }))).pipe(asVoid2),
18521
+ mergeStack: (pullRequest) => gen2(function* () {
18522
+ const started = yield* json(StartedMerge)(put(repo(`/pulls/${pullRequest}/merge-async`)).pipe(bodyOf({
18523
+ merge_action: "default",
18524
+ merge_method: "squash"
18525
+ })));
18526
+ const { uuid } = started.details;
18527
+ if (uuid === undefined) {
18528
+ return yield* new GitHubFailed({
18529
+ message: `merging #${pullRequest} returned no request to follow`
18530
+ });
18531
+ }
18532
+ const settled = yield* mergeRequest(`/pulls/${pullRequest}/merge-async/${uuid}`).pipe(repeat2({
18533
+ schedule: spaced("5 seconds"),
18534
+ until: ({ status }) => status !== "pending"
18535
+ }));
18536
+ if (settled.status === "merged" || settled.status === "enqueued") {
18537
+ return settled.status;
18538
+ }
18539
+ return yield* new GitHubFailed({
18540
+ message: `merging #${pullRequest} ended ${settled.status}: ${settled.details.message}`
18541
+ });
18542
+ }),
18543
+ openPullRequest: (branch) => json(PullRequests)(get4(repo(`/pulls?state=open&head=${repository.owner}:${encodeURIComponent(branch)}`))).pipe(map7((found) => map(fromNullishOr(found[0]), ({ base, body, draft, number, title }) => ({
18544
+ base: base.ref,
18545
+ body: body ?? "",
18546
+ draft: draft ?? false,
18547
+ number,
18548
+ title
18549
+ })))),
18550
+ setBase: (pullRequest, base) => send(patch(repo(`/pulls/${pullRequest}`)).pipe(bodyOf({ base }))).pipe(asVoid2),
18551
+ setDescription: (pullRequest, { body, title }) => send(patch(repo(`/pulls/${pullRequest}`)).pipe(bodyOf({ body, title }))).pipe(asVoid2),
18552
+ stackOf,
18553
+ unstack: (stack) => send(post(repo(`/stacks/${stack}/unstack`))).pipe(asVoid2)
18554
+ };
18555
+ }));
18556
+ };
18557
+
18558
+ // src/clients/github/repository.ts
18559
+ var repositoryFromUrl = (url) => {
18560
+ const match = /github\.com[:/](?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?\/?$/u.exec(url.trim());
18561
+ const owner = match?.groups?.["owner"];
18562
+ const name = match?.groups?.["name"];
18563
+ return owner === undefined || name === undefined ? none2() : some2({ name, owner });
18564
+ };
18565
+
18566
+ // src/clients/github/origin.ts
18567
+ var GitHubFromOrigin = unwrap(gen2(function* () {
18568
+ const jj = yield* Jj;
18569
+ const remotes = yield* jj.read(["git", "remote", "list"]);
18570
+ const origin = remotes.split(`
18571
+ `).find((line) => line.startsWith("origin "));
18572
+ const repository = repositoryFromUrl(origin?.slice("origin ".length) ?? "");
18573
+ if (isNone2(repository)) {
18574
+ return yield* new GitHubFailed({ message: "the `origin` remote isn't a GitHub repository" });
18575
+ }
18576
+ return GitHubLive(repository.value);
18577
+ }));
18578
+
18445
18579
  // src/cli/gh/log.ts
18446
18580
  var logLines2 = (entries) => {
18447
18581
  const rows = entries.toReversed().map(({ bookmark, pullRequest, pushed }) => [
@@ -18469,50 +18603,6 @@ var log2 = (revision) => gen2(function* () {
18469
18603
  var logCommand = make30("log", { revision: revisionFlag("A change of the stack to show") }, ({ revision }) => asUserError(log2(revision).pipe(provide3(GitHubFromOrigin))).pipe(flatMap4((printed) => log(printed.join(`
18470
18604
  `))))).pipe(withHelp("Show changes and their pull requests", "Prints one line per change, top first: its bookmark, its pull request with whether that is a draft or ready for review, and whether the change is pushed, changed since the push, or not pushed.", "Fails if the changes aren't a chain, if one has conflicts, or if one doesn't have exactly one bookmark. `submit` makes the same check before it changes anything.", "Changes nothing, locally or on GitHub."));
18471
18605
 
18472
- // src/clients/git.ts
18473
- class GitFailed extends TaggedError2("GitFailed") {
18474
- }
18475
-
18476
- class Git extends Service()("jj-octo/Git") {
18477
- }
18478
- var GitLive = effect(Git, gen2(function* () {
18479
- const spawner = yield* ChildProcessSpawner;
18480
- const fs = yield* FileSystem;
18481
- const path = yield* Path;
18482
- return {
18483
- mergeText: ({ base, local, remote }) => scoped2(gen2(function* () {
18484
- const directory = yield* fs.makeTempDirectoryScoped();
18485
- const written = { base: `${base}
18486
- `, github: `${remote}
18487
- `, local: `${local}
18488
- ` };
18489
- yield* all2(Object.entries(written).map(([name, text]) => fs.writeFileString(path.join(directory, name), text)));
18490
- const handle = yield* spawner.spawn(make14("git", [
18491
- "merge-file",
18492
- "-p",
18493
- "-L",
18494
- "local",
18495
- "-L",
18496
- "base",
18497
- "-L",
18498
- "GitHub",
18499
- "local",
18500
- "base",
18501
- "github"
18502
- ], { cwd: directory }));
18503
- const [text, stderr, code] = yield* all2([
18504
- mkString(decodeText(handle.stdout)),
18505
- mkString(decodeText(handle.stderr)),
18506
- handle.exitCode
18507
- ], { concurrency: "unbounded" });
18508
- if (code > 127) {
18509
- return yield* new GitFailed({ message: `git merge-file failed: ${stderr.trim()}` });
18510
- }
18511
- return { conflicted: code > 0, text: text.trimEnd() };
18512
- })).pipe(catchTag2("PlatformError", (error) => fail6(new GitFailed({ message: `git merge-file failed: ${error.message}` }))))
18513
- };
18514
- }));
18515
-
18516
18606
  // src/cli/gh/stack/plan.ts
18517
18607
  var basesFor = (branches, trunk) => [
18518
18608
  trunk,
@@ -18687,7 +18777,10 @@ var publish = (stack, { draft, overwriteDescriptions }) => gen2(function* () {
18687
18777
  }
18688
18778
  yield* register(dissolve ? none2() : existing, numbers);
18689
18779
  });
18690
- var submit = (revision, options) => downstackChanges(revision).pipe(flatMap4((stack) => publish(stack, options)));
18780
+ var submit = (revision, options) => gen2(function* () {
18781
+ yield* checkTrunk(yield* (yield* GitHub).defaultBranch);
18782
+ yield* publish(yield* downstackChanges(revision), options);
18783
+ });
18691
18784
  var republish = (revision) => gen2(function* () {
18692
18785
  if ((yield* changesIn(stackRevset(revision))).length === 0) {
18693
18786
  return;
@@ -18773,6 +18866,7 @@ var rebaseStack = (root, required) => gen2(function* () {
18773
18866
  const [name = root] = yield* changesIn(root);
18774
18867
  const changes = yield* changeIdsAt(stack);
18775
18868
  const conflicted = yield* changeIdsAt(`conflicts() & (${stack})`);
18869
+ const conflictedCommits = yield* commitsAt("conflicts() & mutable()");
18776
18870
  const [operation = ""] = lines2(yield* jj.read([
18777
18871
  "operation",
18778
18872
  "log",
@@ -18783,8 +18877,9 @@ var rebaseStack = (root, required) => gen2(function* () {
18783
18877
  String.raw`id ++ "\n"`
18784
18878
  ]));
18785
18879
  yield* jj.run(["rebase", "--source", root, "--destination", "trunk()", "--skip-emptied"]);
18786
- const rebased = ["none()", ...changes.map((change) => `present(${change})`)].join(" | ");
18787
- const conflictedNow = yield* changeIdsAt(`conflicts() & (${rebased})`);
18880
+ const rebased = ["none()", ...changes.map((change) => `change_id(${change})`)].join(" | ");
18881
+ const untouched = ["none()", ...conflictedCommits].join(" | ");
18882
+ const conflictedNow = yield* changeIdsAt(`(conflicts() & (${rebased})) ~ (${untouched})`);
18788
18883
  const mayRestore = !changes.some((change) => required.includes(change));
18789
18884
  if (mayRestore && conflictedNow.some((change) => !conflicted.includes(change))) {
18790
18885
  yield* jj.run(["operation", "restore", operation]);
@@ -18820,7 +18915,10 @@ var sync4 = (required = []) => gen2(function* () {
18820
18915
  });
18821
18916
  }
18822
18917
  });
18823
- var syncCommand = make30("sync", {}, () => asUserError(sync4())).pipe(withHelp("Fetch, and rebase every change onto the trunk", "Fetches, then rebases every change that isn't merged yet onto the trunk, keeping each on the changes below it. A change that has merged is empty after the rebase, so it's abandoned and its bookmark forgotten.", "A stack that would conflict with the trunk is left where it was, and named. Rebase it yourself with `jj rebase` when you want to resolve it.", "Commits added to a branch on GitHub are squashed into the branch's local change, whether or not that change was also edited locally. Where GitHub only rebased a branch, the local change is kept.", "Pushes nothing. Run `jj gh submit` to update the pull requests of a change and the ones below it."));
18918
+ var syncCommand = make30("sync", {}, () => asUserError(gen2(function* () {
18919
+ yield* checkTrunk(yield* (yield* GitHub).defaultBranch);
18920
+ yield* sync4();
18921
+ }).pipe(provide3(GitHubFromOrigin)))).pipe(withHelp("Fetch, and rebase every change onto the trunk", "Fetches, then rebases every change that isn't merged yet onto the trunk, keeping each on the changes below it. A change that has merged is empty after the rebase, so it's abandoned and its bookmark forgotten.", "A stack that would conflict with the trunk is left where it was, and named. Rebase it yourself with `jj rebase` when you want to resolve it.", "Commits added to a branch on GitHub are squashed into the branch's local change, whether or not that change was also edited locally. Where GitHub only rebased a branch, the local change is kept.", "Fails before fetching if `trunk()` isn't GitHub's default branch, which `jj gh init` sets.", "Pushes nothing. Run `jj gh submit` to update the pull requests of a change and the ones below it."));
18824
18922
 
18825
18923
  // src/cli/gh/merge.ts
18826
18924
  class NotMergeable extends TaggedError2("NotMergeable") {
@@ -18836,7 +18934,7 @@ var openPullRequestOf = (bookmark) => gen2(function* () {
18836
18934
  }
18837
18935
  return opened.value;
18838
18936
  });
18839
- var mergeOnGitHub = ({ base, number }) => gen2(function* () {
18937
+ var mergeOnGitHub = ({ base, number }, trunk) => gen2(function* () {
18840
18938
  const github = yield* GitHub;
18841
18939
  const stack = yield* github.stackOf(number);
18842
18940
  if (isSome2(stack)) {
@@ -18844,7 +18942,7 @@ var mergeOnGitHub = ({ base, number }) => gen2(function* () {
18844
18942
  yield* error(`merging ${numbered(open.slice(0, open.indexOf(number) + 1))}`);
18845
18943
  return yield* github.mergeStack(number);
18846
18944
  }
18847
- if (base !== (yield* github.defaultBranch)) {
18945
+ if (base !== trunk) {
18848
18946
  return yield* new NotMergeable({
18849
18947
  message: `#${number} isn't in a GitHub stack, and its base is ${base}, so merging it wouldn't reach the trunk; run \`jj gh submit\` to register the stack, or merge the changes below it first`
18850
18948
  });
@@ -18854,6 +18952,8 @@ var mergeOnGitHub = ({ base, number }) => gen2(function* () {
18854
18952
  return "merged";
18855
18953
  });
18856
18954
  var merge5 = (revision) => gen2(function* () {
18955
+ const trunk = yield* (yield* GitHub).defaultBranch;
18956
+ yield* checkTrunk(trunk);
18857
18957
  const changes = yield* downstackChanges(revision);
18858
18958
  const top = changes.at(-1)?.bookmark ?? "";
18859
18959
  const targets = yield* bookmarkTargets;
@@ -18866,7 +18966,7 @@ var merge5 = (revision) => gen2(function* () {
18866
18966
  const pullRequest = yield* openPullRequestOf(top);
18867
18967
  const above = yield* bookmarksAt(upstackRevset(bookmarkRevset(top)));
18868
18968
  const stackChanges = yield* changeIdsAt(stackRevset(revision));
18869
- if ((yield* mergeOnGitHub(pullRequest)) === "enqueued") {
18969
+ if ((yield* mergeOnGitHub(pullRequest, trunk)) === "enqueued") {
18870
18970
  yield* error("queued to merge; run `jj gh sync` once it has");
18871
18971
  } else {
18872
18972
  yield* error(`merged up to #${pullRequest.number}`);
@@ -18889,7 +18989,7 @@ var submitCommand = make30("submit", {
18889
18989
  }, ({ overwriteDescriptions, publish, revision }) => asUserError(submit(revision, { draft: !publish, overwriteDescriptions }).pipe(provide3(GitHubFromOrigin)))).pipe(withHelp("Push a change and the ones below it, and open their pull requests", "Pushes the bookmark of each of those changes. An empty working copy means the change below it, so from the top of a stack this submits all of it. Opens a pull request for each branch that has none, with the title and body from the change's description. Points each pull request at the branch below it, and the bottom one at the default branch.", "New pull requests are opened as drafts, or ready for review with `--publish`. The flag doesn't change a pull request that already exists.", "Keeps each pull request's title and body the same as its change's description. A description edited on GitHub is written into the change, one edited locally is sent to the pull request, and edits on both sides are merged line by line. Edits to the same lines stop the submit before anything is pushed.", "Registers the pull requests as one GitHub stack. A stack of one change is a single pull request, which GitHub has no stack for.", "If changes were reordered, inserted or dropped since the last submit, the GitHub stack is dissolved and registered again under a new number. The pull requests stay open, including the one of a dropped change."));
18890
18990
 
18891
18991
  // src/cli/gh/command.ts
18892
- var jjGhCommand = make30("jj gh").pipe(withSubcommands([logCommand, mergeCommand, submitCommand, syncCommand]), withHelp("Commands for publishing jj changes as GitHub pull requests", "A stack is a chain of changes, each published as one pull request on the branch its bookmark names. Each pull request targets the branch of the change below it, so it shows only that change's diff. A single change is a stack of one, and is published as an ordinary pull request.", "The stack of a change is every change connected to it through mutable changes. An empty working copy with no description isn't part of it. `log` shows the whole stack. `submit` and `merge` act on a change and the ones below it, which must be a chain: no merges, no conflicts, and exactly one bookmark on each change.", "The repository is the one the `origin` remote points to. The token is read from `GH_TOKEN` or `GITHUB_TOKEN`, and otherwise from `gh auth token`."));
18992
+ var jjGhCommand = make30("jj gh").pipe(withSubcommands([initCommand, logCommand, mergeCommand, submitCommand, syncCommand]), withHelp("Commands for publishing jj changes as GitHub pull requests", "A stack is a chain of changes, each published as one pull request on the branch its bookmark names. Each pull request targets the branch of the change below it, so it shows only that change's diff. A single change is a stack of one, and is published as an ordinary pull request.", "The stack of a change is every change connected to it through mutable changes. An empty working copy with no description isn't part of it. `log` shows the whole stack. `submit` and `merge` act on a change and the ones below it, which must be a chain: no merges, no conflicts, and exactly one bookmark on each change.", "The repository is the one the `origin` remote points to. The token is read from `GH_TOKEN` or `GITHUB_TOKEN`, and otherwise from `gh auth token`."));
18893
18993
 
18894
18994
  // src/main.ts
18895
18995
  runMain2(run5(jjGhCommand, { version: package_default.version }).pipe(provide3([GitLive, JjLive]), provide3(layer13({