@mantaq/sugar 0.0.2 → 0.2.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 +12 -12
- package/dist/index.d.mts +36 -12
- package/dist/index.mjs +38 -20
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ import { events } from "@mantaq/sugar";
|
|
|
89
89
|
|
|
90
90
|
const e = events("fetch", "resolve", "fail");
|
|
91
91
|
|
|
92
|
-
e.fetch.create(); // {
|
|
92
|
+
e.fetch.create(); // { type: "fetch" }
|
|
93
93
|
e.resolve.is(emittedEvent); // boolean
|
|
94
94
|
|
|
95
95
|
const actor = new Actor({
|
|
@@ -203,7 +203,7 @@ map.kill("child1");
|
|
|
203
203
|
|
|
204
204
|
```ts
|
|
205
205
|
map.spawn("child1", () => createChild());
|
|
206
|
-
map.spawn("child1", () => createChild()); //
|
|
206
|
+
map.spawn("child1", () => createChild()); // warns, aborts the previous child, replaces
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
`ensure()` is idempotent — safe to call repeatedly. `spawn()` always replaces.
|
|
@@ -217,8 +217,8 @@ import { withPromise } from "@mantaq/sugar";
|
|
|
217
217
|
|
|
218
218
|
// Correct — abort-aware
|
|
219
219
|
withPromise(fetchData(), input.signal, input.emit, {
|
|
220
|
-
success: (data) => ({
|
|
221
|
-
error: (err) => ({
|
|
220
|
+
success: (data) => ({ type: "loaded", payload: data }),
|
|
221
|
+
error: (err) => ({ type: "loadFailed", payload: String(err) }),
|
|
222
222
|
});
|
|
223
223
|
```
|
|
224
224
|
|
|
@@ -227,14 +227,14 @@ withPromise(fetchData(), input.signal, input.emit, {
|
|
|
227
227
|
```ts
|
|
228
228
|
fetchData()
|
|
229
229
|
.then((data) => {
|
|
230
|
-
input.emit({
|
|
230
|
+
input.emit({ type: "loaded", payload: data }); // fires even if actor destroyed
|
|
231
231
|
})
|
|
232
232
|
.catch((err) => {
|
|
233
|
-
input.emit({
|
|
233
|
+
input.emit({ type: "loadFailed", payload: String(err) }); // same problem
|
|
234
234
|
});
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
`withPromise` checks `
|
|
237
|
+
`withPromise` checks `signal.aborted` before each emit. Manual chains fire events into destroyed actors.
|
|
238
238
|
|
|
239
239
|
## Helpers
|
|
240
240
|
|
|
@@ -302,7 +302,7 @@ Send an event to every key in an `ActorMap` (or any `SendableMap`):
|
|
|
302
302
|
```ts
|
|
303
303
|
import { broadcast } from "@mantaq/sugar";
|
|
304
304
|
|
|
305
|
-
broadcast(map, {
|
|
305
|
+
broadcast(map, { type: "ping" });
|
|
306
306
|
```
|
|
307
307
|
|
|
308
308
|
Works with `ActorMap` or any object implementing `{ keys(): string[]; send(key, event): void }`.
|
|
@@ -344,7 +344,7 @@ Batch-create `EventRef`s as a typed record:
|
|
|
344
344
|
import { events } from "@mantaq/sugar";
|
|
345
345
|
|
|
346
346
|
const e = events("click", "submit");
|
|
347
|
-
e.click.create(); // {
|
|
347
|
+
e.click.create(); // { type: "click" }
|
|
348
348
|
e.click.is(emittedEvent); // boolean
|
|
349
349
|
```
|
|
350
350
|
|
|
@@ -356,8 +356,8 @@ Bridge a promise into actor events. Emits `success` on resolve, `error` on rejec
|
|
|
356
356
|
import { withPromise } from "@mantaq/sugar";
|
|
357
357
|
|
|
358
358
|
withPromise(fetchData(), input.signal, input.emit, {
|
|
359
|
-
success: (data) => ({
|
|
360
|
-
error: (err) => ({
|
|
359
|
+
success: (data) => ({ type: "loaded", payload: data }),
|
|
360
|
+
error: (err) => ({ type: "loadFailed", payload: String(err) }),
|
|
361
361
|
});
|
|
362
362
|
```
|
|
363
363
|
|
|
@@ -368,7 +368,7 @@ Schedule a timeout event through the actor's clock. Aborts cleanly if the actor
|
|
|
368
368
|
```ts
|
|
369
369
|
import { withTimeout } from "@mantaq/sugar";
|
|
370
370
|
|
|
371
|
-
withTimeout(5000, input, () => ({
|
|
371
|
+
withTimeout(5000, input, () => ({ type: "timeout", payload: { reason: "exceeded" } }));
|
|
372
372
|
```
|
|
373
373
|
|
|
374
374
|
## Migration from Core
|
package/dist/index.d.mts
CHANGED
|
@@ -6,8 +6,8 @@ declare function matches(actor: {
|
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/transitions/broadcast.d.ts
|
|
8
8
|
type EventLike = {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
type: string;
|
|
10
|
+
payload?: unknown;
|
|
11
11
|
};
|
|
12
12
|
type SendableEvent = AnyEventRef | EventLike;
|
|
13
13
|
interface SendableMap<T extends SendableEvent = SendableEvent> {
|
|
@@ -17,38 +17,62 @@ 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
|
|
39
63
|
//#region src/effects/promise.d.ts
|
|
40
64
|
type EmitFn = (event: {
|
|
41
|
-
|
|
42
|
-
|
|
65
|
+
type: string;
|
|
66
|
+
payload?: unknown;
|
|
43
67
|
}) => void;
|
|
44
68
|
declare function withPromise<T>(promise: Promise<T>, signal: AbortSignal, emit: EmitFn, events: {
|
|
45
69
|
success: (data: T) => {
|
|
46
|
-
|
|
47
|
-
|
|
70
|
+
type: string;
|
|
71
|
+
payload?: unknown;
|
|
48
72
|
};
|
|
49
73
|
error: (err: unknown) => {
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
type: string;
|
|
75
|
+
payload?: unknown;
|
|
52
76
|
};
|
|
53
77
|
}): void;
|
|
54
78
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { abortEffects
|
|
1
|
+
import { abortEffects } from "@mantaq/core/internal";
|
|
2
2
|
import { Either } from "@mantaq/utils";
|
|
3
3
|
import { event, state } from "@mantaq/core";
|
|
4
4
|
//#region src/actors/matches.ts
|
|
@@ -21,29 +21,44 @@ 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
|
+
});
|
|
49
|
+
this.#reapers.set(key, off);
|
|
50
|
+
}
|
|
40
51
|
}
|
|
41
52
|
send(key, event) {
|
|
42
53
|
this.#actors.get(key)?.send(event);
|
|
43
54
|
}
|
|
44
55
|
kill(key) {
|
|
45
56
|
const actor = this.#actors.get(key);
|
|
46
|
-
if (actor) Either.match(abortEffects(actor), (err) =>
|
|
57
|
+
if (actor) Either.match(abortEffects(actor), (err) => {
|
|
58
|
+
throw new Error(err.message);
|
|
59
|
+
}, () => {});
|
|
60
|
+
this.#reapers.get(key)?.();
|
|
61
|
+
this.#reapers.delete(key);
|
|
47
62
|
this.#actors.delete(key);
|
|
48
63
|
}
|
|
49
64
|
get size() {
|
|
@@ -55,8 +70,8 @@ var ActorMap = class {
|
|
|
55
70
|
keys() {
|
|
56
71
|
return [...this.#actors.keys()];
|
|
57
72
|
}
|
|
58
|
-
ensure(key
|
|
59
|
-
if (!this.#actors.has(key)) this.spawn(key
|
|
73
|
+
ensure(key) {
|
|
74
|
+
if (!this.#actors.has(key)) this.spawn(key);
|
|
60
75
|
}
|
|
61
76
|
snapshot(key) {
|
|
62
77
|
return this.#actors.get(key)?.snapshot();
|
|
@@ -64,9 +79,13 @@ var ActorMap = class {
|
|
|
64
79
|
};
|
|
65
80
|
//#endregion
|
|
66
81
|
//#region src/state.ts
|
|
67
|
-
function states(...
|
|
82
|
+
function states(...entries) {
|
|
68
83
|
const result = {};
|
|
69
|
-
for (const
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const name = typeof entry === "string" ? entry : entry.name;
|
|
86
|
+
const ref = state(name)();
|
|
87
|
+
result[name] = typeof entry === "object" && entry.final ? ref.final() : ref;
|
|
88
|
+
}
|
|
70
89
|
return result;
|
|
71
90
|
}
|
|
72
91
|
//#endregion
|
|
@@ -88,11 +107,10 @@ function withPromise(promise, signal, emit, events) {
|
|
|
88
107
|
//#endregion
|
|
89
108
|
//#region src/effects/timeout.ts
|
|
90
109
|
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
110
|
input.clock.setTimeout(ms, () => {
|
|
93
111
|
if (input.signal.aborted) return;
|
|
94
112
|
input.emit(event());
|
|
95
|
-
});
|
|
113
|
+
}, { signal: input.signal });
|
|
96
114
|
}
|
|
97
115
|
//#endregion
|
|
98
116
|
//#region src/transitions/broadcast.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantaq/sugar",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.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/core": "0.0
|
|
24
|
-
"@mantaq/utils": "0.0.
|
|
23
|
+
"@mantaq/core": "0.2.0",
|
|
24
|
+
"@mantaq/utils": "0.0.2"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@stryker-mutator/core": "^9",
|