@ghostry/fabricator 0.0.3 → 0.0.4
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/dist/esm/Enumeration/Enumerate.js +7 -6
- package/dist/esm/Fabricator/Constructor.js +11 -10
- package/dist/esm/Instance/Core.js +51 -17
- package/dist/esm/Instance/Stack/Async.js +10 -2
- package/dist/esm/Instance/Stack/Sync.js +2 -1
- package/dist/esm/Instance/Stack/Visible.js +13 -0
- package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
- package/dist/esm/Random/index.js +2 -2
- package/dist/types/Enumeration/Enumerate.d.ts +15 -15
- package/dist/types/Fabricator/Constructor.d.ts +10 -7
- package/dist/types/Fabricator/Types.d.ts +15 -14
- package/dist/types/Instance/Core.d.ts +17 -6
- package/dist/types/Instance/Stack/Async.d.ts +7 -0
- package/dist/types/Instance/Stack/Sync.d.ts +5 -3
- package/dist/types/Instance/Stack/Visible.d.ts +22 -0
- package/dist/types/Instance/Types.d.ts +169 -19
- package/dist/types/Primitive/recursive/Fabricator.d.ts +7 -8
- package/dist/types/Random/Types.d.ts +23 -21
- package/dist/types/Random/index.d.ts +3 -3
- package/dist/types/index.d.ts +11 -2
- package/dist/types/internal.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { shuffle } from "../Distribution/index.js";
|
|
2
2
|
import { FabricatorError } from "../Error/index.js";
|
|
3
3
|
import { Constructor } from "../Fabricator/Constructor.js";
|
|
4
|
+
import { toInnermostFrame } from "../Instance/Stack/Visible.js";
|
|
4
5
|
import { toStreamFromTrace } from "../Random/index.js";
|
|
5
6
|
import { plan, resolve } from "./Plan.js";
|
|
6
|
-
function enumerables(source, limits, stack) {
|
|
7
|
-
const Fabricator = Constructor(source, stack);
|
|
7
|
+
function enumerables(source, limits, stack, ancestry) {
|
|
8
|
+
const Fabricator = Constructor(source, stack, ancestry);
|
|
8
9
|
function effectiveSource() {
|
|
9
10
|
var _ref;
|
|
10
|
-
var
|
|
11
|
-
return null != (_ref = null == (
|
|
11
|
+
var _toInnermostFrame;
|
|
12
|
+
return null != (_ref = null == (_toInnermostFrame = toInnermostFrame(stack, ancestry)) ? void 0 : _toInnermostFrame.source) ? _ref : source;
|
|
12
13
|
}
|
|
13
14
|
function iterable(rebuild) {
|
|
14
15
|
return {
|
|
@@ -90,9 +91,9 @@ function enumerables(source, limits, stack) {
|
|
|
90
91
|
}
|
|
91
92
|
function orderer(source, salt) {
|
|
92
93
|
const forked = source.fork(salt);
|
|
93
|
-
const
|
|
94
|
+
const trace = forked.toConstructionTrace();
|
|
94
95
|
const stream = toStreamFromTrace(forked.algorithm, {
|
|
95
|
-
...
|
|
96
|
+
...trace,
|
|
96
97
|
path: [],
|
|
97
98
|
kind: "order"
|
|
98
99
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { FabricatorError } from "../Error/index.js";
|
|
2
|
+
import { toInnermostFrame } from "../Instance/Stack/Visible.js";
|
|
2
3
|
import { Primitive } from "../Primitive/index.js";
|
|
3
4
|
import { isLayered, normalizeSalt } from "../Random/index.js";
|
|
4
5
|
import { toSchema } from "../Schema/Core.js";
|
|
5
6
|
import { Adaptation, Fixed, Kind, Layer, Meta } from "../Types.js";
|
|
6
7
|
import { inline } from "../Utility/Core.js";
|
|
7
|
-
function Constructor(source, stack) {
|
|
8
|
+
function Constructor(source, stack, ancestry) {
|
|
8
9
|
function make(schema, path, context) {
|
|
9
10
|
schema = toSchema(schema);
|
|
10
11
|
const kind = schema[Kind];
|
|
@@ -280,7 +281,7 @@ function Constructor(source, stack) {
|
|
|
280
281
|
function construct(schema, options = {}) {
|
|
281
282
|
var _options_path;
|
|
282
283
|
if ("string" == typeof options.kind && options.kind !== schema[Kind]) throw new FabricatorError.TraceKindMismatchError(schema[Kind], options.kind);
|
|
283
|
-
const context = toConstructionContext(source, options, stack);
|
|
284
|
+
const context = toConstructionContext(source, options, stack, ancestry);
|
|
284
285
|
const made = make(schema, null != (_options_path = options.path) ? _options_path : [], context);
|
|
285
286
|
const adaptations = schema[Adaptation];
|
|
286
287
|
return adaptations ? {
|
|
@@ -290,21 +291,21 @@ function Constructor(source, stack) {
|
|
|
290
291
|
}
|
|
291
292
|
return construct;
|
|
292
293
|
}
|
|
293
|
-
function toConstructionContext(source, options, stack) {
|
|
294
|
-
const { source:
|
|
294
|
+
function toConstructionContext(source, options, stack, ancestry) {
|
|
295
|
+
const { source: resolved, trace } = resolveScope(source, options, stack, ancestry);
|
|
295
296
|
return {
|
|
296
297
|
toTrace: (path, kind)=>({
|
|
297
|
-
...
|
|
298
|
+
...trace,
|
|
298
299
|
path,
|
|
299
300
|
kind
|
|
300
301
|
}),
|
|
301
|
-
algorithm:
|
|
302
|
+
algorithm: resolved.algorithm
|
|
302
303
|
};
|
|
303
304
|
}
|
|
304
|
-
function resolveScope(source, options, stack) {
|
|
305
|
+
function resolveScope(source, options, stack, ancestry) {
|
|
305
306
|
var _ref;
|
|
306
|
-
|
|
307
|
-
const base = null != (_ref = null ==
|
|
307
|
+
var _toInnermostFrame;
|
|
308
|
+
const base = null != (_ref = null == (_toInnermostFrame = toInnermostFrame(stack, ancestry)) ? void 0 : _toInnermostFrame.source) ? _ref : source;
|
|
308
309
|
const salt = inline(()=>{
|
|
309
310
|
if (!options.salt) return;
|
|
310
311
|
if (isLayered(options.salt)) return [
|
|
@@ -320,7 +321,7 @@ function resolveScope(source, options, stack) {
|
|
|
320
321
|
};
|
|
321
322
|
return {
|
|
322
323
|
source: base,
|
|
323
|
-
|
|
324
|
+
trace: base.toConstructionTrace(pins)
|
|
324
325
|
};
|
|
325
326
|
}
|
|
326
327
|
export { Constructor };
|
|
@@ -5,6 +5,7 @@ import { defaultAlgorithm, deriveClock, isLayered, normalizeSalt, toRandomSource
|
|
|
5
5
|
import { registry } from "../Schema/Registry.js";
|
|
6
6
|
import { Layer } from "../Types.js";
|
|
7
7
|
import { inline, isThenable, noop } from "../Utility/Core.js";
|
|
8
|
+
import { toInnermostFrame } from "./Stack/Visible.js";
|
|
8
9
|
const DEFAULT_COMBINATORIAL_LIMIT = 1024;
|
|
9
10
|
function resolveCombinatorialLimit(limit) {
|
|
10
11
|
if (void 0 === limit) return DEFAULT_COMBINATORIAL_LIMIT;
|
|
@@ -14,6 +15,9 @@ function resolveCombinatorialLimit(limit) {
|
|
|
14
15
|
function resolveClock(config) {
|
|
15
16
|
return "number" == typeof config.clock ? config.clock : deriveClock(config.algorithm, normalizeSalt(config.salt));
|
|
16
17
|
}
|
|
18
|
+
function mint() {
|
|
19
|
+
return Symbol("fabricator.instance");
|
|
20
|
+
}
|
|
17
21
|
function overlay(base, over) {
|
|
18
22
|
var _ref, _over_algorithm, _ref1, _over_types, _over_limits;
|
|
19
23
|
var _this;
|
|
@@ -41,26 +45,40 @@ function overlay(base, over) {
|
|
|
41
45
|
}
|
|
42
46
|
};
|
|
43
47
|
}
|
|
44
|
-
function instantiate(config, stack) {
|
|
48
|
+
function instantiate(config, stack, parent) {
|
|
49
|
+
const ancestry = void 0 === parent ? [
|
|
50
|
+
mint()
|
|
51
|
+
] : [
|
|
52
|
+
...parent.ancestry,
|
|
53
|
+
mint()
|
|
54
|
+
];
|
|
45
55
|
const source = toRandomSource({
|
|
46
56
|
salt: config.salt,
|
|
47
57
|
algorithm: config.algorithm,
|
|
48
58
|
clock: resolveClock(config)
|
|
49
59
|
});
|
|
50
|
-
const Fabricator = Constructor(source, stack);
|
|
51
|
-
const { combinatorial, coverage } = enumerables(source, config.limits, stack);
|
|
60
|
+
const Fabricator = Constructor(source, stack, ancestry);
|
|
61
|
+
const { combinatorial, coverage } = enumerables(source, config.limits, stack, ancestry);
|
|
62
|
+
function derive(over) {
|
|
63
|
+
const derived = overlay(config, over);
|
|
64
|
+
return {
|
|
65
|
+
...instantiate(derived, stack, {
|
|
66
|
+
ancestry,
|
|
67
|
+
root: instance.root
|
|
68
|
+
}),
|
|
69
|
+
config: derived
|
|
70
|
+
};
|
|
71
|
+
}
|
|
52
72
|
function fork(forkOverlay = {}) {
|
|
53
|
-
return
|
|
73
|
+
return derive(forkOverlay).instance;
|
|
54
74
|
}
|
|
55
75
|
function wrap(wrapOverlay, block) {
|
|
56
|
-
|
|
57
|
-
var _stack_current;
|
|
58
|
-
const base = null != (_ref = null == (_stack_current = stack.current()) ? void 0 : _stack_current.config) ? _ref : config;
|
|
59
|
-
const scopedConfig = overlay(base, wrapOverlay);
|
|
60
|
-
const scoped = instantiate(scopedConfig, stack);
|
|
76
|
+
const scoped = derive(wrapOverlay);
|
|
61
77
|
const result = stack.enter({
|
|
62
|
-
config:
|
|
63
|
-
source: scoped.source
|
|
78
|
+
config: scoped.config,
|
|
79
|
+
source: scoped.source,
|
|
80
|
+
instance: scoped.instance,
|
|
81
|
+
ancestry
|
|
64
82
|
}, ()=>block(scoped.instance));
|
|
65
83
|
if (!stack.asynchronous && isThenable(result)) {
|
|
66
84
|
result.then(noop, noop);
|
|
@@ -68,25 +86,41 @@ function instantiate(config, stack) {
|
|
|
68
86
|
}
|
|
69
87
|
return result;
|
|
70
88
|
}
|
|
89
|
+
function visibleFrame() {
|
|
90
|
+
return toInnermostFrame(stack, ancestry);
|
|
91
|
+
}
|
|
71
92
|
const context = {
|
|
72
93
|
get salt () {
|
|
73
94
|
var _ref;
|
|
74
|
-
var
|
|
75
|
-
return normalizeSalt((null != (_ref = null == (
|
|
95
|
+
var _visibleFrame;
|
|
96
|
+
return normalizeSalt((null != (_ref = null == (_visibleFrame = visibleFrame()) ? void 0 : _visibleFrame.config) ? _ref : config).salt);
|
|
76
97
|
},
|
|
77
98
|
get algorithm () {
|
|
78
99
|
var _ref1;
|
|
79
|
-
var
|
|
80
|
-
return (null != (_ref1 = null == (
|
|
100
|
+
var _visibleFrame1;
|
|
101
|
+
return (null != (_ref1 = null == (_visibleFrame1 = visibleFrame()) ? void 0 : _visibleFrame1.config) ? _ref1 : config).algorithm;
|
|
81
102
|
},
|
|
82
103
|
get clock () {
|
|
83
104
|
var _ref2;
|
|
84
|
-
var
|
|
85
|
-
return resolveClock(null != (_ref2 = null == (
|
|
105
|
+
var _visibleFrame2;
|
|
106
|
+
return resolveClock(null != (_ref2 = null == (_visibleFrame2 = visibleFrame()) ? void 0 : _visibleFrame2.config) ? _ref2 : config);
|
|
107
|
+
},
|
|
108
|
+
scope: ()=>{
|
|
109
|
+
var _ref;
|
|
110
|
+
var _visibleFrame;
|
|
111
|
+
return null != (_ref = null == (_visibleFrame = visibleFrame()) ? void 0 : _visibleFrame.instance) ? _ref : instance;
|
|
112
|
+
},
|
|
113
|
+
get depth () {
|
|
114
|
+
return stack.visible(ancestry).length;
|
|
86
115
|
}
|
|
87
116
|
};
|
|
88
117
|
const instance = {
|
|
89
118
|
T: config.types,
|
|
119
|
+
ancestry,
|
|
120
|
+
get root () {
|
|
121
|
+
var _ref3;
|
|
122
|
+
return null != (_ref3 = null == parent ? void 0 : parent.root) ? _ref3 : instance;
|
|
123
|
+
},
|
|
90
124
|
Fabricator,
|
|
91
125
|
combinatorial,
|
|
92
126
|
coverage,
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
+
import { toVisible } from "./Visible.js";
|
|
2
3
|
function toStack() {
|
|
3
4
|
const store = new AsyncLocalStorage();
|
|
5
|
+
const getFrames = ()=>{
|
|
6
|
+
var _store_getStore;
|
|
7
|
+
return null != (_store_getStore = store.getStore()) ? _store_getStore : [];
|
|
8
|
+
};
|
|
4
9
|
return {
|
|
5
10
|
asynchronous: true,
|
|
6
|
-
|
|
7
|
-
enter: (frame, block)=>store.run(
|
|
11
|
+
visible: (ancestry)=>toVisible(getFrames(), ancestry),
|
|
12
|
+
enter: (frame, block)=>store.run([
|
|
13
|
+
...getFrames(),
|
|
14
|
+
frame
|
|
15
|
+
], block)
|
|
8
16
|
};
|
|
9
17
|
}
|
|
10
18
|
export { toStack };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { toVisible } from "./Visible.js";
|
|
1
2
|
function toStack() {
|
|
2
3
|
const frames = [];
|
|
3
4
|
return {
|
|
4
5
|
asynchronous: false,
|
|
5
|
-
|
|
6
|
+
visible: (ancestry)=>toVisible(frames, ancestry),
|
|
6
7
|
enter: (frame, block)=>{
|
|
7
8
|
frames.push(frame);
|
|
8
9
|
try {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function onDirectLine(a, b) {
|
|
2
|
+
const shared = Math.min(a.length, b.length);
|
|
3
|
+
for(let index = 0; index < shared; index++)if (a[index] !== b[index]) return false;
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
function toVisible(frames, ancestry) {
|
|
7
|
+
return frames.filter((frame)=>onDirectLine(frame.ancestry, ancestry));
|
|
8
|
+
}
|
|
9
|
+
function toInnermostFrame(stack, ancestry) {
|
|
10
|
+
const frames = stack.visible(ancestry);
|
|
11
|
+
return frames[frames.length - 1];
|
|
12
|
+
}
|
|
13
|
+
export { toInnermostFrame, toVisible };
|
|
@@ -10,7 +10,7 @@ function Fabricator(context, forkSource, make) {
|
|
|
10
10
|
function fabricateAt(depth) {
|
|
11
11
|
const atMax = depth >= meta.depth.max;
|
|
12
12
|
const target = atMax ? meta.terminal : meta.body;
|
|
13
|
-
const construction = privateSource.
|
|
13
|
+
const construction = privateSource.toConstructionTrace();
|
|
14
14
|
const context = {
|
|
15
15
|
toTrace: (path, kind)=>({
|
|
16
16
|
...construction,
|
package/dist/esm/Random/index.js
CHANGED
|
@@ -71,7 +71,7 @@ function toRandomSource(options) {
|
|
|
71
71
|
let algorithm = null != (_options_algorithm = options.algorithm) ? _options_algorithm : defaultAlgorithm;
|
|
72
72
|
const clock = options.clock;
|
|
73
73
|
let constructionOrdinal = 0;
|
|
74
|
-
function
|
|
74
|
+
function toConstructionTrace(pins = {}) {
|
|
75
75
|
var _pins_salt, _pins_clock;
|
|
76
76
|
return {
|
|
77
77
|
salt: null != (_pins_salt = pins.salt) ? _pins_salt : salt,
|
|
@@ -87,7 +87,7 @@ function toRandomSource(options) {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
return {
|
|
90
|
-
|
|
90
|
+
toConstructionTrace,
|
|
91
91
|
algorithm,
|
|
92
92
|
salt,
|
|
93
93
|
fork
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import type { Stack } from "../Instance/Types";
|
|
1
|
+
import type { Ancestry, Stack } from "../Instance/Types";
|
|
2
2
|
import type { RandomSource } from "../Random/Types";
|
|
3
3
|
import type { Enumerable, Limits } from "./Types";
|
|
4
4
|
/**
|
|
5
5
|
* Typed `combinatorial`/`coverage` boundary, closing over one instance's
|
|
6
6
|
* `source` and its already-validated `limits` — same shape as
|
|
7
|
-
* `Constructor(source, stack)`. No separate `clock`: `source`
|
|
7
|
+
* `Constructor(source, stack, ancestry)`. No separate `clock`: `source` carries
|
|
8
8
|
* its resolved clock (`Random/Types.ts`'s `Options.clock`), so `Constructor`'s
|
|
9
|
-
* `toConstructionContext` reads it off
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* `toConstructionContext` reads it off the resolved `ConstructionTrace`.
|
|
10
|
+
* `plan`/ `resolve` (`./Plan.ts`) do the untyped recursive work; this is the
|
|
11
|
+
* one precisely-typed layer, mirroring `Constructor.ts`'s `make`/`construct`
|
|
12
|
+
* split.
|
|
13
13
|
*
|
|
14
14
|
* Two derived salts — one per API — each composed from the _effective_ source's
|
|
15
|
-
* salt (`effectiveSource()` below — the
|
|
16
|
-
* instance's `source`; read fresh on every
|
|
17
|
-
* call, not once when `enumerables()` was
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `construct()`) — a pin,
|
|
21
|
-
*
|
|
22
|
-
* salt.
|
|
15
|
+
* salt (`effectiveSource()` below — the innermost visible `wrap` frame's, or
|
|
16
|
+
* this instance's `source`; read fresh on every
|
|
17
|
+
* `combinatorial(...)`/`coverage(...)` call, not once when `enumerables()` was
|
|
18
|
+
* built, so the same `combinatorial` reference behaves differently inside a
|
|
19
|
+
* `wrap` this instance can see). Each build pins that salt via `new
|
|
20
|
+
* Fabricator(schema, { salt })` (see `Constructor.ts`'s `construct()`) — a pin,
|
|
21
|
+
* not a fork — so every rebuild of one schema draws from the same universe,
|
|
22
|
+
* distinct from anything built under the instance's own salt.
|
|
23
23
|
*
|
|
24
24
|
* `ordinal: null` is pinned alongside it, and is not incidental: a salt says
|
|
25
25
|
* nothing about ordering, so without this pin each lazy rebuild would take the
|
|
@@ -29,7 +29,7 @@ import type { Enumerable, Limits } from "./Types";
|
|
|
29
29
|
* every iteration rebuilds from the same explicit identity, and the `null` can
|
|
30
30
|
* never coincide with a counted construction.
|
|
31
31
|
*/
|
|
32
|
-
export declare function enumerables(source: RandomSource, limits: Limits, stack: Stack): {
|
|
32
|
+
export declare function enumerables(source: RandomSource, limits: Limits, stack: Stack, ancestry: Ancestry): {
|
|
33
33
|
combinatorial: Enumerable;
|
|
34
34
|
coverage: Enumerable;
|
|
35
35
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Stack } from "../Instance/Types";
|
|
1
|
+
import type { Ancestry, Stack } from "../Instance/Types";
|
|
2
2
|
import type { ConstructorOptions, RandomSource } from "../Random/Types";
|
|
3
3
|
import { type Buildable } from "../Types";
|
|
4
4
|
import { type AsFabricator } from "./Types";
|
|
@@ -25,17 +25,20 @@ export type Constructor = {
|
|
|
25
25
|
* fabricator this `construct()` produces draws from that instance's own
|
|
26
26
|
* salt/streams and never another instance's.
|
|
27
27
|
*
|
|
28
|
-
* `stack` is the instance's
|
|
29
|
-
* (`Instance/Core.ts`
|
|
28
|
+
* `stack` is the ambient carrier and `ancestry` is this instance's position in
|
|
29
|
+
* its lineage (`Instance/Core.ts`) — both passed straight through to
|
|
30
30
|
* `resolveScope` on every `construct()` call, never read here directly, so a
|
|
31
|
-
* build reached inside
|
|
32
|
-
* automatically, with nothing threaded through by the caller.
|
|
31
|
+
* build reached inside a `wrap` this instance can see resolves against that
|
|
32
|
+
* frame automatically, with nothing threaded through by the caller. `ancestry`
|
|
33
|
+
* is what decides "can see": a frame entered on a sibling instance is not one
|
|
34
|
+
* this `construct()` will ever resolve against.
|
|
33
35
|
*
|
|
34
36
|
* No separate `clock` parameter: `source` already carries its own resolved
|
|
35
37
|
* clock intrinsically (`Random/Types.ts`'s `Options.clock`, baked in when the
|
|
36
38
|
* source was built), and `resolveScope`'s chosen source — the active `wrap`
|
|
37
39
|
* frame's, or this one — is exactly the source whose clock a construction
|
|
38
40
|
* should resolve "now" against. `toConstructionContext` reads it straight off
|
|
39
|
-
* the resolved
|
|
41
|
+
* the resolved construction trace rather than threading a second value
|
|
42
|
+
* alongside `source`.
|
|
40
43
|
*/
|
|
41
|
-
export declare function Constructor(source: RandomSource, stack: Stack): Constructor;
|
|
44
|
+
export declare function Constructor(source: RandomSource, stack: Stack, ancestry: Ancestry): Constructor;
|
|
@@ -14,19 +14,20 @@ import type { PlainObject } from "../Utility/Types";
|
|
|
14
14
|
* `toTrace` records this node's {@link Trace} — a plain object literal, no
|
|
15
15
|
* hashing. Hashing is paid only where a kind actually calls
|
|
16
16
|
* `toStreamFromTrace(algorithm, trace)`. Bound once in `construct()` to this
|
|
17
|
-
* one construction's already-resolved `RandomSource
|
|
17
|
+
* one construction's already-resolved `RandomSource`/`ConstructionTrace` pair
|
|
18
18
|
* (see `Constructor.ts`'s `resolveScope`) — every leaf calls `toTrace` with
|
|
19
19
|
* only its own structural `path` and kind, never re-resolving the
|
|
20
|
-
* construction
|
|
21
|
-
* `toTrace`: each lazy expansion
|
|
22
|
-
* forked `RandomSource` (see `recursive/Fabricator.ts`), so a
|
|
23
|
-
* expansion count can never perturb, or be perturbed by,
|
|
24
|
-
* from the same `initialize()` instance —
|
|
25
|
-
* (`Random/Types.ts`) is the isolation primitive.
|
|
20
|
+
* construction-owned slots itself. `T.recursive` is the one kind that rebinds
|
|
21
|
+
* `toTrace`: each lazy expansion resolves its construction trace on the node's
|
|
22
|
+
* own private forked `RandomSource` (see `recursive/Fabricator.ts`), so a
|
|
23
|
+
* data-dependent expansion count can never perturb, or be perturbed by,
|
|
24
|
+
* anything else built from the same `initialize()` instance —
|
|
25
|
+
* `RandomSource.fork` (`Random/Types.ts`) is the isolation primitive.
|
|
26
26
|
*
|
|
27
27
|
* `algorithm` rather than the `RandomSource` itself: stream derivation depends
|
|
28
|
-
* on no per-source state, and a leaf has no business with
|
|
29
|
-
* `clock` is not a field of its own — it is
|
|
28
|
+
* on no per-source state, and a leaf has no business with
|
|
29
|
+
* `toConstructionTrace`/`fork`. `clock` is not a field of its own — it is
|
|
30
|
+
* always `trace.clock`.
|
|
30
31
|
*
|
|
31
32
|
* `self` is what makes `case "recursive.self"` resolve to "recurse one level
|
|
32
33
|
* deeper, right now" — absent outside any active recursion, which is how `case
|
|
@@ -47,11 +48,11 @@ export type ConstructionContext = {
|
|
|
47
48
|
* `toStreamFromTrace(algorithm, trace)`. The guard is the call site's own `if
|
|
48
49
|
* (meta.produce)` branch (or the equivalent drawing path), not an unevaluated
|
|
49
50
|
* closure. `algorithm` rather than the `RandomSource`: derivation depends on no
|
|
50
|
-
* per-source state, and a leaf has no business with
|
|
51
|
-
* `clock`: it is `trace.clock`, always. A
|
|
52
|
-
* `element`, an object's `fields`, a choice's
|
|
53
|
-
* its own trailing parameter: those vary per
|
|
54
|
-
* shared prefix this replaces.
|
|
51
|
+
* per-source state, and a leaf has no business with
|
|
52
|
+
* `toConstructionTrace`/`fork`. No `clock`: it is `trace.clock`, always. A
|
|
53
|
+
* kind-specific extra — an array's `element`, an object's `fields`, a choice's
|
|
54
|
+
* `weightings` — still follows as its own trailing parameter: those vary per
|
|
55
|
+
* kind and were never part of the shared prefix this replaces.
|
|
55
56
|
*/
|
|
56
57
|
export type FabricatorContext<$Schema> = {
|
|
57
58
|
schema: $Schema;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RandomSource } from "../Random/Types";
|
|
2
2
|
import type { PlainObject } from "../Utility/Types";
|
|
3
|
-
import type { Config, Instance, Overlay, Stack } from "./Types";
|
|
3
|
+
import type { Ancestry, Config, Instance, Overlay, Stack } from "./Types";
|
|
4
4
|
/**
|
|
5
5
|
* `combinatorial`'s default limit — `2**10`, so it admits ten independent
|
|
6
6
|
* binary axes before requiring the caller to raise it explicitly. Each
|
|
@@ -51,12 +51,23 @@ export declare function overlay<$Registry extends PlainObject>(base: Partial<Con
|
|
|
51
51
|
* than each independently re-deriving one from the same config (and so silently
|
|
52
52
|
* diverging/duplicating construction ordinals).
|
|
53
53
|
*
|
|
54
|
-
* `stack`
|
|
55
|
-
* function never reads
|
|
56
|
-
* `Fabricator`/`combinatorial`/`coverage`
|
|
57
|
-
*
|
|
54
|
+
* `stack` and `ancestry` are threaded straight through to
|
|
55
|
+
* `Constructor`/`enumerables` — this function never reads the stack itself,
|
|
56
|
+
* only passes both along so every built `Fabricator`/`combinatorial`/`coverage`
|
|
57
|
+
* can consult whichever frame is visible to _this_ instance _at the moment each
|
|
58
|
+
* is called_, not at this moment.
|
|
59
|
+
*
|
|
60
|
+
* `parent` describes the instance this one is derived from, and its absence is
|
|
61
|
+
* the single marker of a root: `initialize` passes none, every `fork`/`wrap`
|
|
62
|
+
* passes the receiver's.
|
|
63
|
+
*
|
|
64
|
+
* A fresh token is minted either way, so no two instances share an identity,
|
|
65
|
+
* and the `ancestry` built here is _this_ instance's: the parent's plus one.
|
|
58
66
|
*/
|
|
59
|
-
export declare function instantiate<$Registry extends PlainObject>(config: Config<$Registry>, stack: Stack
|
|
67
|
+
export declare function instantiate<$Registry extends PlainObject>(config: Config<$Registry>, stack: Stack, parent?: {
|
|
68
|
+
ancestry: Ancestry;
|
|
69
|
+
root: Instance<PlainObject>;
|
|
70
|
+
}): {
|
|
60
71
|
instance: Instance<$Registry>;
|
|
61
72
|
source: RandomSource;
|
|
62
73
|
};
|
|
@@ -7,6 +7,13 @@ import type { Stack } from "../Types";
|
|
|
7
7
|
* package importable on a runtime with no `node:async_hooks` while every
|
|
8
8
|
* runtime that has one gets async-safe `wrap` with nothing to configure.
|
|
9
9
|
*
|
|
10
|
+
* The store holds the whole open chain, not one `Frame`: `run` _replaces_ the
|
|
11
|
+
* store for the duration of `block`, so `enter` rebuilds the chain with the new
|
|
12
|
+
* frame appended. A fresh array per `enter` is also what isolates concurrent
|
|
13
|
+
* `wrap`s — each async context keeps the chain it entered with, and an inner
|
|
14
|
+
* `enter` cannot mutate an outer one's view. Chains are at nesting depth, so
|
|
15
|
+
* copying one costs nothing worth avoiding.
|
|
16
|
+
*
|
|
10
17
|
* `AsyncLocalStorage.run` returns whatever `block` returns, so this satisfies
|
|
11
18
|
* `enter`'s sync-preserving `<$Return>` signature exactly as the sync carrier
|
|
12
19
|
* does — a synchronous `wrap` is unaffected by which carrier is in play.
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { Stack } from "../Types";
|
|
2
2
|
/**
|
|
3
3
|
* The synchronous carrier: a private `Frame[]`, pushed on `enter` and popped in
|
|
4
|
-
* a `finally` — correct even around a `throw` from `block`.
|
|
4
|
+
* a `finally` — correct even around a `throw` from `block`. Already in
|
|
5
|
+
* outermost-first order, which is the order `visible` reports, so it hands the
|
|
6
|
+
* array straight to `toVisible` and does no filtering of its own.
|
|
5
7
|
*
|
|
6
8
|
* Selected by the `#stack` `default` condition (`package.json`), i.e. on any
|
|
7
9
|
* runtime without `node:async_hooks` — in practice a browser bundle. Its frame
|
|
8
10
|
* cannot survive an `await`: `enter` returns `block()` without awaiting, so an
|
|
9
|
-
* async block's frame unwinds at the block's first suspension point, and
|
|
10
|
-
* shared
|
|
11
|
+
* async block's frame unwinds at the block's first suspension point, and one
|
|
12
|
+
* shared array could not represent two overlapping scopes even if it did await.
|
|
11
13
|
* Both are why `asynchronous` is `false` and `wrap` refuses an async block here
|
|
12
14
|
* rather than resolving it against the base instance with no signal.
|
|
13
15
|
*/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Ancestry, Frame, Stack } from "../Types";
|
|
2
|
+
/**
|
|
3
|
+
* The frames in `frames` that `ancestry` may resolve against, outermost first —
|
|
4
|
+
* the single definition of {@link Stack.visible}'s rule, which both carriers
|
|
5
|
+
* delegate to so neither can drift from the other. A carrier's own job is
|
|
6
|
+
* reduced to holding the chain in whatever way its runtime allows.
|
|
7
|
+
*
|
|
8
|
+
* Order is preserved rather than reduced to the innermost match, because the
|
|
9
|
+
* count is `context.depth` and the innermost is just the last element.
|
|
10
|
+
* Skipping, rather than stopping at, the first invisible frame is the outward
|
|
11
|
+
* walk: with a parent's `wrap` open and a child's nested inside it, that
|
|
12
|
+
* child's sibling must pass over the inner frame and still resolve against the
|
|
13
|
+
* outer one.
|
|
14
|
+
*/
|
|
15
|
+
export declare function toVisible(frames: ReadonlyArray<Frame>, ancestry: Ancestry): ReadonlyArray<Frame>;
|
|
16
|
+
/**
|
|
17
|
+
* The one frame a read resolves against: the innermost frame visible to
|
|
18
|
+
* `ancestry`, or `undefined` outside any. Every consumer — `resolveScope`,
|
|
19
|
+
* `effectiveSource`, the `context` getters — goes through here rather than
|
|
20
|
+
* indexing `visible()` itself, so "innermost visible" has one definition.
|
|
21
|
+
*/
|
|
22
|
+
export declare function toInnermostFrame(stack: Stack, ancestry: Ancestry): Frame | undefined;
|
|
@@ -47,27 +47,77 @@ export type Overlay<$Registry extends PlainObject> = Partial<Omit<Config<$Regist
|
|
|
47
47
|
readonly salt?: Salt | Layered;
|
|
48
48
|
readonly clock?: Date | "derived" | undefined;
|
|
49
49
|
};
|
|
50
|
+
declare const Brand: unique symbol;
|
|
51
|
+
/**
|
|
52
|
+
* One instance's identity — minted fresh by every `instantiate`
|
|
53
|
+
* (`Instance/Core.ts`) and never equal to any other. Branded, and the brand key
|
|
54
|
+
* is module-private, so a token cannot be forged from outside this package: the
|
|
55
|
+
* only way to hold one is to have been handed an `Instance`.
|
|
56
|
+
*/
|
|
57
|
+
export type Token = symbol & {
|
|
58
|
+
readonly [Brand]: "Instance";
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Where an instance sits in its lineage: every token from the root down to and
|
|
62
|
+
* including its own, so `ancestry[0]` is the lineage identity (`a.ancestry[0]
|
|
63
|
+
* === b.ancestry[0]` answers "same root?") and the last element is the instance
|
|
64
|
+
* itself. Non-empty by construction — a root `initialize()` mints one token
|
|
65
|
+
* before there is anything to inherit.
|
|
66
|
+
*
|
|
67
|
+
* Derived from the _receiver_: `fork` and `wrap` alike append to the ancestry
|
|
68
|
+
* of the instance they were called on, exactly as they both lay their overlay
|
|
69
|
+
* over that instance's `config`. One rule, so an instance's position and its
|
|
70
|
+
* configuration always agree about who its parent is.
|
|
71
|
+
*
|
|
72
|
+
* Two chains describe instances on the same ancestral line when either is a
|
|
73
|
+
* prefix of the other, which is what {@link Stack.visible} tests. That relation
|
|
74
|
+
* decides whose calls resolve against whose frames: a parent's calls resolve
|
|
75
|
+
* against a child's frame and a child's against a parent's, while two siblings
|
|
76
|
+
* resolve against neither's. Note this never crosses lineages, and not for want
|
|
77
|
+
* of identity — two roots hold two separate carriers, so a `wrap` on one pushes
|
|
78
|
+
* where the other's reads never look.
|
|
79
|
+
*/
|
|
80
|
+
export type Ancestry = readonly [Token, ...ReadonlyArray<Token>];
|
|
50
81
|
/**
|
|
51
82
|
* One active `wrap` — its resolved config plus the single `RandomSource` every
|
|
52
83
|
* build reached inside that `wrap` shares, whether reached implicitly (any
|
|
53
|
-
* instance
|
|
84
|
+
* instance on the origin's ancestral line consulting the frame) or explicitly
|
|
54
85
|
* (`scope.Fabricator`, the `Instance` passed to the block). Storing the scope's
|
|
55
86
|
* own already-built `source` here, rather than each consumer re-deriving one
|
|
56
87
|
* from `config`, keeps the two routes resolving against the _same_ source —
|
|
57
88
|
* sharing one set of construction-ordinal counters — instead of each silently
|
|
58
89
|
* starting its own.
|
|
90
|
+
*
|
|
91
|
+
* `instance` is that same scope, kept so `context.scope()` can hand back the
|
|
92
|
+
* configuration in effect as a usable `Instance` rather than only as its
|
|
93
|
+
* separate `salt`/`algorithm`/`clock` fields.
|
|
94
|
+
*
|
|
95
|
+
* `ancestry` is the **origin's** — the instance `wrap` was called on — not the
|
|
96
|
+
* scope's. The scope is a fresh child of the origin, so keying on it would make
|
|
97
|
+
* every `fork` taken off that origin a _sibling_ of the scope, and calls on
|
|
98
|
+
* those forks would stop resolving against the frame. Keying on the origin
|
|
99
|
+
* keeps everything on the origin's own line resolving against it, which is the
|
|
100
|
+
* whole point of entering one.
|
|
59
101
|
*/
|
|
60
102
|
export type Frame = {
|
|
61
103
|
readonly config: Config<PlainObject>;
|
|
62
104
|
readonly source: RandomSource;
|
|
105
|
+
readonly instance: Instance<PlainObject>;
|
|
106
|
+
readonly ancestry: Ancestry;
|
|
63
107
|
};
|
|
64
108
|
/**
|
|
65
|
-
* The
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
109
|
+
* The ambient frame carrier. Created once at a root `initialize()` and threaded
|
|
110
|
+
* — never re-created — through every `fork`/`wrap` descended from it (see
|
|
111
|
+
* `instantiate`, `Instance/Core.ts`).
|
|
112
|
+
*
|
|
113
|
+
* A carrier holds a chain of open frames and nothing else; which of them any
|
|
114
|
+
* given reader may see is {@link Ancestry}'s business, resolved by
|
|
115
|
+
* {@link visible}. Two unrelated `initialize()` calls hold separate carriers
|
|
116
|
+
* and so stay fully isolated — and because a reader is now gated on ancestry
|
|
117
|
+
* rather than on carrier identity, handing the _same_ carrier to two
|
|
118
|
+
* `initialize()` calls does not join them either: their roots mint unrelated
|
|
119
|
+
* tokens, so neither one's reads ever resolve the other's frames. `initialize({
|
|
120
|
+
* stack })` is therefore purely a choice of carrier.
|
|
71
121
|
*/
|
|
72
122
|
export type Stack = {
|
|
73
123
|
/**
|
|
@@ -80,24 +130,71 @@ export type Stack = {
|
|
|
80
130
|
* instance. See `Instance/Stack/Sync.ts` and `Instance/Stack/Async.ts`.
|
|
81
131
|
*/
|
|
82
132
|
readonly asynchronous: boolean;
|
|
83
|
-
current(): Frame | undefined;
|
|
84
133
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
134
|
+
* Every open frame `ancestry` may resolve against, outermost first: those
|
|
135
|
+
* whose own `ancestry` is a prefix of this one or has this one as a prefix.
|
|
136
|
+
* The innermost visible frame — what a build or a `context` read actually
|
|
137
|
+
* resolves against — is the last element, and the count is `context.depth`.
|
|
138
|
+
*
|
|
139
|
+
* Filtering, rather than simply reporting the innermost frame, is what makes
|
|
140
|
+
* the outward walk possible: with a parent's `wrap` open and a child's nested
|
|
141
|
+
* inside it, the child's _sibling_ must skip the inner frame and still find
|
|
142
|
+
* the outer one. Callers never filter themselves — `toVisible`
|
|
143
|
+
* (`Instance/Stack/Visible.ts`) is the single definition both carriers
|
|
144
|
+
* delegate to, so the rule cannot drift between them.
|
|
145
|
+
*/
|
|
146
|
+
visible(ancestry: Ancestry): ReadonlyArray<Frame>;
|
|
147
|
+
/**
|
|
148
|
+
* Append `frame`, run `block`, remove it in a `finally`, so a frame unwinds
|
|
149
|
+
* correctly even if `block` throws. Appends rather than replaces: the chain
|
|
150
|
+
* has to stay intact for {@link visible} to walk outward past a frame this
|
|
151
|
+
* reader cannot see.
|
|
87
152
|
*/
|
|
88
153
|
enter<$Return>(frame: Frame, block: () => $Return): $Return;
|
|
89
154
|
};
|
|
90
155
|
/**
|
|
91
|
-
*
|
|
92
|
-
* or
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
156
|
+
* What is in effect _right now_ — the innermost `wrap` frame this instance's
|
|
157
|
+
* calls can resolve against, or the instance itself when there is none.
|
|
158
|
+
*
|
|
159
|
+
* The four value properties are getters, so they are a live view only while
|
|
160
|
+
* this stays an object: destructuring one, or spreading the object, calls that
|
|
161
|
+
* getter once and freezes the result.
|
|
162
|
+
*
|
|
163
|
+
* {@link Context.scope} is deliberately a **function** rather than another
|
|
164
|
+
* getter: it is the one member a caller _acts through_ rather than reads, so
|
|
165
|
+
* freezing it would yield correct-looking code deriving from the wrong base. As
|
|
166
|
+
* a function it survives destructuring — `const { scope } = instance.context`,
|
|
167
|
+
* then `scope()`, still resolves live. Distinct from `ConstructionContext`
|
|
168
|
+
* (`Fabricator/Types.ts`), which is one construction's internal dispatch
|
|
169
|
+
* plumbing; this is the caller-facing "what is in effect."
|
|
96
170
|
*/
|
|
97
171
|
export type Context = {
|
|
98
172
|
readonly salt: ReadonlyArray<string>;
|
|
99
173
|
readonly algorithm: Algorithm;
|
|
100
174
|
readonly clock: number;
|
|
175
|
+
/**
|
|
176
|
+
* The configuration in effect as an `Instance` — the visible frame's own
|
|
177
|
+
* scope, or this instance outside any. This is how to compose deliberately
|
|
178
|
+
* against whatever is active: `context.scope().wrap({ salt: layer("x") },
|
|
179
|
+
* ...)` lays over the frame in effect, where a plain `wrap` lays over the
|
|
180
|
+
* instance it was called on. Unlike rebuilding an overlay out of `salt` by
|
|
181
|
+
* hand, it carries `types`, `limits`, `algorithm` and `clock` across too.
|
|
182
|
+
*
|
|
183
|
+
* A function, not a getter, so capturing it captures the _lookup_ rather than
|
|
184
|
+
* one answer (see this type's own note above). What it returns is an ordinary
|
|
185
|
+
* `Instance`, fixed like any other — so holding the **result** across a frame
|
|
186
|
+
* change is a caller stating they wanted that one, while holding `scope`
|
|
187
|
+
* itself stays live.
|
|
188
|
+
*/
|
|
189
|
+
scope(): Instance<PlainObject>;
|
|
190
|
+
/**
|
|
191
|
+
* How many frames are currently visible to this instance — 0 outside any.
|
|
192
|
+
* Genuine dynamic nesting depth, counted off the carrier rather than inferred
|
|
193
|
+
* from {@link Ancestry}: a frame a sibling cannot see is not counted for that
|
|
194
|
+
* sibling, and entering two `wrap`s on one instance reads as 2 even though
|
|
195
|
+
* neither deepened anyone's ancestry.
|
|
196
|
+
*/
|
|
197
|
+
readonly depth: number;
|
|
101
198
|
};
|
|
102
199
|
/**
|
|
103
200
|
* A single initialized library instance: the registry it was given, and a
|
|
@@ -109,6 +206,49 @@ export type Context = {
|
|
|
109
206
|
export interface Instance<$Registry extends PlainObject> extends Pick<RandomSource, "salt"> {
|
|
110
207
|
/** The registry of type definers this instance was initialized with. */
|
|
111
208
|
readonly T: $Registry;
|
|
209
|
+
/**
|
|
210
|
+
* This instance's position in its lineage — see {@link Ancestry}. Exposed so a
|
|
211
|
+
* caller can reason about which frames a given instance's calls resolve
|
|
212
|
+
* against; the tokens themselves are opaque and comparable only by identity.
|
|
213
|
+
* For the ordinary "same lineage?" question, compare {@link root} instead.
|
|
214
|
+
*/
|
|
215
|
+
readonly ancestry: Ancestry;
|
|
216
|
+
/**
|
|
217
|
+
* The instance at the head of this lineage — the one `initialize()` returned.
|
|
218
|
+
* A root's own `root` is itself, so this is never `undefined` and no caller
|
|
219
|
+
* handles absence.
|
|
220
|
+
*
|
|
221
|
+
* Its job is identity: `a.root === b.root` answers "same lineage?", which is
|
|
222
|
+
* what `fork`/`wrap` descent preserves and what two separate `initialize()`
|
|
223
|
+
* calls never share — even when handed the same `stack`.
|
|
224
|
+
*
|
|
225
|
+
* It is **not** a way to reach "the ambient instance": every instance in a
|
|
226
|
+
* lineage resolves against the frames on its own line, so there is nothing to
|
|
227
|
+
* reach for. Nor is it a configuration to build against in preference to this
|
|
228
|
+
* one — `root`'s config is the lineage's starting point, not whatever is
|
|
229
|
+
* currently in effect. For that, see {@link Context.scope}.
|
|
230
|
+
*
|
|
231
|
+
* Typed at `PlainObject`, which is a deliberate shortcut rather than a
|
|
232
|
+
* necessity — unlike {@link Context.scope}, whose registry depends on which
|
|
233
|
+
* instance entered the innermost visible frame and so cannot be known
|
|
234
|
+
* statically at all.
|
|
235
|
+
*
|
|
236
|
+
* A root's registry is fixed at `initialize` and nothing later disturbs it: a
|
|
237
|
+
* `fork({ types })` mints a _new_ instance with a different registry and
|
|
238
|
+
* leaves the root exactly as it was. What is lost is the descendant's ability
|
|
239
|
+
* to name it — after such a fork this instance's `$Registry` is the fork's,
|
|
240
|
+
* so the root's is no longer recoverable from it. Typing this
|
|
241
|
+
* `Instance<$Registry>` would therefore be wrong if someone overrode
|
|
242
|
+
* `types`.
|
|
243
|
+
*
|
|
244
|
+
* Recovering it would mean threading a second parameter (`Instance<$Registry,
|
|
245
|
+
* $Root>`) through `fork` and `wrap`, which is a poor trade for an accessor
|
|
246
|
+
* whose job is identity: if you mean to _build_, you want the registry of the
|
|
247
|
+
* instance you are holding, not the one the lineage started from. So `root.T`
|
|
248
|
+
* is untyped, while `root.Fabricator` is unaffected since it carries no
|
|
249
|
+
* registry parameter.
|
|
250
|
+
*/
|
|
251
|
+
readonly root: Instance<PlainObject>;
|
|
112
252
|
/**
|
|
113
253
|
* Turn a Schema built from `T` into a live Fabricator, deriving fresh
|
|
114
254
|
* randomness from this instance's own salt for whichever leaves actually need
|
|
@@ -147,10 +287,19 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
|
|
|
147
287
|
* — a build reached after an `await` inside `block` sees this instance's own
|
|
148
288
|
* configuration again, not the wrap's.
|
|
149
289
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
290
|
+
* The overlay lays over _this instance's_ config, exactly as `fork`'s does,
|
|
291
|
+
* whether or not a frame is already open. So a nested `wrap` accumulates when
|
|
292
|
+
* it is called on the enclosing scope — `wrap(a, (scope) => scope.wrap(b,
|
|
293
|
+
* ...))` — and restates from this instance when it is called on a receiver
|
|
294
|
+
* bound outside, as a destructured `wrap` is. To compose onto whatever is
|
|
295
|
+
* active regardless of receiver, go through `context.scope().wrap(...)`.
|
|
296
|
+
*
|
|
297
|
+
* While the block runs, calls made on this instance's ancestral line resolve
|
|
298
|
+
* against the scope — its forks, their forks, and its own ancestors up to the
|
|
299
|
+
* root. Calls on a _sibling_ do not: a frame entered on one `fork` is not one
|
|
300
|
+
* that another `fork` of the same parent can resolve against, which keeps two
|
|
301
|
+
* unrelated derivations from drawing each other's data. See
|
|
302
|
+
* {@link Ancestry}.
|
|
154
303
|
*/
|
|
155
304
|
wrap<$Return, const $WrapRegistry extends PlainObject = $Registry>(overlay: Overlay<$WrapRegistry>, block: (scope: Instance<$WrapRegistry>) => $Return): $Return;
|
|
156
305
|
/**
|
|
@@ -161,3 +310,4 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
|
|
|
161
310
|
*/
|
|
162
311
|
readonly context: Context;
|
|
163
312
|
}
|
|
313
|
+
export {};
|
|
@@ -28,14 +28,13 @@ export type Fabricator<$Schema extends {
|
|
|
28
28
|
* how deep this `fabricate()` goes — so no structural path distinguishes
|
|
29
29
|
* sibling expansions at the same depth (an `array` of three `self` children
|
|
30
30
|
* calls `fabricateAt` three times on one shared element Fabricator; the schema
|
|
31
|
-
* does not tell them apart).
|
|
32
|
-
*
|
|
33
|
-
* `
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* instance.
|
|
31
|
+
* does not tell them apart). `forkSource` mints an isolated `RandomSource`
|
|
32
|
+
* salted from this node's draw, and each `fabricateAt` resolves a
|
|
33
|
+
* `ConstructionTrace` on it (`RandomSource.toConstructionTrace`), recorded on
|
|
34
|
+
* each expansion's `trace`. The private source's construction counter orders
|
|
35
|
+
* expansions; nothing to increment here. Isolation also keeps this node's
|
|
36
|
+
* data-dependent draws from perturbing (or being perturbed by) an unrelated
|
|
37
|
+
* Fabricator from the same `initialize()` instance.
|
|
39
38
|
*
|
|
40
39
|
* Each `self` gets its own independently-dispatched expansion — calling
|
|
41
40
|
* `context.self` twice (two array slots) is two `fabricateAt` calls, each with
|
|
@@ -68,8 +68,8 @@ export type Trace = {
|
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
70
|
* Caller-supplied overrides for the construction-owned {@link Trace} slots
|
|
71
|
-
* {@link RandomSource.
|
|
72
|
-
* they are per-node and applied in `construct()`, not here.
|
|
71
|
+
* {@link RandomSource.toConstructionTrace} resolves. `path`/`kind` are the only
|
|
72
|
+
* slots absent: they are per-node and applied in `construct()`, not here.
|
|
73
73
|
*
|
|
74
74
|
* `salt` is a pin like the rest — it substitutes into that trace slot and does
|
|
75
75
|
* nothing else. It does not fork, so it neither resets nor sidesteps this
|
|
@@ -82,17 +82,17 @@ export type Trace = {
|
|
|
82
82
|
* ordinal" — is taken verbatim and does not advance the counter. That is all a
|
|
83
83
|
* replay needs, since every real {@link Trace} carries a defined ordinal.
|
|
84
84
|
*/
|
|
85
|
-
export type
|
|
85
|
+
export type ConstructionPins = {
|
|
86
86
|
salt?: ReadonlyArray<string> | undefined;
|
|
87
87
|
clock?: number | undefined;
|
|
88
88
|
ordinal?: number | null | undefined;
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
91
|
+
* Every {@link Trace} slot fixed once per construction, before a leaf supplies
|
|
92
|
+
* its own `path`/`kind`. `RandomSource.toConstructionTrace` resolves this once;
|
|
93
|
+
* callers spread it into a full {@link Trace} per leaf and hand that to
|
|
94
|
+
* `toStreamFromTrace`. One construction-ordinal bump is reused across every
|
|
95
|
+
* leaf that construction dispatches.
|
|
96
96
|
*/
|
|
97
97
|
export type ConstructionTrace = Omit<Trace, "path" | "kind">;
|
|
98
98
|
/**
|
|
@@ -173,9 +173,9 @@ export type Options = {
|
|
|
173
173
|
* constructions like any other. Two same-salt builds therefore diverge.
|
|
174
174
|
*
|
|
175
175
|
* `clock` / `ordinal` pin the construction-owned {@link Trace} slots
|
|
176
|
-
* {@link RandomSource.
|
|
177
|
-
* given `ordinal` — a number, or `null` for "no ordinal" — is taken
|
|
178
|
-
* with no counter bump, which is all a replay needs; without it, the
|
|
176
|
+
* {@link RandomSource.toConstructionTrace} would otherwise resolve. Definedness,
|
|
177
|
+
* not `in`: a given `ordinal` — a number, or `null` for "no ordinal" — is taken
|
|
178
|
+
* verbatim with no counter bump, which is all a replay needs; without it, the
|
|
179
179
|
* construction takes the source counter's next value. A salted construction is
|
|
180
180
|
* not, by default, asking for a different "now"; a replayed trace whose `clock`
|
|
181
181
|
* is present explicitly is.
|
|
@@ -205,20 +205,22 @@ export type ConstructorOptions = {
|
|
|
205
205
|
*/
|
|
206
206
|
export type RandomSource = {
|
|
207
207
|
/**
|
|
208
|
-
* Resolve
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* leaf: the returned {@link ConstructionTrace} is what every leaf
|
|
213
|
-
* completes into a full {@link Trace} and hands to
|
|
214
|
-
* construction-ordinal bump serves the whole
|
|
208
|
+
* Resolve this source's `salt`/`clock` and the next construction ordinal,
|
|
209
|
+
* each overridable by {@link ConstructionPins}. Called once per `new
|
|
210
|
+
* Fabricator(...)` (or per lazy expansion of a `T.recursive` schema, each of
|
|
211
|
+
* which resolves its own construction trace on a private forked source),
|
|
212
|
+
* never per leaf: the returned {@link ConstructionTrace} is what every leaf
|
|
213
|
+
* beneath it completes into a full {@link Trace} and hands to
|
|
214
|
+
* `toStreamFromTrace`. One construction-ordinal bump serves the whole
|
|
215
|
+
* construction.
|
|
215
216
|
*/
|
|
216
|
-
|
|
217
|
+
toConstructionTrace(pins?: ConstructionPins): ConstructionTrace;
|
|
217
218
|
/**
|
|
218
219
|
* The algorithm this source (and every fork of it) hashes with. Stream
|
|
219
220
|
* derivation is _not_ a member: it depends on no per-source state, so it is
|
|
220
|
-
* the free function `toStreamFromTrace(algorithm, trace)`.
|
|
221
|
-
* only stateful member (the construction
|
|
221
|
+
* the free function `toStreamFromTrace(algorithm, trace)`.
|
|
222
|
+
* `toConstructionTrace` is the only stateful member (the construction
|
|
223
|
+
* counter).
|
|
222
224
|
*/
|
|
223
225
|
readonly algorithm: Algorithm;
|
|
224
226
|
/**
|
|
@@ -63,7 +63,7 @@ export declare function toStream(algorithm: Algorithm, seed: string): Stream;
|
|
|
63
63
|
* `toStream(algorithm, encode(trace)).seed === stream.seed`. Not a
|
|
64
64
|
* {@link RandomSource} member: derivation depends on no per-source state (a
|
|
65
65
|
* fork shares only the algorithm), so it is a free function of `(algorithm,
|
|
66
|
-
* trace)`. `
|
|
66
|
+
* trace)`. `toConstructionTrace` is the only stateful member.
|
|
67
67
|
*
|
|
68
68
|
* {@link deriveClock} cannot route through this: a {@link Trace} carries
|
|
69
69
|
* `clock`, and `deriveClock` is what produces it. That circularity is why
|
|
@@ -76,7 +76,7 @@ export declare function toStreamFromTrace(algorithm: Algorithm, trace: Trace): S
|
|
|
76
76
|
* single `initialize()` instance owns for its lifetime. `options.clock` is
|
|
77
77
|
* baked in here, once, as a plain number — the `"derived"` policy is already
|
|
78
78
|
* resolved by the caller (`Instance/Core.ts`'s `resolveClock`) before a source
|
|
79
|
-
* is ever built, so every
|
|
80
|
-
* instant, and `fork` threads it forward unchanged.
|
|
79
|
+
* is ever built, so every construction trace this source resolves carries the
|
|
80
|
+
* identical instant, and `fork` threads it forward unchanged.
|
|
81
81
|
*/
|
|
82
82
|
export declare function toRandomSource(options: Options): RandomSource;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -50,7 +50,10 @@ export declare function initialize<const $Registry extends PlainObject = typeof
|
|
|
50
50
|
clock?: Date | "derived";
|
|
51
51
|
/**
|
|
52
52
|
* The ambient carrier backing `wrap` for this lineage — override only to
|
|
53
|
-
* force a specific one.
|
|
53
|
+
* force a specific one. A choice of carrier and nothing more: lineage
|
|
54
|
+
* identity is the instance's own `ancestry[0]`, so handing the same carrier
|
|
55
|
+
* to two `initialize()` calls does not join them — neither one's reads ever
|
|
56
|
+
* resolve the other's frames.
|
|
54
57
|
*
|
|
55
58
|
* Left unset (the norm), the `#stack` package import picks it: every
|
|
56
59
|
* runtime with `node:async_hooks` — Node, Bun, Deno — gets the
|
|
@@ -149,5 +152,11 @@ export type { Trace } from "./Random/Types";
|
|
|
149
152
|
* parameter is a `Partial` of; `Overlay` is what `fork`/`wrap` accept;
|
|
150
153
|
* `Context` is `instance.context`'s own type, so a caller writing a helper that
|
|
151
154
|
* reads it can name the parameter.
|
|
155
|
+
*
|
|
156
|
+
* `Ancestry` comes with `Stack`, whose `visible` takes one, so anyone
|
|
157
|
+
* implementing a carrier can name the parameter. `Token` is deliberately _not_
|
|
158
|
+
* exported: a chain is opaque, its elements are comparable only by identity,
|
|
159
|
+
* and the ordinary "same lineage?" question is `a.root === b.root` rather than
|
|
160
|
+
* anything a caller needs to name.
|
|
152
161
|
*/
|
|
153
|
-
export type { Config, Context, Overlay, Stack } from "./Instance/Types";
|
|
162
|
+
export type { Ancestry, Config, Context, Overlay, Stack, } from "./Instance/Types";
|
package/dist/types/internal.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export type { Axis, Enumerable, Pin, Resolvable } from "./Enumeration/Types";
|
|
|
63
63
|
* directly, beneath the level `initialize()` itself exposes.
|
|
64
64
|
*/
|
|
65
65
|
export { defaultAlgorithm, encode, randomSalt, toRandomSource, toStream, toStreamFromTrace, } from "./Random";
|
|
66
|
-
export type { Algorithm, ConstructionTrace, ConstructorOptions,
|
|
66
|
+
export type { Algorithm, ConstructionPins, ConstructionTrace, ConstructorOptions, Salt, Trace, } from "./Random/Types";
|
|
67
67
|
/**
|
|
68
68
|
* The synchronous ambient carrier. `#stack` (`package.json`) selects it only
|
|
69
69
|
* where there is no `node:async_hooks` — in practice a browser bundle — so on
|