@effectionx/context-api 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # Context Apis
2
+
3
+ Often called "Algebraic Effects" or "Contextual Effects", Context apis let you
4
+ access an operation via the context in a way that it can be easily (and
5
+ contextually) wrapped with middleware.
6
+
7
+ ---
8
+
9
+ Let's say that you want to define a log operation that behaves differently in
10
+ different context. The basic form will just log values to the console.
11
+
12
+ ```ts
13
+ // file logging.ts
14
+ import { createApi } from "@effectionx/context-api";
15
+
16
+ // create the `logging` api. By default, it just logs to the console.
17
+ const logging = createApi<Logging>(
18
+ "logging",
19
+ function* log(...values: unknown[]) {
20
+ console.log(...values);
21
+ },
22
+ );
23
+
24
+ // export the logging operations.
25
+ export const { log } = logging.operations;
26
+ ```
27
+
28
+ Now you can use the logging api wherever you want:
29
+
30
+ ```ts
31
+ import { log } from "./logging.ts";
32
+
33
+ export function* op() {
34
+ yield* log(`I am in an operation`);
35
+ }
36
+ ```
37
+
38
+ However, use can use the `around` function to wrap middleware around your
39
+ logging operation. This lets you do stuff like silence logging, or even to
40
+ re-route it somewhere else than from the `console` completely.
41
+
42
+ ```ts
43
+ import { logging } from "./logging.ts";
44
+
45
+ function* initCustomLogging(externallogger) {
46
+ yield* logging.around({
47
+ *log(...values, next) {
48
+ externalLogger.log(...values);
49
+ // since we override the logger entirely, we do not invoke next.
50
+ },
51
+ });
52
+ }
53
+ ```
54
+
55
+ The best part is that the middleware is only in effect inside the scope in which
56
+ it is installed.
57
+
58
+ Middleware can be useful for automatic instrumentation. For example, let's
59
+ assume that `fetch` was a an api called `fetching`:
60
+
61
+ ```ts
62
+ import { fetch, fetching } from "./fetching.ts";
63
+
64
+ function* instrumentFetch(tracer) {
65
+ yield* fetching.around({
66
+ *fetch(...args, next) {
67
+ try {
68
+ tracer.begin("fetch", args),
69
+ return yield* next(...args);
70
+ } finally {
71
+ tracer.end("fetch", args);
72
+ }
73
+ }
74
+ })
75
+ }
76
+ ```
77
+
78
+ or mocking inside test cases:
79
+
80
+ ```ts
81
+ import { fetch, fetching } from "./fetching.ts";
82
+
83
+ function* useMocks() {
84
+ yield* fetching.around({
85
+ *fetch(...args, next) {
86
+ if (args[0] === "/my-path") {
87
+ return new MockResponse("my-path");
88
+ } else {
89
+ return yield* next(...args);
90
+ }
91
+ },
92
+ });
93
+ }
94
+ ```
package/esm/mod.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { type Operation } from "effection";
2
+ export type Around<A> = {
3
+ [K in keyof Operations<A>]: A[K] extends (...args: infer TArgs) => infer TReturn ? Middleware<TArgs, TReturn> : Middleware<[], A[K]>;
4
+ };
5
+ export interface Middleware<TArgs extends unknown[], TReturn> {
6
+ (args: TArgs, next: (...args: TArgs) => TReturn): TReturn;
7
+ }
8
+ export interface Api<A> {
9
+ operations: Operations<A>;
10
+ around: (around: Partial<Around<A>>) => Operation<void>;
11
+ }
12
+ export type Operations<T> = {
13
+ [K in keyof T]: T[K] extends ((...args: infer TArgs) => infer TReturn) ? (...args: TArgs) => TReturn : T[K] extends Operation<infer TReturn> ? Operation<TReturn> : never;
14
+ };
15
+ export declare function createApi<A extends {}>(name: string, handler: A): Api<A>;
16
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAE1D,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;KACrB,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAC9B,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,GAClE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO;IAC1D,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,GAAG,CAAC,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;KACzB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,GAClE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAC3B,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,GAC1D,KAAK;CACV,CAAC;AAEF,wBAAgB,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CA+CxE"}
package/esm/mod.js ADDED
@@ -0,0 +1,45 @@
1
+ // deno-lint-ignore-file ban-types no-explicit-any
2
+ import { createContext } from "effection";
3
+ export function createApi(name, handler) {
4
+ let fields = Object.keys(handler);
5
+ let middleware = fields.reduce((sum, field) => {
6
+ return Object.assign(sum, {
7
+ [field]: (args, next) => next(...args),
8
+ });
9
+ }, {});
10
+ let context = createContext(`$api:${name}`, middleware);
11
+ let operations = fields.reduce((api, field) => {
12
+ let handle = handler[field];
13
+ if (typeof handle === "function") {
14
+ return Object.assign(api, {
15
+ [field]: function* (...args) {
16
+ let around = yield* context.expect();
17
+ let middleware = around[field];
18
+ return yield* middleware(args, handle);
19
+ },
20
+ });
21
+ }
22
+ else {
23
+ return Object.assign(api, {
24
+ [field]: {
25
+ *[Symbol.iterator]() {
26
+ let around = yield* context.expect();
27
+ let middleware = around[field];
28
+ return yield* middleware([], () => handle);
29
+ },
30
+ },
31
+ });
32
+ }
33
+ }, {});
34
+ function* around(around) {
35
+ let current = yield* context.expect();
36
+ yield* context.set(fields.reduce((sum, field) => {
37
+ let prior = current[field];
38
+ let middleware = around[field];
39
+ return Object.assign(sum, {
40
+ [field]: (args, next) => middleware(args, (...args) => prior(args, next)),
41
+ });
42
+ }, Object.assign({}, current)));
43
+ }
44
+ return { operations, around };
45
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@effectionx/context-api",
3
+ "version": "0.0.1",
4
+ "author": "engineering@frontside.com",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/thefrontside/effectionx.git"
8
+ },
9
+ "license": "MIT",
10
+ "bugs": {
11
+ "url": "https://github.com/thefrontside/effectionx/issues"
12
+ },
13
+ "main": "./script/mod.js",
14
+ "module": "./esm/mod.js",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./esm/mod.js",
18
+ "require": "./script/mod.js"
19
+ }
20
+ },
21
+ "engines": {
22
+ "node": ">= 16"
23
+ },
24
+ "sideEffects": false,
25
+ "_generatedBy": "dnt@dev"
26
+ }
@@ -0,0 +1,16 @@
1
+ import { type Operation } from "effection";
2
+ export type Around<A> = {
3
+ [K in keyof Operations<A>]: A[K] extends (...args: infer TArgs) => infer TReturn ? Middleware<TArgs, TReturn> : Middleware<[], A[K]>;
4
+ };
5
+ export interface Middleware<TArgs extends unknown[], TReturn> {
6
+ (args: TArgs, next: (...args: TArgs) => TReturn): TReturn;
7
+ }
8
+ export interface Api<A> {
9
+ operations: Operations<A>;
10
+ around: (around: Partial<Around<A>>) => Operation<void>;
11
+ }
12
+ export type Operations<T> = {
13
+ [K in keyof T]: T[K] extends ((...args: infer TArgs) => infer TReturn) ? (...args: TArgs) => TReturn : T[K] extends Operation<infer TReturn> ? Operation<TReturn> : never;
14
+ };
15
+ export declare function createApi<A extends {}>(name: string, handler: A): Api<A>;
16
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAE1D,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;KACrB,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAC9B,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,GAClE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO;IAC1D,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,GAAG,CAAC,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;KACzB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,GAClE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAC3B,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,GAC1D,KAAK;CACV,CAAC;AAEF,wBAAgB,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CA+CxE"}
package/script/mod.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createApi = createApi;
4
+ // deno-lint-ignore-file ban-types no-explicit-any
5
+ const effection_1 = require("effection");
6
+ function createApi(name, handler) {
7
+ let fields = Object.keys(handler);
8
+ let middleware = fields.reduce((sum, field) => {
9
+ return Object.assign(sum, {
10
+ [field]: (args, next) => next(...args),
11
+ });
12
+ }, {});
13
+ let context = (0, effection_1.createContext)(`$api:${name}`, middleware);
14
+ let operations = fields.reduce((api, field) => {
15
+ let handle = handler[field];
16
+ if (typeof handle === "function") {
17
+ return Object.assign(api, {
18
+ [field]: function* (...args) {
19
+ let around = yield* context.expect();
20
+ let middleware = around[field];
21
+ return yield* middleware(args, handle);
22
+ },
23
+ });
24
+ }
25
+ else {
26
+ return Object.assign(api, {
27
+ [field]: {
28
+ *[Symbol.iterator]() {
29
+ let around = yield* context.expect();
30
+ let middleware = around[field];
31
+ return yield* middleware([], () => handle);
32
+ },
33
+ },
34
+ });
35
+ }
36
+ }, {});
37
+ function* around(around) {
38
+ let current = yield* context.expect();
39
+ yield* context.set(fields.reduce((sum, field) => {
40
+ let prior = current[field];
41
+ let middleware = around[field];
42
+ return Object.assign(sum, {
43
+ [field]: (args, next) => middleware(args, (...args) => prior(args, next)),
44
+ });
45
+ }, Object.assign({}, current)));
46
+ }
47
+ return { operations, around };
48
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }