New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

affects

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

affects - npm Package Compare versions

Comparing version 0.1.0 to 1.0.0

44

dist/affects.cjs.development.js

@@ -28,34 +28,34 @@ 'use strict';

var asyncLocalStorage = /*#__PURE__*/new async_hooks.AsyncLocalStorage();
var contextSymbol = /*#__PURE__*/Symbol('affects-context');
function createContext(defaultValue) {
var handlerSymbol = /*#__PURE__*/Symbol('affects-handler');
function createHandler(defaultValue) {
var _ref;
return _ref = {}, _ref[contextSymbol] = true, _ref.defaultValue = defaultValue, _ref;
return _ref = {}, _ref[handlerSymbol] = true, _ref.defaultValue = defaultValue, _ref;
}
function perform(Context) {
function perform(Handler) {
var map = asyncLocalStorage.getStore();
if (!(map instanceof Map) || !map.has(Context)) {
return Context['defaultValue'];
if (!(map instanceof Map) || !map.has(Handler)) {
return Handler['defaultValue'];
}
return map.get(Context);
return map.get(Handler);
}
function createRunner() {
for (var _len = arguments.length, pairs = new Array(_len), _key = 0; _key < _len; _key++) {
pairs[_key] = arguments[_key];
}
function createRunner(callback) {
return function runWithHandlers() {
for (var _len = arguments.length, pairs = new Array(_len), _key = 0; _key < _len; _key++) {
pairs[_key] = arguments[_key];
}
pairs.forEach(function (_ref2) {
var Context = _ref2[0];
pairs.forEach(function (_ref2) {
var Handler = _ref2[0];
if (!Context) {
throw new Error('Missing Context in pair');
}
if (!Handler) {
throw new Error('Missing Handler in pair');
}
if (!Context[contextSymbol]) {
throw new Error('Context needs to be created by `createContext`');
}
});
return function (callback) {
if (!Handler[handlerSymbol]) {
throw new Error('Handler needs to be created by `createHandler`');
}
});
var parentMap = asyncLocalStorage.getStore();

@@ -70,5 +70,5 @@ var map = new Map(pairs);

exports.createContext = createContext;
exports.createHandler = createHandler;
exports.createRunner = createRunner;
exports.perform = perform;
//# sourceMappingURL=affects.cjs.development.js.map

@@ -1,2 +0,2 @@

"use strict";function e(){for(var e=new Map,r=arguments.length,t=new Array(r),n=0;n<r;n++)t[n]=arguments[n];for(var o=0,a=t;o<a.length;o++){var u=a[o];u instanceof Map&&u.forEach((function(r,t){e.set(t,r)}))}return e}Object.defineProperty(exports,"__esModule",{value:!0});var r=new(require("async_hooks").AsyncLocalStorage),t=Symbol("affects-context");exports.createContext=function(e){var r;return(r={})[t]=!0,r.defaultValue=e,r},exports.createRunner=function(){for(var n=arguments.length,o=new Array(n),a=0;a<n;a++)o[a]=arguments[a];return o.forEach((function(e){var r=e[0];if(!r)throw new Error("Missing Context in pair");if(!r[t])throw new Error("Context needs to be created by `createContext`")})),function(t){var n=e(r.getStore(),new Map(o));return r.run(n,(function(){return t()}))}},exports.perform=function(e){var t=r.getStore();return t instanceof Map&&t.has(e)?t.get(e):e.defaultValue};
"use strict";function r(){for(var r=new Map,e=arguments.length,n=new Array(e),t=0;t<e;t++)n[t]=arguments[t];for(var a=0,o=n;a<o.length;a++){var u=o[a];u instanceof Map&&u.forEach((function(e,n){r.set(n,e)}))}return r}Object.defineProperty(exports,"__esModule",{value:!0});var e=new(require("async_hooks").AsyncLocalStorage),n=Symbol("affects-handler");exports.createHandler=function(r){var e;return(e={})[n]=!0,e.defaultValue=r,e},exports.createRunner=function(t){return function(){for(var a=arguments.length,o=new Array(a),u=0;u<a;u++)o[u]=arguments[u];o.forEach((function(r){var e=r[0];if(!e)throw new Error("Missing Handler in pair");if(!e[n])throw new Error("Handler needs to be created by `createHandler`")}));var f=e.getStore(),c=new Map(o),i=r(f,c);return e.run(i,(function(){return t()}))}},exports.perform=function(r){var n=e.getStore();return n instanceof Map&&n.has(r)?n.get(r):r.defaultValue};
//# sourceMappingURL=affects.cjs.production.min.js.map

@@ -24,34 +24,34 @@ import { AsyncLocalStorage } from 'async_hooks';

var asyncLocalStorage = /*#__PURE__*/new AsyncLocalStorage();
var contextSymbol = /*#__PURE__*/Symbol('affects-context');
function createContext(defaultValue) {
var handlerSymbol = /*#__PURE__*/Symbol('affects-handler');
function createHandler(defaultValue) {
var _ref;
return _ref = {}, _ref[contextSymbol] = true, _ref.defaultValue = defaultValue, _ref;
return _ref = {}, _ref[handlerSymbol] = true, _ref.defaultValue = defaultValue, _ref;
}
function perform(Context) {
function perform(Handler) {
var map = asyncLocalStorage.getStore();
if (!(map instanceof Map) || !map.has(Context)) {
return Context['defaultValue'];
if (!(map instanceof Map) || !map.has(Handler)) {
return Handler['defaultValue'];
}
return map.get(Context);
return map.get(Handler);
}
function createRunner() {
for (var _len = arguments.length, pairs = new Array(_len), _key = 0; _key < _len; _key++) {
pairs[_key] = arguments[_key];
}
function createRunner(callback) {
return function runWithHandlers() {
for (var _len = arguments.length, pairs = new Array(_len), _key = 0; _key < _len; _key++) {
pairs[_key] = arguments[_key];
}
pairs.forEach(function (_ref2) {
var Context = _ref2[0];
pairs.forEach(function (_ref2) {
var Handler = _ref2[0];
if (!Context) {
throw new Error('Missing Context in pair');
}
if (!Handler) {
throw new Error('Missing Handler in pair');
}
if (!Context[contextSymbol]) {
throw new Error('Context needs to be created by `createContext`');
}
});
return function (callback) {
if (!Handler[handlerSymbol]) {
throw new Error('Handler needs to be created by `createHandler`');
}
});
var parentMap = asyncLocalStorage.getStore();

@@ -66,3 +66,3 @@ var map = new Map(pairs);

export { createContext, createRunner, perform };
export { createHandler, createRunner, perform };
//# sourceMappingURL=affects.esm.js.map

@@ -1,15 +0,15 @@

declare const contextSymbol: unique symbol;
declare type ContextBox<T = unknown> = {
[contextSymbol]: true;
declare const handlerSymbol: unique symbol;
declare type HandlerBox<T = unknown> = {
[handlerSymbol]: true;
defaultValue: T;
};
declare type ContextType<T extends ContextBox> = T['defaultValue'];
declare type HandlerTypeValueType<T extends HandlerBox> = T['defaultValue'];
declare type RunnerCallback<T> = (...args: any[]) => T;
declare type ContextTuple<T> = [ContextBox<T>, ContextType<ContextBox<T>>];
export declare function createContext<T>(defaultValue: T): {
readonly [contextSymbol]: true;
declare type HandlerTuple<T> = [HandlerBox<T>, HandlerTypeValueType<HandlerBox<T>>];
export declare function createHandler<T>(defaultValue: T): {
readonly [handlerSymbol]: true;
readonly defaultValue: T;
};
export declare function perform<T extends ContextBox>(Context: T): ContextType<T>;
export declare function createRunner<T>(...pairs: Array<ContextTuple<T>>): <T_1>(callback: RunnerCallback<T_1>) => T_1;
export declare function perform<T extends HandlerBox>(Handler: T): HandlerTypeValueType<T>;
export declare function createRunner<T>(callback: RunnerCallback<T>): <HandlerType>(...pairs: HandlerTuple<HandlerType>[]) => T;
export {};
{
"version": "0.1.0",
"version": "1.0.0",
"license": "MIT",

@@ -4,0 +4,0 @@ "main": "dist/index.js",

### Affects
Affects is a Algebraic Effects inspired library for Node.
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/reaktivo/affects/blob/master/LICENSE)
[![npm version](https://img.shields.io/npm/v/affects.svg?style=flat)](https://www.npmjs.com/package/affects)
![Build status](https://github.com/reaktivo/affects/actions/workflows/main.yml/badge.svg)
[![Coverage](https://img.shields.io/codecov/c/github/reaktivo/affects.svg)](https://codecov.io/gh/reaktivo/affects)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/reaktivo/affects/compare)
## What is Affects
Affects is a Algebraic Effects inspired library. If you're coming from the React world, you can think of it as the provider of Context and the useContext hook, but for regular javascript.
Affects provides user friendly thread-local storage to Node.js.
## Examples
```js
import { createHandler, createRunner, perform } from 'affects';
// First create a handler that you can use to refer and set a default value
const User = createHandler({ name: '', email: '' });
// Use the handler in a function
async function emailUser() {
const user = perform(User);
await thirdPartyApi.email(user.email);
}
async function expressEmailUser(request, response) {
if (!request.user) {
return;
}
const run = createRunner(() => emailUser());
await run([User, request.user]);
response.send(200);
}
```
Although this example might not be the best, you will be able to notice that `emailUser` doesn't actually receive the user from an argument nor from scope, `perform(User)` is actually obtaining the value from storage local to that thread.
This is a feature that is helpful in multiple scenarios:
- Dependency injection
- Mocking
- Etc
### Mocking
Let's take the following example:
```js
export const WriteToPath = createHandler(fs.writeFile);
export const writeConfig = createRunner(async () => {
const home = await getUserDir();
perform(WriteToPath)(`${home}/config.txt`, config);
});
// another file
import { writeConfig } from './writeConfig';
writeConfig();
```
By default, `writeConfig` when performing `WriteToPath` will obtain a copy of `fs.writeToFile`, with no modification.
If you would need to run the function, but replace any writing to disk with logs to console, you would:
```js
import { writeConfig, WriteToPath } from './writeConfig';
writeConfig([WriteToPath, (path, data) => console.log(`Wrote to ${path}`)]);
```
If you have read [Algebraic Effects for the Rest of Us](https://overreacted.io/algebraic-effects-for-the-rest-of-us/), you might find the following adapted version of Dan Abramov's example easier to follow:
```js
import { createHandler, createRunner, perform } from 'affects';
const AskName = createHandler();
function getName(user) {
let name = user.name;
if (name === null) {
name = perform(AskName);
}
return name;
}
function makeFriends(user1, user2) {
user1.friendNames.push(getName(user2));
user2.friendNames.push(getName(user1));
}
const arya = { name: null, friendNames: [] };
const gendry = { name: 'Gendry', friendNames: [] };
const run = createRunner(() => makeFriends(arya, gendry));
run([AskName, 'Arya Stark']);
```
## Api
Affects only exports 3 functions:
### `createHandler`
Will create an object with a unique identity that is used to refer to when calling `perform`. Additionally it will hold a default value that is used if `perform` is called outside a runner.
You might find it similar to [React's `createContext`](https://reactjs.org/docs/context.html#reactcreatecontext), in fact, it's modeled after it.
```js
const MyHandler = createHandler(defaultValue);
```
### `createRunner`
In most [papers](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-tr-2016-v2.pdf) and [blog posts](https://overreacted.io/algebraic-effects-for-the-rest-of-us/), handlers are inserted in a way similar to the handling of raised exceptions. This is sadly not possible without either modifying the language or converting everything into generator functions.
With `affects`, handlers are passed in as pairs to the function returned by createRunner
```js
// someFunction is a function that calls `perform` either directly or not, synchronously or not.
const run = createRunner(someFunction);
run([Handler, customValue], [AnotherHandler, anotherCustomValue]);
```
### `perform`
Perform will pull in the matching value for a handler, in a similar way to [React's `useContext`](https://reactjs.org/docs/hooks-reference.html#usecontext). Note that unlike `useContext`, `perform` will work across asynchronous work boundaries. Another important feature of `perform` is that it's fully typed and will use the type of your defaultValue as the type that it returns.
```js
const user = perform(User);
```
## Typescript
Affects is fully typed, meaning that `perform`ed values will return the correct types.
## Installation
`npm install affects`
## Limitations
Affects doesn't solve the ["color of your function"](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) which means that effects that were originally intended to be synchronous can't be resumed in an asynchronous way.
## License
react-nest is open source software [licensed as MIT](https://github.com/reaktivo/react-nest/blob/master/LICENSE).

@@ -1,7 +0,7 @@

import { createContext, createRunner, perform } from '.';
import { createHandler, createRunner, perform } from '.';
describe('Runner', () => {
it('runner returns the return value of callback', () => {
const run = createRunner();
const result = run(() => 'A return value');
const run = createRunner(() => 'A return value');
const result = run();

@@ -12,5 +12,5 @@ expect(result).toBe('A return value');

it('allows a performed context to use the default context value', () => {
const Age = createContext(34);
const run = createRunner();
const result = run(() => perform(Age));
const Age = createHandler(34);
const run = createRunner(() => perform(Age));
const result = run();

@@ -21,5 +21,5 @@ expect(result).toBe(34);

it('allows a performed context to use the passed in context value', () => {
const Age = createContext(34);
const run = createRunner([Age, 35]);
const result = run(() => perform(Age));
const Age = createHandler(34);
const run = createRunner(() => perform(Age));
const result = run([Age, 35]);

@@ -30,13 +30,11 @@ expect(result).toBe(35);

it('it allows nested runners to read context values correctly', () => {
const Age = createContext(33);
const Name = createContext('Alejandro');
const Age = createHandler(33);
const Name = createHandler('Alejandro');
const outerRun = createRunner([Age, 34]);
const innerRun = createRunner([Name, 'Marcel']);
const outerRun = createRunner(() => innerRun([Name, 'Marcel']));
const innerRun = createRunner(
() => `${perform(Name)} is ${perform(Age)} years old`
);
const result = outerRun(() => {
return innerRun(() => {
return `${perform(Name)} is ${perform(Age)} years old`;
});
});
const result = outerRun([Age, 34]);

@@ -47,6 +45,4 @@ expect(result).toBe('Marcel is 34 years old');

it('allows async tasks to read values correctly', async () => {
const Age = createContext(0);
const run = createRunner([Age, 34]);
const result = await run(async () => {
const Age = createHandler(0);
const run = createRunner(async () => {
await new Promise(resolve => setTimeout(resolve, 10));

@@ -56,2 +52,4 @@ return perform(Age);

const result = await run([Age, 34]);
expect(result).toBe(34);

@@ -62,4 +60,4 @@ });

// @ts-expect-error
const fn = () => createRunner([]);
expect(fn).toThrowErrorMatchingInlineSnapshot(`"Missing Context in pair"`);
const fn = () => createRunner()([]);
expect(fn).toThrowErrorMatchingInlineSnapshot(`"Missing Handler in pair"`);
});

@@ -69,5 +67,5 @@

// @ts-expect-error
const fn = () => createRunner([{ defaultValue: 'xyz' }]);
const fn = () => createRunner()([{ defaultValue: 'xyz' }]);
expect(fn).toThrowErrorMatchingInlineSnapshot(
`"Context needs to be created by \`createContext\`"`
`"Handler needs to be created by \`createHandler\`"`
);

@@ -79,5 +77,5 @@ });

it('should return default context value when run outside runner', () => {
const Age = createContext(34);
const Age = createHandler(34);
expect(perform(Age)).toBe(34);
});
});

@@ -5,12 +5,12 @@ import { AsyncLocalStorage } from 'async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();
const contextSymbol = Symbol('affects-context');
const handlerSymbol = Symbol('affects-handler');
type ContextBox<T = unknown> = { [contextSymbol]: true; defaultValue: T };
type ContextType<T extends ContextBox> = T['defaultValue'];
type HandlerBox<T = unknown> = { [handlerSymbol]: true; defaultValue: T };
type HandlerTypeValueType<T extends HandlerBox> = T['defaultValue'];
type RunnerCallback<T> = (...args: any[]) => T;
type ContextTuple<T> = [ContextBox<T>, ContextType<ContextBox<T>>];
type HandlerTuple<T> = [HandlerBox<T>, HandlerTypeValueType<HandlerBox<T>>];
export function createContext<T>(defaultValue: T) {
export function createHandler<T>(defaultValue: T) {
return {
[contextSymbol]: true,
[handlerSymbol]: true,
defaultValue,

@@ -20,24 +20,28 @@ } as const;

export function perform<T extends ContextBox>(Context: T): ContextType<T> {
export function perform<T extends HandlerBox>(
Handler: T
): HandlerTypeValueType<T> {
const map = asyncLocalStorage.getStore();
if (!(map instanceof Map) || !map.has(Context)) {
return Context['defaultValue'];
if (!(map instanceof Map) || !map.has(Handler)) {
return Handler['defaultValue'];
}
return map.get(Context);
return map.get(Handler);
}
export function createRunner<T>(...pairs: Array<ContextTuple<T>>) {
pairs.forEach(([Context]) => {
if (!Context) {
throw new Error('Missing Context in pair');
}
export function createRunner<T>(callback: RunnerCallback<T>) {
return function runWithHandlers<HandlerType>(
...pairs: Array<HandlerTuple<HandlerType>>
): T {
pairs.forEach(([Handler]) => {
if (!Handler) {
throw new Error('Missing Handler in pair');
}
if (!Context[contextSymbol]) {
throw new Error('Context needs to be created by `createContext`');
}
});
if (!Handler[handlerSymbol]) {
throw new Error('Handler needs to be created by `createHandler`');
}
});
return function<T>(callback: RunnerCallback<T>): T {
const parentMap = asyncLocalStorage.getStore();

@@ -44,0 +48,0 @@ const map = new Map(pairs);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc