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

awai

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

awai - npm Package Compare versions

Comparing version 0.0.1-alpha-1 to 0.0.1-alpha-10

112

dist/index.d.ts

@@ -0,1 +1,33 @@

type Resolver<T> = (payload: T) => any;
type FilterPredicate<T> = (value: T) => boolean;
declare class AwaitableEvent<T> {
private _awaiters;
then(resolve: Resolver<T>): Promise<T>;
emit(value: T): void;
filter(predicate: FilterPredicate<T>): Promise<unknown>;
}
declare const delay: (ms: number) => Promise<void>;
declare const fork: (forkFn: () => Promise<any>) => Promise<void>;
declare const rejectAfter: (ms: number) => Promise<void>;
type Callback<A extends any[], R extends any> = (...args: A) => R;
type BaseEvents<Args> = {
invoked: AwaitableEvent<Args>;
failed: AwaitableEvent<any>;
};
type AsyncEvents<Args, Return> = BaseEvents<Args> & {
completed: AwaitableEvent<Return>;
};
declare function action<Args extends any[]>(): Function & {
events: BaseEvents<Args>;
};
declare function action<Args extends any[], Return extends any>(T: Callback<Args, Return>): Callback<Args, Return> & {
events: AsyncEvents<Args, Return>;
};
type AsyncSetter<T> = (nextValueOrResolver: T | Promise<T> | ((current?: T | undefined) => T | Promise<T>)) => void;

@@ -19,4 +51,19 @@

type Resolver<T> = (payload: T) => any;
interface ReadableState<T> {
events: {
changed: AwaitableEvent<T>;
};
get: () => T;
then: (resolver: Resolver<T>) => Promise<T>;
}
type Id$1 = string;
type FamilyState<T> = ReadableState<Record<Id$1, T>> & {
events: {
changed: AwaitableEvent<Record<Id$1, T>>;
};
getNode: (id: Id$1) => T;
setNode: (id: Id$1, stateNode: T) => void;
};
interface ReadableAsyncState<T> {

@@ -34,22 +81,6 @@ events: {

}
declare const isReadableAsyncState: <T>(value: unknown) => value is ReadableAsyncState<T>;
interface ReadableState<T> {
events: {
changed: AwaitableEvent<T>;
};
get: () => T;
then: (resolver: Resolver<T>) => Promise<T>;
}
type InferReadableType<T> = T extends ReadableAsyncState<infer U> ? U : T extends ReadableState<infer Q> ? Q : unknown;
type Key = string;
type FamilyState<T extends ReadableState<any> | ReadableAsyncState<any>> = ReadableState<Record<string, T>> & {
events: {
set: AwaitableEvent<T>;
};
getNode: (id: Key) => T;
set: (id: Key, value: T) => void;
};
type InferReadableType<T> = T extends ReadableState<infer U> | ReadableAsyncState<infer U> ? U : unknown;
type Setter<T> = (nextValueOrResolver: T | ((current: T) => T)) => Promise<T>;

@@ -75,32 +106,2 @@

type FilterPredicate<T> = (value: T) => boolean;
declare class AwaitableEvent<T> {
private _awaiters;
then(resolve: Resolver<T>): Promise<T>;
emit(value: T): void;
filter(predicate: FilterPredicate<T>): Promise<unknown>;
}
declare const delay: (ms: number) => Promise<void>;
declare const fork: (forkFn: () => Promise<any>) => Promise<void>;
declare const rejectAfter: (ms: number) => Promise<void>;
type Callback<A extends any[], R extends any> = (...args: A) => R;
type BaseEvents<Args> = {
invoked: AwaitableEvent<Args>;
failed: AwaitableEvent<any>;
};
type AsyncEvents<Args, Return> = BaseEvents<Args> & {
completed: AwaitableEvent<Return>;
};
declare function action<Args extends any[]>(): Function & {
events: BaseEvents<Args>;
};
declare function action<Args extends any[], Return extends any>(T: Callback<Args, Return>): Callback<Args, Return> & {
events: AsyncEvents<Args, Return>;
};
type InitialValue<T> = T | Promise<T> | (() => Promise<T>);

@@ -111,4 +112,9 @@ type AsyncState<T> = ReadableAsyncState<T> & WritableAsyncState<T>;

declare const familyState: <T extends ReadableState<any> | ReadableAsyncState<any>>() => FamilyState<T>;
type State<T> = ReadableState<T> & WritableState<T>;
declare const state: <T>(initialValue: T) => State<T>;
type Id = string;
declare const familyState: <T, Initializer extends (id: Id) => T | Promise<T>, Family extends Record<string, NodeType>, NodeType = ReturnType<Initializer> extends PromiseLike<infer Q> ? AsyncState<Q> : State<ReturnType<Initializer>>>(initializer: Initializer) => FamilyState<NodeType>;
declare const scenario: (scenarioFn: () => Promise<any>) => Promise<never>;

@@ -127,6 +133,2 @@

type State<T> = ReadableState<T> & WritableState<T>;
declare const state: <T>(initialValue: T) => State<T>;
export { AsyncState, AwaitableEvent, State, action, asyncState, delay, familyState, fork, rejectAfter, scenario, scenarioOnEvery, scenarioOnce, selector, state };
export { AsyncSetter, AsyncState, AsyncStatus, AsyncValue, AwaitableEvent, FamilyState, InferReadableType, ReadableAsyncState, ReadableState, Setter, State, WritableAsyncState, WritableState, action, asyncState, delay, familyState, fork, isReadableAsyncState, rejectAfter, scenario, scenarioOnEvery, scenarioOnce, selector, state };

@@ -159,2 +159,19 @@ 'use strict';

const scenario = async (scenarioFn) => {
while (true) {
await scenarioFn().catch(() => void 0);
}
};
const scenarioOnEvery = async (awaitableEvent, scenarioFn) => {
while (true) {
const event = await awaitableEvent;
scenarioFn(event).catch(noop);
}
};
const scenarioOnce = (scenarioFn) => {
scenarioFn().catch(noop);
};
const state = (initialValue) => {

@@ -186,50 +203,34 @@ let value = initialValue;

const familyState = () => {
const familyState = (initializer) => {
const family = state({});
let mounted = true;
const events = {
changed: new AwaitableEvent(),
set: new AwaitableEvent()
changed: new AwaitableEvent()
};
const getNode = (id) => {
if (id in family.get()) {
return family.get()[id];
}
const initialValue = initializer(id);
const stateNode = initialValue instanceof Promise ? asyncState(initialValue) : state(initialValue);
family.set((current) => ({ ...current, [id]: stateNode }));
events.changed.emit(family.get());
scenarioOnEvery(stateNode.events.changed, async () => {
events.changed.emit(family.get());
});
return stateNode;
};
const setNode = (id, stateNode) => {
family.set((current) => ({ ...current, [id]: stateNode }));
events.changed.emit(family.get());
};
const get = () => {
return family.get();
};
const getNode = (id) => family.get()[id];
const set = (id, stateNode) => {
family.set((current) => ({ ...current, [id]: stateNode }));
events.set.emit(stateNode);
};
const then = async (resolve) => {
const result = await resolve(get());
const result = await resolve(family.get());
return result;
};
(async () => {
while (mounted) {
await Promise.race([
events.set,
...Object.values(family.get()).map((stateNode) => stateNode.events.changed)
]);
events.changed.emit(family.get());
}
})();
return { events, get, getNode, set, then };
return { events, get, getNode, setNode, then };
};
const scenario = async (scenarioFn) => {
while (true) {
await scenarioFn().catch(() => void 0);
}
};
const scenarioOnEvery = async (awaitableEvent, scenarioFn) => {
while (true) {
const event = await awaitableEvent;
scenarioFn(event).catch(noop);
}
};
const scenarioOnce = (scenarioFn) => {
scenarioFn().catch(noop);
};
const getCommonStatus = (states) => {

@@ -282,7 +283,8 @@ const asyncStates = states.filter(isReadableAsyncState);

const status = getStatus();
if (status === AsyncStatus.FAILURE) {
queueMicrotask(() => {
events.changed.emit(get());
});
if (status === AsyncStatus.LOADING) {
return;
}
queueMicrotask(() => {
events.changed.emit(get());
});
}

@@ -337,2 +339,3 @@ })();

exports.fork = fork;
exports.isReadableAsyncState = isReadableAsyncState;
exports.rejectAfter = rejectAfter;

@@ -339,0 +342,0 @@ exports.scenario = scenario;

{
"name": "awai",
"version": "0.0.1-alpha-1",
"version": "0.0.1-alpha-10",
"author": "Yuriy Yakym",

@@ -11,3 +11,5 @@ "description": "State management library",

"type": "module",
"files": ["dist"],
"files": [
"dist"
],
"scripts": {

@@ -14,0 +16,0 @@ "build": "rm -rf dist && rollup -c rollup.config.js",

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

# [DIRTY DRAFT] JavaScript state management library
<div align="center">
<h1>Awai</h1>
<p>Minimalistic, dependency-free state management library</p>
Minimalistic, dependency-free, written in TypeScript.
Besides state management utils, this repository suggests unique architectural approach.
The architecture consists of three main parts: state, action and scenario.
The library was written using a concept of a promise-like object which has no terminal state and may resolve multiple times. Let's call it re-resolvable.
<div>
<img src="https://github.com/yuriyyakym/awai/actions/workflows/tests.yml/badge.svg" />
<img src="https://img.shields.io/badge/stability-experimental-blue.svg" />
<img src="https://img.shields.io/badge/License-MIT-blue.svg" />
</div>
Such re-resolvable can be used instead of event emitters, and when you try to do so, you are naturally forced into a different way of algorithmic thinking.
Let's create a simple state node, and dive deeper.
```ts
const counter = state(0);
```
`counter` is an object with following properties: `get`, `set`, `events`.
While `get` and `set` methods are self-explanatory, `events` property is quite unusual - every event is re-resolvable.
As of now, there is only `changed` event on state node.
A piece of code may be better than thousand of words:
```ts
const counter = state(0);
setTimeout(counter.set, 100, 'hello');
setTimeout(counter.set, 200, 'there');
const value1 = await counter.events.changed;
const value2 = await counter.events.changed;
console.log(`${value1} ${value2}`); // hello there
```
It's quite remarkable that `counter.events.changed` has always the same reference. Notice how it was resolved with two different values in the above snippet.
---
It's time to create some actions.
```ts
const increment = action(() => counter.set(current => current + 1));
const decrement = action(() => counter.set(current => current - 1));
```
Action is just a function which, similarly to state objects, has its re-resolvable `events`, for example:
```ts
setTimeout(increment, 100);
await increment.events.invoked;
console.log(`incremented after 100ms`);
```
More utils with re-resolvable events to come in future.
All this brings us to the `scenario` concept - an async function, which describes small piece of logics.
Let's say we've got a new requirement to preserve counter in `sessionStorage` (write-only for now).
```ts
scenario(async () => {
const value = await counter.events.changed;
sessionStorage.setItem('counter', value);
});
```
Scenario is a sequence of awaited events. Since those events are promise-like, they may be combined with other promises using Promise methods like `any`, `race`, `all`, etc.
Side note: beware of stale values when using `Promise.all`. Let's say you want a scenario to happen only after both user and license state nodes are updated.
```ts
// Wrong - user or license may be outdated
const [user, license] = await Promise.all([
userState.events.changed,
licenseState.events.changed,
]);
// Good - both user and license are up to date
await Promise.all([userState.events.changed, licenseState.events.changed]);
const user = userState.get();
const license = licenseState.get();
```
This library provides variety of syntax sugar helpers for scenarios. Like: `scenarioOnEvery`, `scenarioOnce`. More to be described later.
This is how you can implement state preserving using `scenarioOnEvery`:
```ts
scenarioOnEvery(counter.events.changed, async () => {
// There are two ways of retrieving state value: `state.get()` and `await state`
sessionStorage.setItem('counter', await counter);
});
```
---
Actions may be empty. When that's the case, they do nothing but emit `invoked` event when called, and this may be used to control a scenario flow. This is how we can implement `increment` and `decrement` functionalities:
```ts
const increment = action();
const decrement = action();
scenarioOnEvery(increment.events.invoked, () => {
counter.set(current => current + 1);
});
scenarioOnEvery(decrement.events.invoked, () => {
counter.set(current => current - 1);
});
```
---
More advanced example how to implement a debounced scenario:
```ts
const TIMEOUT_SYMBOL = Symbol();
scenarioOnEvery(increment.events.invoked, async () => {
const result = await Promise.race([
increment.events.invoked
delay(DEBOUNCE_TIMEOUT).then(() => TIMEOUT_SYMBOL),
]);
if (result === TIMEOUT_SYMBOL) {
// Some debounced functionality here
}
});
```
<br />
<p>| <a href="https://awai.vercel.app/">Documentation</a> | <a href="https://github.com/yuriyyakym/awai-react">Awai-React</a> |</p>
</div>

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