@mantaq/sugar 0.1.0 → 0.3.0
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 +23 -0
- package/dist/index.d.mts +39 -5
- package/dist/index.mjs +51 -20
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -307,6 +307,29 @@ broadcast(map, { type: "ping" });
|
|
|
307
307
|
|
|
308
308
|
Works with `ActorMap` or any object implementing `{ keys(): string[]; send(key, event): void }`.
|
|
309
309
|
|
|
310
|
+
### `onOutput(actor, handler)`
|
|
311
|
+
|
|
312
|
+
Route an actor's emitted outputs to a handler. `regions` auto-wire child
|
|
313
|
+
outputs into the parent; ActorMap children do not — this is the public
|
|
314
|
+
wrapper for that wiring seam, no internal import needed:
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import { ActorMap, onOutput } from "@mantaq/sugar";
|
|
318
|
+
|
|
319
|
+
const requests = new ActorMap(
|
|
320
|
+
(id) => {
|
|
321
|
+
const child = createHandler(id);
|
|
322
|
+
onOutput(child, (e) => {
|
|
323
|
+
if (result.is(e)) parent.send(e);
|
|
324
|
+
});
|
|
325
|
+
return child;
|
|
326
|
+
},
|
|
327
|
+
{ autoReap: true },
|
|
328
|
+
);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The `is()` guard narrows the output to the receiver's declared input.
|
|
332
|
+
|
|
310
333
|
### `tag(...stateRefs)`
|
|
311
334
|
|
|
312
335
|
Group multiple `StateRef`s and test if a snapshot matches any of them. Useful for UI state grouping.
|
package/dist/index.d.mts
CHANGED
|
@@ -17,25 +17,59 @@ interface SendableMap<T extends SendableEvent = SendableEvent> {
|
|
|
17
17
|
declare function broadcast<const T extends SendableEvent>(map: SendableMap<T>, event: T): void;
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/actors/actor-map.d.ts
|
|
20
|
+
interface ActorMapOptions {
|
|
21
|
+
/**
|
|
22
|
+
* When true, a child that reaches a final state (or dies into `__error`)
|
|
23
|
+
* is removed from the map automatically. Without it, completed children
|
|
24
|
+
* linger until explicitly `kill`ed.
|
|
25
|
+
*/
|
|
26
|
+
autoReap?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Keyed registry of one actor type. `spawn(key)` builds a fresh instance via
|
|
30
|
+
* the factory — same actor shape, keyed by id. The factory receives the key.
|
|
31
|
+
*/
|
|
20
32
|
declare class ActorMap implements SendableMap<SendableEvent> {
|
|
21
33
|
#private;
|
|
22
|
-
constructor(
|
|
23
|
-
spawn(key: string
|
|
34
|
+
constructor(factory: (id: string) => AnyActor, options?: ActorMapOptions);
|
|
35
|
+
spawn(key: string): void;
|
|
24
36
|
send(key: string, event: SendableEvent): void;
|
|
25
37
|
kill(key: string): void;
|
|
26
38
|
get size(): number;
|
|
27
39
|
has(key: string): boolean;
|
|
28
40
|
keys(): string[];
|
|
29
|
-
ensure(key: string
|
|
41
|
+
ensure(key: string): void;
|
|
30
42
|
snapshot(key: string): Snapshot | undefined;
|
|
31
43
|
}
|
|
32
44
|
//#endregion
|
|
33
45
|
//#region src/state.d.ts
|
|
34
|
-
|
|
46
|
+
interface StatesEntry {
|
|
47
|
+
name: string;
|
|
48
|
+
final?: boolean;
|
|
49
|
+
}
|
|
50
|
+
type StatesArg = string | StatesEntry;
|
|
51
|
+
type NameOf<T> = T extends string ? T : T extends {
|
|
52
|
+
name: infer N extends string;
|
|
53
|
+
} ? N : never;
|
|
54
|
+
type FinalOf<T> = T extends {
|
|
55
|
+
final: true;
|
|
56
|
+
} ? true : false;
|
|
57
|
+
type StatesRecord<T extends StatesArg[]> = { [K in T[number] as NameOf<K>]: StateRef<NameOf<K>, unknown, FinalOf<K>>; };
|
|
58
|
+
declare function states<const T extends StatesArg[]>(...entries: T): StatesRecord<T>;
|
|
35
59
|
//#endregion
|
|
36
60
|
//#region src/event.d.ts
|
|
37
61
|
declare function events<T extends string>(...names: T[]): { [K in T]: EventRef<K>; };
|
|
38
62
|
//#endregion
|
|
63
|
+
//#region src/output.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Route an actor's emitted outputs to a handler.
|
|
66
|
+
*
|
|
67
|
+
* `regions` auto-wire child outputs into the parent. ActorMap children do
|
|
68
|
+
* not — this is the public wrapper for that wiring seam. One call in the
|
|
69
|
+
* factory connects a child's declared outputs to a receiver.
|
|
70
|
+
*/
|
|
71
|
+
declare function onOutput(actor: AnyActor, handler: (event: InternalEvent) => void): void;
|
|
72
|
+
//#endregion
|
|
39
73
|
//#region src/effects/promise.d.ts
|
|
40
74
|
type EmitFn = (event: {
|
|
41
75
|
type: string;
|
|
@@ -65,4 +99,4 @@ declare function tag(...stateNames: StateRef<string, unknown>[]): Tag;
|
|
|
65
99
|
declare function isIn(snapshot: Snapshot, stateRefName: string): boolean;
|
|
66
100
|
declare function activeLeaves(snapshot: Snapshot): string[];
|
|
67
101
|
//#endregion
|
|
68
|
-
export { ActorMap, type EventLike, type SendableEvent, type SendableMap, activeLeaves, broadcast, events, isIn, matches, states, tag, withPromise, withTimeout };
|
|
102
|
+
export { ActorMap, type EventLike, type SendableEvent, type SendableMap, activeLeaves, broadcast, events, isIn, matches, onOutput, states, tag, withPromise, withTimeout };
|
package/dist/index.mjs
CHANGED
|
@@ -21,29 +21,45 @@ function matches(actor, pattern) {
|
|
|
21
21
|
}
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/actors/actor-map.ts
|
|
24
|
+
/**
|
|
25
|
+
* Keyed registry of one actor type. `spawn(key)` builds a fresh instance via
|
|
26
|
+
* the factory — same actor shape, keyed by id. The factory receives the key.
|
|
27
|
+
*/
|
|
24
28
|
var ActorMap = class {
|
|
25
29
|
#actors = /* @__PURE__ */ new Map();
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
#reapers = /* @__PURE__ */ new Map();
|
|
31
|
+
#factory;
|
|
32
|
+
#autoReap;
|
|
33
|
+
constructor(factory, options = {}) {
|
|
34
|
+
this.#factory = factory;
|
|
35
|
+
this.#autoReap = options.autoReap === true;
|
|
29
36
|
}
|
|
30
|
-
spawn(key
|
|
31
|
-
if (this.#actors.has(key))
|
|
32
|
-
|
|
33
|
-
this.kill(key);
|
|
34
|
-
}
|
|
35
|
-
const child = factory();
|
|
36
|
-
if (this.#parent) Either.match(setOutputHandler(child, (event) => {
|
|
37
|
-
this.#parent.send(event);
|
|
38
|
-
}), (err) => console.error(err.message), () => {});
|
|
37
|
+
spawn(key) {
|
|
38
|
+
if (this.#actors.has(key)) this.kill(key);
|
|
39
|
+
const child = this.#factory(key);
|
|
39
40
|
this.#actors.set(key, child);
|
|
41
|
+
if (this.#autoReap) {
|
|
42
|
+
if (child.snapshot().done) {
|
|
43
|
+
this.#actors.delete(key);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const off = child.on("done", () => {
|
|
47
|
+
if (this.#actors.get(key) === child) this.#actors.delete(key);
|
|
48
|
+
this.#reapers.delete(key);
|
|
49
|
+
});
|
|
50
|
+
this.#reapers.set(key, off);
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
send(key, event) {
|
|
42
54
|
this.#actors.get(key)?.send(event);
|
|
43
55
|
}
|
|
44
56
|
kill(key) {
|
|
45
57
|
const actor = this.#actors.get(key);
|
|
46
|
-
if (actor) Either.match(abortEffects(actor), (err) =>
|
|
58
|
+
if (actor) Either.match(abortEffects(actor), (err) => {
|
|
59
|
+
throw new Error(err.message);
|
|
60
|
+
}, () => {});
|
|
61
|
+
this.#reapers.get(key)?.();
|
|
62
|
+
this.#reapers.delete(key);
|
|
47
63
|
this.#actors.delete(key);
|
|
48
64
|
}
|
|
49
65
|
get size() {
|
|
@@ -55,8 +71,8 @@ var ActorMap = class {
|
|
|
55
71
|
keys() {
|
|
56
72
|
return [...this.#actors.keys()];
|
|
57
73
|
}
|
|
58
|
-
ensure(key
|
|
59
|
-
if (!this.#actors.has(key)) this.spawn(key
|
|
74
|
+
ensure(key) {
|
|
75
|
+
if (!this.#actors.has(key)) this.spawn(key);
|
|
60
76
|
}
|
|
61
77
|
snapshot(key) {
|
|
62
78
|
return this.#actors.get(key)?.snapshot();
|
|
@@ -64,9 +80,13 @@ var ActorMap = class {
|
|
|
64
80
|
};
|
|
65
81
|
//#endregion
|
|
66
82
|
//#region src/state.ts
|
|
67
|
-
function states(...
|
|
83
|
+
function states(...entries) {
|
|
68
84
|
const result = {};
|
|
69
|
-
for (const
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const name = typeof entry === "string" ? entry : entry.name;
|
|
87
|
+
const ref = state(name)();
|
|
88
|
+
result[name] = typeof entry === "object" && entry.final ? ref.final() : ref;
|
|
89
|
+
}
|
|
70
90
|
return result;
|
|
71
91
|
}
|
|
72
92
|
//#endregion
|
|
@@ -77,6 +97,18 @@ function events(...names) {
|
|
|
77
97
|
return result;
|
|
78
98
|
}
|
|
79
99
|
//#endregion
|
|
100
|
+
//#region src/output.ts
|
|
101
|
+
/**
|
|
102
|
+
* Route an actor's emitted outputs to a handler.
|
|
103
|
+
*
|
|
104
|
+
* `regions` auto-wire child outputs into the parent. ActorMap children do
|
|
105
|
+
* not — this is the public wrapper for that wiring seam. One call in the
|
|
106
|
+
* factory connects a child's declared outputs to a receiver.
|
|
107
|
+
*/
|
|
108
|
+
function onOutput(actor, handler) {
|
|
109
|
+
setOutputHandler(actor, handler);
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
80
112
|
//#region src/effects/promise.ts
|
|
81
113
|
function withPromise(promise, signal, emit, events) {
|
|
82
114
|
promise.then((data) => {
|
|
@@ -88,11 +120,10 @@ function withPromise(promise, signal, emit, events) {
|
|
|
88
120
|
//#endregion
|
|
89
121
|
//#region src/effects/timeout.ts
|
|
90
122
|
function withTimeout(ms, input, event) {
|
|
91
|
-
if (typeof ms !== "number" || ms < 0 || !Number.isFinite(ms)) console.warn(`[withTimeout] invalid ms value: ${ms}. Timeout may not fire.`);
|
|
92
123
|
input.clock.setTimeout(ms, () => {
|
|
93
124
|
if (input.signal.aborted) return;
|
|
94
125
|
input.emit(event());
|
|
95
|
-
});
|
|
126
|
+
}, { signal: input.signal });
|
|
96
127
|
}
|
|
97
128
|
//#endregion
|
|
98
129
|
//#region src/transitions/broadcast.ts
|
|
@@ -119,4 +150,4 @@ function tag(...stateNames) {
|
|
|
119
150
|
return { has: (snapshot) => names.some((name) => isIn(snapshot, name)) };
|
|
120
151
|
}
|
|
121
152
|
//#endregion
|
|
122
|
-
export { ActorMap, activeLeaves, broadcast, events, isIn, matches, states, tag, withPromise, withTimeout };
|
|
153
|
+
export { ActorMap, activeLeaves, broadcast, events, isIn, matches, onOutput, states, tag, withPromise, withTimeout };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantaq/sugar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Userland sugar for mantaq actors — matches, effect helpers, transition utilities.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@mantaq/
|
|
24
|
-
"@mantaq/
|
|
23
|
+
"@mantaq/utils": "0.0.2",
|
|
24
|
+
"@mantaq/core": "0.2.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@stryker-mutator/core": "^9",
|