ff-effect 0.0.1 → 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/for/orpc/index.cjs +67 -0
- package/dist/for/orpc/index.cjs.map +1 -0
- package/dist/for/orpc/index.d.cts +27 -0
- package/dist/for/orpc/index.d.ts +27 -0
- package/dist/for/orpc/index.js +40 -0
- package/dist/for/orpc/index.js.map +1 -0
- package/dist/index.cjs +22 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -1
- package/src/for/orpc/index.ts +3 -0
- package/src/for/orpc/procedure.test.ts +59 -0
- package/src/for/orpc/procedure.ts +38 -0
- package/src/index.ts +1 -0
- package/src/wrap-client.test.ts +87 -0
- package/src/wrap-client.ts +32 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/for/orpc/index.ts
|
|
21
|
+
var orpc_exports = {};
|
|
22
|
+
__export(orpc_exports, {
|
|
23
|
+
createHandler: () => createHandler
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(orpc_exports);
|
|
26
|
+
|
|
27
|
+
// src/for/orpc/procedure.ts
|
|
28
|
+
var import_effect3 = require("effect");
|
|
29
|
+
|
|
30
|
+
// src/extract.ts
|
|
31
|
+
var import_effect = require("effect");
|
|
32
|
+
function extract(effect, options) {
|
|
33
|
+
return import_effect.Effect.gen(function* () {
|
|
34
|
+
const runtime = yield* import_effect.Effect.runtime();
|
|
35
|
+
const context = runtime.context.pipe(
|
|
36
|
+
options?.exclude ? import_effect.Context.omit(...options.exclude) : (e) => e
|
|
37
|
+
);
|
|
38
|
+
return (...params) => (0, import_effect.pipe)(effect(...params), import_effect.Effect.provide(context));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/run-promise-unwrapped.ts
|
|
43
|
+
var import_effect2 = require("effect");
|
|
44
|
+
async function runPromiseUnwrapped(effect) {
|
|
45
|
+
const exit = await import_effect2.Effect.runPromiseExit(effect);
|
|
46
|
+
return import_effect2.Exit.match(exit, {
|
|
47
|
+
onSuccess: (value) => value,
|
|
48
|
+
onFailure: (cause) => {
|
|
49
|
+
throw import_effect2.Cause.isFailType(cause) ? cause.error : cause;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/for/orpc/procedure.ts
|
|
55
|
+
function createHandler(builder, handler) {
|
|
56
|
+
return import_effect3.Effect.gen(function* () {
|
|
57
|
+
const ext_handler = yield* extract(handler);
|
|
58
|
+
return builder.handler(
|
|
59
|
+
async (opt) => ext_handler(opt).pipe((e) => runPromiseUnwrapped(e))
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
64
|
+
0 && (module.exports = {
|
|
65
|
+
createHandler
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/for/orpc/index.ts","../../../src/for/orpc/procedure.ts","../../../src/extract.ts","../../../src/run-promise-unwrapped.ts"],"sourcesContent":["import { createHandler } from './procedure';\n\nexport { createHandler };\n","import { Effect } from 'effect';\nimport { extract } from '../../extract';\nimport { runPromiseUnwrapped } from '../../run-promise-unwrapped';\n\ntype SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER> = {\n\thandler: (handler: (_: INPUT) => Promise<OUTPUT>) => IMPLEMENTED_HANDLER;\n};\n\n/**\n * Create a Effect-based handler\n *\n * @example\n * ```ts\n * // Before - Plain oRPC\n * const procedure = os\n * .input(v.object({ hello: v.string() }))\n * .handler(({ input }) => 'world');\n *\n * // After\n * const procedure = createHandler(\n * os.input(v.object({ hello: v.string() })),\n * Effect.fn(function* ({ input }) {\n * return 'world'\n * }),\n * );\n * ```\n **/\nexport function createHandler<INPUT, OUTPUT, IMPLEMENTED_HANDLER, R>(\n\tbuilder: SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER>,\n\thandler: (opt: INPUT) => Effect.Effect<OUTPUT, unknown, R>,\n) {\n\treturn Effect.gen(function* () {\n\t\tconst ext_handler = yield* extract(handler);\n\t\treturn builder.handler(async (opt) =>\n\t\t\text_handler(opt).pipe((e) => runPromiseUnwrapped(e)),\n\t\t);\n\t});\n}\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;;;ACAA,IAAAA,iBAAuB;;;ACAvB,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,IAAAC,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;;;AFYO,SAAS,cACf,SACA,SACC;AACD,SAAO,sBAAO,IAAI,aAAa;AAC9B,UAAM,cAAc,OAAO,QAAQ,OAAO;AAC1C,WAAO,QAAQ;AAAA,MAAQ,OAAO,QAC7B,YAAY,GAAG,EAAE,KAAK,CAAC,MAAM,oBAAoB,CAAC,CAAC;AAAA,IACpD;AAAA,EACD,CAAC;AACF;","names":["import_effect","import_effect"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
|
|
3
|
+
type SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER> = {
|
|
4
|
+
handler: (handler: (_: INPUT) => Promise<OUTPUT>) => IMPLEMENTED_HANDLER;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Create a Effect-based handler
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Before - Plain oRPC
|
|
12
|
+
* const procedure = os
|
|
13
|
+
* .input(v.object({ hello: v.string() }))
|
|
14
|
+
* .handler(({ input }) => 'world');
|
|
15
|
+
*
|
|
16
|
+
* // After
|
|
17
|
+
* const procedure = createHandler(
|
|
18
|
+
* os.input(v.object({ hello: v.string() })),
|
|
19
|
+
* Effect.fn(function* ({ input }) {
|
|
20
|
+
* return 'world'
|
|
21
|
+
* }),
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
**/
|
|
25
|
+
declare function createHandler<INPUT, OUTPUT, IMPLEMENTED_HANDLER, R>(builder: SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER>, handler: (opt: INPUT) => Effect.Effect<OUTPUT, unknown, R>): Effect.Effect<IMPLEMENTED_HANDLER, never, Exclude<R, never>>;
|
|
26
|
+
|
|
27
|
+
export { createHandler };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
|
|
3
|
+
type SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER> = {
|
|
4
|
+
handler: (handler: (_: INPUT) => Promise<OUTPUT>) => IMPLEMENTED_HANDLER;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Create a Effect-based handler
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Before - Plain oRPC
|
|
12
|
+
* const procedure = os
|
|
13
|
+
* .input(v.object({ hello: v.string() }))
|
|
14
|
+
* .handler(({ input }) => 'world');
|
|
15
|
+
*
|
|
16
|
+
* // After
|
|
17
|
+
* const procedure = createHandler(
|
|
18
|
+
* os.input(v.object({ hello: v.string() })),
|
|
19
|
+
* Effect.fn(function* ({ input }) {
|
|
20
|
+
* return 'world'
|
|
21
|
+
* }),
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
**/
|
|
25
|
+
declare function createHandler<INPUT, OUTPUT, IMPLEMENTED_HANDLER, R>(builder: SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER>, handler: (opt: INPUT) => Effect.Effect<OUTPUT, unknown, R>): Effect.Effect<IMPLEMENTED_HANDLER, never, Exclude<R, never>>;
|
|
26
|
+
|
|
27
|
+
export { createHandler };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/for/orpc/procedure.ts
|
|
2
|
+
import { Effect as Effect3 } from "effect";
|
|
3
|
+
|
|
4
|
+
// src/extract.ts
|
|
5
|
+
import { Context, Effect, pipe } from "effect";
|
|
6
|
+
function extract(effect, options) {
|
|
7
|
+
return Effect.gen(function* () {
|
|
8
|
+
const runtime = yield* Effect.runtime();
|
|
9
|
+
const context = runtime.context.pipe(
|
|
10
|
+
options?.exclude ? Context.omit(...options.exclude) : (e) => e
|
|
11
|
+
);
|
|
12
|
+
return (...params) => pipe(effect(...params), Effect.provide(context));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/run-promise-unwrapped.ts
|
|
17
|
+
import { Cause, Effect as Effect2, Exit } from "effect";
|
|
18
|
+
async function runPromiseUnwrapped(effect) {
|
|
19
|
+
const exit = await Effect2.runPromiseExit(effect);
|
|
20
|
+
return Exit.match(exit, {
|
|
21
|
+
onSuccess: (value) => value,
|
|
22
|
+
onFailure: (cause) => {
|
|
23
|
+
throw Cause.isFailType(cause) ? cause.error : cause;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/for/orpc/procedure.ts
|
|
29
|
+
function createHandler(builder, handler) {
|
|
30
|
+
return Effect3.gen(function* () {
|
|
31
|
+
const ext_handler = yield* extract(handler);
|
|
32
|
+
return builder.handler(
|
|
33
|
+
async (opt) => ext_handler(opt).pipe((e) => runPromiseUnwrapped(e))
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
createHandler
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/for/orpc/procedure.ts","../../../src/extract.ts","../../../src/run-promise-unwrapped.ts"],"sourcesContent":["import { Effect } from 'effect';\nimport { extract } from '../../extract';\nimport { runPromiseUnwrapped } from '../../run-promise-unwrapped';\n\ntype SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER> = {\n\thandler: (handler: (_: INPUT) => Promise<OUTPUT>) => IMPLEMENTED_HANDLER;\n};\n\n/**\n * Create a Effect-based handler\n *\n * @example\n * ```ts\n * // Before - Plain oRPC\n * const procedure = os\n * .input(v.object({ hello: v.string() }))\n * .handler(({ input }) => 'world');\n *\n * // After\n * const procedure = createHandler(\n * os.input(v.object({ hello: v.string() })),\n * Effect.fn(function* ({ input }) {\n * return 'world'\n * }),\n * );\n * ```\n **/\nexport function createHandler<INPUT, OUTPUT, IMPLEMENTED_HANDLER, R>(\n\tbuilder: SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER>,\n\thandler: (opt: INPUT) => Effect.Effect<OUTPUT, unknown, R>,\n) {\n\treturn Effect.gen(function* () {\n\t\tconst ext_handler = yield* extract(handler);\n\t\treturn builder.handler(async (opt) =>\n\t\t\text_handler(opt).pipe((e) => runPromiseUnwrapped(e)),\n\t\t);\n\t});\n}\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,SAAS,UAAAA,eAAc;;;ACAvB,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,UAAAC,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;;;AFYO,SAAS,cACf,SACA,SACC;AACD,SAAOC,QAAO,IAAI,aAAa;AAC9B,UAAM,cAAc,OAAO,QAAQ,OAAO;AAC1C,WAAO,QAAQ;AAAA,MAAQ,OAAO,QAC7B,YAAY,GAAG,EAAE,KAAK,CAAC,MAAM,oBAAoB,CAAC,CAAC;AAAA,IACpD;AAAA,EACD,CAAC;AACF;","names":["Effect","Effect","Effect"]}
|
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ff-effect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"import": "./dist/index.js",
|
|
9
9
|
"require": "./dist/index.cjs"
|
|
10
|
+
},
|
|
11
|
+
"./for/*": {
|
|
12
|
+
"types": "./dist/for/*/index.d.ts",
|
|
13
|
+
"import": "./dist/for/*/index.js",
|
|
14
|
+
"require": "./dist/for/*/index.cjs"
|
|
10
15
|
}
|
|
11
16
|
},
|
|
12
17
|
"files": [
|
|
@@ -19,10 +24,14 @@
|
|
|
19
24
|
"dev": "tsup --watch"
|
|
20
25
|
},
|
|
21
26
|
"devDependencies": {
|
|
27
|
+
"@effect/vitest": "^0.27.0",
|
|
28
|
+
"@orpc/contract": "^1.11.3",
|
|
29
|
+
"@orpc/server": "^1.11.3",
|
|
22
30
|
"@total-typescript/tsconfig": "^1.0.4",
|
|
23
31
|
"@types/bun": "^1.3.2",
|
|
24
32
|
"tsup": "^8.5.0",
|
|
25
33
|
"typescript": "^5.9.3",
|
|
34
|
+
"valibot": "^1.1.0",
|
|
26
35
|
"vitest": "^4.0.10"
|
|
27
36
|
},
|
|
28
37
|
"peerDependencies": {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { layer } from '@effect/vitest';
|
|
2
|
+
import { call, implement, os } from '@orpc/server';
|
|
3
|
+
import { Effect } from 'effect';
|
|
4
|
+
import * as v from 'valibot';
|
|
5
|
+
import { expect, expectTypeOf } from 'vitest';
|
|
6
|
+
import { createHandler } from './procedure.js';
|
|
7
|
+
|
|
8
|
+
class Deps extends Effect.Service<Deps>()('deps', {
|
|
9
|
+
effect: Effect.gen(function* () {
|
|
10
|
+
return {
|
|
11
|
+
value: 'world',
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
}) {}
|
|
15
|
+
|
|
16
|
+
layer(Deps.Default)((it) => {
|
|
17
|
+
it(
|
|
18
|
+
'basic',
|
|
19
|
+
Effect.fn(function* () {
|
|
20
|
+
const effectProcedure = createHandler(
|
|
21
|
+
os.input(v.object({ message: v.string() })),
|
|
22
|
+
Effect.fn(function* ({ input }) {
|
|
23
|
+
return `${input.message} ${(yield* Deps).value}`;
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
const procedure = yield* effectProcedure;
|
|
27
|
+
|
|
28
|
+
const result = call(procedure, { message: 'hello' });
|
|
29
|
+
expect(result).toBe('hello world');
|
|
30
|
+
expectTypeOf(effectProcedure).toEqualTypeOf<
|
|
31
|
+
Effect.Effect<typeof procedure, never, Deps>
|
|
32
|
+
>();
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
it(
|
|
37
|
+
'contract',
|
|
38
|
+
Effect.fn(function* () {
|
|
39
|
+
const contract = {
|
|
40
|
+
sayHi: os.input(v.object({ message: v.string() })).output(v.string()),
|
|
41
|
+
};
|
|
42
|
+
const osContract = implement(contract);
|
|
43
|
+
|
|
44
|
+
const effectProcedure = createHandler(
|
|
45
|
+
osContract.sayHi,
|
|
46
|
+
Effect.fn(function* ({ input }) {
|
|
47
|
+
return `${input.message} ${(yield* Deps).value}`;
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
const procedure = yield* effectProcedure;
|
|
51
|
+
|
|
52
|
+
const result = call(procedure, { message: 'hello' });
|
|
53
|
+
expect(result).toBe('hello world');
|
|
54
|
+
expectTypeOf(effectProcedure).toEqualTypeOf<
|
|
55
|
+
Effect.Effect<typeof procedure, never, Deps>
|
|
56
|
+
>();
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { extract } from '../../extract';
|
|
3
|
+
import { runPromiseUnwrapped } from '../../run-promise-unwrapped';
|
|
4
|
+
|
|
5
|
+
type SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER> = {
|
|
6
|
+
handler: (handler: (_: INPUT) => Promise<OUTPUT>) => IMPLEMENTED_HANDLER;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a Effect-based handler
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Before - Plain oRPC
|
|
15
|
+
* const procedure = os
|
|
16
|
+
* .input(v.object({ hello: v.string() }))
|
|
17
|
+
* .handler(({ input }) => 'world');
|
|
18
|
+
*
|
|
19
|
+
* // After
|
|
20
|
+
* const procedure = createHandler(
|
|
21
|
+
* os.input(v.object({ hello: v.string() })),
|
|
22
|
+
* Effect.fn(function* ({ input }) {
|
|
23
|
+
* return 'world'
|
|
24
|
+
* }),
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
**/
|
|
28
|
+
export function createHandler<INPUT, OUTPUT, IMPLEMENTED_HANDLER, R>(
|
|
29
|
+
builder: SimpleBuilder<INPUT, OUTPUT, IMPLEMENTED_HANDLER>,
|
|
30
|
+
handler: (opt: INPUT) => Effect.Effect<OUTPUT, unknown, R>,
|
|
31
|
+
) {
|
|
32
|
+
return Effect.gen(function* () {
|
|
33
|
+
const ext_handler = yield* extract(handler);
|
|
34
|
+
return builder.handler(async (opt) =>
|
|
35
|
+
ext_handler(opt).pipe((e) => runPromiseUnwrapped(e)),
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -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
|
+
}
|