@effect-app/infra 4.0.0-beta.281 → 4.0.0-beta.282

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,24 @@
1
1
  # @effect-app/infra
2
2
 
3
+ ## 4.0.0-beta.282
4
+
5
+ ### Patch Changes
6
+
7
+ - b2df3fa: Align `Schema.Void` with TypeScript `void` return-value semantics (effect-smol PR #2475 / `b7d46ab`).
8
+
9
+ `S.Void` now accepts **any present value** at runtime and discards it to `undefined`, while keeping the decoded/encoded type as `void` — matching a `void` return whose result callers never observe. This is implemented as an override of the AST node in a new `effect-app/SchemaAST` module (`export * from "effect/SchemaAST"` plus a `Void` subclass whose parser mirrors the PR's `fromAnyToConst(undefined)`), so there is a single canonical `Void` used everywhere, including RPC success schemas.
10
+
11
+ - New `effect-app/SchemaAST` module; internal `SchemaAST` imports across the libs now route through it.
12
+ - Removed `ForceVoid` from `effect-app/client/makeClient` — use `S.Void` directly, which now carries this behaviour.
13
+ - `S.Void_` remains available as effect's original (`undefined`-only) Void.
14
+
15
+ - f150cf9: Remove obsolete queue machinery left unused after the move to cluster entities: the SB/SQLite/mem `QueueMaker` implementations (`QueueMaker/{SQLQueue,memQueue,sbqueue}.ts`), their `ServiceBus.ts`/`memQueue.ts` transports, and `RequestFiberSet.ts` (its `setRootParentSpan` helper is inlined into `MainFiberSet`). `effect-app/QueueMaker` keeps only `QueueMeta`; the `QueueBase` interface and empty `QueueMaker` ops object are dropped.
16
+ - e8842aa: Update effect packages to `4.0.0-beta.88` (from `beta.86`): `effect`, `@effect/platform-node`, `@effect/platform-browser`, `@effect/atom-vue`, `@effect/sql-sqlite-node`, `@effect/vitest`. Also bump `@effect-app/cli` to `2.1.0-beta.35`. No source changes required — typecheck and tests pass unchanged.
17
+ - Updated dependencies [b2df3fa]
18
+ - Updated dependencies [f150cf9]
19
+ - Updated dependencies [e8842aa]
20
+ - effect-app@4.0.0-beta.282
21
+
3
22
  ## 4.0.0-beta.281
4
23
 
5
24
  ### Patch Changes
@@ -18,25 +18,25 @@ export interface ClusterCosmosConfig {
18
18
  export declare const makeMessageStorage: (options?: {
19
19
  readonly prefix?: string | undefined;
20
20
  } | undefined) => Effect.Effect<{
21
- readonly saveRequest: <R extends import("effect/unstable/rpc/Rpc").Any>(envelope: import("effect/unstable/cluster/Message").OutgoingRequest<R>) => Effect.Effect<MessageStorage.SaveResult<R>, import("effect/unstable/cluster/ClusterError").MalformedMessage | PersistenceError, never>;
22
- readonly saveEnvelope: (envelope: import("effect/unstable/cluster/Message").OutgoingEnvelope) => Effect.Effect<void, import("effect/unstable/cluster/ClusterError").MalformedMessage | PersistenceError, never>;
23
- readonly saveReply: <R extends import("effect/unstable/rpc/Rpc").Any>(reply: Reply.ReplyWithContext<R>) => Effect.Effect<void, import("effect/unstable/cluster/ClusterError").MalformedMessage | PersistenceError, never>;
24
- readonly clearReplies: (requestId: Snowflake.Snowflake) => Effect.Effect<void, PersistenceError, never>;
25
- readonly repliesFor: <R extends import("effect/unstable/rpc/Rpc").Any>(requests: Iterable<import("effect/unstable/cluster/Message").OutgoingRequest<R>>) => Effect.Effect<Reply.Reply<R>[], import("effect/unstable/cluster/ClusterError").MalformedMessage | PersistenceError, never>;
26
- readonly repliesForUnfiltered: (requestIds: Iterable<Snowflake.Snowflake>) => Effect.Effect<Reply.Encoded[], import("effect/unstable/cluster/ClusterError").MalformedMessage | PersistenceError, never>;
21
+ readonly saveRequest: <R extends import("effect/unstable/rpc/Rpc").Any>(envelope: import("effect/unstable/cluster/Message").OutgoingRequest<R>) => Effect.Effect<MessageStorage.SaveResult<R>, PersistenceError | import("effect/unstable/cluster/ClusterError").MalformedMessage>;
22
+ readonly saveEnvelope: (envelope: import("effect/unstable/cluster/Message").OutgoingEnvelope) => Effect.Effect<void, PersistenceError | import("effect/unstable/cluster/ClusterError").MalformedMessage>;
23
+ readonly saveReply: <R extends import("effect/unstable/rpc/Rpc").Any>(reply: Reply.ReplyWithContext<R>) => Effect.Effect<void, PersistenceError | import("effect/unstable/cluster/ClusterError").MalformedMessage>;
24
+ readonly clearReplies: (requestId: Snowflake.Snowflake) => Effect.Effect<void, PersistenceError>;
25
+ readonly repliesFor: <R extends import("effect/unstable/rpc/Rpc").Any>(requests: Iterable<import("effect/unstable/cluster/Message").OutgoingRequest<R>>) => Effect.Effect<Array<Reply.Reply<R>>, PersistenceError | import("effect/unstable/cluster/ClusterError").MalformedMessage>;
26
+ readonly repliesForUnfiltered: (requestIds: Iterable<Snowflake.Snowflake>) => Effect.Effect<Array<Reply.Encoded>, PersistenceError | import("effect/unstable/cluster/ClusterError").MalformedMessage>;
27
27
  readonly requestIdForPrimaryKey: (options: {
28
28
  readonly address: import("effect/unstable/cluster/EntityAddress").EntityAddress;
29
29
  readonly tag: string;
30
30
  readonly id: string;
31
- }) => Effect.Effect<Option.Option<Snowflake.Snowflake>, PersistenceError, never>;
32
- readonly registerReplyHandler: <R extends import("effect/unstable/rpc/Rpc").Any>(message: import("effect/unstable/cluster/Message").OutgoingRequest<R> | import("effect/unstable/cluster/Message").IncomingRequest<R>) => Effect.Effect<void, import("effect/unstable/cluster/ClusterError").EntityNotAssignedToRunner, never>;
33
- readonly unregisterReplyHandler: (requestId: Snowflake.Snowflake) => Effect.Effect<void, never, never>;
34
- readonly unregisterShardReplyHandlers: (shardId: ShardId.ShardId) => Effect.Effect<void, never, never>;
35
- readonly unprocessedMessages: (shardIds: Iterable<ShardId.ShardId>) => Effect.Effect<import("effect/unstable/cluster/Message").Incoming<any>[], PersistenceError, never>;
36
- readonly unprocessedMessagesById: <R extends import("effect/unstable/rpc/Rpc").Any>(messageIds: Iterable<Snowflake.Snowflake>) => Effect.Effect<import("effect/unstable/cluster/Message").Incoming<R>[], PersistenceError, never>;
37
- readonly resetShards: (shardIds: Iterable<ShardId.ShardId>) => Effect.Effect<void, PersistenceError, never>;
38
- readonly resetAddress: (address: import("effect/unstable/cluster/EntityAddress").EntityAddress) => Effect.Effect<void, PersistenceError, never>;
39
- readonly clearAddress: (address: import("effect/unstable/cluster/EntityAddress").EntityAddress) => Effect.Effect<void, PersistenceError, never>;
31
+ }) => Effect.Effect<Option.Option<Snowflake.Snowflake>, PersistenceError>;
32
+ readonly registerReplyHandler: <R extends import("effect/unstable/rpc/Rpc").Any>(message: import("effect/unstable/cluster/Message").OutgoingRequest<R> | import("effect/unstable/cluster/Message").IncomingRequest<R>) => Effect.Effect<void, import("effect/unstable/cluster/ClusterError").EntityNotAssignedToRunner>;
33
+ readonly unregisterReplyHandler: (requestId: Snowflake.Snowflake) => Effect.Effect<void>;
34
+ readonly unregisterShardReplyHandlers: (shardId: ShardId.ShardId) => Effect.Effect<void>;
35
+ readonly unprocessedMessages: (shardIds: Iterable<ShardId.ShardId>) => Effect.Effect<Array<import("effect/unstable/cluster/Message").Incoming<any>>, PersistenceError>;
36
+ readonly unprocessedMessagesById: <R extends import("effect/unstable/rpc/Rpc").Any>(messageIds: Iterable<Snowflake.Snowflake>) => Effect.Effect<Array<import("effect/unstable/cluster/Message").Incoming<R>>, PersistenceError>;
37
+ readonly resetShards: (shardIds: Iterable<ShardId.ShardId>) => Effect.Effect<void, PersistenceError>;
38
+ readonly resetAddress: (address: import("effect/unstable/cluster/EntityAddress").EntityAddress) => Effect.Effect<void, PersistenceError>;
39
+ readonly clearAddress: (address: import("effect/unstable/cluster/EntityAddress").EntityAddress) => Effect.Effect<void, PersistenceError>;
40
40
  readonly withTransaction: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
41
41
  }, never, CosmosClient | Snowflake.Generator>;
42
42
  export declare const makeRunnerStorage: (options?: {
@@ -2,6 +2,7 @@ import * as Context from "effect-app/Context";
2
2
  import * as Effect from "effect-app/Effect";
3
3
  import * as Layer from "effect-app/Layer";
4
4
  import * as Fiber from "effect/Fiber";
5
+ export declare const setRootParentSpan: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
5
6
  declare const MainFiberSet_base: Context.ServiceClass<MainFiberSet, "MainFiberSet", {
6
7
  interrupt: Effect.Effect<void, never, never>;
7
8
  join: Effect.Effect<void, never, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"MainFiberSet.d.ts","sourceRoot":"","sources":["../src/MainFiberSet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AACzC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;;;;uBAwCT,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;qBA/BvC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;qBACpB,SAAS,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;;;;;;;;;;;AAiDvD;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,iBAAyD;IACzF,MAAM,CAAC,QAAQ,CAAC,IAAI,0CAAgC;IACpD,MAAM,CAAC,QAAQ,CAAC,QAAQ,mCAIvB;IACD,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAI,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,mEAAkD;IAC/G,MAAM,CAAC,QAAQ,CAAC,gBAAgB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,sEACb;CAC7D"}
1
+ {"version":3,"file":"MainFiberSet.d.ts","sourceRoot":"","sources":["../src/MainFiberSet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AAEzC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAoBrC,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,2BAC4B,CAAA;;;;uBAmCvE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;qBA/BvC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;qBACpB,SAAS,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;;;;;;;;;;;AAiDvD;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,iBAAyD;IACzF,MAAM,CAAC,QAAQ,CAAC,IAAI,0CAAgC;IACpD,MAAM,CAAC,QAAQ,CAAC,QAAQ,mCAIvB;IACD,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAI,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,mEAAkD;IAC/G,MAAM,CAAC,QAAQ,CAAC,gBAAgB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,sEACb;CAC7D"}
@@ -1,11 +1,23 @@
1
1
  import * as Context from "effect-app/Context";
2
2
  import * as Effect from "effect-app/Effect";
3
3
  import * as Layer from "effect-app/Layer";
4
+ import * as Option from "effect-app/Option";
4
5
  import * as Fiber from "effect/Fiber";
5
6
  import * as FiberSet from "effect/FiberSet";
6
7
  import { InfraLogger } from "./logger.js";
7
8
  import { reportNonInterruptedFailureCause } from "./QueueMaker/errors.js";
8
- import { setRootParentSpan } from "./RequestFiberSet.js";
9
+ // Walk to the root span so a daemon fiber's spans aren't parented to a span that
10
+ // may already be closing (e.g. the originating request).
11
+ const getRootParentSpan = Effect.gen(function* () {
12
+ let span = yield* Effect.currentSpan.pipe(Effect.catchTag("NoSuchElementError", () => Effect.succeed(null)));
13
+ if (!span)
14
+ return span;
15
+ while (span._tag === "Span" && Option.isSome(span.parent)) {
16
+ span = span.parent.value;
17
+ }
18
+ return span;
19
+ });
20
+ export const setRootParentSpan = (self) => getRootParentSpan.pipe(Effect.andThen((span) => span ? Effect.withParentSpan(self, span) : self));
9
21
  const make = Effect.gen(function* () {
10
22
  const set = yield* FiberSet.make();
11
23
  const add = (...fibers) => Effect.sync(() => fibers.forEach((_) => FiberSet.addUnsafe(set, _)));
@@ -55,4 +67,4 @@ export class MainFiberSet extends Context.Service()("MainFiberSet", { make }) {
55
67
  static run = (self) => this.pipe(Effect.andThen((_) => _.run(self)));
56
68
  static forkDaemonReport = (self) => this.pipe(Effect.andThen((_) => _.forkDaemonReport(self)));
57
69
  }
58
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWFpbkZpYmVyU2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL01haW5GaWJlclNldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG9CQUFvQixDQUFBO0FBQzdDLE9BQU8sS0FBSyxNQUFNLE1BQU0sbUJBQW1CLENBQUE7QUFDM0MsT0FBTyxLQUFLLEtBQUssTUFBTSxrQkFBa0IsQ0FBQTtBQUN6QyxPQUFPLEtBQUssS0FBSyxNQUFNLGNBQWMsQ0FBQTtBQUNyQyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBRTNDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDekMsT0FBTyxFQUFFLGdDQUFnQyxFQUFFLE1BQU0sd0JBQXdCLENBQUE7QUFDekUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sc0JBQXNCLENBQUE7QUFFeEQsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDL0IsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUksRUFBa0IsQ0FBQTtJQUNsRCxNQUFNLEdBQUcsR0FBRyxDQUFDLEdBQUcsTUFBNEIsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDckgsTUFBTSxNQUFNLEdBQUcsQ0FBQyxNQUFxQyxFQUFFLEVBQUUsQ0FDdkQsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDdEUsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQ2xDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsV0FBVyxLQUFLLHFDQUFxQyxDQUFDLENBQUMsRUFDdEcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQ25DLENBQUE7SUFDRCxNQUFNLEdBQUcsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBRTdCLGtEQUFrRDtJQUNsRCxrREFBa0Q7SUFDbEQsNkJBQTZCO0lBQzdCLGFBQWE7SUFDYixNQUFNO0lBQ04sbUZBQW1GO0lBQ25GLDhFQUE4RTtJQUM5RSx5REFBeUQ7SUFDekQsS0FBSztJQUVMLCtEQUErRDtJQUMvRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBRXpDOzs7Ozs7OztPQVFHO0lBQ0gsU0FBUyxnQkFBZ0IsQ0FBVSxJQUE0QjtRQUM3RCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQ2QsTUFBTSxDQUFDLE1BQU0sRUFDYixNQUFNLENBQUMsVUFBVSxDQUFDLGdDQUFnQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQ3ZELGlCQUFpQixFQUNqQixNQUFNLENBQUMsZUFBZSxFQUN0QixHQUFHLENBQ0osQ0FBQTtJQUNILENBQUM7SUFDRCxPQUFPO1FBQ0wsU0FBUztRQUNULElBQUk7UUFDSixnQkFBZ0I7UUFDaEIsR0FBRztRQUNILEdBQUc7UUFDSCxNQUFNO0tBQ1AsQ0FBQTtBQUNILENBQUMsQ0FBQyxDQUFBO0FBRUY7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxZQUFhLFNBQVEsT0FBTyxDQUFDLE9BQU8sRUFBZ0IsQ0FBQyxjQUFjLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUN6RixNQUFNLENBQVUsSUFBSSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNwRCxNQUFNLENBQVUsUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQ2xDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFDN0IsS0FBSyxDQUFDLGFBQWEsRUFDbkIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQ3pCLENBQUE7SUFDRCxNQUFNLENBQVUsR0FBRyxHQUFHLENBQU8sSUFBZ0MsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUMvRyxNQUFNLENBQVUsZ0JBQWdCLEdBQUcsQ0FBVSxJQUE0QixFQUFFLEVBQUUsQ0FDM0UsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO0NBQzdEIn0=
70
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWFpbkZpYmVyU2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL01haW5GaWJlclNldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG9CQUFvQixDQUFBO0FBQzdDLE9BQU8sS0FBSyxNQUFNLE1BQU0sbUJBQW1CLENBQUE7QUFDM0MsT0FBTyxLQUFLLEtBQUssTUFBTSxrQkFBa0IsQ0FBQTtBQUN6QyxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxLQUFLLE1BQU0sY0FBYyxDQUFBO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFHM0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUN6QyxPQUFPLEVBQUUsZ0NBQWdDLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQTtBQUV6RSxpRkFBaUY7QUFDakYseURBQXlEO0FBQ3pELE1BQU0saUJBQWlCLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDNUMsSUFBSSxJQUFJLEdBQTBCLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUM5RCxNQUFNLENBQUMsUUFBUSxDQUFDLG9CQUFvQixFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FDbEUsQ0FBQTtJQUNELElBQUksQ0FBQyxJQUFJO1FBQUUsT0FBTyxJQUFJLENBQUE7SUFDdEIsT0FBTyxJQUFJLENBQUMsSUFBSSxLQUFLLE1BQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQzFELElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQTtJQUMxQixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUE7QUFDYixDQUFDLENBQUMsQ0FBQTtBQUVGLE1B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
package/dist/logger.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  export declare const InfraLogger: {
2
- logWarning: (...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
3
- logError: (...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
4
- logFatal: (...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
2
+ logWarning: (...message: readonly any[]) => import("effect/Effect").Effect<void, never, never>;
3
+ logError: (...message: readonly any[]) => import("effect/Effect").Effect<void, never, never>;
4
+ logFatal: (...message: readonly any[]) => import("effect/Effect").Effect<void, never, never>;
5
5
  logInfo: (...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
6
6
  logDebug: (...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
7
- logWithLevel: (level: import("effect/LogLevel").Severity, ...message: ReadonlyArray<any>) => import("effect/Effect").Effect<void, never, never>;
7
+ logWithLevel: (level: import("effect/LogLevel").Severity, ...message: readonly any[]) => import("effect/Effect").Effect<void, never, never>;
8
8
  };
9
9
  //# sourceMappingURL=logger.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import * as S from "effect-app/Schema";
2
- import * as SchemaAST from "effect/SchemaAST";
2
+ import * as SchemaAST from "effect-app/SchemaAST";
3
3
  const get = ["Get", "Index", "List", "All", "Find", "Search"];
4
4
  const del = ["Delete", "Remove", "Destroy"];
5
5
  const patch = ["Patch", "Update", "Edit"];
@@ -42,4 +42,4 @@ export const determineMethod = (fullName, schema) => {
42
42
  return { _tag: "command", method: "POST" };
43
43
  };
44
44
  export const isCommand = (method) => method._tag === "command";
45
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcm91dGluZy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssQ0FBQyxNQUFNLG1CQUFtQixDQUFBO0FBRXRDLE9BQU8sS0FBSyxTQUFTLE1BQU0sa0JBQWtCLENBQUE7QUFFN0MsTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFBO0FBQzdELE1BQU0sR0FBRyxHQUFHLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQTtBQUMzQyxNQUFNLEtBQUssR0FBRyxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUE7QUFFekMsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLEdBQVksRUFBVyxFQUFFO0lBQ3RELHFHQUFxRztJQUNyRywrRUFBK0U7SUFDL0Usc0VBQXNFO0lBQ3RFLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUE7SUFDeEMsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLFFBQVE7UUFBRSxPQUFPLElBQUksQ0FBQTtJQUMxQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssT0FBTyxJQUFJLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFDLEVBQUUsQ0FBQztRQUMzRSxPQUFPLElBQUksQ0FBQTtJQUNiLENBQUM7SUFFRCxPQUFPLEtBQUssQ0FBQTtBQUNkLENBQUMsQ0FBQTtBQUVELE1BQU0sY0FBYyxHQUFHLENBQUMsR0FBWSxFQUFXLEVBQUU7SUFDL0MsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLE9BQU87UUFBRSxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0lBQ2hFLGlDQUFpQztJQUNqQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssU0FBUztRQUFFLE9BQU8sS0FBSyxDQUFBO0lBQ3hDLE9BQU8sR0FBRyxDQUFDLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7QUFDM0UsQ0FBQyxDQUFBO0FBRUQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxNQUE0QyxFQUFXLEVBQUU7SUFDNUUsSUFBSSxRQUFRLElBQUksTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNO1FBQUUsT0FBTyxjQUFjLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUEsQ0FBQyxtQkFBbUI7SUFDL0csT0FBTyxjQUFjLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQ25DLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLFFBQWdCLEVBQUUsTUFBYSxFQUFFLEVBQUU7SUFDakUsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNoQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUUsQ0FBQTtJQUV6QyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzlDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQWMsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFXLENBQUE7SUFDMUYsQ0FBQztJQUNELElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDOUMsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQVcsQ0FBQTtJQUN0RixDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBVyxDQUFBO0lBQ3JHLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQVcsQ0FBQTtBQUNyRCxDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxTQUFTLEdBQUcsQ0FBQyxNQUEwQyxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLFNBQVMsQ0FBQSJ9
45
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcm91dGluZy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssQ0FBQyxNQUFNLG1CQUFtQixDQUFBO0FBRXRDLE9BQU8sS0FBSyxTQUFTLE1BQU0sc0JBQXNCLENBQUE7QUFFakQsTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFBO0FBQzdELE1BQU0sR0FBRyxHQUFHLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQTtBQUMzQyxNQUFNLEtBQUssR0FBRyxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUE7QUFFekMsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLEdBQVksRUFBVyxFQUFFO0lBQ3RELHFHQUFxRztJQUNyRywrRUFBK0U7SUFDL0Usc0VBQXNFO0lBQ3RFLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUE7SUFDeEMsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLFFBQVE7UUFBRSxPQUFPLElBQUksQ0FBQTtJQUMxQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssT0FBTyxJQUFJLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLHFCQUFxQixDQUFDLEVBQUUsQ0FBQztRQUMzRSxPQUFPLElBQUksQ0FBQTtJQUNiLENBQUM7SUFFRCxPQUFPLEtBQUssQ0FBQTtBQUNkLENBQUMsQ0FBQTtBQUVELE1BQU0sY0FBYyxHQUFHLENBQUMsR0FBWSxFQUFXLEVBQUU7SUFDL0MsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLE9BQU87UUFBRSxPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0lBQ2hFLGlDQUFpQztJQUNqQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssU0FBUztRQUFFLE9BQU8sS0FBSyxDQUFBO0lBQ3hDLE9BQU8sR0FBRyxDQUFDLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7QUFDM0UsQ0FBQyxDQUFBO0FBRUQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxNQUE0QyxFQUFXLEVBQUU7SUFDNUUsSUFBSSxRQUFRLElBQUksTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNO1FBQUUsT0FBTyxjQUFjLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUEsQ0FBQyxtQkFBbUI7SUFDL0csT0FBTyxjQUFjLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQ25DLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLFFBQWdCLEVBQUUsTUFBYSxFQUFFLEVBQUU7SUFDakUsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNoQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUUsQ0FBQTtJQUV6QyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzlDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQWMsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFXLENBQUE7SUFDMUYsQ0FBQztJQUNELElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDOUMsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQVcsQ0FBQTtJQUN0RixDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBVyxDQUFBO0lBQ3JHLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQVcsQ0FBQTtBQUNyRCxDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxTQUFTLEdBQUcsQ0FBQyxNQUEwQyxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLFNBQVMsQ0FBQSJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/infra",
3
- "version": "4.0.0-beta.281",
3
+ "version": "4.0.0-beta.282",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -10,12 +10,12 @@
10
10
  "proper-lockfile": "^4.1.2",
11
11
  "pure-rand": "8.4.0",
12
12
  "query-string": "^9.4.0",
13
- "effect-app": "4.0.0-beta.281"
13
+ "effect-app": "4.0.0-beta.282"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@azure/cosmos": "^4.9.3",
17
17
  "@azure/service-bus": "^7.9.5",
18
- "@effect/sql-sqlite-node": "4.0.0-beta.86",
18
+ "@effect/sql-sqlite-node": "4.0.0-beta.88",
19
19
  "@sentry/node": "10.55.0",
20
20
  "@sentry/opentelemetry": "10.55.0",
21
21
  "@types/better-sqlite3": "^7.6.13",
@@ -34,12 +34,12 @@
34
34
  "peerDependencies": {
35
35
  "@azure/cosmos": "^4.9.3",
36
36
  "@azure/service-bus": "^7.9.5",
37
- "@effect/vitest": "^4.0.0-beta.86",
37
+ "@effect/vitest": "^4.0.0-beta.88",
38
38
  "@sendgrid/helpers": "^8.0.0",
39
39
  "@sendgrid/mail": "^8.1.6",
40
40
  "@sentry/node": "10.55.0",
41
41
  "@sentry/opentelemetry": "10.55.0",
42
- "effect": "^4.0.0-beta.86",
42
+ "effect": "^4.0.0-beta.88",
43
43
  "jwt-decode": "^4.0.0",
44
44
  "redis": "^3.1.2",
45
45
  "redlock": "^4.2.0"
@@ -1,12 +1,29 @@
1
1
  import * as Context from "effect-app/Context"
2
2
  import * as Effect from "effect-app/Effect"
3
3
  import * as Layer from "effect-app/Layer"
4
+ import * as Option from "effect-app/Option"
4
5
  import * as Fiber from "effect/Fiber"
5
6
  import * as FiberSet from "effect/FiberSet"
7
+ import type * as Tracer from "effect/Tracer"
6
8
 
7
9
  import { InfraLogger } from "./logger.ts"
8
10
  import { reportNonInterruptedFailureCause } from "./QueueMaker/errors.ts"
9
- import { setRootParentSpan } from "./RequestFiberSet.ts"
11
+
12
+ // Walk to the root span so a daemon fiber's spans aren't parented to a span that
13
+ // may already be closing (e.g. the originating request).
14
+ const getRootParentSpan = Effect.gen(function*() {
15
+ let span: Tracer.AnySpan | null = yield* Effect.currentSpan.pipe(
16
+ Effect.catchTag("NoSuchElementError", () => Effect.succeed(null))
17
+ )
18
+ if (!span) return span
19
+ while (span._tag === "Span" && Option.isSome(span.parent)) {
20
+ span = span.parent.value
21
+ }
22
+ return span
23
+ })
24
+
25
+ export const setRootParentSpan = <A, E, R>(self: Effect.Effect<A, E, R>) =>
26
+ getRootParentSpan.pipe(Effect.andThen((span) => span ? Effect.withParentSpan(self, span) : self))
10
27
 
11
28
  const make = Effect.gen(function*() {
12
29
  const set = yield* FiberSet.make<unknown, never>()
@@ -1,6 +1,6 @@
1
1
  import * as S from "effect-app/Schema"
2
2
  import type { AST } from "effect-app/Schema"
3
- import * as SchemaAST from "effect/SchemaAST"
3
+ import * as SchemaAST from "effect-app/SchemaAST"
4
4
 
5
5
  const get = ["Get", "Index", "List", "All", "Find", "Search"]
6
6
  const del = ["Delete", "Remove", "Destroy"]
@@ -1,186 +0,0 @@
1
- import { reportNonInterruptedFailure } from "@effect-app/infra/QueueMaker/errors"
2
- import { subMinutes } from "date-fns"
3
- import type { NonEmptyReadonlyArray } from "effect-app/Array"
4
- import * as Effect from "effect-app/Effect"
5
- import * as Option from "effect-app/Option"
6
- import { type QueueBase, QueueMeta } from "effect-app/QueueMaker"
7
- import * as S from "effect-app/Schema"
8
- import { type NonEmptyString255 } from "effect-app/Schema"
9
- import { getRequestContext, setupRequestContextWithCustomSpan } from "effect-app/setupRequest"
10
- import { pretty } from "effect-app/utils"
11
- import * as Fiber from "effect/Fiber"
12
- import * as Tracer from "effect/Tracer"
13
- import { SqlClient } from "effect/unstable/sql"
14
- import { InfraLogger } from "../logger.ts"
15
- import { messagingSpanArgs } from "../otel.ts"
16
- import { SQLModel } from "../SQL.ts"
17
-
18
- export const QueueId = S.Finite.pipe(S.brand("QueueId"))
19
- export type QueueId = typeof QueueId.Type
20
-
21
- // TODO: let the model track and Auto Generate versionColumn on every update instead
22
- export const makeSQLQueue = Effect.fnUntraced(function*<
23
- Evt extends { id: S.StringId; _tag: string },
24
- DrainEvt extends { id: S.StringId; _tag: string },
25
- EvtE,
26
- DrainEvtE
27
- >(
28
- queueName: NonEmptyString255,
29
- queueDrainName: NonEmptyString255,
30
- schema: S.Codec<Evt, EvtE>,
31
- drainSchema: S.Codec<DrainEvt, DrainEvtE>
32
- ) {
33
- const base = {
34
- id: SQLModel.Generated(QueueId),
35
- meta: SQLModel.JsonFromString(QueueMeta),
36
- name: S.NonEmptyString255,
37
- createdAt: SQLModel.DateTimeInsert,
38
- updatedAt: SQLModel.DateTimeUpdate,
39
- // TODO: at+owner
40
- processingAt: SQLModel.FieldOption(S.Date),
41
- finishedAt: SQLModel.FieldOption(S.Date),
42
- etag: S.String // TODO: use a SQLModel thing that auto updates it?
43
- // TODO: record locking.. / optimistic locking
44
- // rowVersion: SQLModel.DateTimeFromNumberWithNow
45
- }
46
- class Queue extends SQLModel.Class<Queue>("Queue")({
47
- body: SQLModel.JsonFromString(schema),
48
- ...base
49
- }) {}
50
- class Drain extends SQLModel.Class<Drain>("Drain")({
51
- body: SQLModel.JsonFromString(drainSchema),
52
- ...base
53
- }) {}
54
- const sql = yield* SqlClient.SqlClient
55
-
56
- const queueRepo = yield* SQLModel.makeRepository(Queue, {
57
- tableName: "queue",
58
- spanPrefix: "QueueRepo",
59
- idColumn: "id",
60
- versionColumn: "etag"
61
- })
62
-
63
- const drainRepo = yield* SQLModel.makeRepository(Drain, {
64
- tableName: "queue",
65
- spanPrefix: "DrainRepo",
66
- idColumn: "id",
67
- versionColumn: "etag"
68
- })
69
-
70
- const decodeDrain = S.decodeEffectConcurrently(Drain)
71
-
72
- const drain = Effect.gen(function*() {
73
- const limit = subMinutes(new Date(), 15)
74
- return yield* sql<typeof Drain.Encoded>`SELECT *
75
- FROM queue
76
- WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
77
- LIMIT 1`
78
- })
79
-
80
- const q = {
81
- offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
82
- yield* queueRepo.insertVoid(Queue.insert.make({
83
- body,
84
- meta,
85
- name: queueName,
86
- processingAt: Option.none(),
87
- finishedAt: Option.none(),
88
- etag: crypto.randomUUID()
89
- }))
90
- }),
91
- take: Effect.gen(function*() {
92
- while (true) {
93
- const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
94
- if (first) {
95
- const dec = yield* decodeDrain(first)
96
- const { createdAt, updatedAt, ...rest } = dec
97
- return yield* drainRepo.update(
98
- Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
99
- )
100
- }
101
- if (first) return first
102
- yield* Effect.sleep(250)
103
- }
104
- }),
105
- finish: Effect.fn(function*({ createdAt, updatedAt, ...q }: Drain) {
106
- return yield* drainRepo.updateVoid(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) })) // auto in lib , etag: crypto.randomUUID()
107
- })
108
- }
109
- const queue = {
110
- publish: Effect.fn(`publish ${queueName}`, {
111
- kind: "producer",
112
- attributes: {
113
- "messaging.system": "sql",
114
- "messaging.operation.name": "publish",
115
- "messaging.destination.name": queueName
116
- }
117
- })(function*(
118
- ...messages: NonEmptyReadonlyArray<Evt>
119
- ) {
120
- yield* Effect.annotateCurrentSpan({
121
- "messaging.batch.message_count": messages.length,
122
- "messaging.message.types": messages.map((_) => _._tag)
123
- })
124
- const requestContext = yield* getRequestContext
125
- yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
126
- }),
127
- drain: <DrainE, DrainR>(
128
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
129
- sessionId?: string
130
- ) => {
131
- const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
132
- const processMessage = Effect.fnUntraced(function*({ body, meta }: Drain) {
133
- let effect = InfraLogger
134
- .logDebug(`[${queueDrainName}] Processing incoming message`)
135
- .pipe(
136
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
137
- Effect.andThen(handleEvent(body)),
138
- silenceAndReportError,
139
- (_) => {
140
- const args = messagingSpanArgs({
141
- operation: "process",
142
- system: "sql",
143
- destination: queueDrainName,
144
- messageId: body.id,
145
- conversationId: sessionId,
146
- extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
147
- }, "consumer")
148
- return setupRequestContextWithCustomSpan(
149
- _,
150
- meta,
151
- args.name,
152
- {
153
- captureStackTrace: false,
154
- kind: args.kind,
155
- attributes: args.attributes
156
- }
157
- )
158
- }
159
- )
160
- if (meta.span) {
161
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
162
- }
163
- return yield* effect
164
- })
165
-
166
- return Effect.fn(`receive ${queueDrainName}`, {
167
- kind: "consumer",
168
- attributes: {
169
- "messaging.system": "sql",
170
- "messaging.operation.name": "receive",
171
- "messaging.destination.name": queueDrainName,
172
- ...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
173
- }
174
- })(function*() {
175
- const x = yield* q.take
176
- yield* processMessage(x).pipe(
177
- Effect.uninterruptible,
178
- Effect.forkChild,
179
- Effect.flatMap(Fiber.join),
180
- Effect.tap(q.finish(x))
181
- )
182
- }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
183
- }
184
- }
185
- return queue as QueueBase<Evt, DrainEvt>
186
- })
@@ -1,136 +0,0 @@
1
- import type { NonEmptyReadonlyArray } from "effect-app/Array"
2
- import * as Effect from "effect-app/Effect"
3
- import { QueueMeta } from "effect-app/QueueMaker"
4
- import * as S from "effect-app/Schema"
5
- import { getRequestContext, setupRequestContextWithCustomSpan } from "effect-app/setupRequest"
6
- import { pretty } from "effect-app/utils"
7
- import * as Cause from "effect/Cause"
8
- import * as Fiber from "effect/Fiber"
9
- import { flow } from "effect/Function"
10
- import * as Q from "effect/Queue"
11
- import * as Tracer from "effect/Tracer"
12
- import { InfraLogger } from "../logger.ts"
13
- import { MemQueue } from "../memQueue.ts"
14
- import { messagingSpanArgs } from "../otel.ts"
15
- import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.ts"
16
-
17
- export const makeMemQueue = Effect.fnUntraced(function*<
18
- Evt extends { id: S.StringId; _tag: string },
19
- DrainEvt extends { id: S.StringId; _tag: string },
20
- EvtE,
21
- DrainEvtE
22
- >(
23
- queueName: string,
24
- queueDrainName: string,
25
- schema: S.Codec<Evt, EvtE>,
26
- drainSchema: S.Codec<DrainEvt, DrainEvtE>
27
- ) {
28
- const mem = yield* MemQueue
29
- const q = yield* mem.getOrCreateQueue(queueName)
30
- const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
31
-
32
- const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
33
- const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
34
- const encodePublish = S.encodeEffect(wireSchemaJson)
35
- const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
36
- const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
37
-
38
- const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
39
-
40
- const queue = {
41
- publish: Effect.fn(`publish ${queueName}`, {
42
- kind: "producer",
43
- attributes: {
44
- "messaging.system": "memory",
45
- "messaging.operation.name": "publish",
46
- "messaging.destination.name": queueName
47
- }
48
- })(function*(
49
- ...messages: NonEmptyReadonlyArray<Evt>
50
- ) {
51
- yield* Effect.annotateCurrentSpan({
52
- "messaging.batch.message_count": messages.length,
53
- "messaging.message.types": messages.map((_) => _._tag)
54
- })
55
- const requestContext = yield* getRequestContext
56
- // we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
57
- yield* Effect.forEach(
58
- messages,
59
- (m) =>
60
- encodePublish({ body: m, meta: requestContext }).pipe(
61
- Effect.orDie,
62
- Effect.flatMap((_) => Q.offer(q, _))
63
- ),
64
- { discard: true }
65
- )
66
- }),
67
- drain: <DrainE, DrainR>(
68
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
69
- sessionId?: string
70
- ) => {
71
- const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
72
- const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
73
- const processMessage = Effect.fnUntraced(function*(msg: string) {
74
- // we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
75
- const { body, meta } = yield* parseDrain(msg).pipe(Effect.orDie)
76
- let effect = InfraLogger
77
- .logDebug(`[${queueDrainName}] Processing incoming message`)
78
- .pipe(
79
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
80
- Effect.andThen(handleEvent(body)),
81
- silenceAndReportError,
82
- (_) => {
83
- const args = messagingSpanArgs({
84
- operation: "process",
85
- system: "memory",
86
- destination: queueDrainName,
87
- messageId: body.id,
88
- conversationId: sessionId,
89
- extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
90
- }, "consumer")
91
- return setupRequestContextWithCustomSpan(
92
- _,
93
- meta,
94
- args.name,
95
- {
96
- captureStackTrace: false,
97
- kind: args.kind,
98
- attributes: args.attributes
99
- }
100
- )
101
- }
102
- )
103
- if (meta.span) {
104
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
105
- }
106
- return yield* effect
107
- })
108
- return Effect.fn(`receive ${queueDrainName}`, {
109
- kind: "consumer",
110
- attributes: {
111
- "messaging.system": "memory",
112
- "messaging.operation.name": "receive",
113
- "messaging.destination.name": queueDrainName,
114
- ...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
115
- }
116
- })(function*() {
117
- const x = yield* Q.take(qDrain)
118
- const exit = yield* processMessage(x).pipe(
119
- Effect.uninterruptible,
120
- Effect.forkChild,
121
- Effect.flatMap(Fiber.join)
122
- )
123
- if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) {
124
- // normally a failed item would be returned to the queue and retried up to X times.
125
- yield* Q.offer(qDrain, x).pipe(
126
- // TODO: retry count tracking and max retries.
127
- Effect.delay("5 seconds"),
128
- Effect.tapCause(reportError),
129
- Effect.forkDetach
130
- )
131
- }
132
- }, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
133
- }
134
- }
135
- return queue
136
- })
@@ -1,125 +0,0 @@
1
- import type { NonEmptyReadonlyArray } from "effect-app/Array"
2
- import * as Effect from "effect-app/Effect"
3
- import { QueueMeta } from "effect-app/QueueMaker"
4
- import * as S from "effect-app/Schema"
5
- import type { StringId } from "effect-app/Schema"
6
- import { getRequestContext, setupRequestContextWithCustomSpan } from "effect-app/setupRequest"
7
- import { pretty } from "effect-app/utils"
8
- import * as Cause from "effect/Cause"
9
- import { flow } from "effect/Function"
10
- import * as Tracer from "effect/Tracer"
11
- import { InfraLogger } from "../logger.ts"
12
- import { messagingSpanArgs } from "../otel.ts"
13
- import { Receiver, Sender } from "../ServiceBus.ts"
14
- import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.ts"
15
-
16
- export function makeServiceBusQueue<
17
- Evt extends { id: StringId; _tag: string },
18
- DrainEvt extends { id: StringId; _tag: string },
19
- EvtE,
20
- DrainEvtE
21
- >(
22
- schema: S.Codec<Evt, EvtE>,
23
- drainSchema: S.Codec<DrainEvt, DrainEvtE>
24
- ) {
25
- const wireSchema = S.Struct({
26
- body: schema,
27
- meta: QueueMeta
28
- })
29
- const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
30
- const encodePublish = S.encodeEffect(wireSchemaJson)
31
- const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
32
- const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
33
- const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
34
-
35
- return Effect.gen(function*() {
36
- const sender = yield* Sender
37
- const receiver = yield* Receiver
38
- const silenceAndReportError = reportNonInterruptedFailure({ name: receiver.name })
39
- const reportError = reportNonInterruptedFailureCause({ name: receiver.name })
40
-
41
- // TODO: or do async?
42
- // This will make sure that the host receives the error (MainFiberSet.join), who will then interrupt everything and commence a shutdown and restart of app
43
- // const deferred = yield* Deferred.make<never, ServiceBusError | Error>()
44
-
45
- const queue = {
46
- drain: <DrainE, DrainR>(
47
- handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
48
- sessionId?: string
49
- ) => {
50
- const processMessage = Effect.fnUntraced(function*(messageBody: unknown) {
51
- const { body, meta } = yield* parseDrain(messageBody).pipe(Effect.orDie)
52
- let effect = InfraLogger
53
- .logDebug(`[${receiver.name}] Processing incoming message`)
54
- .pipe(
55
- Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
56
- Effect.andThen(handleEvent(body)),
57
- Effect.orDie,
58
- // we silenceAndReportError here, so that the error is reported, and moves into the Exit.
59
- silenceAndReportError,
60
- (_) => {
61
- const args = messagingSpanArgs({
62
- operation: "process",
63
- system: "servicebus",
64
- destination: receiver.name,
65
- messageId: body.id,
66
- conversationId: sessionId,
67
- extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
68
- }, "consumer")
69
- return setupRequestContextWithCustomSpan(
70
- _,
71
- meta,
72
- args.name,
73
- {
74
- captureStackTrace: false,
75
- kind: args.kind,
76
- attributes: args.attributes
77
- }
78
- )
79
- }
80
- )
81
- if (meta.span) {
82
- effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
83
- }
84
- // we reportError here, so that we report the error only, and keep flowing
85
- const exit = yield* Effect.tapCause(effect, reportError)
86
- return yield* exit
87
- })
88
-
89
- return receiver
90
- .subscribe({
91
- processMessage: (x) => processMessage(x.body).pipe(Effect.uninterruptible),
92
- processError: (err) => reportQueueError(Cause.fail(err.error))
93
- }, sessionId)
94
- .pipe(Effect.andThen(Effect.never))
95
- },
96
-
97
- publish: Effect.fn(`publish ${sender.name}`, {
98
- kind: "producer",
99
- attributes: {
100
- "messaging.system": "servicebus",
101
- "messaging.operation.name": "publish",
102
- "messaging.destination.name": sender.name
103
- }
104
- })(function*(...messages: NonEmptyReadonlyArray<Evt>) {
105
- yield* Effect.annotateCurrentSpan({
106
- "messaging.batch.message_count": messages.length,
107
- "messaging.message.types": messages.map((_) => _._tag)
108
- })
109
- const requestContext = yield* getRequestContext
110
- const msgs = yield* Effect.forEach(messages, (m) =>
111
- encodePublish({ body: m, meta: requestContext }).pipe(
112
- Effect.orDie,
113
- Effect.map((body) => ({
114
- body,
115
- messageId: m.id, /* correllationid: requestId */
116
- contentType: "application/json",
117
- sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
118
- }))
119
- ))
120
- yield* sender.sendMessages(msgs)
121
- })
122
- }
123
- return queue
124
- })
125
- }
@@ -1,110 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import * as Context from "effect-app/Context"
3
- import * as Effect from "effect-app/Effect"
4
- import * as Layer from "effect-app/Layer"
5
- import * as Option from "effect-app/Option"
6
- import * as Fiber from "effect/Fiber"
7
- import * as FiberSet from "effect/FiberSet"
8
- import type * as Tracer from "effect/Tracer"
9
- import { InfraLogger } from "./logger.ts"
10
- import { reportRequestError, reportUnknownRequestError } from "./reportError.ts"
11
-
12
- const getRootParentSpan = Effect.gen(function*() {
13
- let span: Tracer.AnySpan | null = yield* Effect.currentSpan.pipe(
14
- Effect.catchTag("NoSuchElementError", () => Effect.succeed(null))
15
- )
16
- if (!span) return span
17
- while (span._tag === "Span" && Option.isSome(span.parent)) {
18
- span = span.parent.value
19
- }
20
- return span
21
- })
22
-
23
- export const setRootParentSpan = <A, E, R>(self: Effect.Effect<A, E, R>) =>
24
- getRootParentSpan.pipe(Effect.andThen((span) => span ? Effect.withParentSpan(self, span) : self))
25
-
26
- const make = Effect.gen(function*() {
27
- const set = yield* FiberSet.make<any, any>()
28
- const add = (...fibers: Fiber.Fiber<any, any>[]) =>
29
- Effect.sync(() => fibers.forEach((_) => FiberSet.addUnsafe(set, _)))
30
- const addAll = (fibers: readonly Fiber.Fiber<any, any>[]) =>
31
- Effect.sync(() => fibers.forEach((_) => FiberSet.addUnsafe(set, _)))
32
- const join = FiberSet.size(set).pipe(
33
- Effect.andThen((count) => InfraLogger.logInfo(`Joining ${count} current fibers on the RequestFiberSet`)),
34
- Effect.andThen(FiberSet.join(set))
35
- )
36
- const run = FiberSet.run(set)
37
- const register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
38
- self.pipe(Effect.forkChild, Effect.tap(add), Effect.andThen(Fiber.join))
39
-
40
- // const waitUntilEmpty = Effect.gen(function*() {
41
- // const currentSize = yield* FiberSet.size(set)
42
- // if (currentSize === 0) {
43
- // return
44
- // }
45
- // yield* Effect.logInfo("Waiting RequestFiberSet to be empty: " + currentSize)
46
- // while ((yield* FiberSet.size(set)) > 0) yield* Effect.sleep("250 millis")
47
- // yield* Effect.logDebug("RequestFiberSet is empty")
48
- // })
49
- // TODO: loop and interrupt all fibers in the set continuously?
50
- const interrupt = Fiber.interruptAll(set)
51
-
52
- /**
53
- * Forks the effect into a new fiber attached to the RequestFiberSet scope. Because the
54
- * new fiber isn't attached to the parent, when the fiber executing the
55
- * returned effect terminates, the forked fiber will continue running.
56
- * The fiber will be interrupted when the RequestFiberSet scope is closed.
57
- *
58
- * Reports errors.
59
- */
60
- function forkDaemonReport<R, E, A>(self: Effect.Effect<A, E, R>) {
61
- return self.pipe(
62
- reportRequestError,
63
- Effect.uninterruptible,
64
- run
65
- )
66
- }
67
-
68
- /**
69
- * Forks the effect into a new fiber attached to the RequestFiberSet scope. Because the
70
- * new fiber isn't attached to the parent, when the fiber executing the
71
- * returned effect terminates, the forked fiber will continue running.
72
- * The fiber will be interrupted when the RequestFiberSet scope is closed.
73
- *
74
- * Reports unexpected errors.
75
- */
76
- function forkDaemonReportUnexpected<R, E, A>(self: Effect.Effect<A, E, R>) {
77
- return self
78
- .pipe(
79
- reportUnknownRequestError,
80
- Effect.uninterruptible,
81
- run
82
- )
83
- }
84
-
85
- return {
86
- interrupt,
87
- join,
88
- run,
89
- add,
90
- addAll,
91
- register,
92
- forkDaemonReport,
93
- forkDaemonReportUnexpected
94
- }
95
- })
96
-
97
- /**
98
- * Whenever you fork a fiber for a Request, and you want to prevent dependent services to close prematurely on interruption,
99
- * like the ServiceBus Sender, you should register these fibers in this FiberSet.
100
- */
101
- export class RequestFiberSet extends Context.Service<RequestFiberSet>()("RequestFiberSet", { make }) {
102
- static readonly Live = Layer.effect(this, this.make)
103
- static readonly register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
104
- this.pipe(Effect.andThen((_) => _.register(self)))
105
- static readonly run = <A, E, R>(self: Effect.Effect<A, E, R>) => this.pipe(Effect.andThen((_) => _.run(self)))
106
- static readonly forkDaemonReport = <R, E, A>(self: Effect.Effect<A, E, R>) =>
107
- this.pipe(Effect.andThen((_) => _.forkDaemonReport(self)))
108
- static readonly forkDaemonReportUnexpected = <R, E, A>(self: Effect.Effect<A, E, R>) =>
109
- this.pipe(Effect.andThen((_) => _.forkDaemonReportUnexpected(self)))
110
- }
package/src/ServiceBus.ts DELETED
@@ -1,219 +0,0 @@
1
- /* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
2
- import { type OperationOptionsBase, type ProcessErrorArgs, ServiceBusClient, type ServiceBusMessage, type ServiceBusMessageBatch, type ServiceBusReceivedMessage, type ServiceBusReceiver } from "@azure/service-bus"
3
- import * as Context from "effect-app/Context"
4
- import * as Effect from "effect-app/Effect"
5
- import * as Layer from "effect-app/Layer"
6
- import * as Cause from "effect/Cause"
7
- import * as Exit from "effect/Exit"
8
- import * as FiberSet from "effect/FiberSet"
9
- import type * as Scope from "effect/Scope"
10
- import { InfraLogger } from "./logger.ts"
11
-
12
- const logged = (name: string) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
13
- Effect.logInfo(name).pipe(
14
- Effect.andThen(self),
15
- Effect.tap(Effect.logInfo(name + " done")),
16
- Effect.withLogSpan(name)
17
- )
18
-
19
- function makeClient(url: string) {
20
- return Effect.acquireRelease(
21
- Effect.sync(() => new ServiceBusClient(url)).pipe(logged("ServiceBus.client.create")),
22
- (client) => Effect.promise(() => client.close()).pipe(logged("ServiceBus.client.close"))
23
- )
24
- }
25
-
26
- export class ServiceBusClientTag
27
- extends Context.Opaque<ServiceBusClientTag, ServiceBusClient>()("@services/Client", { make: makeClient })
28
- {
29
- static readonly layer = (url: string) => this.toLayer(this.make(url))
30
- }
31
-
32
- const makeSender_ = Effect.fnUntraced(function*(queueName: string) {
33
- const serviceBusClient = yield* ServiceBusClientTag
34
-
35
- return yield* Effect.acquireRelease(
36
- Effect.sync(() => serviceBusClient.createSender(queueName)).pipe(
37
- logged(`ServiceBus.sender.create ${queueName}`)
38
- ),
39
- (sender) => Effect.promise(() => sender.close()).pipe(logged(`ServiceBus.sender.close ${queueName}`))
40
- )
41
- })
42
-
43
- const makeSender = Effect.fnUntraced(function*(name: string) {
44
- const sender = yield* makeSender_(name)
45
- const sendMessages = Effect.fnUntraced(function*(
46
- messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
47
- options?: Omit<OperationOptionsBase, "abortSignal">
48
- ) {
49
- return yield* Effect.promise((abortSignal) => sender.sendMessages(messages, { ...options, abortSignal }))
50
- })
51
-
52
- return { name, sendMessages }
53
- })
54
-
55
- export class Sender extends Context.Opaque<Sender, {
56
- name: string
57
- sendMessages: (
58
- messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
59
- options?: Omit<OperationOptionsBase, "abortSignal">
60
- ) => Effect.Effect<void>
61
- }>()("Sender", { make: makeSender }) {
62
- static readonly layer = (name: string) => this.toLayer(this.make(name))
63
- }
64
-
65
- export const SenderTag = <Id>() => <Key extends string>(queueName: Key) => {
66
- const tag = Context.Service<Id, Sender>(`ServiceBus.Sender.${queueName}`)
67
-
68
- return Object.assign(tag, {
69
- layer: Layer.effect(
70
- tag,
71
- Sender.make(queueName).pipe(Effect.map(Sender.of))
72
- )
73
- })
74
- }
75
-
76
- const makeReceiver = Effect.fnUntraced(function*(name: string) {
77
- const serviceBusClient = yield* ServiceBusClientTag
78
-
79
- const makeReceiver = Effect.fnUntraced(
80
- function*(queueName: string, waitTillEmpty: Effect.Effect<void>, sessionId?: string) {
81
- const annotate = sessionId !== undefined
82
- ? Effect.annotateLogs({ "messaging.session.id": sessionId })
83
- : <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => self
84
- return yield* Effect.acquireRelease(
85
- (sessionId
86
- ? Effect.promise(() => serviceBusClient.acceptSession(queueName, sessionId))
87
- : Effect.sync(() => serviceBusClient.createReceiver(queueName)))
88
- .pipe(logged(`ServiceBus.receiver.create ${queueName}`), annotate),
89
- (r) =>
90
- waitTillEmpty.pipe(
91
- logged(`ServiceBus.receiver.waitTillEmpty ${queueName}`),
92
- Effect.andThen(
93
- Effect.promise(() => r.close()).pipe(
94
- logged(`ServiceBus.receiver.close ${queueName}`)
95
- )
96
- ),
97
- logged(`ServiceBus.receiver.release ${queueName}`),
98
- annotate
99
- )
100
- )
101
- }
102
- )
103
-
104
- const make = (waitTillEmpty: Effect.Effect<void>) => makeReceiver(name, waitTillEmpty)
105
-
106
- const makeSession = (sessionId: string, waitTillEmpty: Effect.Effect<void>) =>
107
- makeReceiver(name, waitTillEmpty, sessionId)
108
-
109
- return {
110
- name,
111
- make,
112
- makeSession,
113
- subscribe: Effect.fnUntraced(function*<RMsg, RErr>(hndlr: MessageHandlers<RMsg, RErr>, sessionId?: string) {
114
- const fs = yield* FiberSet.make()
115
- const fr = yield* FiberSet.runtime(fs)<RMsg | RErr>()
116
- const wait = Effect
117
- .gen(function*() {
118
- if ((yield* FiberSet.size(fs)) > 0) {
119
- yield* InfraLogger.logDebug("Waiting ServiceBusFiberSet to be empty: " + (yield* FiberSet.size(fs)))
120
- }
121
- while ((yield* FiberSet.size(fs)) > 0) yield* Effect.sleep("250 millis")
122
- })
123
- const r = yield* sessionId
124
- ? makeSession(
125
- sessionId,
126
- wait
127
- )
128
- : make(wait)
129
-
130
- const runEffect = <E>(effect: Effect.Effect<void, E, RMsg | RErr>) =>
131
- new Promise<void>((resolve, reject) =>
132
- fr(effect)
133
- .addObserver((exit) => {
134
- if (Exit.isSuccess(exit)) {
135
- resolve(exit.value)
136
- } else {
137
- // disable @typescript-eslint/prefer-promise-reject-errors
138
- reject(Cause.pretty(exit.cause))
139
- }
140
- })
141
- )
142
-
143
- const annotate = sessionId !== undefined
144
- ? Effect.annotateLogs({ "messaging.session.id": sessionId })
145
- : <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => self
146
- yield* Effect.acquireRelease(
147
- Effect
148
- .sync(() => {
149
- const s = r
150
- .subscribe({
151
- processError: (err) =>
152
- runEffect(
153
- hndlr
154
- .processError(err)
155
- .pipe(
156
- Effect.catchCause((cause) => Effect.logError("ServiceBus Error", cause)),
157
- annotate
158
- )
159
- ),
160
- processMessage: (msg) => runEffect(hndlr.processMessage(msg).pipe(annotate))
161
- // DO NOT CATCH ERRORS here as they should return to the queue!
162
- })
163
- return { close: Effect.promise(() => s.close()) }
164
- })
165
- .pipe(logged("ServiceBus.subscription.create"), annotate),
166
- (subscription) =>
167
- subscription.close.pipe(
168
- logged("ServiceBus.subscription.close"),
169
- annotate
170
- )
171
- ) as Effect.Effect<void, never, Scope.Scope> // wth is going on here
172
- })
173
- }
174
- })
175
-
176
- export class Receiver extends Context.Opaque<Receiver, {
177
- name: string
178
- make: (waitTillEmpty: Effect.Effect<void>) => Effect.Effect<ServiceBusReceiver, never, Scope.Scope>
179
- makeSession: (
180
- sessionId: string,
181
- waitTillEmpty: Effect.Effect<void>
182
- ) => Effect.Effect<ServiceBusReceiver, never, Scope.Scope>
183
- subscribe<RMsg, RErr>(
184
- hndlr: MessageHandlers<RMsg, RErr>,
185
- sessionId?: string
186
- ): Effect.Effect<void, never, Scope.Scope | RMsg | RErr>
187
- }>()("Receiver") {
188
- static readonly make = makeReceiver
189
- static readonly layer = (name: string) => this.toLayer(makeReceiver(name))
190
- }
191
-
192
- export const ReceiverTag = <Id>() => <Key extends string>(queueName: Key) => {
193
- const tag = Context.Service<Id, Receiver>(`ServiceBus.Receiver.${queueName}`)
194
-
195
- return Object.assign(tag, {
196
- layer: Layer.effect(
197
- tag,
198
- makeReceiver(queueName).pipe(Effect.map(Receiver.of))
199
- )
200
- })
201
- }
202
-
203
- export const SenderReceiver = (queue: string, queueDrain?: string) =>
204
- Layer.mergeAll(Sender.layer(queue), Receiver.layer(queueDrain ?? queue))
205
-
206
- export interface MessageHandlers<RMsg, RErr> {
207
- /**
208
- * Handler that processes messages from service bus.
209
- *
210
- * @param message - A message received from Service Bus.
211
- */
212
- processMessage(message: ServiceBusReceivedMessage): Effect.Effect<void, never, RMsg>
213
- /**
214
- * Handler that processes errors that occur during receiving.
215
- * @param args - The error and additional context to indicate where
216
- * the error originated.
217
- */
218
- processError(args: ProcessErrorArgs): Effect.Effect<void, never, RErr>
219
- }
package/src/memQueue.ts DELETED
@@ -1,22 +0,0 @@
1
- import * as Context from "effect-app/Context"
2
- import * as Effect from "effect-app/Effect"
3
- import * as Queue from "effect/Queue"
4
-
5
- const make = Effect
6
- .gen(function*() {
7
- const store = yield* Effect.sync(() => new Map<string, Queue.Queue<string>>())
8
-
9
- return {
10
- getOrCreateQueue: Effect.fnUntraced(function*(k: string) {
11
- const q = store.get(k)
12
- if (q) return q
13
- const newQ = yield* Queue.unbounded<string>()
14
- store.set(k, newQ)
15
- return newQ
16
- })
17
- }
18
- })
19
-
20
- export class MemQueue extends Context.Opaque<MemQueue>()("effect-app/MemQueue", { make }) {
21
- static readonly Live = this.toLayer(this.make)
22
- }