@pithos/core 2.3.0 → 2.5.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 +1 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/autocompletion.d.ts +135 -1
- package/dist/eidos/abstract-factory/abstract-factory.d.ts +125 -0
- package/dist/eidos/abstract-factory/abstract-factory.d.ts.map +1 -0
- package/dist/eidos/abstract-factory/abstract-factory.js +128 -0
- package/dist/eidos/abstract-factory/abstract-factory.js.map +1 -0
- package/dist/eidos/adapter/adapter.d.ts +97 -0
- package/dist/eidos/adapter/adapter.d.ts.map +1 -0
- package/dist/eidos/adapter/adapter.js +90 -0
- package/dist/eidos/adapter/adapter.js.map +1 -0
- package/dist/eidos/bridge/bridge.d.ts +81 -0
- package/dist/eidos/bridge/bridge.d.ts.map +1 -0
- package/dist/eidos/bridge/bridge.js +75 -0
- package/dist/eidos/bridge/bridge.js.map +1 -0
- package/dist/eidos/builder/builder.d.ts +181 -0
- package/dist/eidos/builder/builder.d.ts.map +1 -0
- package/dist/eidos/builder/builder.js +139 -0
- package/dist/eidos/builder/builder.js.map +1 -0
- package/dist/eidos/chain/chain.d.ts +99 -0
- package/dist/eidos/chain/chain.d.ts.map +1 -0
- package/dist/eidos/chain/chain.js +111 -0
- package/dist/eidos/chain/chain.js.map +1 -0
- package/dist/eidos/command/command.d.ts +267 -0
- package/dist/eidos/command/command.d.ts.map +1 -0
- package/dist/eidos/command/command.js +298 -0
- package/dist/eidos/command/command.js.map +1 -0
- package/dist/eidos/composite/composite.d.ts +168 -0
- package/dist/eidos/composite/composite.d.ts.map +1 -0
- package/dist/eidos/composite/composite.js +157 -0
- package/dist/eidos/composite/composite.js.map +1 -0
- package/dist/eidos/decorator/decorator.d.ts +138 -0
- package/dist/eidos/decorator/decorator.d.ts.map +1 -0
- package/dist/eidos/decorator/decorator.js +143 -0
- package/dist/eidos/decorator/decorator.js.map +1 -0
- package/dist/eidos/facade/facade.d.ts +61 -0
- package/dist/eidos/facade/facade.d.ts.map +1 -0
- package/dist/eidos/facade/facade.js +63 -0
- package/dist/eidos/facade/facade.js.map +1 -0
- package/dist/eidos/factory-method/factory-method.d.ts +76 -0
- package/dist/eidos/factory-method/factory-method.d.ts.map +1 -0
- package/dist/eidos/factory-method/factory-method.js +60 -0
- package/dist/eidos/factory-method/factory-method.js.map +1 -0
- package/dist/eidos/flyweight/flyweight.d.ts +40 -0
- package/dist/eidos/flyweight/flyweight.d.ts.map +1 -0
- package/dist/eidos/flyweight/flyweight.js +41 -0
- package/dist/eidos/flyweight/flyweight.js.map +1 -0
- package/dist/eidos/interpreter/interpreter.d.ts +82 -0
- package/dist/eidos/interpreter/interpreter.d.ts.map +1 -0
- package/dist/eidos/interpreter/interpreter.js +84 -0
- package/dist/eidos/interpreter/interpreter.js.map +1 -0
- package/dist/eidos/iterator/iterator.d.ts +164 -0
- package/dist/eidos/iterator/iterator.d.ts.map +1 -0
- package/dist/eidos/iterator/iterator.js +258 -0
- package/dist/eidos/iterator/iterator.js.map +1 -0
- package/dist/eidos/mediator/mediator.d.ts +102 -0
- package/dist/eidos/mediator/mediator.d.ts.map +1 -0
- package/dist/eidos/mediator/mediator.js +112 -0
- package/dist/eidos/mediator/mediator.js.map +1 -0
- package/dist/eidos/memento/memento.d.ts +103 -0
- package/dist/eidos/memento/memento.d.ts.map +1 -0
- package/dist/eidos/memento/memento.js +114 -0
- package/dist/eidos/memento/memento.js.map +1 -0
- package/dist/eidos/observer/observer-lite.d.ts +49 -0
- package/dist/eidos/observer/observer-lite.d.ts.map +1 -0
- package/dist/eidos/observer/observer-lite.js +55 -0
- package/dist/eidos/observer/observer-lite.js.map +1 -0
- package/dist/eidos/observer/observer.d.ts +96 -0
- package/dist/eidos/observer/observer.d.ts.map +1 -0
- package/dist/eidos/observer/observer.js +117 -0
- package/dist/eidos/observer/observer.js.map +1 -0
- package/dist/eidos/prototype/prototype.d.ts +32 -0
- package/dist/eidos/prototype/prototype.d.ts.map +1 -0
- package/dist/eidos/prototype/prototype.js +33 -0
- package/dist/eidos/prototype/prototype.js.map +1 -0
- package/dist/eidos/proxy/proxy.d.ts +108 -0
- package/dist/eidos/proxy/proxy.d.ts.map +1 -0
- package/dist/eidos/proxy/proxy.js +121 -0
- package/dist/eidos/proxy/proxy.js.map +1 -0
- package/dist/eidos/singleton/singleton.d.ts +76 -0
- package/dist/eidos/singleton/singleton.d.ts.map +1 -0
- package/dist/eidos/singleton/singleton.js +77 -0
- package/dist/eidos/singleton/singleton.js.map +1 -0
- package/dist/eidos/state/state-lite.d.ts +102 -0
- package/dist/eidos/state/state-lite.d.ts.map +1 -0
- package/dist/eidos/state/state-lite.js +69 -0
- package/dist/eidos/state/state-lite.js.map +1 -0
- package/dist/eidos/state/state.d.ts +152 -0
- package/dist/eidos/state/state.d.ts.map +1 -0
- package/dist/eidos/state/state.js +85 -0
- package/dist/eidos/state/state.js.map +1 -0
- package/dist/eidos/strategy/strategy.d.ts +148 -0
- package/dist/eidos/strategy/strategy.d.ts.map +1 -0
- package/dist/eidos/strategy/strategy.js +167 -0
- package/dist/eidos/strategy/strategy.js.map +1 -0
- package/dist/eidos/template/template.d.ts +95 -0
- package/dist/eidos/template/template.d.ts.map +1 -0
- package/dist/eidos/template/template.js +110 -0
- package/dist/eidos/template/template.js.map +1 -0
- package/dist/eidos/visitor/visitor.d.ts +78 -0
- package/dist/eidos/visitor/visitor.d.ts.map +1 -0
- package/dist/eidos/visitor/visitor.js +80 -0
- package/dist/eidos/visitor/visitor.js.map +1 -0
- package/dist/zygos/result/index.d.ts +19 -0
- package/dist/zygos/result/index.d.ts.map +1 -0
- package/dist/zygos/result/index.js +29 -0
- package/dist/zygos/result/index.js.map +1 -0
- package/package.json +28 -3
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional State Pattern.
|
|
3
|
+
*
|
|
4
|
+
* In OOP, the State pattern requires a Context class, a State interface,
|
|
5
|
+
* and concrete State subclasses that the Context delegates to.
|
|
6
|
+
* In functional TypeScript, a state machine is a record of transitions
|
|
7
|
+
* keyed by state, where each transition maps an event to the next state
|
|
8
|
+
* (and optionally updates context).
|
|
9
|
+
*
|
|
10
|
+
* @module eidos/state
|
|
11
|
+
* @since 2.4.0
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { createMachine } from "@pithos/core/eidos/state/state";
|
|
16
|
+
*
|
|
17
|
+
* const light = createMachine({
|
|
18
|
+
* green: { timer: { to: "yellow" } },
|
|
19
|
+
* yellow: { timer: { to: "red" } },
|
|
20
|
+
* red: { timer: { to: "green" } },
|
|
21
|
+
* }, "green");
|
|
22
|
+
*
|
|
23
|
+
* light.current(); // "green"
|
|
24
|
+
* light.send("timer"); // "yellow"
|
|
25
|
+
* light.send("timer"); // "red"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
import { some, none } from "../../zygos/option.js";
|
|
29
|
+
/**
|
|
30
|
+
* Type guard for context transitions.
|
|
31
|
+
*/
|
|
32
|
+
function hasUpdate(t) {
|
|
33
|
+
return "update" in t;
|
|
34
|
+
}
|
|
35
|
+
export function createMachine(definition, initial, initialContext) {
|
|
36
|
+
let state = initial;
|
|
37
|
+
let ctx = initialContext;
|
|
38
|
+
const listeners = new Set();
|
|
39
|
+
const send = (event) => {
|
|
40
|
+
const transitions = definition[state];
|
|
41
|
+
const transition = transitions[event];
|
|
42
|
+
if (transition === undefined)
|
|
43
|
+
return state;
|
|
44
|
+
const from = state;
|
|
45
|
+
state = transition.to;
|
|
46
|
+
// Stryker disable next-line ConditionalExpression: ctx check is defensive - TypeScript prevents update on machines without context
|
|
47
|
+
if (hasUpdate(transition) && ctx !== undefined) {
|
|
48
|
+
ctx = transition.update(ctx);
|
|
49
|
+
}
|
|
50
|
+
for (const listener of listeners) {
|
|
51
|
+
listener(from, event, state);
|
|
52
|
+
}
|
|
53
|
+
return state;
|
|
54
|
+
};
|
|
55
|
+
const trySend = (event) => {
|
|
56
|
+
const transitions = definition[state];
|
|
57
|
+
if (transitions[event] === undefined)
|
|
58
|
+
return none;
|
|
59
|
+
return some(send(event));
|
|
60
|
+
};
|
|
61
|
+
const onTransition = (listener) => {
|
|
62
|
+
listeners.add(listener);
|
|
63
|
+
return () => { listeners.delete(listener); };
|
|
64
|
+
};
|
|
65
|
+
const base = {
|
|
66
|
+
current: () => state,
|
|
67
|
+
send,
|
|
68
|
+
matches: (s) => state === s,
|
|
69
|
+
trySend,
|
|
70
|
+
onTransition,
|
|
71
|
+
reset: () => {
|
|
72
|
+
state = initial;
|
|
73
|
+
ctx = initialContext;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
if (initialContext !== undefined) {
|
|
77
|
+
return {
|
|
78
|
+
...base,
|
|
79
|
+
// INTENTIONAL: ctx is guaranteed to be C when initialContext is provided
|
|
80
|
+
context: () => ctx,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return base;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/eidos/state/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAyE3C;;GAEG;AACH,SAAS,SAAS,CAChB,CAAmB;IAEnB,OAAO,QAAQ,IAAI,CAAC,CAAC;AACvB,CAAC;AA4ED,MAAM,UAAU,aAAa,CAC3B,UAAsC,EACtC,OAAU,EACV,cAAkB;IAElB,IAAI,KAAK,GAAM,OAAO,CAAC;IACvB,IAAI,GAAG,GAAkB,cAAc,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,GAAG,EAA4B,CAAC;IAEtD,MAAM,IAAI,GAAG,CAAC,KAAQ,EAAK,EAAE;QAC3B,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAE3C,MAAM,IAAI,GAAG,KAAK,CAAC;QACnB,KAAK,GAAG,UAAU,CAAC,EAAE,CAAC;QAEtB,mIAAmI;QACnI,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC/C,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,KAAQ,EAAa,EAAE;QACtC,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAClD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,QAAkC,EAAgB,EAAE;QACxE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK;QACpB,IAAI;QACJ,OAAO,EAAE,CAAC,CAAI,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC;QAC9B,OAAO;QACP,YAAY;QACZ,KAAK,EAAE,GAAG,EAAE;YACV,KAAK,GAAG,OAAO,CAAC;YAChB,GAAG,GAAG,cAAc,CAAC;QACvB,CAAC;KACF,CAAC;IAEF,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO;YACL,GAAG,IAAI;YACP,yEAAyE;YACzE,OAAO,EAAE,GAAG,EAAE,CAAC,GAAQ;SACxB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional Strategy Pattern.
|
|
3
|
+
*
|
|
4
|
+
* In OOP, the Strategy pattern requires an interface, concrete classes, and a
|
|
5
|
+
* context class to swap algorithms at runtime. In functional TypeScript, a
|
|
6
|
+
* strategy is simply a function - the pattern becomes composition.
|
|
7
|
+
*
|
|
8
|
+
* @module eidos/strategy
|
|
9
|
+
* @since 2.4.0
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://pithos.dev/api/eidos/strategy/ | Explanations, examples and live demo}
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { type Strategy, createStrategies, safeStrategy } from "@pithos/core/eidos/strategy";
|
|
16
|
+
*
|
|
17
|
+
* const sorting = createStrategies({
|
|
18
|
+
* asc: (data: number[]) => [...data].sort((a, b) => a - b),
|
|
19
|
+
* desc: (data: number[]) => [...data].sort((a, b) => b - a),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* sorting.execute("asc", [3, 1, 2]); // [1, 2, 3]
|
|
23
|
+
* sorting.use("desc")([3, 1, 2]); // [3, 2, 1]
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import type { Result } from "../../zygos/result/result.js";
|
|
27
|
+
import type { Option } from "../../zygos/option.js";
|
|
28
|
+
import type { GenericSchema, Infer } from "../../kanon/types/base.js";
|
|
29
|
+
/**
|
|
30
|
+
* A Strategy is a function that transforms an input into an output.
|
|
31
|
+
* This replaces the GoF Strategy interface + concrete classes.
|
|
32
|
+
*
|
|
33
|
+
* @template In - The input type
|
|
34
|
+
* @template Out - The output type
|
|
35
|
+
* @since 2.4.0
|
|
36
|
+
*/
|
|
37
|
+
export type Strategy<In, Out> = (input: In) => Out;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a strategy resolver from a record of named strategies.
|
|
40
|
+
* Replaces the GoF Context class - instead of `setStrategy()` + `execute()`,
|
|
41
|
+
* you simply pick a function by key and call it.
|
|
42
|
+
*
|
|
43
|
+
* @template K - Union of strategy keys
|
|
44
|
+
* @template In - The input type
|
|
45
|
+
* @template Out - The output type
|
|
46
|
+
* @param strategies - Record mapping keys to strategy functions
|
|
47
|
+
* @returns Resolver with `use` (get fn) and `execute` (run by key)
|
|
48
|
+
* @since 2.4.0
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const pricing = createStrategies({
|
|
53
|
+
* regular: (price: number) => price,
|
|
54
|
+
* vip: (price: number) => price * 0.8,
|
|
55
|
+
* premium: (price: number) => price * 0.7,
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* const applyVip = pricing.use("vip");
|
|
59
|
+
* applyVip(100); // 80
|
|
60
|
+
*
|
|
61
|
+
* pricing.execute("premium", 100); // 70
|
|
62
|
+
*
|
|
63
|
+
* // Safe lookup for dynamic keys (e.g. from config)
|
|
64
|
+
* const key: string = getFromConfig();
|
|
65
|
+
* pricing.get(key); // Option<Strategy<number, number>>
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function createStrategies<K extends string, In, Out>(strategies: Record<K, Strategy<In, Out>>): {
|
|
69
|
+
/** Get a strategy by key. Typed keys only. */
|
|
70
|
+
use: (key: K) => Strategy<In, Out>;
|
|
71
|
+
/** Execute a strategy by key with the given input. */
|
|
72
|
+
execute: (key: K, input: In) => Out;
|
|
73
|
+
/** Safe lookup for dynamic/runtime keys. Returns `Option<Strategy>`. */
|
|
74
|
+
get: (key: string) => Option<Strategy<In, Out>>;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Wraps a strategy to return a zygos `Result` instead of throwing.
|
|
78
|
+
* Catches any exception and wraps it in `Err<Error>`.
|
|
79
|
+
*
|
|
80
|
+
* @deprecated Use `Result.fromThrowable` from `@zygos/result/result` instead.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { Result } from "../../zygos/result/result.js";
|
|
84
|
+
*
|
|
85
|
+
* const safeParseJson = Result.fromThrowable(
|
|
86
|
+
* (input: string) => JSON.parse(input),
|
|
87
|
+
* (e) => e instanceof Error ? e : new Error(String(e)),
|
|
88
|
+
* );
|
|
89
|
+
* safeParseJson('{"ok":true}'); // Ok({ ok: true })
|
|
90
|
+
* safeParseJson("invalid"); // Err(SyntaxError(...))
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @see {@link https://pithos.dev/api/eidos/strategy/ | Full explanation, examples and live demo}
|
|
94
|
+
* @template In - The input type
|
|
95
|
+
* @template Out - The output type
|
|
96
|
+
* @param strategy - The strategy to make safe
|
|
97
|
+
* @returns A new strategy returning `Result<Out, Error>`
|
|
98
|
+
* @since 2.4.0
|
|
99
|
+
*/
|
|
100
|
+
export declare function safeStrategy<In, Out>(strategy: Strategy<In, Out>): Strategy<In, Result<Out, Error>>;
|
|
101
|
+
/**
|
|
102
|
+
* Composes a primary strategy with a fallback.
|
|
103
|
+
* If the primary throws, the fallback is executed instead.
|
|
104
|
+
*
|
|
105
|
+
* @template In - The input type
|
|
106
|
+
* @template Out - The output type
|
|
107
|
+
* @param primary - The strategy to try first
|
|
108
|
+
* @param fallback - The strategy to use if primary throws
|
|
109
|
+
* @returns A new combined strategy
|
|
110
|
+
* @since 2.4.0
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const fromCache: Strategy<string, Data> = (key) => cache.get(key);
|
|
115
|
+
* const fromApi: Strategy<string, Data> = (key) => api.fetch(key);
|
|
116
|
+
*
|
|
117
|
+
* const fetchData = withFallback(fromCache, fromApi);
|
|
118
|
+
* fetchData("user:123"); // tries cache, falls back to API
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function withFallback<In, Out>(primary: Strategy<In, Out>, fallback: Strategy<In, Out>): Strategy<In, Out>;
|
|
122
|
+
/**
|
|
123
|
+
* Validates input against a kanon schema before executing the strategy.
|
|
124
|
+
* Bridges kanon validation, zygos Result, and the strategy pattern.
|
|
125
|
+
*
|
|
126
|
+
* @template S - The kanon schema type
|
|
127
|
+
* @template Out - The output type of the strategy
|
|
128
|
+
* @param schema - Kanon schema to validate input against
|
|
129
|
+
* @param strategy - Strategy to execute on validated input
|
|
130
|
+
* @returns A function accepting unknown input and returning `Result<Out, string>`
|
|
131
|
+
* @since 2.4.0
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { number } from "@pithos/core/kanon";
|
|
136
|
+
*
|
|
137
|
+
* const double = withValidation(
|
|
138
|
+
* number().min(0),
|
|
139
|
+
* (n) => n * 2,
|
|
140
|
+
* );
|
|
141
|
+
*
|
|
142
|
+
* double(5); // Ok(10)
|
|
143
|
+
* double(-1); // Err("Number must be >= 0")
|
|
144
|
+
* double("hello"); // Err("Expected number")
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export declare function withValidation<S extends GenericSchema, Out>(schema: S, strategy: Strategy<Infer<S>, Out>): (input: unknown) => Result<Out, string>;
|
|
148
|
+
//# sourceMappingURL=strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../../../src/eidos/strategy/strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,GAAG,EACxD,UAAU,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAGtC,8CAA8C;eACnC,CAAC,KAAG,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IAChC,sDAAsD;mBACvC,CAAC,SAAS,EAAE,KAAG,GAAG;IACjC,wEAAwE;eAC7D,MAAM,KAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;EAQhD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,GAAG,EAClC,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,GAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAQlC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,GAAG,EAClC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,EAC1B,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,GAC1B,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAQnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EAAE,GAAG,EACzD,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,GAChC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAEzC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional Strategy Pattern.
|
|
3
|
+
*
|
|
4
|
+
* In OOP, the Strategy pattern requires an interface, concrete classes, and a
|
|
5
|
+
* context class to swap algorithms at runtime. In functional TypeScript, a
|
|
6
|
+
* strategy is simply a function - the pattern becomes composition.
|
|
7
|
+
*
|
|
8
|
+
* @module eidos/strategy
|
|
9
|
+
* @since 2.4.0
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://pithos.dev/api/eidos/strategy/ | Explanations, examples and live demo}
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { type Strategy, createStrategies, safeStrategy } from "@pithos/core/eidos/strategy";
|
|
16
|
+
*
|
|
17
|
+
* const sorting = createStrategies({
|
|
18
|
+
* asc: (data: number[]) => [...data].sort((a, b) => a - b),
|
|
19
|
+
* desc: (data: number[]) => [...data].sort((a, b) => b - a),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* sorting.execute("asc", [3, 1, 2]); // [1, 2, 3]
|
|
23
|
+
* sorting.use("desc")([3, 1, 2]); // [3, 2, 1]
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { ok, err } from "../../zygos/result/result.js";
|
|
27
|
+
import { some, none } from "../../zygos/option.js";
|
|
28
|
+
import { ensure } from "../../bridges/ensure.js";
|
|
29
|
+
/**
|
|
30
|
+
* Creates a strategy resolver from a record of named strategies.
|
|
31
|
+
* Replaces the GoF Context class - instead of `setStrategy()` + `execute()`,
|
|
32
|
+
* you simply pick a function by key and call it.
|
|
33
|
+
*
|
|
34
|
+
* @template K - Union of strategy keys
|
|
35
|
+
* @template In - The input type
|
|
36
|
+
* @template Out - The output type
|
|
37
|
+
* @param strategies - Record mapping keys to strategy functions
|
|
38
|
+
* @returns Resolver with `use` (get fn) and `execute` (run by key)
|
|
39
|
+
* @since 2.4.0
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const pricing = createStrategies({
|
|
44
|
+
* regular: (price: number) => price,
|
|
45
|
+
* vip: (price: number) => price * 0.8,
|
|
46
|
+
* premium: (price: number) => price * 0.7,
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* const applyVip = pricing.use("vip");
|
|
50
|
+
* applyVip(100); // 80
|
|
51
|
+
*
|
|
52
|
+
* pricing.execute("premium", 100); // 70
|
|
53
|
+
*
|
|
54
|
+
* // Safe lookup for dynamic keys (e.g. from config)
|
|
55
|
+
* const key: string = getFromConfig();
|
|
56
|
+
* pricing.get(key); // Option<Strategy<number, number>>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function createStrategies(strategies) {
|
|
60
|
+
return {
|
|
61
|
+
/** Get a strategy by key. Typed keys only. */
|
|
62
|
+
use: (key) => strategies[key],
|
|
63
|
+
/** Execute a strategy by key with the given input. */
|
|
64
|
+
execute: (key, input) => strategies[key](input),
|
|
65
|
+
/** Safe lookup for dynamic/runtime keys. Returns `Option<Strategy>`. */
|
|
66
|
+
get: (key) => {
|
|
67
|
+
if (key in strategies) {
|
|
68
|
+
// INTENTIONAL: key validated by `in` check, TS can't narrow string to K
|
|
69
|
+
return some(strategies[key]);
|
|
70
|
+
}
|
|
71
|
+
return none;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wraps a strategy to return a zygos `Result` instead of throwing.
|
|
77
|
+
* Catches any exception and wraps it in `Err<Error>`.
|
|
78
|
+
*
|
|
79
|
+
* @deprecated Use `Result.fromThrowable` from `@zygos/result/result` instead.
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* import { Result } from "../../zygos/result/result.js";
|
|
83
|
+
*
|
|
84
|
+
* const safeParseJson = Result.fromThrowable(
|
|
85
|
+
* (input: string) => JSON.parse(input),
|
|
86
|
+
* (e) => e instanceof Error ? e : new Error(String(e)),
|
|
87
|
+
* );
|
|
88
|
+
* safeParseJson('{"ok":true}'); // Ok({ ok: true })
|
|
89
|
+
* safeParseJson("invalid"); // Err(SyntaxError(...))
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://pithos.dev/api/eidos/strategy/ | Full explanation, examples and live demo}
|
|
93
|
+
* @template In - The input type
|
|
94
|
+
* @template Out - The output type
|
|
95
|
+
* @param strategy - The strategy to make safe
|
|
96
|
+
* @returns A new strategy returning `Result<Out, Error>`
|
|
97
|
+
* @since 2.4.0
|
|
98
|
+
*/
|
|
99
|
+
export function safeStrategy(strategy) {
|
|
100
|
+
return (input) => {
|
|
101
|
+
try {
|
|
102
|
+
return ok(strategy(input));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Composes a primary strategy with a fallback.
|
|
111
|
+
* If the primary throws, the fallback is executed instead.
|
|
112
|
+
*
|
|
113
|
+
* @template In - The input type
|
|
114
|
+
* @template Out - The output type
|
|
115
|
+
* @param primary - The strategy to try first
|
|
116
|
+
* @param fallback - The strategy to use if primary throws
|
|
117
|
+
* @returns A new combined strategy
|
|
118
|
+
* @since 2.4.0
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const fromCache: Strategy<string, Data> = (key) => cache.get(key);
|
|
123
|
+
* const fromApi: Strategy<string, Data> = (key) => api.fetch(key);
|
|
124
|
+
*
|
|
125
|
+
* const fetchData = withFallback(fromCache, fromApi);
|
|
126
|
+
* fetchData("user:123"); // tries cache, falls back to API
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export function withFallback(primary, fallback) {
|
|
130
|
+
return (input) => {
|
|
131
|
+
try {
|
|
132
|
+
return primary(input);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return fallback(input);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Validates input against a kanon schema before executing the strategy.
|
|
141
|
+
* Bridges kanon validation, zygos Result, and the strategy pattern.
|
|
142
|
+
*
|
|
143
|
+
* @template S - The kanon schema type
|
|
144
|
+
* @template Out - The output type of the strategy
|
|
145
|
+
* @param schema - Kanon schema to validate input against
|
|
146
|
+
* @param strategy - Strategy to execute on validated input
|
|
147
|
+
* @returns A function accepting unknown input and returning `Result<Out, string>`
|
|
148
|
+
* @since 2.4.0
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* import { number } from "@pithos/core/kanon";
|
|
153
|
+
*
|
|
154
|
+
* const double = withValidation(
|
|
155
|
+
* number().min(0),
|
|
156
|
+
* (n) => n * 2,
|
|
157
|
+
* );
|
|
158
|
+
*
|
|
159
|
+
* double(5); // Ok(10)
|
|
160
|
+
* double(-1); // Err("Number must be >= 0")
|
|
161
|
+
* double("hello"); // Err("Expected number")
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function withValidation(schema, strategy) {
|
|
165
|
+
return (input) => ensure(schema, input).map(strategy);
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategy.js","sourceRoot":"","sources":["../../../src/eidos/strategy/strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAezC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAwC;IAExC,OAAO;QACL,8CAA8C;QAC9C,GAAG,EAAE,CAAC,GAAM,EAAqB,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QACnD,sDAAsD;QACtD,OAAO,EAAE,CAAC,GAAM,EAAE,KAAS,EAAO,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;QAC3D,wEAAwE;QACxE,GAAG,EAAE,CAAC,GAAW,EAA6B,EAAE;YAC9C,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;gBACtB,wEAAwE;gBACxE,OAAO,IAAI,CAAC,UAAU,CAAC,GAAQ,CAAC,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA2B;IAE3B,OAAO,CAAC,KAAS,EAAE,EAAE;QACnB,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAC1B,OAA0B,EAC1B,QAA2B;IAE3B,OAAO,CAAC,KAAS,EAAO,EAAE;QACxB,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAS,EACT,QAAiC;IAEjC,OAAO,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional Template Method Pattern.
|
|
3
|
+
*
|
|
4
|
+
* In OOP, the Template Method pattern requires an abstract class that defines
|
|
5
|
+
* the algorithm skeleton with abstract/hook methods, and concrete subclasses
|
|
6
|
+
* that override specific steps.
|
|
7
|
+
*
|
|
8
|
+
* In functional TypeScript, a template is just `(steps) => algorithm`.
|
|
9
|
+
* The base pattern is absorbed by the language — no wrapper needed.
|
|
10
|
+
*
|
|
11
|
+
* However, {@link templateWithDefaults} provides real value: it handles
|
|
12
|
+
* merging default step implementations with caller overrides.
|
|
13
|
+
*
|
|
14
|
+
* @module eidos/template
|
|
15
|
+
* @since 2.4.0
|
|
16
|
+
*
|
|
17
|
+
* @see {@link https://pithos.dev/api/eidos/template/ | Explanations, examples and live demo}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // No import needed for basic templates — just write a function:
|
|
22
|
+
* const processData = (steps: { parse: (raw: string) => number[]; analyze: (data: number[]) => string }) =>
|
|
23
|
+
* (raw: string) => steps.analyze(steps.parse(raw));
|
|
24
|
+
*
|
|
25
|
+
* const csvProcessor = processData({
|
|
26
|
+
* parse: (raw) => raw.split(",").map(Number),
|
|
27
|
+
* analyze: (data) => `sum: ${data.reduce((a, b) => a + b, 0)}`,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // For templates with defaults, use templateWithDefaults:
|
|
31
|
+
* import { templateWithDefaults } from "@pithos/core/eidos/template/template";
|
|
32
|
+
*
|
|
33
|
+
* const report = templateWithDefaults(
|
|
34
|
+
* (steps: { header: () => string; body: (d: string) => string }) =>
|
|
35
|
+
* (data: string) => `${steps.header()}\n${steps.body(data)}`,
|
|
36
|
+
* { header: () => "=== Report ===", body: (d) => d },
|
|
37
|
+
* );
|
|
38
|
+
*
|
|
39
|
+
* report({ header: () => "Custom" })("content");
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @deprecated **Pattern absorbed by the language.**
|
|
43
|
+
*
|
|
44
|
+
* In functional TypeScript, a template is just `(steps) => algorithm`.
|
|
45
|
+
* This function is the identity — it exists only so you find this message.
|
|
46
|
+
*
|
|
47
|
+
* Write your template directly:
|
|
48
|
+
* ```ts
|
|
49
|
+
* const gameAI = (steps: { collect: () => number; attack: (n: number) => string }) =>
|
|
50
|
+
* () => steps.attack(steps.collect());
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* For templates with default steps, use {@link templateWithDefaults} which
|
|
54
|
+
* provides actual value (merging defaults with overrides).
|
|
55
|
+
*
|
|
56
|
+
* @see {@link https://pithos.dev/api/eidos/template/ | Full explanation, examples and live demo}
|
|
57
|
+
*/
|
|
58
|
+
export declare function template<Steps extends Record<string, (...args: any[]) => unknown>, Fn>(skeleton: (steps: Steps) => Fn): (steps: Steps) => Fn;
|
|
59
|
+
/**
|
|
60
|
+
* Creates a template method with default steps that can be partially overridden.
|
|
61
|
+
*
|
|
62
|
+
* This is the functional equivalent of an abstract class with hook methods
|
|
63
|
+
* that have default implementations. Only the steps you want to customize
|
|
64
|
+
* need to be provided.
|
|
65
|
+
*
|
|
66
|
+
* @template Steps - The record type describing the customizable steps
|
|
67
|
+
* @template Fn - The resulting algorithm function type
|
|
68
|
+
* @param skeleton - A function that wires the steps into an algorithm
|
|
69
|
+
* @param defaults - Default implementations for all steps
|
|
70
|
+
* @returns A function that accepts partial step overrides and returns the algorithm
|
|
71
|
+
* @since 2.4.0
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const report = templateWithDefaults(
|
|
76
|
+
* (steps: {
|
|
77
|
+
* header: () => string;
|
|
78
|
+
* body: (data: string) => string;
|
|
79
|
+
* footer: () => string;
|
|
80
|
+
* }) =>
|
|
81
|
+
* (data: string) => [steps.header(), steps.body(data), steps.footer()].join("\n"),
|
|
82
|
+
* {
|
|
83
|
+
* header: () => "=== Report ===",
|
|
84
|
+
* body: (data) => data,
|
|
85
|
+
* footer: () => "=== End ===",
|
|
86
|
+
* },
|
|
87
|
+
* );
|
|
88
|
+
*
|
|
89
|
+
* // Only override what you need
|
|
90
|
+
* const custom = report({ header: () => "** Custom **" });
|
|
91
|
+
* custom("content"); // "** Custom **\ncontent\n=== End ==="
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function templateWithDefaults<Steps extends object, Fn>(skeleton: (steps: Steps) => Fn, defaults: NoInfer<Steps>): (overrides?: Partial<NoInfer<Steps>>) => Fn;
|
|
95
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/eidos/template/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,QAAQ,CAGtB,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,EACzD,EAAE,EAEF,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,EAAE,GAC7B,CAAC,KAAK,EAAE,KAAK,KAAK,EAAE,CAEtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,EAC3D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,EAAE,EAC9B,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,GACvB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAY7C"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional Template Method Pattern.
|
|
3
|
+
*
|
|
4
|
+
* In OOP, the Template Method pattern requires an abstract class that defines
|
|
5
|
+
* the algorithm skeleton with abstract/hook methods, and concrete subclasses
|
|
6
|
+
* that override specific steps.
|
|
7
|
+
*
|
|
8
|
+
* In functional TypeScript, a template is just `(steps) => algorithm`.
|
|
9
|
+
* The base pattern is absorbed by the language — no wrapper needed.
|
|
10
|
+
*
|
|
11
|
+
* However, {@link templateWithDefaults} provides real value: it handles
|
|
12
|
+
* merging default step implementations with caller overrides.
|
|
13
|
+
*
|
|
14
|
+
* @module eidos/template
|
|
15
|
+
* @since 2.4.0
|
|
16
|
+
*
|
|
17
|
+
* @see {@link https://pithos.dev/api/eidos/template/ | Explanations, examples and live demo}
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // No import needed for basic templates — just write a function:
|
|
22
|
+
* const processData = (steps: { parse: (raw: string) => number[]; analyze: (data: number[]) => string }) =>
|
|
23
|
+
* (raw: string) => steps.analyze(steps.parse(raw));
|
|
24
|
+
*
|
|
25
|
+
* const csvProcessor = processData({
|
|
26
|
+
* parse: (raw) => raw.split(",").map(Number),
|
|
27
|
+
* analyze: (data) => `sum: ${data.reduce((a, b) => a + b, 0)}`,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // For templates with defaults, use templateWithDefaults:
|
|
31
|
+
* import { templateWithDefaults } from "@pithos/core/eidos/template/template";
|
|
32
|
+
*
|
|
33
|
+
* const report = templateWithDefaults(
|
|
34
|
+
* (steps: { header: () => string; body: (d: string) => string }) =>
|
|
35
|
+
* (data: string) => `${steps.header()}\n${steps.body(data)}`,
|
|
36
|
+
* { header: () => "=== Report ===", body: (d) => d },
|
|
37
|
+
* );
|
|
38
|
+
*
|
|
39
|
+
* report({ header: () => "Custom" })("content");
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @deprecated **Pattern absorbed by the language.**
|
|
43
|
+
*
|
|
44
|
+
* In functional TypeScript, a template is just `(steps) => algorithm`.
|
|
45
|
+
* This function is the identity — it exists only so you find this message.
|
|
46
|
+
*
|
|
47
|
+
* Write your template directly:
|
|
48
|
+
* ```ts
|
|
49
|
+
* const gameAI = (steps: { collect: () => number; attack: (n: number) => string }) =>
|
|
50
|
+
* () => steps.attack(steps.collect());
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* For templates with default steps, use {@link templateWithDefaults} which
|
|
54
|
+
* provides actual value (merging defaults with overrides).
|
|
55
|
+
*
|
|
56
|
+
* @see {@link https://pithos.dev/api/eidos/template/ | Full explanation, examples and live demo}
|
|
57
|
+
*/
|
|
58
|
+
export function template(skeleton) {
|
|
59
|
+
return skeleton;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates a template method with default steps that can be partially overridden.
|
|
63
|
+
*
|
|
64
|
+
* This is the functional equivalent of an abstract class with hook methods
|
|
65
|
+
* that have default implementations. Only the steps you want to customize
|
|
66
|
+
* need to be provided.
|
|
67
|
+
*
|
|
68
|
+
* @template Steps - The record type describing the customizable steps
|
|
69
|
+
* @template Fn - The resulting algorithm function type
|
|
70
|
+
* @param skeleton - A function that wires the steps into an algorithm
|
|
71
|
+
* @param defaults - Default implementations for all steps
|
|
72
|
+
* @returns A function that accepts partial step overrides and returns the algorithm
|
|
73
|
+
* @since 2.4.0
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const report = templateWithDefaults(
|
|
78
|
+
* (steps: {
|
|
79
|
+
* header: () => string;
|
|
80
|
+
* body: (data: string) => string;
|
|
81
|
+
* footer: () => string;
|
|
82
|
+
* }) =>
|
|
83
|
+
* (data: string) => [steps.header(), steps.body(data), steps.footer()].join("\n"),
|
|
84
|
+
* {
|
|
85
|
+
* header: () => "=== Report ===",
|
|
86
|
+
* body: (data) => data,
|
|
87
|
+
* footer: () => "=== End ===",
|
|
88
|
+
* },
|
|
89
|
+
* );
|
|
90
|
+
*
|
|
91
|
+
* // Only override what you need
|
|
92
|
+
* const custom = report({ header: () => "** Custom **" });
|
|
93
|
+
* custom("content"); // "** Custom **\ncontent\n=== End ==="
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function templateWithDefaults(skeleton, defaults) {
|
|
97
|
+
return (overrides) => {
|
|
98
|
+
// Stryker disable next-line ConditionalExpression: Stryker can't find related tests due to vitest.related config
|
|
99
|
+
if (!overrides)
|
|
100
|
+
return skeleton(defaults);
|
|
101
|
+
const merged = { ...defaults };
|
|
102
|
+
for (const key of Object.keys(overrides)) {
|
|
103
|
+
if (overrides[key] !== undefined) {
|
|
104
|
+
merged[key] = overrides[key];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return skeleton(merged);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../src/eidos/template/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,UAAU,QAAQ,CAMtB,QAA8B;IAE9B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAA8B,EAC9B,QAAwB;IAExB,OAAO,CAAC,SAAS,EAAE,EAAE;QACnB,iHAAiH;QACjH,IAAI,CAAC,SAAS;YAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAoB,EAAE,CAAC;YAC5D,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAuB,CAAC;YACrD,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC"}
|