From bd7a4defdcbe62be6f268be7d24877a645b10a07 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 21 May 2024 09:28:42 +0200 Subject: [PATCH] Remove the forge() method, use memo() instead (#4) Signed-off-by: Matteo Collina --- README.md | 44 ++-------------- asyncforge.d.ts | 12 +---- asyncforge.js | 24 +-------- asyncforge.test-d.ts | 34 +----------- example.mjs | 13 ++--- test/basic.test.js | 94 ---------------------------------- test/do-not-start-memo.test.js | 73 -------------------------- test/fixture/basic/a.js | 9 ---- test/fixture/basic/b.js | 10 ---- test/fixture/basic/index.js | 9 ---- test/memo.test.js | 36 ++++++++++++- 11 files changed, 49 insertions(+), 309 deletions(-) delete mode 100644 test/basic.test.js delete mode 100644 test/do-not-start-memo.test.js delete mode 100644 test/fixture/basic/a.js delete mode 100644 test/fixture/basic/b.js delete mode 100644 test/fixture/basic/index.js diff --git a/README.md b/README.md index 45e52b0..bdb3e11 100644 --- a/README.md +++ b/README.md @@ -14,17 +14,12 @@ npm i asyncforge ## Usage ```js -import { start, forge, memo } from 'asyncforge' - -const a = forge((config) => { - return { - value: config.foo - } -}) +import { memo } from 'asyncforge' +const a = memo() const b = memo() -start({ foo: 'bar' }) +a.set(42) b.set(123) // simulate an event loop turn @@ -41,7 +36,7 @@ setImmediate(() => { }) }) -start({ foo: 'baz' }) +a.set(43) b.set(321) // simulate an event loop turn @@ -50,16 +45,6 @@ setImmediate(() => { console.log('a', a()) console.log('b', b()) }) - -const c = memo("myKeyC"); -const d = memo("myKeyD"); - -setAll({ - [c.key]: 42, - [d.key]: 24, -}); - -console.log(c(), d()); // 42 24 ``` ## TypeScript @@ -67,26 +52,7 @@ console.log(c(), d()); // 42 24 You can call the `asyncforge` functions in a type-safe way: ```ts -// You can define the `AsyncForgeConfig` interface so that `start` and `forge` can use it (TS module augmentation) -declare module "asyncforge" { - interface AsyncForgeConfig { - foo: string; - baz: number; - } -} - -// This is correct -start({ foo: "bar", baz: 42 }) - -// TypeScript will complain, since it's not following the definition of AsyncForgeConfig -start({ wrong: true }) - -// Valid -forge(({ baz: data, foo: value }) => ({ data, value })); - -// Invalid -forge(({ invalid }) => ({ invalid })); - +import { start, memo, setAll } from "asyncforge"; const memoNum = memo(); // This is okay for TypeScript, since you're passing a number diff --git a/asyncforge.d.ts b/asyncforge.d.ts index ed89614..1cef37c 100644 --- a/asyncforge.d.ts +++ b/asyncforge.d.ts @@ -1,15 +1,5 @@ declare namespace asyncforge { - interface AsyncForgeConfig {} - - type ForgeCallback = { - (config: AsyncForgeConfig): T; - name?: string; - }; - - export function start(config: AsyncForgeConfig): void; - export function forge( - fn: ForgeCallback - ): () => ReturnType>; + export function start(): void; export function memo( name?: string ): { diff --git a/asyncforge.js b/asyncforge.js index b3d3f87..5f39368 100644 --- a/asyncforge.js +++ b/asyncforge.js @@ -4,30 +4,11 @@ const { AsyncLocalStorage } = require('node:async_hooks') const asyncLocalStorage = new AsyncLocalStorage() -function start (config) { +function start () { const store = Object.create(null) - store.config = config asyncLocalStorage.enterWith(store) } -let forgeCounter = 0 -function forge (fn) { - const sym = Symbol('forge.' + (fn.name || forgeCounter++)) - return function () { - let store = asyncLocalStorage.getStore() - if (!store) { - store = Object.create(null) - asyncLocalStorage.enterWith(store) - } - if (store[sym]) { - return store[sym] - } - const res = fn(store.config) - store[sym] = res - return res - } -} - let memoCounter = 0 function memo (name) { name = name || 'memo' + memoCounter++ @@ -65,7 +46,6 @@ function setAll (memos) { asyncLocalStorage.enterWith(store) } -module.exports.start = start -module.exports.forge = forge module.exports.memo = memo +module.exports.start = start module.exports.setAll = setAll diff --git a/asyncforge.test-d.ts b/asyncforge.test-d.ts index e589507..6fcef1b 100644 --- a/asyncforge.test-d.ts +++ b/asyncforge.test-d.ts @@ -1,37 +1,7 @@ import { expectError, expectType } from "tsd"; -import { start, forge, memo, setAll } from "."; +import { start, memo, setAll } from "."; -declare module "." { - interface AsyncForgeConfig { - foo: string; - baz: number; - } -} - -// start -expectType(start({ foo: "bar", baz: 42 })); -expectType(start({ baz: 24, foo: "xyz" })); -expectError(start({})); -expectError(start({ foo: false, baz: 42 })); - -// forge -const forgeObject = forge(() => ({ something: "else" })); -expectType<{ something: string }>(forgeObject()); -expectError<{ something: boolean }>(forgeObject()); - -const forgeString = forge(() => ""); -expectType(forgeString()); -expectError(forgeString()); - -const remapForge = forge(({ baz: data, foo: value }) => ({ data, value })); -expectType<{ data: number; value: string }>(remapForge()); -expectType<() => void>(forge((config) => console.log(config.baz))); -expectError<() => void>(forge((config) => config.invalid)); - -const getFooString = forge((config) => { - return config.baz + config.foo; -}); -expectType(getFooString()); +expectType<() => void>(start); // memo const memoNum = memo(); diff --git a/example.mjs b/example.mjs index 9e7a2bc..9b5a73b 100644 --- a/example.mjs +++ b/example.mjs @@ -1,14 +1,9 @@ -import { start, forge, memo } from './asyncforge.js' - -const a = forge((config) => { - return { - value: config.foo - } -}) +import { memo } from './asyncforge.js' +const a = memo() const b = memo() -start({ foo: 'bar' }) +a.set(42) b.set(123) // simulate an event loop turn @@ -25,7 +20,7 @@ setImmediate(() => { }) }) -start({ foo: 'baz' }) +a.set(43) b.set(321) // simulate an event loop turn diff --git a/test/basic.test.js b/test/basic.test.js deleted file mode 100644 index c85a0ab..0000000 --- a/test/basic.test.js +++ /dev/null @@ -1,94 +0,0 @@ -'use strict' - -const { test } = require('node:test') -const assert = require('node:assert/strict') -const { start, forge } = require('../') -const { setImmediate: immediate } = require('node:timers/promises') -const tspl = require('@matteo.collina/tspl') - -test('forge', async (t) => { - const { a, b } = require('./fixture/basic') - - assert.deepEqual(a(), { value: undefined }) - assert.equal(a(), a()) - assert.deepEqual(b(), { - fromA: { - value: undefined - } - }) -}) - -test('start and forge', async (t) => { - const { a, b } = require('./fixture/basic') - - start({ - foo: 'bar' - }) - - assert.deepEqual(a(), { value: 'bar' }) - assert.equal(a(), a()) - assert.deepEqual(b(), { - fromA: { - value: 'bar' - } - }) - - await immediate() - - assert.deepEqual(a(), { value: 'bar' }) - - start({ - foo: 'baz' - }) - - assert.deepEqual(a(), { value: 'baz' }) - assert.equal(a(), a()) - assert.deepEqual(b(), { - fromA: { - value: 'baz' - } - }) - - await immediate() - - assert.deepEqual(a(), { value: 'baz' }) -}) - -test('start and forge with different config', async (t) => { - const p = tspl(t, { plan: 6 }) - const a = forge((config) => { - return { - value: config.foo - } - }) - - start({ - foo: 'bar' - }) - - p.deepEqual(a(), { value: 'bar' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - }) - - queueMicrotask(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - }) - - start({ - foo: 'baz' - }) - - p.deepEqual(a(), { value: 'baz' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'baz' }) - }) - - queueMicrotask(() => { - p.deepStrictEqual(a(), { value: 'baz' }) - }) - - await p.completed -}) diff --git a/test/do-not-start-memo.test.js b/test/do-not-start-memo.test.js deleted file mode 100644 index 267e517..0000000 --- a/test/do-not-start-memo.test.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict' - -const { test } = require('node:test') -const { start, memo } = require('../') -const tspl = require('@matteo.collina/tspl') - -test('memo without start', async (t) => { - const p = tspl(t, { plan: 6 }) - const a = memo() - - p.throws(() => a(), { message: 'asyncforge store is not initialized for memo0' }) - a.set({ value: 'bar' }) - p.deepStrictEqual(a(), { value: 'bar' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - }) - - queueMicrotask(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - }) - - start() - - p.deepStrictEqual(a(), undefined) - a.set({ value: 'baz' }) - - p.deepEqual(a(), { value: 'baz' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'baz' }) - }) - - queueMicrotask(() => { - p.deepStrictEqual(a(), { value: 'baz' }) - }) - - await p.completed -}) - -test('memo with name', async (t) => { - const p = tspl(t, { plan: 1 }) - const a = memo('custom-name') - - p.throws(() => a(), { message: 'asyncforge store is not initialized for custom-name' }) - - await p.completed -}) - -test('nested', async (t) => { - const p = tspl(t, { plan: 5 }) - const a = memo() - - p.throws(() => a(), { message: 'asyncforge store is not initialized for memo1' }) - a.set({ value: 'bar' }) - p.deepStrictEqual(a(), { value: 'bar' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - - a.set({ value: 'baz' }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'baz' }) - }) - }) - - setImmediate(() => { - p.deepStrictEqual(a(), { value: 'bar' }) - }) - - await p.completed -}) diff --git a/test/fixture/basic/a.js b/test/fixture/basic/a.js deleted file mode 100644 index 9191176..0000000 --- a/test/fixture/basic/a.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const { forge } = require('../../../') - -module.exports = forge((config) => { - return { - value: config?.foo - } -}) diff --git a/test/fixture/basic/b.js b/test/fixture/basic/b.js deleted file mode 100644 index 35a3f61..0000000 --- a/test/fixture/basic/b.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -const { forge } = require('../../../') -const a = require('./a') - -module.exports = forge((config) => { - return { - fromA: a() - } -}) diff --git a/test/fixture/basic/index.js b/test/fixture/basic/index.js deleted file mode 100644 index 9531bfb..0000000 --- a/test/fixture/basic/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const a = require('./a') -const b = require('./b') - -module.exports = { - a, - b -} diff --git a/test/memo.test.js b/test/memo.test.js index 6f74fb9..b9ca27b 100644 --- a/test/memo.test.js +++ b/test/memo.test.js @@ -5,9 +5,11 @@ const { start, memo } = require('../') const tspl = require('@matteo.collina/tspl') test('memo', async (t) => { - const p = tspl(t, { plan: 6 }) + const p = tspl(t, { plan: 7 }) const a = memo() + p.throws(a, /asyncforge store is not initialized for memo0/) + start() p.deepStrictEqual(a(), undefined) @@ -66,3 +68,35 @@ test('nested', async (t) => { await p.completed }) + +test('memo without start', async (t) => { + const p = tspl(t, { plan: 6 }) + const a = memo() + + p.throws(a, /asyncforge store is not initialized for memo\d+/) + a.set({ value: 'bar' }) + p.deepStrictEqual(a(), { value: 'bar' }) + + setImmediate(() => { + p.deepStrictEqual(a(), { value: 'bar' }) + }) + + queueMicrotask(() => { + p.deepStrictEqual(a(), { value: 'bar' }) + }) + + p.deepStrictEqual(a(), { value: 'bar' }) + a.set({ value: 'baz' }) + + p.deepEqual(a(), { value: 'baz' }) + + setImmediate(() => { + p.deepStrictEqual(a(), { value: 'baz' }) + }) + + queueMicrotask(() => { + p.deepStrictEqual(a(), { value: 'baz' }) + }) + + await p.completed +})