@flux-control/effect-modbus-rs 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -198,6 +198,14 @@ var superviseReconnect = (reconnect, state, resolved) => Effect2.gen(function* (
198
198
  return;
199
199
  }
200
200
  });
201
+ var claimReconnect = (reconnect, state, resolved, scope, onClaim) => Effect2.gen(function* () {
202
+ const claimed = yield* SubscriptionRef.modify(state, (current) => ConnectionState.$is("Connected")(current) ? [true, ConnectionState.Reconnecting({ attempt: 0 })] : [false, current]);
203
+ if (!claimed)
204
+ return;
205
+ if (onClaim)
206
+ yield* onClaim;
207
+ yield* Effect2.forkIn(superviseReconnect(reconnect, state, resolved), scope);
208
+ });
201
209
  // src/AsciiTransportService.ts
202
210
  import { Context, Layer } from "effect";
203
211
 
@@ -461,12 +469,7 @@ var makeMockTransport = (devices) => {
461
469
  const report = (error) => {
462
470
  if (!supervised || !supervised.triggers(error))
463
471
  return Effect4.void;
464
- return Effect4.gen(function* () {
465
- const claimed = yield* SubscriptionRef2.modify(connectionState, (current) => ConnectionState.$is("Connected")(current) ? [true, ConnectionState.Reconnecting({ attempt: 0 })] : [false, current]);
466
- if (!claimed)
467
- return;
468
- yield* Effect4.forkIn(superviseReconnect(reconnectOnce, connectionState, supervised), serviceScope);
469
- });
472
+ return claimReconnect(reconnectOnce, connectionState, supervised, serviceScope);
470
473
  };
471
474
  const guard = Effect4.andThen(supervised ? guardCircuit(connectionState) : Effect4.void, Effect4.suspend(() => {
472
475
  const injected = options.fault?.();
@@ -568,13 +571,7 @@ function makeTransportScoped(transportKey, openMethod, serviceName, config) {
568
571
  const report = (error) => {
569
572
  if (!supervised || closed || !supervised.triggers(error))
570
573
  return Effect5.void;
571
- return Effect5.gen(function* () {
572
- const claimed = yield* SubscriptionRef3.modify(connectionState, (current) => ConnectionState.$is("Connected")(current) ? [true, ConnectionState.Reconnecting({ attempt: 0 })] : [false, current]);
573
- if (!claimed)
574
- return;
575
- yield* Effect5.logDebug(`${serviceName}: reconnecting after ${error.message}`);
576
- yield* Effect5.forkIn(superviseReconnect(reconnectOnce, connectionState, supervised), serviceScope);
577
- });
574
+ return claimReconnect(reconnectOnce, connectionState, supervised, serviceScope, Effect5.logDebug(`${serviceName}: reconnecting after ${error.message}`));
578
575
  };
579
576
  const resilience = {
580
577
  guard: supervised ? guardCircuit(connectionState) : Effect5.void,
@@ -1,4 +1,4 @@
1
- import { Data, Duration, Effect, SubscriptionRef } from 'effect';
1
+ import { Data, Duration, Effect, type Scope, SubscriptionRef } from 'effect';
2
2
  import { ModbusCircuitOpenError, type ModbusError } from './errors';
3
3
  import { type ModbusErrorTag, type ModbusRetryPolicy } from './retry';
4
4
  /**
@@ -168,3 +168,27 @@ export declare const guardCircuit: (state: SubscriptionRef.SubscriptionRef<Conne
168
168
  * @returns An Effect that runs until the link is restored.
169
169
  */
170
170
  export declare const superviseReconnect: (reconnect: Effect.Effect<void, ModbusError>, state: SubscriptionRef.SubscriptionRef<ConnectionState>, resolved: ResolvedReconnect) => Effect.Effect<void>;
171
+ /**
172
+ * Claims the `Connected` → `Reconnecting` transition and starts the supervisor.
173
+ *
174
+ * The transition is claimed atomically, so of the fibers that fail together
175
+ * exactly one starts the supervisor and the rest simply carry on failing — one
176
+ * reconnect for the whole transport, not one per call site. Fibers that lose
177
+ * the claim do nothing at all; `onClaim` runs in the winner only, after the
178
+ * transition is published and before the supervisor is forked.
179
+ *
180
+ * Callers decide *whether* a failure is worth reporting (the supervisor's
181
+ * `triggers` predicate, plus whatever local state means "no longer eligible");
182
+ * this owns *how* the transition is made, so the claim protocol has one
183
+ * implementation.
184
+ *
185
+ * @param reconnect - The transport's own reconnect operation.
186
+ * @param state - The state cell the transition is claimed on.
187
+ * @param resolved - Reconnect configuration with defaults applied.
188
+ * @param scope - Scope the supervisor fiber is forked into; it is interrupted
189
+ * when the scope closes.
190
+ * @param onClaim - Optional effect run by the claiming fiber only.
191
+ * @returns An Effect that completes once the supervisor is forked, or
192
+ * immediately when another fiber already holds the claim.
193
+ */
194
+ export declare const claimReconnect: (reconnect: Effect.Effect<void, ModbusError>, state: SubscriptionRef.SubscriptionRef<ConnectionState>, resolved: ResolvedReconnect, scope: Scope.Scope, onClaim?: Effect.Effect<void>) => Effect.Effect<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flux-control/effect-modbus-rs",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Type-safe Modbus communication via Effect-TS, wrapping the modbus-rs npm bindings.",
5
5
  "license": "GPL-3.0",
6
6
  "repository": {
@@ -30,6 +30,7 @@
30
30
  "format:fix": "oxfmt",
31
31
  "lint": "oxlint",
32
32
  "lint:fix": "oxlint --fix",
33
+ "lock": "bash tools/refresh-lockfile.sh",
33
34
  "prepare": "husky",
34
35
  "release": "changeset publish",
35
36
  "test": "bun test ./src/ ./examples/",