ff-effect 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "ff-effect",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
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,3 @@
1
+ import { createHandler } from './procedure';
2
+
3
+ export { createHandler };
@@ -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
+ }