ff-effect 0.0.2 → 0.0.3

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.cjs CHANGED
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  extract: () => extract,
24
- runPromiseUnwrapped: () => runPromiseUnwrapped
24
+ runPromiseUnwrapped: () => runPromiseUnwrapped,
25
+ wrapClient: () => wrapClient
25
26
  });
26
27
  module.exports = __toCommonJS(index_exports);
27
28
 
@@ -48,9 +49,28 @@ async function runPromiseUnwrapped(effect) {
48
49
  }
49
50
  });
50
51
  }
52
+
53
+ // src/wrap-client.ts
54
+ var import_effect3 = require("effect");
55
+ function isMessage(x) {
56
+ return typeof x === "string";
57
+ }
58
+ function wrapClient(opts) {
59
+ return (func, overrides) => import_effect3.Effect.tryPromise({
60
+ try: () => func(opts.client),
61
+ catch: (cause) => {
62
+ const message = overrides?.error != null ? isMessage(overrides.error) ? overrides.error : overrides.error(cause) : void 0;
63
+ return opts.error({
64
+ cause,
65
+ ...message != null && { message }
66
+ });
67
+ }
68
+ });
69
+ }
51
70
  // Annotate the CommonJS export names for ESM import in node:
52
71
  0 && (module.exports = {
53
72
  extract,
54
- runPromiseUnwrapped
73
+ runPromiseUnwrapped,
74
+ wrapClient
55
75
  });
56
76
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/extract.ts","../src/run-promise-unwrapped.ts"],"sourcesContent":["export { extract } from './extract.js';\nexport { runPromiseUnwrapped } from './run-promise-unwrapped.js';\n","import { Context, Effect, pipe } from \"effect\";\n\ntype InferClass<T> = T extends new (...args: any[]) => infer R ? R : never;\n\n// biome-ignore lint/suspicious/noExplicitAny: intended\nexport function extract<\n\tP extends any[],\n\tA,\n\tE,\n\tR,\n\tINFERRED_EXCLUDED extends Context.Tag<any, any> = never,\n\tEXCLUDED = InferClass<INFERRED_EXCLUDED>,\n>(\n\teffect: (...params: P) => Effect.Effect<A, E, R>,\n\toptions?: { exclude?: Array<INFERRED_EXCLUDED> }\n): Effect.Effect<\n\t(...params: P) => Effect.Effect<A, E, Extract<R, EXCLUDED>>,\n\tnever,\n\tExclude<R, EXCLUDED>\n> {\n\t// @ts-expect-error quite hard to type, check unit test\n\treturn Effect.gen(function* () {\n\t\tconst runtime = yield* Effect.runtime();\n\n\t\tconst context = runtime.context.pipe(\n\t\t\toptions?.exclude ? Context.omit(...options.exclude) : (e) => e\n\t\t) as Context.Context<Exclude<R, EXCLUDED>>;\n\n\t\treturn (...params: P) =>\n\t\t\tpipe(effect(...params), Effect.provide(context));\n\t});\n}\n","import { Cause, Effect, Exit } from \"effect\";\n\n/**\n * A simple wrapper around Effect.runPromiseExit that throws the error if it's a failure\n **/\nexport async function runPromiseUnwrapped<A, E>(\n\teffect: Effect.Effect<A, E, never>\n) {\n\tconst exit = await Effect.runPromiseExit(effect);\n\treturn Exit.match(exit, {\n\t\tonSuccess: (value) => value,\n\t\tonFailure: (cause) => {\n\t\t\tthrow Cause.isFailType(cause) ? cause.error : cause;\n\t\t},\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAsC;AAK/B,SAAS,QAQf,QACA,SAKC;AAED,SAAO,qBAAO,IAAI,aAAa;AAC9B,UAAM,UAAU,OAAO,qBAAO,QAAQ;AAEtC,UAAM,UAAU,QAAQ,QAAQ;AAAA,MAC/B,SAAS,UAAU,sBAAQ,KAAK,GAAG,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC9D;AAEA,WAAO,IAAI,eACV,oBAAK,OAAO,GAAG,MAAM,GAAG,qBAAO,QAAQ,OAAO,CAAC;AAAA,EACjD,CAAC;AACF;;;AC/BA,IAAAA,iBAAoC;AAKpC,eAAsB,oBACrB,QACC;AACD,QAAM,OAAO,MAAM,sBAAO,eAAe,MAAM;AAC/C,SAAO,oBAAK,MAAM,MAAM;AAAA,IACvB,WAAW,CAAC,UAAU;AAAA,IACtB,WAAW,CAAC,UAAU;AACrB,YAAM,qBAAM,WAAW,KAAK,IAAI,MAAM,QAAQ;AAAA,IAC/C;AAAA,EACD,CAAC;AACF;","names":["import_effect"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/extract.ts","../src/run-promise-unwrapped.ts","../src/wrap-client.ts"],"sourcesContent":["export { extract } from './extract.js';\nexport { runPromiseUnwrapped } from './run-promise-unwrapped.js';\nexport { wrapClient } from './wrap-client.js';\n","import { Context, Effect, pipe } from \"effect\";\n\ntype InferClass<T> = T extends new (...args: any[]) => infer R ? R : never;\n\n// biome-ignore lint/suspicious/noExplicitAny: intended\nexport function extract<\n\tP extends any[],\n\tA,\n\tE,\n\tR,\n\tINFERRED_EXCLUDED extends Context.Tag<any, any> = never,\n\tEXCLUDED = InferClass<INFERRED_EXCLUDED>,\n>(\n\teffect: (...params: P) => Effect.Effect<A, E, R>,\n\toptions?: { exclude?: Array<INFERRED_EXCLUDED> }\n): Effect.Effect<\n\t(...params: P) => Effect.Effect<A, E, Extract<R, EXCLUDED>>,\n\tnever,\n\tExclude<R, EXCLUDED>\n> {\n\t// @ts-expect-error quite hard to type, check unit test\n\treturn Effect.gen(function* () {\n\t\tconst runtime = yield* Effect.runtime();\n\n\t\tconst context = runtime.context.pipe(\n\t\t\toptions?.exclude ? Context.omit(...options.exclude) : (e) => e\n\t\t) as Context.Context<Exclude<R, EXCLUDED>>;\n\n\t\treturn (...params: P) =>\n\t\t\tpipe(effect(...params), Effect.provide(context));\n\t});\n}\n","import { Cause, Effect, Exit } from \"effect\";\n\n/**\n * A simple wrapper around Effect.runPromiseExit that throws the error if it's a failure\n **/\nexport async function runPromiseUnwrapped<A, E>(\n\teffect: Effect.Effect<A, E, never>\n) {\n\tconst exit = await Effect.runPromiseExit(effect);\n\treturn Exit.match(exit, {\n\t\tonSuccess: (value) => value,\n\t\tonFailure: (cause) => {\n\t\t\tthrow Cause.isFailType(cause) ? cause.error : cause;\n\t\t},\n\t});\n}\n","import { Effect } from 'effect';\n\ntype Cause = unknown;\ntype Message = string;\nfunction isMessage(x: unknown): x is Message {\n\treturn typeof x === 'string';\n}\n\nexport function wrapClient<CLIENT, ERROR extends Error>(opts: {\n\tclient: CLIENT;\n\terror: (ctx: { cause: Cause; message?: Message }) => ERROR;\n}) {\n\treturn <OUTPUT>(\n\t\tfunc: (client: CLIENT) => Promise<OUTPUT>,\n\t\toverrides?: { error?: ((cause: Cause) => Message) | Message },\n\t) =>\n\t\tEffect.tryPromise({\n\t\t\ttry: () => func(opts.client),\n\t\t\tcatch: (cause) => {\n\t\t\t\tconst message =\n\t\t\t\t\toverrides?.error != null\n\t\t\t\t\t\t? isMessage(overrides.error)\n\t\t\t\t\t\t\t? overrides.error\n\t\t\t\t\t\t\t: overrides.error(cause)\n\t\t\t\t\t\t: undefined;\n\t\t\t\treturn opts.error({\n\t\t\t\t\tcause,\n\t\t\t\t\t...(message != null && { message }),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAsC;AAK/B,SAAS,QAQf,QACA,SAKC;AAED,SAAO,qBAAO,IAAI,aAAa;AAC9B,UAAM,UAAU,OAAO,qBAAO,QAAQ;AAEtC,UAAM,UAAU,QAAQ,QAAQ;AAAA,MAC/B,SAAS,UAAU,sBAAQ,KAAK,GAAG,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC9D;AAEA,WAAO,IAAI,eACV,oBAAK,OAAO,GAAG,MAAM,GAAG,qBAAO,QAAQ,OAAO,CAAC;AAAA,EACjD,CAAC;AACF;;;AC/BA,IAAAA,iBAAoC;AAKpC,eAAsB,oBACrB,QACC;AACD,QAAM,OAAO,MAAM,sBAAO,eAAe,MAAM;AAC/C,SAAO,oBAAK,MAAM,MAAM;AAAA,IACvB,WAAW,CAAC,UAAU;AAAA,IACtB,WAAW,CAAC,UAAU;AACrB,YAAM,qBAAM,WAAW,KAAK,IAAI,MAAM,QAAQ;AAAA,IAC/C;AAAA,EACD,CAAC;AACF;;;ACfA,IAAAC,iBAAuB;AAIvB,SAAS,UAAU,GAA0B;AAC5C,SAAO,OAAO,MAAM;AACrB;AAEO,SAAS,WAAwC,MAGrD;AACF,SAAO,CACN,MACA,cAEA,sBAAO,WAAW;AAAA,IACjB,KAAK,MAAM,KAAK,KAAK,MAAM;AAAA,IAC3B,OAAO,CAAC,UAAU;AACjB,YAAM,UACL,WAAW,SAAS,OACjB,UAAU,UAAU,KAAK,IACxB,UAAU,QACV,UAAU,MAAM,KAAK,IACtB;AACJ,aAAO,KAAK,MAAM;AAAA,QACjB;AAAA,QACA,GAAI,WAAW,QAAQ,EAAE,QAAQ;AAAA,MAClC,CAAC;AAAA,IACF;AAAA,EACD,CAAC;AACH;","names":["import_effect","import_effect"]}
package/dist/index.d.cts CHANGED
@@ -10,4 +10,16 @@ declare function extract<P extends any[], A, E, R, INFERRED_EXCLUDED extends Con
10
10
  **/
11
11
  declare function runPromiseUnwrapped<A, E>(effect: Effect.Effect<A, E, never>): Promise<A>;
12
12
 
13
- export { extract, runPromiseUnwrapped };
13
+ type Cause = unknown;
14
+ type Message = string;
15
+ declare function wrapClient<CLIENT, ERROR extends Error>(opts: {
16
+ client: CLIENT;
17
+ error: (ctx: {
18
+ cause: Cause;
19
+ message?: Message;
20
+ }) => ERROR;
21
+ }): <OUTPUT>(func: (client: CLIENT) => Promise<OUTPUT>, overrides?: {
22
+ error?: ((cause: Cause) => Message) | Message;
23
+ }) => Effect.Effect<OUTPUT, ERROR, never>;
24
+
25
+ export { extract, runPromiseUnwrapped, wrapClient };
package/dist/index.d.ts CHANGED
@@ -10,4 +10,16 @@ declare function extract<P extends any[], A, E, R, INFERRED_EXCLUDED extends Con
10
10
  **/
11
11
  declare function runPromiseUnwrapped<A, E>(effect: Effect.Effect<A, E, never>): Promise<A>;
12
12
 
13
- export { extract, runPromiseUnwrapped };
13
+ type Cause = unknown;
14
+ type Message = string;
15
+ declare function wrapClient<CLIENT, ERROR extends Error>(opts: {
16
+ client: CLIENT;
17
+ error: (ctx: {
18
+ cause: Cause;
19
+ message?: Message;
20
+ }) => ERROR;
21
+ }): <OUTPUT>(func: (client: CLIENT) => Promise<OUTPUT>, overrides?: {
22
+ error?: ((cause: Cause) => Message) | Message;
23
+ }) => Effect.Effect<OUTPUT, ERROR, never>;
24
+
25
+ export { extract, runPromiseUnwrapped, wrapClient };
package/dist/index.js CHANGED
@@ -21,8 +21,27 @@ async function runPromiseUnwrapped(effect) {
21
21
  }
22
22
  });
23
23
  }
24
+
25
+ // src/wrap-client.ts
26
+ import { Effect as Effect3 } from "effect";
27
+ function isMessage(x) {
28
+ return typeof x === "string";
29
+ }
30
+ function wrapClient(opts) {
31
+ return (func, overrides) => Effect3.tryPromise({
32
+ try: () => func(opts.client),
33
+ catch: (cause) => {
34
+ const message = overrides?.error != null ? isMessage(overrides.error) ? overrides.error : overrides.error(cause) : void 0;
35
+ return opts.error({
36
+ cause,
37
+ ...message != null && { message }
38
+ });
39
+ }
40
+ });
41
+ }
24
42
  export {
25
43
  extract,
26
- runPromiseUnwrapped
44
+ runPromiseUnwrapped,
45
+ wrapClient
27
46
  };
28
47
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/extract.ts","../src/run-promise-unwrapped.ts"],"sourcesContent":["import { Context, Effect, pipe } from \"effect\";\n\ntype InferClass<T> = T extends new (...args: any[]) => infer R ? R : never;\n\n// biome-ignore lint/suspicious/noExplicitAny: intended\nexport function extract<\n\tP extends any[],\n\tA,\n\tE,\n\tR,\n\tINFERRED_EXCLUDED extends Context.Tag<any, any> = never,\n\tEXCLUDED = InferClass<INFERRED_EXCLUDED>,\n>(\n\teffect: (...params: P) => Effect.Effect<A, E, R>,\n\toptions?: { exclude?: Array<INFERRED_EXCLUDED> }\n): Effect.Effect<\n\t(...params: P) => Effect.Effect<A, E, Extract<R, EXCLUDED>>,\n\tnever,\n\tExclude<R, EXCLUDED>\n> {\n\t// @ts-expect-error quite hard to type, check unit test\n\treturn Effect.gen(function* () {\n\t\tconst runtime = yield* Effect.runtime();\n\n\t\tconst context = runtime.context.pipe(\n\t\t\toptions?.exclude ? Context.omit(...options.exclude) : (e) => e\n\t\t) as Context.Context<Exclude<R, EXCLUDED>>;\n\n\t\treturn (...params: P) =>\n\t\t\tpipe(effect(...params), Effect.provide(context));\n\t});\n}\n","import { Cause, Effect, Exit } from \"effect\";\n\n/**\n * A simple wrapper around Effect.runPromiseExit that throws the error if it's a failure\n **/\nexport async function runPromiseUnwrapped<A, E>(\n\teffect: Effect.Effect<A, E, never>\n) {\n\tconst exit = await Effect.runPromiseExit(effect);\n\treturn Exit.match(exit, {\n\t\tonSuccess: (value) => value,\n\t\tonFailure: (cause) => {\n\t\t\tthrow Cause.isFailType(cause) ? cause.error : cause;\n\t\t},\n\t});\n}\n"],"mappings":";AAAA,SAAS,SAAS,QAAQ,YAAY;AAK/B,SAAS,QAQf,QACA,SAKC;AAED,SAAO,OAAO,IAAI,aAAa;AAC9B,UAAM,UAAU,OAAO,OAAO,QAAQ;AAEtC,UAAM,UAAU,QAAQ,QAAQ;AAAA,MAC/B,SAAS,UAAU,QAAQ,KAAK,GAAG,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC9D;AAEA,WAAO,IAAI,WACV,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,QAAQ,OAAO,CAAC;AAAA,EACjD,CAAC;AACF;;;AC/BA,SAAS,OAAO,UAAAA,SAAQ,YAAY;AAKpC,eAAsB,oBACrB,QACC;AACD,QAAM,OAAO,MAAMA,QAAO,eAAe,MAAM;AAC/C,SAAO,KAAK,MAAM,MAAM;AAAA,IACvB,WAAW,CAAC,UAAU;AAAA,IACtB,WAAW,CAAC,UAAU;AACrB,YAAM,MAAM,WAAW,KAAK,IAAI,MAAM,QAAQ;AAAA,IAC/C;AAAA,EACD,CAAC;AACF;","names":["Effect"]}
1
+ {"version":3,"sources":["../src/extract.ts","../src/run-promise-unwrapped.ts","../src/wrap-client.ts"],"sourcesContent":["import { Context, Effect, pipe } from \"effect\";\n\ntype InferClass<T> = T extends new (...args: any[]) => infer R ? R : never;\n\n// biome-ignore lint/suspicious/noExplicitAny: intended\nexport function extract<\n\tP extends any[],\n\tA,\n\tE,\n\tR,\n\tINFERRED_EXCLUDED extends Context.Tag<any, any> = never,\n\tEXCLUDED = InferClass<INFERRED_EXCLUDED>,\n>(\n\teffect: (...params: P) => Effect.Effect<A, E, R>,\n\toptions?: { exclude?: Array<INFERRED_EXCLUDED> }\n): Effect.Effect<\n\t(...params: P) => Effect.Effect<A, E, Extract<R, EXCLUDED>>,\n\tnever,\n\tExclude<R, EXCLUDED>\n> {\n\t// @ts-expect-error quite hard to type, check unit test\n\treturn Effect.gen(function* () {\n\t\tconst runtime = yield* Effect.runtime();\n\n\t\tconst context = runtime.context.pipe(\n\t\t\toptions?.exclude ? Context.omit(...options.exclude) : (e) => e\n\t\t) as Context.Context<Exclude<R, EXCLUDED>>;\n\n\t\treturn (...params: P) =>\n\t\t\tpipe(effect(...params), Effect.provide(context));\n\t});\n}\n","import { Cause, Effect, Exit } from \"effect\";\n\n/**\n * A simple wrapper around Effect.runPromiseExit that throws the error if it's a failure\n **/\nexport async function runPromiseUnwrapped<A, E>(\n\teffect: Effect.Effect<A, E, never>\n) {\n\tconst exit = await Effect.runPromiseExit(effect);\n\treturn Exit.match(exit, {\n\t\tonSuccess: (value) => value,\n\t\tonFailure: (cause) => {\n\t\t\tthrow Cause.isFailType(cause) ? cause.error : cause;\n\t\t},\n\t});\n}\n","import { Effect } from 'effect';\n\ntype Cause = unknown;\ntype Message = string;\nfunction isMessage(x: unknown): x is Message {\n\treturn typeof x === 'string';\n}\n\nexport function wrapClient<CLIENT, ERROR extends Error>(opts: {\n\tclient: CLIENT;\n\terror: (ctx: { cause: Cause; message?: Message }) => ERROR;\n}) {\n\treturn <OUTPUT>(\n\t\tfunc: (client: CLIENT) => Promise<OUTPUT>,\n\t\toverrides?: { error?: ((cause: Cause) => Message) | Message },\n\t) =>\n\t\tEffect.tryPromise({\n\t\t\ttry: () => func(opts.client),\n\t\t\tcatch: (cause) => {\n\t\t\t\tconst message =\n\t\t\t\t\toverrides?.error != null\n\t\t\t\t\t\t? isMessage(overrides.error)\n\t\t\t\t\t\t\t? overrides.error\n\t\t\t\t\t\t\t: overrides.error(cause)\n\t\t\t\t\t\t: undefined;\n\t\t\t\treturn opts.error({\n\t\t\t\t\tcause,\n\t\t\t\t\t...(message != null && { message }),\n\t\t\t\t});\n\t\t\t},\n\t\t});\n}\n"],"mappings":";AAAA,SAAS,SAAS,QAAQ,YAAY;AAK/B,SAAS,QAQf,QACA,SAKC;AAED,SAAO,OAAO,IAAI,aAAa;AAC9B,UAAM,UAAU,OAAO,OAAO,QAAQ;AAEtC,UAAM,UAAU,QAAQ,QAAQ;AAAA,MAC/B,SAAS,UAAU,QAAQ,KAAK,GAAG,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC9D;AAEA,WAAO,IAAI,WACV,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,QAAQ,OAAO,CAAC;AAAA,EACjD,CAAC;AACF;;;AC/BA,SAAS,OAAO,UAAAA,SAAQ,YAAY;AAKpC,eAAsB,oBACrB,QACC;AACD,QAAM,OAAO,MAAMA,QAAO,eAAe,MAAM;AAC/C,SAAO,KAAK,MAAM,MAAM;AAAA,IACvB,WAAW,CAAC,UAAU;AAAA,IACtB,WAAW,CAAC,UAAU;AACrB,YAAM,MAAM,WAAW,KAAK,IAAI,MAAM,QAAQ;AAAA,IAC/C;AAAA,EACD,CAAC;AACF;;;ACfA,SAAS,UAAAC,eAAc;AAIvB,SAAS,UAAU,GAA0B;AAC5C,SAAO,OAAO,MAAM;AACrB;AAEO,SAAS,WAAwC,MAGrD;AACF,SAAO,CACN,MACA,cAEAA,QAAO,WAAW;AAAA,IACjB,KAAK,MAAM,KAAK,KAAK,MAAM;AAAA,IAC3B,OAAO,CAAC,UAAU;AACjB,YAAM,UACL,WAAW,SAAS,OACjB,UAAU,UAAU,KAAK,IACxB,UAAU,QACV,UAAU,MAAM,KAAK,IACtB;AACJ,aAAO,KAAK,MAAM;AAAA,QACjB;AAAA,QACA,GAAI,WAAW,QAAQ,EAAE,QAAQ;AAAA,MAClC,CAAC;AAAA,IACF;AAAA,EACD,CAAC;AACH;","names":["Effect","Effect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ff-effect",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { extract } from './extract.js';
2
2
  export { runPromiseUnwrapped } from './run-promise-unwrapped.js';
3
+ export { wrapClient } from './wrap-client.js';
@@ -0,0 +1,87 @@
1
+ import { Effect } from 'effect';
2
+ import { expect, test } from 'vitest';
3
+ import { wrapClient } from './wrap-client.js';
4
+
5
+ class TestError extends Error {
6
+ constructor(
7
+ public _cause: unknown,
8
+ public _message?: string,
9
+ ) {
10
+ super('error');
11
+ }
12
+ }
13
+
14
+ type TestClient = {
15
+ value: string;
16
+ };
17
+
18
+ test('resolves successfully', () =>
19
+ Effect.gen(function* () {
20
+ const client: TestClient = { value: 'test-value' };
21
+ const wrap = wrapClient({
22
+ client,
23
+ error: ({ cause, message }) => new TestError(cause, message),
24
+ });
25
+
26
+ const result = yield* wrap((c) => Promise.resolve(c.value));
27
+
28
+ expect(result).toBe('test-value');
29
+ }).pipe(Effect.runPromise));
30
+
31
+ test('rejects without message when no overrides provided', () =>
32
+ Effect.gen(function* () {
33
+ const client: TestClient = { value: 'test-value' };
34
+ const wrap = wrapClient({
35
+ client,
36
+ error: ({ cause, message }) => new TestError(cause, message),
37
+ });
38
+
39
+ const causedBy = new Error('original error');
40
+ const effect = wrap(() => Promise.reject(causedBy));
41
+
42
+ const result = yield* Effect.flip(effect);
43
+
44
+ expect(result).toBeInstanceOf(TestError);
45
+ expect(result._cause).toBe(causedBy);
46
+ expect(result._message).toBeUndefined();
47
+ }).pipe(Effect.runPromise));
48
+
49
+ test('rejects with string override', () =>
50
+ Effect.gen(function* () {
51
+ const client: TestClient = { value: 'test-value' };
52
+ const wrap = wrapClient({
53
+ client,
54
+ error: ({ cause, message }) => new TestError(cause, message),
55
+ });
56
+
57
+ const causedBy = new Error('original error');
58
+ const effect = wrap(() => Promise.reject(causedBy), {
59
+ error: 'custom message',
60
+ });
61
+
62
+ const result = yield* Effect.flip(effect);
63
+
64
+ expect(result).toBeInstanceOf(TestError);
65
+ expect(result._cause).toBe(causedBy);
66
+ expect(result._message).toBe('custom message');
67
+ }).pipe(Effect.runPromise));
68
+
69
+ test('rejects with function override', () =>
70
+ Effect.gen(function* () {
71
+ const client: TestClient = { value: 'test-value' };
72
+ const wrap = wrapClient({
73
+ client,
74
+ error: ({ cause, message }) => new TestError(cause, message),
75
+ });
76
+
77
+ const causedBy = new Error('original error');
78
+ const effect = wrap(() => Promise.reject(causedBy), {
79
+ error: (cause) => `Error: ${(cause as Error).message}`,
80
+ });
81
+
82
+ const result = yield* Effect.flip(effect);
83
+
84
+ expect(result).toBeInstanceOf(TestError);
85
+ expect(result._cause).toBe(causedBy);
86
+ expect(result._message).toBe('Error: original error');
87
+ }).pipe(Effect.runPromise));
@@ -0,0 +1,32 @@
1
+ import { Effect } from 'effect';
2
+
3
+ type Cause = unknown;
4
+ type Message = string;
5
+ function isMessage(x: unknown): x is Message {
6
+ return typeof x === 'string';
7
+ }
8
+
9
+ export function wrapClient<CLIENT, ERROR extends Error>(opts: {
10
+ client: CLIENT;
11
+ error: (ctx: { cause: Cause; message?: Message }) => ERROR;
12
+ }) {
13
+ return <OUTPUT>(
14
+ func: (client: CLIENT) => Promise<OUTPUT>,
15
+ overrides?: { error?: ((cause: Cause) => Message) | Message },
16
+ ) =>
17
+ Effect.tryPromise({
18
+ try: () => func(opts.client),
19
+ catch: (cause) => {
20
+ const message =
21
+ overrides?.error != null
22
+ ? isMessage(overrides.error)
23
+ ? overrides.error
24
+ : overrides.error(cause)
25
+ : undefined;
26
+ return opts.error({
27
+ cause,
28
+ ...(message != null && { message }),
29
+ });
30
+ },
31
+ });
32
+ }