@r-machine/testing 1.0.0-alpha.11

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 ADDED
@@ -0,0 +1,26 @@
1
+ ⚠️ **WARNING: THIS LIBRARY IS STILL IN DEVELOPMENT** ⚠️
2
+
3
+ ---
4
+
5
+ <img src="r-machine.logo.svg" width="158px" align="center" alt="R-Machine logo" />
6
+
7
+ # @r-machine/testing — Testing utilities for R-Machine
8
+
9
+ [![NPM Version](https://img.shields.io/npm/v/%40r-machine%2Ftesting?label=latest)](https://www.npmjs.com/package/@r-machine/testing)
10
+ [![R-Machine CI status](https://github.com/codecarvings/r-machine/actions/workflows/ci.yml/badge.svg?event=push&branch=main)](https://github.com/codecarvings/r-machine/actions/workflows/ci.yml?query=branch%3Amain)
11
+
12
+ ---
13
+
14
+ ## License
15
+
16
+ `@r-machine/testing` is licensed under the
17
+ [GNU Affero General Public License v3.0](./LICENSE) (AGPL-3.0-only).
18
+
19
+ This means:
20
+
21
+ - ✅ Free to use in open source projects with a compatible license
22
+ - ✅ Free to modify and distribute under the same terms
23
+ - ❌ **Cannot** be used in closed-source / proprietary software
24
+
25
+ > If you need to use `@r-machine/testing` in a proprietary project,
26
+ > reach out at licensing@codecarvings.com to discuss a commercial arrangement.
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of @r-machine/testing, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ERR_VERIFY_SETUP_INVALID = exports.ERR_PLUG_ALREADY_MOCKED = void 0;
16
+ // RMachineUsageError
17
+ exports.ERR_PLUG_ALREADY_MOCKED = "ERR_PLUG_ALREADY_MOCKED";
18
+ exports.ERR_VERIFY_SETUP_INVALID = "ERR_VERIFY_SETUP_INVALID";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export declare const ERR_PLUG_ALREADY_MOCKED = "ERR_PLUG_ALREADY_MOCKED";
14
+ export declare const ERR_VERIFY_SETUP_INVALID = "ERR_VERIFY_SETUP_INVALID";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export declare const ERR_PLUG_ALREADY_MOCKED = "ERR_PLUG_ALREADY_MOCKED";
14
+ export declare const ERR_VERIFY_SETUP_INVALID = "ERR_VERIFY_SETUP_INVALID";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ // RMachineUsageError
14
+ export const ERR_PLUG_ALREADY_MOCKED = "ERR_PLUG_ALREADY_MOCKED";
15
+ export const ERR_VERIFY_SETUP_INVALID = "ERR_VERIFY_SETUP_INVALID";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of @r-machine/testing, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ERR_VERIFY_SETUP_INVALID = exports.ERR_PLUG_ALREADY_MOCKED = void 0;
16
+ var error_codes_js_1 = require("./error-codes.cjs");
17
+ Object.defineProperty(exports, "ERR_PLUG_ALREADY_MOCKED", { enumerable: true, get: function () { return error_codes_js_1.ERR_PLUG_ALREADY_MOCKED; } });
18
+ Object.defineProperty(exports, "ERR_VERIFY_SETUP_INVALID", { enumerable: true, get: function () { return error_codes_js_1.ERR_VERIFY_SETUP_INVALID; } });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { ERR_PLUG_ALREADY_MOCKED, ERR_VERIFY_SETUP_INVALID } from "./error-codes.cjs";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { ERR_PLUG_ALREADY_MOCKED, ERR_VERIFY_SETUP_INVALID } from "./error-codes.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of @r-machine/testing, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { ERR_PLUG_ALREADY_MOCKED, ERR_VERIFY_SETUP_INVALID } from "./error-codes.js";
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of r-machine, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.createEventCollector = createEventCollector;
16
+ const core_1 = require("r-machine/core");
17
+ /**
18
+ * Subscribe a buffer to the runtime event bus of the given R-Machine
19
+ * instance (reached via a strategy or RMachine itself). The returned
20
+ * collector accumulates every emitted internal event in insertion order
21
+ * until disposed.
22
+ *
23
+ * Designed for test isolation: each call creates a private buffer with
24
+ * its own subscription, so parallel tests don't share state as long as
25
+ * each test uses its own strategy/machine instance.
26
+ */
27
+ function createEventCollector(target) {
28
+ const events = [];
29
+ const unsubscribe = target[core_1.BUS_ACCESSOR]().subscribe((event) => {
30
+ events.push(event);
31
+ });
32
+ let disposed = false;
33
+ return {
34
+ get events() {
35
+ return events;
36
+ },
37
+ clear() {
38
+ events.length = 0;
39
+ },
40
+ dispose() {
41
+ if (disposed) {
42
+ return;
43
+ }
44
+ disposed = true;
45
+ unsubscribe();
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { type BusBridge, type InternalEvent } from "r-machine/core";
14
+ export interface EventCollector {
15
+ /**
16
+ * Events collected so far, in emit order. Treat as read-only — mutating
17
+ * this array will not affect future collection but will surprise other
18
+ * readers. Use `clear()` to reset.
19
+ */
20
+ readonly events: readonly InternalEvent[];
21
+ /**
22
+ * Drop all collected events. Useful between phases of a test when you
23
+ * want to assert on a fresh window without re-creating the collector.
24
+ */
25
+ clear(): void;
26
+ /**
27
+ * Unsubscribe from the bus. After dispose, `events` no longer grows.
28
+ * Safe to call multiple times.
29
+ */
30
+ dispose(): void;
31
+ }
32
+ /**
33
+ * Subscribe a buffer to the runtime event bus of the given R-Machine
34
+ * instance (reached via a strategy or RMachine itself). The returned
35
+ * collector accumulates every emitted internal event in insertion order
36
+ * until disposed.
37
+ *
38
+ * Designed for test isolation: each call creates a private buffer with
39
+ * its own subscription, so parallel tests don't share state as long as
40
+ * each test uses its own strategy/machine instance.
41
+ */
42
+ export declare function createEventCollector(target: BusBridge): EventCollector;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { type BusBridge, type InternalEvent } from "r-machine/core";
14
+ export interface EventCollector {
15
+ /**
16
+ * Events collected so far, in emit order. Treat as read-only — mutating
17
+ * this array will not affect future collection but will surprise other
18
+ * readers. Use `clear()` to reset.
19
+ */
20
+ readonly events: readonly InternalEvent[];
21
+ /**
22
+ * Drop all collected events. Useful between phases of a test when you
23
+ * want to assert on a fresh window without re-creating the collector.
24
+ */
25
+ clear(): void;
26
+ /**
27
+ * Unsubscribe from the bus. After dispose, `events` no longer grows.
28
+ * Safe to call multiple times.
29
+ */
30
+ dispose(): void;
31
+ }
32
+ /**
33
+ * Subscribe a buffer to the runtime event bus of the given R-Machine
34
+ * instance (reached via a strategy or RMachine itself). The returned
35
+ * collector accumulates every emitted internal event in insertion order
36
+ * until disposed.
37
+ *
38
+ * Designed for test isolation: each call creates a private buffer with
39
+ * its own subscription, so parallel tests don't share state as long as
40
+ * each test uses its own strategy/machine instance.
41
+ */
42
+ export declare function createEventCollector(target: BusBridge): EventCollector;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { BUS_ACCESSOR } from "r-machine/core";
14
+ /**
15
+ * Subscribe a buffer to the runtime event bus of the given R-Machine
16
+ * instance (reached via a strategy or RMachine itself). The returned
17
+ * collector accumulates every emitted internal event in insertion order
18
+ * until disposed.
19
+ *
20
+ * Designed for test isolation: each call creates a private buffer with
21
+ * its own subscription, so parallel tests don't share state as long as
22
+ * each test uses its own strategy/machine instance.
23
+ */
24
+ export function createEventCollector(target) {
25
+ const events = [];
26
+ const unsubscribe = target[BUS_ACCESSOR]().subscribe((event) => {
27
+ events.push(event);
28
+ });
29
+ let disposed = false;
30
+ return {
31
+ get events() {
32
+ return events;
33
+ },
34
+ clear() {
35
+ events.length = 0;
36
+ },
37
+ dispose() {
38
+ if (disposed) {
39
+ return;
40
+ }
41
+ disposed = true;
42
+ unsubscribe();
43
+ },
44
+ };
45
+ }
package/lib/index.cjs ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of r-machine, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.verifyResourceAtlas = exports.mockPlug = exports.createEventCollector = void 0;
16
+ var event_collector_js_1 = require("./event-collector.cjs");
17
+ Object.defineProperty(exports, "createEventCollector", { enumerable: true, get: function () { return event_collector_js_1.createEventCollector; } });
18
+ var mock_plug_js_1 = require("./mock-plug.cjs");
19
+ Object.defineProperty(exports, "mockPlug", { enumerable: true, get: function () { return mock_plug_js_1.mockPlug; } });
20
+ var verify_resource_atlas_js_1 = require("./verify-resource-atlas.cjs");
21
+ Object.defineProperty(exports, "verifyResourceAtlas", { enumerable: true, get: function () { return verify_resource_atlas_js_1.verifyResourceAtlas; } });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { createEventCollector, type EventCollector } from "./event-collector.cjs";
14
+ export { mockPlug } from "./mock-plug.cjs";
15
+ export { type SourceLocation, type VerifyIssue, type VerifyReport, type VerifyResourceAtlasOptions, verifyResourceAtlas, } from "./verify-resource-atlas.cjs";
package/lib/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { createEventCollector, type EventCollector } from "./event-collector.js";
14
+ export { mockPlug } from "./mock-plug.js";
15
+ export { type SourceLocation, type VerifyIssue, type VerifyReport, type VerifyResourceAtlasOptions, verifyResourceAtlas, } from "./verify-resource-atlas.js";
package/lib/index.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export { createEventCollector } from "./event-collector.js";
14
+ export { mockPlug } from "./mock-plug.js";
15
+ export { verifyResourceAtlas, } from "./verify-resource-atlas.js";
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of r-machine, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.mockPlug = void 0;
16
+ const core_1 = require("r-machine/core");
17
+ const errors_1 = require("r-machine/errors");
18
+ const errors_2 = require("#r-machine/testing/errors");
19
+ const plugMockSymbol = Symbol("plugMock");
20
+ const mockPlug = (plug) => {
21
+ return {
22
+ with: (_data) => {
23
+ const prevResolve = (0, core_1.getPlugResolve)(plug);
24
+ if (prevResolve[plugMockSymbol]) {
25
+ throw new errors_1.RMachineUsageError(errors_2.ERR_PLUG_ALREADY_MOCKED, "Plug is already mocked.");
26
+ }
27
+ const resolve = (locale, chain) => {
28
+ // TODO: WIP - For now, just return the original resolve result.
29
+ // Plan: read data.$.locale (if set) to override locale; call prevResolve; deep-merge _data.
30
+ // Caveat: prevResolve may install a lazy getter on $.kit[selfKey] for kit self-reference.
31
+ // Deep-merging enumerates kit keys and would invoke that getter; if the self-juncture isn't
32
+ // cached yet (factory still running), the getter throws. Handle this in the merge step —
33
+ // e.g. skip own-property getters or short-circuit on self-ref keys.
34
+ return prevResolve(locale, chain);
35
+ };
36
+ resolve[plugMockSymbol] = true;
37
+ (0, core_1.setPlugResolve)(plug, resolve);
38
+ return () => {
39
+ (0, core_1.setPlugResolve)(plug, prevResolve);
40
+ };
41
+ },
42
+ };
43
+ };
44
+ exports.mockPlug = mockPlug;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { type AnyListPlugHead, type AnyMapPlugHead, type AnyPlugHead, type ExtractCtx, type ExtractKit, type ExtractResAtlas, type PlugBody } from "r-machine/core";
14
+ import type { MockSurfaceMap } from "./mock-surface.cjs";
15
+ type ResetPlug = () => void;
16
+ interface MapMockPlug<PH extends AnyMapPlugHead> {
17
+ readonly with: (data: MockPlugMapData<PH>) => ResetPlug;
18
+ }
19
+ interface ListMockPlug<PH extends AnyListPlugHead> {
20
+ readonly with: (data: MockPlugListData<PH>) => ResetPlug;
21
+ }
22
+ interface MockPlug {
23
+ <PH extends AnyMapPlugHead>(plug: PlugBody<PH>): MapMockPlug<PH>;
24
+ <PH extends AnyListPlugHead>(plug: PlugBody<PH>): ListMockPlug<PH>;
25
+ }
26
+ export declare const mockPlug: MockPlug;
27
+ type MockPlugMapDataDeps<PH extends AnyMapPlugHead> = MockSurfaceMap<ExtractResAtlas<PH>, Omit<PH["deps"], "$">>;
28
+ type TupleToObject<T extends readonly unknown[]> = {
29
+ [K in keyof T as K extends `${number}` ? K : never]: T[K];
30
+ };
31
+ type MockPlugListDeps<PH extends AnyListPlugHead> = MockSurfaceMap<ExtractResAtlas<PH>, Omit<TupleToObject<PH["deps"] extends readonly unknown[] ? PH["deps"] : never>, "$">>;
32
+ type MockCtxContent<PH extends AnyPlugHead, C> = {
33
+ [K in keyof C]?: K extends "kit" ? MockSurfaceMap<ExtractResAtlas<PH>, ExtractKit<PH>> : K extends "ports" ? Partial<C[K]> : C[K];
34
+ };
35
+ type MockCtx<PH extends AnyPlugHead> = keyof ExtractCtx<PH> extends never ? Record<string, never> : MockCtxContent<PH, ExtractCtx<PH>>;
36
+ type MockPlugMapData<PH extends AnyMapPlugHead> = {
37
+ $?: MockCtx<PH>;
38
+ } & MockPlugMapDataDeps<PH>;
39
+ type MockPlugListData<PH extends AnyListPlugHead> = {
40
+ $?: MockCtx<PH>;
41
+ } & MockPlugListDeps<PH>;
42
+ export {};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { type AnyListPlugHead, type AnyMapPlugHead, type AnyPlugHead, type ExtractCtx, type ExtractKit, type ExtractResAtlas, type PlugBody } from "r-machine/core";
14
+ import type { MockSurfaceMap } from "./mock-surface.js";
15
+ type ResetPlug = () => void;
16
+ interface MapMockPlug<PH extends AnyMapPlugHead> {
17
+ readonly with: (data: MockPlugMapData<PH>) => ResetPlug;
18
+ }
19
+ interface ListMockPlug<PH extends AnyListPlugHead> {
20
+ readonly with: (data: MockPlugListData<PH>) => ResetPlug;
21
+ }
22
+ interface MockPlug {
23
+ <PH extends AnyMapPlugHead>(plug: PlugBody<PH>): MapMockPlug<PH>;
24
+ <PH extends AnyListPlugHead>(plug: PlugBody<PH>): ListMockPlug<PH>;
25
+ }
26
+ export declare const mockPlug: MockPlug;
27
+ type MockPlugMapDataDeps<PH extends AnyMapPlugHead> = MockSurfaceMap<ExtractResAtlas<PH>, Omit<PH["deps"], "$">>;
28
+ type TupleToObject<T extends readonly unknown[]> = {
29
+ [K in keyof T as K extends `${number}` ? K : never]: T[K];
30
+ };
31
+ type MockPlugListDeps<PH extends AnyListPlugHead> = MockSurfaceMap<ExtractResAtlas<PH>, Omit<TupleToObject<PH["deps"] extends readonly unknown[] ? PH["deps"] : never>, "$">>;
32
+ type MockCtxContent<PH extends AnyPlugHead, C> = {
33
+ [K in keyof C]?: K extends "kit" ? MockSurfaceMap<ExtractResAtlas<PH>, ExtractKit<PH>> : K extends "ports" ? Partial<C[K]> : C[K];
34
+ };
35
+ type MockCtx<PH extends AnyPlugHead> = keyof ExtractCtx<PH> extends never ? Record<string, never> : MockCtxContent<PH, ExtractCtx<PH>>;
36
+ type MockPlugMapData<PH extends AnyMapPlugHead> = {
37
+ $?: MockCtx<PH>;
38
+ } & MockPlugMapDataDeps<PH>;
39
+ type MockPlugListData<PH extends AnyListPlugHead> = {
40
+ $?: MockCtx<PH>;
41
+ } & MockPlugListDeps<PH>;
42
+ export {};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import { getPlugResolve, setPlugResolve, } from "r-machine/core";
14
+ import { RMachineUsageError } from "r-machine/errors";
15
+ import { ERR_PLUG_ALREADY_MOCKED } from "#r-machine/testing/errors";
16
+ const plugMockSymbol = Symbol("plugMock");
17
+ export const mockPlug = (plug) => {
18
+ return {
19
+ with: (_data) => {
20
+ const prevResolve = getPlugResolve(plug);
21
+ if (prevResolve[plugMockSymbol]) {
22
+ throw new RMachineUsageError(ERR_PLUG_ALREADY_MOCKED, "Plug is already mocked.");
23
+ }
24
+ const resolve = (locale, chain) => {
25
+ // TODO: WIP - For now, just return the original resolve result.
26
+ // Plan: read data.$.locale (if set) to override locale; call prevResolve; deep-merge _data.
27
+ // Caveat: prevResolve may install a lazy getter on $.kit[selfKey] for kit self-reference.
28
+ // Deep-merging enumerates kit keys and would invoke that getter; if the self-juncture isn't
29
+ // cached yet (factory still running), the getter throws. Handle this in the merge step —
30
+ // e.g. skip own-property getters or short-circuit on self-ref keys.
31
+ return prevResolve(locale, chain);
32
+ };
33
+ resolve[plugMockSymbol] = true;
34
+ setPlugResolve(plug, resolve);
35
+ return () => {
36
+ setPlugResolve(plug, prevResolve);
37
+ };
38
+ },
39
+ };
40
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Sergio Turolla
4
+ *
5
+ * This file is part of r-machine, licensed under the
6
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
7
+ *
8
+ * You may use, modify, and distribute this file under the terms
9
+ * of the AGPL-3.0. See LICENSE in this package for details.
10
+ *
11
+ * If you need to use this software in a proprietary project,
12
+ * contact: licensing@codecarvings.com
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import type { Action, AnyResAtlas, AnyResDomain, ExtractNamespace, Getter, HandleMap, RelayBrand } from "r-machine/core";
14
+ type MockSurfaceItem<I> = I extends Getter<infer V> ? V : I extends Action<infer F> ? F : I extends RelayBrand ? never : I;
15
+ type MockSurface<RD extends AnyResDomain> = {
16
+ [K in keyof RD as K extends `$${string}` ? never : K]?: MockSurfaceItem<RD[K]>;
17
+ };
18
+ export type MockSurfaceMap<RA extends AnyResAtlas, HM extends HandleMap<RA>> = {
19
+ [K in keyof HM]?: MockSurface<RA["shape"][ExtractNamespace<HM[K]>]>;
20
+ };
21
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ import type { Action, AnyResAtlas, AnyResDomain, ExtractNamespace, Getter, HandleMap, RelayBrand } from "r-machine/core";
14
+ type MockSurfaceItem<I> = I extends Getter<infer V> ? V : I extends Action<infer F> ? F : I extends RelayBrand ? never : I;
15
+ type MockSurface<RD extends AnyResDomain> = {
16
+ [K in keyof RD as K extends `$${string}` ? never : K]?: MockSurfaceItem<RD[K]>;
17
+ };
18
+ export type MockSurfaceMap<RA extends AnyResAtlas, HM extends HandleMap<RA>> = {
19
+ [K in keyof HM]?: MockSurface<RA["shape"][ExtractNamespace<HM[K]>]>;
20
+ };
21
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) 2026 Sergio Turolla
3
+ *
4
+ * This file is part of r-machine, licensed under the
5
+ * GNU Affero General Public License v3.0 (AGPL-3.0-only).
6
+ *
7
+ * You may use, modify, and distribute this file under the terms
8
+ * of the AGPL-3.0. See LICENSE in this package for details.
9
+ *
10
+ * If you need to use this software in a proprietary project,
11
+ * contact: licensing@codecarvings.com
12
+ */
13
+ export {};