effect-app 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @effect-app/prelude
2
2
 
3
+ ## 4.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 76911ed: Add `suspendWaiting` so a command can park on a dialog without the spinner or the waiting toast. The button stays disabled. Both come back when the effect finishes and the rest of the command runs.
8
+ - 7a99d1b: Upgrade Effect packages to npm `4.0.2`.
9
+
3
10
  ## 4.0.1
4
11
 
5
12
  ### Patch Changes
@@ -0,0 +1,38 @@
1
+ import * as Context from "./Context.js";
2
+ import * as Effect from "./Effect.js";
3
+ /**
4
+ * Notified with the suspension depth after each change. `0` means the command
5
+ * is doing work. A positive depth means it is parked on a caller.
6
+ */
7
+ export type WaitingGateController = (depth: number) => Effect.Effect<void>;
8
+ export interface WaitingGateShape {
9
+ /** `false` for the default gate. Suspension then changes nothing. */
10
+ readonly active: boolean;
11
+ readonly depth: number;
12
+ readonly add: (delta: number) => Effect.Effect<void>;
13
+ /** Stack a controller. The returned function restores the previous one. */
14
+ readonly bind: (controller: WaitingGateController) => () => void;
15
+ }
16
+ /**
17
+ * Per-command gate for {@link suspendWaiting}. Commands provide an active gate.
18
+ * The default is inactive, so the helper is a no-op outside a command.
19
+ */
20
+ export declare const WaitingGate: Context.Reference<WaitingGateShape>;
21
+ /** Build the gate one command owns. `onDepth` runs synchronously inside `add`. */
22
+ export declare const makeWaitingGate: (onDepth?: (depth: number) => void) => WaitingGateShape;
23
+ /**
24
+ * Run `self` while command chrome is quiet.
25
+ *
26
+ * The spinner and the waiting toast pause for `self` and resume when it
27
+ * finishes, including when it is interrupted. The command stays blocked for
28
+ * the whole run, so the button cannot start again.
29
+ *
30
+ * Outside a command the default gate is inactive and `self` runs unchanged.
31
+ *
32
+ * ```ts
33
+ * const decision = yield* suspendWaiting(ask(pickupDate))
34
+ * yield* close(orderId)
35
+ * ```
36
+ */
37
+ export declare const suspendWaiting: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
38
+ //# sourceMappingURL=waitingGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitingGate.d.ts","sourceRoot":"","sources":["../src/waitingGate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,qBAAqB,KAAK,MAAM,IAAI,CAAA;CACjE;AASD;;;GAGG;AACH,eAAO,MAAM,WAAW,qCAEtB,CAAA;AAEF,kFAAkF;AAClF,eAAO,MAAM,eAAe,aAAc,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,KAAG,gBAwBnE,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAOzF,CAAA"}
@@ -0,0 +1,63 @@
1
+ import * as Context from "./Context.js";
2
+ import * as Effect from "./Effect.js";
3
+ const inactiveWaitingGate = {
4
+ active: false,
5
+ depth: 0,
6
+ add: () => Effect.void,
7
+ bind: () => () => { }
8
+ };
9
+ /**
10
+ * Per-command gate for {@link suspendWaiting}. Commands provide an active gate.
11
+ * The default is inactive, so the helper is a no-op outside a command.
12
+ */
13
+ export const WaitingGate = Context.Reference("effect-app/WaitingGate", {
14
+ defaultValue: () => inactiveWaitingGate
15
+ });
16
+ /** Build the gate one command owns. `onDepth` runs synchronously inside `add`. */
17
+ export const makeWaitingGate = (onDepth) => {
18
+ let depth = 0;
19
+ let controller;
20
+ return {
21
+ active: true,
22
+ get depth() {
23
+ return depth;
24
+ },
25
+ add: (delta) => Effect.uninterruptible(Effect.gen(function* () {
26
+ depth += delta;
27
+ if (depth < 0)
28
+ depth = 0;
29
+ const next = depth;
30
+ onDepth?.(next);
31
+ if (controller)
32
+ yield* controller(next);
33
+ })),
34
+ bind: (next) => {
35
+ const previous = controller;
36
+ controller = next;
37
+ return () => {
38
+ if (controller === next)
39
+ controller = previous;
40
+ };
41
+ }
42
+ };
43
+ };
44
+ /**
45
+ * Run `self` while command chrome is quiet.
46
+ *
47
+ * The spinner and the waiting toast pause for `self` and resume when it
48
+ * finishes, including when it is interrupted. The command stays blocked for
49
+ * the whole run, so the button cannot start again.
50
+ *
51
+ * Outside a command the default gate is inactive and `self` runs unchanged.
52
+ *
53
+ * ```ts
54
+ * const decision = yield* suspendWaiting(ask(pickupDate))
55
+ * yield* close(orderId)
56
+ * ```
57
+ */
58
+ export const suspendWaiting = (self) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
59
+ const gate = yield* WaitingGate;
60
+ yield* gate.add(1);
61
+ return yield* Effect.ensuring(restore(self), gate.add(-1));
62
+ }));
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FpdGluZ0dhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2FpdGluZ0dhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUE7QUFDdkMsT0FBTyxLQUFLLE1BQU0sTUFBTSxhQUFhLENBQUE7QUFpQnJDLE1BQU0sbUJBQW1CLEdBQXFCO0lBQzVDLE1BQU0sRUFBRSxLQUFLO0lBQ2IsS0FBSyxFQUFFLENBQUM7SUFDUixHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUk7SUFDdEIsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFFLENBQUM7Q0FDckIsQ0FBQTtBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFtQix3QkFBd0IsRUFBRTtJQUN2RixZQUFZLEVBQUUsR0FBRyxFQUFFLENBQUMsbUJBQW1CO0NBQ3hDLENBQUMsQ0FBQTtBQUVGLGtGQUFrRjtBQUNsRixNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsQ0FBQyxPQUFpQyxFQUFvQixFQUFFO0lBQ3JGLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQTtJQUNiLElBQUksVUFBNkMsQ0FBQTtJQUNqRCxPQUFPO1FBQ0wsTUFBTSxFQUFFLElBQUk7UUFDWixJQUFJLEtBQUs7WUFDUCxPQUFPLEtBQUssQ0FBQTtRQUNkLENBQUM7UUFDRCxHQUFHLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUNiLE1BQU0sQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7WUFDekMsS0FBSyxJQUFJLEtBQUssQ0FBQTtZQUNkLElBQUksS0FBSyxHQUFHLENBQUM7Z0JBQUUsS0FBSyxHQUFHLENBQUMsQ0FBQTtZQUN4QixNQUFNLElBQUksR0FBRyxLQUFLLENBQUE7WUFDbEIsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUE7WUFDZixJQUFJLFVBQVU7Z0JBQUUsS0FBSyxDQUFDLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQ3pDLENBQUMsQ0FBQyxDQUFDO1FBQ0wsSUFBSSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7WUFDYixNQUFNLFFBQVEsR0FBRyxVQUFVLENBQUE7WUFDM0IsVUFBVSxHQUFHLElBQUksQ0FBQTtZQUNqQixPQUFPLEdBQUcsRUFBRTtnQkFDVixJQUFJLFVBQVUsS0FBSyxJQUFJO29CQUFFLFVBQVUsR0FBRyxRQUFRLENBQUE7WUFDaEQsQ0FBQyxDQUFBO1FBQ0gsQ0FBQztLQUNGLENBQUE7QUFDSCxDQUFDLENBQUE7QUFFRDs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQVUsSUFBNEIsRUFBMEIsRUFBRSxDQUM5RixNQUFNLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUNyQyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztJQUNsQixNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQyxXQUFXLENBQUE7SUFDL0IsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNsQixPQUFPLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBQzVELENBQUMsQ0FBQyxDQUNILENBQUEifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"withToast.d.ts","sourceRoot":"","sources":["../src/withToast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"withToast.d.ts","sourceRoot":"","sources":["../src/withToast.ts"],"names":[],"mappings":"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"}
package/dist/withToast.js CHANGED
@@ -6,6 +6,7 @@ import * as Layer from "./Layer.js";
6
6
  import * as S from "./Schema.js";
7
7
  import { CurrentToastId, Toast } from "./toast.js";
8
8
  import { wrapEffect } from "./utils.js";
9
+ import { WaitingGate } from "./waitingGate.js";
9
10
  // @effect-diagnostics-next-line missingEffectServiceDependency:off
10
11
  export class WithToast extends Context.Service()("WithToast", {
11
12
  make: Effect.gen(function* () {
@@ -22,8 +23,46 @@ export class WithToast extends Context.Service()("WithToast", {
22
23
  const toastId = t === null
23
24
  ? stableToastId
24
25
  : stableToastId ?? `wait-${Math.random().toString(36).slice(2)}`;
25
- const waitingFiber = t === null ? undefined : yield* Effect.forkChild(Effect.sleep("1 seconds").pipe(Effect.andThen(toast.info(t, { id: toastId, timeout: Infinity, ...meta }))));
26
- const interruptWaiting = waitingFiber ? Fiber.interrupt(waitingFiber) : Effect.void;
26
+ const gate = yield* WaitingGate;
27
+ let waitingFiber;
28
+ let toastShown = false;
29
+ let unbind = () => { };
30
+ const armWaiting = () => Effect.gen(function* () {
31
+ if (waitingFiber !== undefined || t === null || toastId === undefined)
32
+ return;
33
+ waitingFiber = yield* Effect.forkChild(Effect.sleep("1 seconds").pipe(Effect.andThen(Effect.uninterruptible(Effect.gen(function* () {
34
+ toastShown = true;
35
+ yield* toast.info(t, { id: toastId, timeout: Infinity, ...meta });
36
+ }))), Effect.asVoid));
37
+ });
38
+ const disarmWaiting = () => Effect.gen(function* () {
39
+ const fiber = waitingFiber;
40
+ waitingFiber = undefined;
41
+ if (fiber)
42
+ yield* Fiber.interrupt(fiber);
43
+ if (toastShown && toastId !== undefined) {
44
+ toastShown = false;
45
+ yield* toast.dismiss(toastId);
46
+ }
47
+ });
48
+ // An active gate pauses the toast while `suspendWaiting` is parked and
49
+ // starts a fresh one-second delay when work resumes. The inactive
50
+ // default keeps the original fork.
51
+ if (t !== null && gate.active) {
52
+ unbind = gate.bind((depth) => (depth === 0 ? armWaiting() : disarmWaiting()));
53
+ if (gate.depth === 0)
54
+ yield* armWaiting();
55
+ }
56
+ else if (t !== null) {
57
+ waitingFiber = yield* Effect.forkChild(Effect.sleep("1 seconds").pipe(Effect.andThen(toast.info(t, { id: toastId, timeout: Infinity, ...meta })), Effect.asVoid));
58
+ }
59
+ const interruptWaiting = Effect.uninterruptible(Effect.gen(function* () {
60
+ unbind();
61
+ const fiber = waitingFiber;
62
+ waitingFiber = undefined;
63
+ if (fiber)
64
+ yield* Fiber.interrupt(fiber);
65
+ }));
27
66
  return yield* self.pipe(Effect.tap(Effect.fnUntraced(function* (a) {
28
67
  yield* interruptWaiting;
29
68
  const t = yield* wrapEffect(options.onSuccess)(a, ...args);
@@ -61,4 +100,4 @@ export class WithToast extends Context.Service()("WithToast", {
61
100
  static Default = this.DefaultWithoutDependencies;
62
101
  static handle = (options) => (self, ...args) => this.use((_) => _(options)(self, ...args));
63
102
  }
64
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aFRvYXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dpdGhUb2FzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssS0FBSyxNQUFNLGNBQWMsQ0FBQTtBQUNyQyxPQUFPLEtBQUssS0FBSyxNQUFNLGNBQWMsQ0FBQTtBQUNyQyxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQTtBQUN2QyxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQTtBQUNyQyxPQUFPLEtBQUssS0FBSyxNQUFNLFlBQVksQ0FBQTtBQUVuQyxPQUFPLEtBQUssQ0FBQyxNQUFNLGFBQWEsQ0FBQTtBQUNoQyxPQUFPLEVBQUUsY0FBYyxFQUFFLEtBQUssRUFBZ0IsTUFBTSxZQUFZLENBQUE7QUFDaEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQWtDdkMsbUVBQW1FO0FBQ25FLE1BQU0sT0FBTyxTQUFVLFNBQVEsT0FBTyxDQUFDLE9BQU8sRUFBYSxDQUFDLFdBQVcsRUFBRTtJQUN2RSxJQUFJLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7UUFDeEIsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsS0FBSyxDQUFBO1FBQzFCLE9BQU8sQ0FDTCxPQUFtRCxFQUNuRCxFQUFFLENBQ0YsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBQyxJQUE0QixFQUFFLEdBQUcsSUFBVTtZQUNyRSxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsT0FBTyxJQUFJLEtBQUssQ0FBQTtZQUU1QyxNQUFNLGFBQWEsR0FBRyxPQUFPLE9BQU8sQ0FBQyxhQUFhLEtBQUssVUFBVTtnQkFDL0QsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQ2hDLENBQUMsQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFBO1lBRXpCLE1BQU0sU0FBUyxHQUFXLEtBQUssQ0FBQyxD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
103
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aFRvYXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dpdGhUb2FzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssS0FBSyxNQUFNLGNBQWMsQ0FBQTtBQUNyQyxPQUFPLEtBQUssS0FBSyxNQUFNLGNBQWMsQ0FBQTtBQUNyQyxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQTtBQUN2QyxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQTtBQUNyQyxPQUFPLEtBQUssS0FBSyxNQUFNLFlBQVksQ0FBQTtBQUVuQyxPQUFPLEtBQUssQ0FBQyxNQUFNLGFBQWEsQ0FBQTtBQUNoQyxPQUFPLEVBQUUsY0FBYyxFQUFFLEtBQUssRUFBZ0IsTUFBTSxZQUFZLENBQUE7QUFDaEUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUN2QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUE7QUFrQzlDLG1FQUFtRTtBQUNuRSxNQUFNLE9BQU8sU0FBVSxTQUFRLE9BQU8sQ0FBQyxPQUFPLEVBQWEsQ0FBQyxXQUFXLEVBQUU7SUFDdkUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQ3hCLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQTtRQUMxQixPQUFPLENBQ0wsT0FBbUQsRUFDbkQsRUFBRSxDQUNGLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUMsSUFBNEIsRUFBRSxHQUFHLElBQVU7WUFDckUsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sSUFBSSxLQUFLLENBQUE7WUFFNUMsTUFBTSxhQUFhLEdBQUcsT0FBTyxPQUFPLENBQUMsYUFBYSxLQUFLLFVBQVU7Z0JBQy9ELENBQUMsQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUNoQyxDQUFDLENBQUMsT0FBTyxDQUFD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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-app",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "vitest": "^4.1.7"
26
26
  },
27
27
  "peerDependencies": {
28
- "effect": "^4.0.1"
28
+ "effect": "^4.0.2"
29
29
  },
30
30
  "typesVersions": {
31
31
  "*": {
@@ -0,0 +1,82 @@
1
+ import * as Context from "./Context.ts"
2
+ import * as Effect from "./Effect.ts"
3
+
4
+ /**
5
+ * Notified with the suspension depth after each change. `0` means the command
6
+ * is doing work. A positive depth means it is parked on a caller.
7
+ */
8
+ export type WaitingGateController = (depth: number) => Effect.Effect<void>
9
+
10
+ export interface WaitingGateShape {
11
+ /** `false` for the default gate. Suspension then changes nothing. */
12
+ readonly active: boolean
13
+ readonly depth: number
14
+ readonly add: (delta: number) => Effect.Effect<void>
15
+ /** Stack a controller. The returned function restores the previous one. */
16
+ readonly bind: (controller: WaitingGateController) => () => void
17
+ }
18
+
19
+ const inactiveWaitingGate: WaitingGateShape = {
20
+ active: false,
21
+ depth: 0,
22
+ add: () => Effect.void,
23
+ bind: () => () => {}
24
+ }
25
+
26
+ /**
27
+ * Per-command gate for {@link suspendWaiting}. Commands provide an active gate.
28
+ * The default is inactive, so the helper is a no-op outside a command.
29
+ */
30
+ export const WaitingGate = Context.Reference<WaitingGateShape>("effect-app/WaitingGate", {
31
+ defaultValue: () => inactiveWaitingGate
32
+ })
33
+
34
+ /** Build the gate one command owns. `onDepth` runs synchronously inside `add`. */
35
+ export const makeWaitingGate = (onDepth?: (depth: number) => void): WaitingGateShape => {
36
+ let depth = 0
37
+ let controller: WaitingGateController | undefined
38
+ return {
39
+ active: true,
40
+ get depth() {
41
+ return depth
42
+ },
43
+ add: (delta) =>
44
+ Effect.uninterruptible(Effect.gen(function*() {
45
+ depth += delta
46
+ if (depth < 0) depth = 0
47
+ const next = depth
48
+ onDepth?.(next)
49
+ if (controller) yield* controller(next)
50
+ })),
51
+ bind: (next) => {
52
+ const previous = controller
53
+ controller = next
54
+ return () => {
55
+ if (controller === next) controller = previous
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Run `self` while command chrome is quiet.
63
+ *
64
+ * The spinner and the waiting toast pause for `self` and resume when it
65
+ * finishes, including when it is interrupted. The command stays blocked for
66
+ * the whole run, so the button cannot start again.
67
+ *
68
+ * Outside a command the default gate is inactive and `self` runs unchanged.
69
+ *
70
+ * ```ts
71
+ * const decision = yield* suspendWaiting(ask(pickupDate))
72
+ * yield* close(orderId)
73
+ * ```
74
+ */
75
+ export const suspendWaiting = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
76
+ Effect.uninterruptibleMask((restore) =>
77
+ Effect.gen(function*() {
78
+ const gate = yield* WaitingGate
79
+ yield* gate.add(1)
80
+ return yield* Effect.ensuring(restore(self), gate.add(-1))
81
+ })
82
+ )
package/src/withToast.ts CHANGED
@@ -7,6 +7,7 @@ import type * as Option from "./Option.ts"
7
7
  import * as S from "./Schema.ts"
8
8
  import { CurrentToastId, Toast, type ToastId } from "./toast.ts"
9
9
  import { wrapEffect } from "./utils.ts"
10
+ import { WaitingGate } from "./waitingGate.ts"
10
11
 
11
12
  export interface ToastOptions<A, E, Args extends ReadonlyArray<unknown>, WaiR, SucR, ErrR> {
12
13
  stableToastId?: undefined | string | ((...args: Args) => string | undefined)
@@ -66,12 +67,57 @@ export class WithToast extends Context.Service<WithToast>()("WithToast", {
66
67
  ? stableToastId
67
68
  : stableToastId ?? `wait-${Math.random().toString(36).slice(2)}`
68
69
 
69
- const waitingFiber = t === null ? undefined : yield* Effect.forkChild(
70
- Effect.sleep("1 seconds").pipe(
71
- Effect.andThen(toast.info(t, { id: toastId!, timeout: Infinity, ...meta }))
70
+ const gate = yield* WaitingGate
71
+ let waitingFiber: Fiber.Fiber<void> | undefined
72
+ let toastShown = false
73
+ let unbind = () => {}
74
+
75
+ const armWaiting = () =>
76
+ Effect.gen(function*() {
77
+ if (waitingFiber !== undefined || t === null || toastId === undefined) return
78
+ waitingFiber = yield* Effect.forkChild(
79
+ Effect.sleep("1 seconds").pipe(
80
+ Effect.andThen(Effect.uninterruptible(Effect.gen(function*() {
81
+ toastShown = true
82
+ yield* toast.info(t, { id: toastId, timeout: Infinity, ...meta })
83
+ }))),
84
+ Effect.asVoid
85
+ )
86
+ )
87
+ })
88
+
89
+ const disarmWaiting = () =>
90
+ Effect.gen(function*() {
91
+ const fiber = waitingFiber
92
+ waitingFiber = undefined
93
+ if (fiber) yield* Fiber.interrupt(fiber)
94
+ if (toastShown && toastId !== undefined) {
95
+ toastShown = false
96
+ yield* toast.dismiss(toastId)
97
+ }
98
+ })
99
+
100
+ // An active gate pauses the toast while `suspendWaiting` is parked and
101
+ // starts a fresh one-second delay when work resumes. The inactive
102
+ // default keeps the original fork.
103
+ if (t !== null && gate.active) {
104
+ unbind = gate.bind((depth) => (depth === 0 ? armWaiting() : disarmWaiting()))
105
+ if (gate.depth === 0) yield* armWaiting()
106
+ } else if (t !== null) {
107
+ waitingFiber = yield* Effect.forkChild(
108
+ Effect.sleep("1 seconds").pipe(
109
+ Effect.andThen(toast.info(t, { id: toastId!, timeout: Infinity, ...meta })),
110
+ Effect.asVoid
111
+ )
72
112
  )
73
- )
74
- const interruptWaiting = waitingFiber ? Fiber.interrupt(waitingFiber) : Effect.void
113
+ }
114
+
115
+ const interruptWaiting = Effect.uninterruptible(Effect.gen(function*() {
116
+ unbind()
117
+ const fiber = waitingFiber
118
+ waitingFiber = undefined
119
+ if (fiber) yield* Fiber.interrupt(fiber)
120
+ }))
75
121
 
76
122
  return yield* self.pipe(
77
123
  Effect.tap(Effect.fnUntraced(function*(a) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitingGate.test.d.ts","sourceRoot":"","sources":["../waitingGate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,64 @@
1
+ import * as Deferred from "effect/Deferred"
2
+ import * as Effect from "effect/Effect"
3
+ import * as Fiber from "effect/Fiber"
4
+ import { describe, expect, test } from "vitest"
5
+ import { makeWaitingGate, suspendWaiting, WaitingGate } from "../src/waitingGate.js"
6
+
7
+ const run = <A, E>(self: Effect.Effect<A, E, never>) => Effect.runPromise(self)
8
+
9
+ describe("suspendWaiting", () => {
10
+ test("brackets depth and tells the controller", async () => {
11
+ const depths: Array<number> = []
12
+ const gate = makeWaitingGate((depth) => depths.push(depth))
13
+ gate.bind((depth) => Effect.sync(() => depths.push(depth)))
14
+
15
+ await run(
16
+ suspendWaiting(Effect.sync(() => {
17
+ expect(gate.depth).toBe(1)
18
+ }))
19
+ .pipe(Effect.provideService(WaitingGate, gate))
20
+ )
21
+
22
+ expect(gate.depth).toBe(0)
23
+ expect(depths).toEqual([1, 1, 0, 0])
24
+ })
25
+
26
+ test("nests", async () => {
27
+ const gate = makeWaitingGate()
28
+ await run(
29
+ suspendWaiting(suspendWaiting(Effect.sync(() => {
30
+ expect(gate.depth).toBe(2)
31
+ })))
32
+ .pipe(Effect.provideService(WaitingGate, gate))
33
+ )
34
+ expect(gate.depth).toBe(0)
35
+ })
36
+
37
+ test("restores depth when the effect is interrupted", async () => {
38
+ const gate = makeWaitingGate()
39
+ await run(Effect.gen(function*() {
40
+ const entered = yield* Deferred.make<void>()
41
+ const fiber = yield* suspendWaiting(Effect.gen(function*() {
42
+ yield* Deferred.succeed(entered, undefined)
43
+ return yield* Effect.never
44
+ }))
45
+ .pipe(
46
+ Effect.provideService(WaitingGate, gate),
47
+ Effect.forkChild
48
+ )
49
+ yield* Deferred.await(entered)
50
+ expect(gate.depth).toBe(1)
51
+ yield* Fiber.interrupt(fiber)
52
+ expect(gate.depth).toBe(0)
53
+ }))
54
+ })
55
+
56
+ test("does nothing on the default gate", async () => {
57
+ await run(Effect.gen(function*() {
58
+ const gate = yield* WaitingGate
59
+ expect(gate.active).toBe(false)
60
+ yield* suspendWaiting(Effect.void)
61
+ expect(gate.depth).toBe(0)
62
+ }))
63
+ })
64
+ })