@ghostry/fabricator 0.0.1 → 0.0.3
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 +18 -12
- package/dist/esm/Adapter/Core.js +3 -3
- package/dist/esm/Enumeration/Enumerate.js +16 -13
- package/dist/esm/Error/index.js +16 -8
- package/dist/esm/Fabricator/Constructor.js +12 -14
- package/dist/esm/Harnessing/Core.js +30 -0
- package/dist/esm/Harnessing/Salt.js +12 -0
- package/dist/esm/Harnessing/Types.js +1 -0
- package/dist/esm/Instance/Core.js +25 -41
- package/dist/esm/Instance/Stack/Async.js +10 -0
- package/dist/esm/Instance/Stack/Sync.js +16 -0
- package/dist/esm/Primitive/bigint/Registry.js +12 -12
- package/dist/esm/Primitive/boolean/Registry.js +2 -1
- package/dist/esm/Primitive/date/Registry.js +15 -14
- package/dist/esm/Primitive/null/Registry.js +2 -1
- package/dist/esm/Primitive/number/Registry.js +18 -17
- package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
- package/dist/esm/Primitive/symbol/Registry.js +2 -1
- package/dist/esm/Primitive/undefined/Registry.js +2 -1
- package/dist/esm/Random/index.js +24 -79
- package/dist/esm/Utility/Core.js +6 -1
- package/dist/esm/adapting.js +2 -0
- package/dist/esm/harnessing.js +1 -0
- package/dist/esm/index.js +5 -4
- package/dist/esm/internal.js +2 -2
- package/dist/types/Adapter/Core.d.ts +30 -33
- package/dist/types/Adapter/Types.d.ts +78 -88
- package/dist/types/Bound.d.ts +15 -15
- package/dist/types/Distribution/index.d.ts +54 -61
- package/dist/types/Enumeration/Enumerate.d.ts +24 -24
- package/dist/types/Enumeration/Plan.d.ts +22 -26
- package/dist/types/Enumeration/Types.d.ts +38 -43
- package/dist/types/Error/index.d.ts +103 -89
- package/dist/types/Fabricator/Constructor.d.ts +24 -26
- package/dist/types/Fabricator/Types.d.ts +73 -81
- package/dist/types/Harnessing/Core.d.ts +43 -0
- package/dist/types/Harnessing/Salt.d.ts +31 -0
- package/dist/types/Harnessing/Types.d.ts +79 -0
- package/dist/types/Instance/Core.d.ts +38 -74
- package/dist/types/Instance/Stack/Async.d.ts +14 -0
- package/dist/types/Instance/Stack/Sync.d.ts +14 -0
- package/dist/types/Instance/Types.d.ts +98 -102
- package/dist/types/Primitive/always/Schema.d.ts +8 -8
- package/dist/types/Primitive/always/Types.d.ts +7 -7
- package/dist/types/Primitive/array/Registry.d.ts +10 -8
- package/dist/types/Primitive/array/Schema.d.ts +4 -5
- package/dist/types/Primitive/array/Types.d.ts +6 -6
- package/dist/types/Primitive/bigint/Registry.d.ts +6 -6
- package/dist/types/Primitive/bigint/Schema.d.ts +8 -8
- package/dist/types/Primitive/bigint/Types.d.ts +5 -5
- package/dist/types/Primitive/boolean/Outcomes.d.ts +8 -8
- package/dist/types/Primitive/boolean/Registry.d.ts +2 -12
- package/dist/types/Primitive/boolean/Schema.d.ts +8 -8
- package/dist/types/Primitive/boolean/Types.d.ts +3 -3
- package/dist/types/Primitive/choice/Fabricator.d.ts +6 -7
- package/dist/types/Primitive/choice/Registry.d.ts +13 -13
- package/dist/types/Primitive/choice/Schema.d.ts +6 -6
- package/dist/types/Primitive/choice/Types.d.ts +11 -11
- package/dist/types/Primitive/date/Registry.d.ts +25 -48
- package/dist/types/Primitive/date/Schema.d.ts +9 -10
- package/dist/types/Primitive/date/Types.d.ts +4 -4
- package/dist/types/Primitive/enum/Registry.d.ts +13 -13
- package/dist/types/Primitive/enum/Schema.d.ts +5 -5
- package/dist/types/Primitive/enum/Types.d.ts +17 -19
- package/dist/types/Primitive/namespace.d.ts +10 -10
- package/dist/types/Primitive/null/Registry.d.ts +2 -2
- package/dist/types/Primitive/null/Schema.d.ts +2 -2
- package/dist/types/Primitive/null/Types.d.ts +3 -3
- package/dist/types/Primitive/nullable/Fabricator.d.ts +7 -7
- package/dist/types/Primitive/nullable/Schema.d.ts +13 -13
- package/dist/types/Primitive/nullable/Types.d.ts +9 -10
- package/dist/types/Primitive/nullish/Fabricator.d.ts +12 -13
- package/dist/types/Primitive/nullish/Schema.d.ts +8 -8
- package/dist/types/Primitive/nullish/Types.d.ts +12 -13
- package/dist/types/Primitive/number/Registry.d.ts +20 -38
- package/dist/types/Primitive/number/Schema.d.ts +11 -12
- package/dist/types/Primitive/number/Types.d.ts +13 -13
- package/dist/types/Primitive/number/defaults.d.ts +3 -3
- package/dist/types/Primitive/object/Fabricator.d.ts +15 -15
- package/dist/types/Primitive/object/Registry.d.ts +8 -8
- package/dist/types/Primitive/object/Schema.d.ts +10 -10
- package/dist/types/Primitive/object/Types.d.ts +20 -22
- package/dist/types/Primitive/object/compute/Fabricator.d.ts +4 -5
- package/dist/types/Primitive/object/compute/Schema.d.ts +4 -4
- package/dist/types/Primitive/object/compute/Types.d.ts +19 -20
- package/dist/types/Primitive/object/omittable/Fabricator.d.ts +10 -10
- package/dist/types/Primitive/object/omittable/Outcomes.d.ts +2 -2
- package/dist/types/Primitive/object/omittable/Schema.d.ts +11 -11
- package/dist/types/Primitive/object/omittable/Types.d.ts +14 -15
- package/dist/types/Primitive/object/optional/Fabricator.d.ts +16 -18
- package/dist/types/Primitive/object/optional/Outcomes.d.ts +2 -2
- package/dist/types/Primitive/object/optional/Schema.d.ts +4 -4
- package/dist/types/Primitive/object/optional/Types.d.ts +17 -17
- package/dist/types/Primitive/opaque/Registry.d.ts +3 -3
- package/dist/types/Primitive/opaque/Schema.d.ts +8 -9
- package/dist/types/Primitive/record/Registry.d.ts +8 -6
- package/dist/types/Primitive/record/Schema.d.ts +9 -11
- package/dist/types/Primitive/record/Types.d.ts +25 -25
- package/dist/types/Primitive/recursive/Fabricator.d.ts +20 -21
- package/dist/types/Primitive/recursive/Registry.d.ts +6 -7
- package/dist/types/Primitive/recursive/Schema.d.ts +8 -8
- package/dist/types/Primitive/recursive/Terminate.d.ts +13 -14
- package/dist/types/Primitive/recursive/Types.d.ts +31 -31
- package/dist/types/Primitive/recursive/self/Fabricator.d.ts +11 -12
- package/dist/types/Primitive/recursive/self/Schema.d.ts +10 -10
- package/dist/types/Primitive/recursive/self/Types.d.ts +7 -8
- package/dist/types/Primitive/string/Constants.d.ts +12 -13
- package/dist/types/Primitive/string/Fabricator.d.ts +2 -2
- package/dist/types/Primitive/string/Registry.d.ts +10 -16
- package/dist/types/Primitive/string/Schema.d.ts +5 -5
- package/dist/types/Primitive/string/Types.d.ts +17 -17
- package/dist/types/Primitive/symbol/Fabricator.d.ts +2 -2
- package/dist/types/Primitive/symbol/Registry.d.ts +4 -11
- package/dist/types/Primitive/symbol/Schema.d.ts +6 -6
- package/dist/types/Primitive/symbol/Types.d.ts +2 -2
- package/dist/types/Primitive/tuple/Fabricator.d.ts +7 -8
- package/dist/types/Primitive/tuple/Schema.d.ts +5 -5
- package/dist/types/Primitive/tuple/Types.d.ts +24 -25
- package/dist/types/Primitive/undefinable/Fabricator.d.ts +7 -7
- package/dist/types/Primitive/undefinable/Schema.d.ts +13 -13
- package/dist/types/Primitive/undefinable/Types.d.ts +10 -10
- package/dist/types/Primitive/undefined/Registry.d.ts +2 -2
- package/dist/types/Primitive/undefined/Schema.d.ts +4 -4
- package/dist/types/Primitive/undefined/Types.d.ts +3 -3
- package/dist/types/Random/Generator/sfc32.d.ts +4 -4
- package/dist/types/Random/Types.d.ts +151 -293
- package/dist/types/Random/index.d.ts +56 -81
- package/dist/types/Schema/Core.d.ts +19 -21
- package/dist/types/Schema/Registry.d.ts +3 -3
- package/dist/types/Schema/Types.d.ts +41 -48
- package/dist/types/Types.d.ts +39 -45
- package/dist/types/Utility/Core.d.ts +18 -9
- package/dist/types/adapting.d.ts +32 -0
- package/dist/types/harnessing.d.ts +30 -0
- package/dist/types/index.d.ts +95 -128
- package/dist/types/internal.d.ts +53 -40
- package/package.json +39 -7
- package/dist/esm/Random/CallSite.js +0 -56
- package/dist/types/Random/CallSite.d.ts +0 -59
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Layer } from "../Types";
|
|
2
2
|
/**
|
|
3
|
-
* A zero-argument function that produces a float in `[0, 1)`, exactly
|
|
4
|
-
*
|
|
3
|
+
* A zero-argument function that produces a float in `[0, 1)`, exactly like
|
|
4
|
+
* `Math.random()` does.
|
|
5
5
|
*/
|
|
6
6
|
export type NumberGenerator = () => number;
|
|
7
7
|
/**
|
|
@@ -14,7 +14,7 @@ export type Stream = {
|
|
|
14
14
|
*/
|
|
15
15
|
readonly seed: string;
|
|
16
16
|
/**
|
|
17
|
-
* The count of times a number
|
|
17
|
+
* The count of times a number has been requested from this stream.
|
|
18
18
|
*/
|
|
19
19
|
readonly iterations: number;
|
|
20
20
|
};
|
|
@@ -25,360 +25,218 @@ export type Stream = {
|
|
|
25
25
|
*/
|
|
26
26
|
export type Algorithm = (seed: string) => NumberGenerator;
|
|
27
27
|
/**
|
|
28
|
-
* Everything a single leaf's randomness is derived from. Encoded as a
|
|
29
|
-
*
|
|
30
|
-
* `toStream(algorithm, encode(trace)).seed === stream.seed` for every
|
|
31
|
-
*
|
|
28
|
+
* Everything a single leaf's randomness is derived from. Encoded as a fixed
|
|
29
|
+
* tuple (`Random/index.ts`'s `encode`), this _is_ the stream seed:
|
|
30
|
+
* `toStream(algorithm, encode(trace)).seed === stream.seed` for every traced
|
|
31
|
+
* leaf, so the derived seed is not a field of its own.
|
|
32
32
|
*
|
|
33
|
-
* Attached to every built Fabricator as `.trace` — recording is
|
|
34
|
-
*
|
|
35
|
-
* `
|
|
36
|
-
*
|
|
33
|
+
* Attached to every built Fabricator as `.trace` — recording is unconditional,
|
|
34
|
+
* even for a node that never draws (a bare `object`, `always`,
|
|
35
|
+
* `object.compute`). Minting a stream from it (`toStreamFromTrace`) is still
|
|
36
|
+
* paid only by nodes that draw.
|
|
37
37
|
*
|
|
38
38
|
* Slots, in encoded order:
|
|
39
39
|
*
|
|
40
|
-
* - `
|
|
41
|
-
* ({@link RandomSource.
|
|
40
|
+
* - `salt` — the _instance's_ salt, normalized to its parts
|
|
41
|
+
* ({@link RandomSource.salt}), not anything derived per leaf.
|
|
42
42
|
* - `clock` — this construction's resolved "now," epoch milliseconds
|
|
43
|
-
* (`Instance/Types.ts`'s `Config.clock`). Second, not after the
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* clock is always a concrete instant before any leaf dispatches
|
|
43
|
+
* (`Instance/Types.ts`'s `Config.clock`). Second, not after the per-leaf
|
|
44
|
+
* slots: blast radius matches `salt` — both are instance-level and perturb
|
|
45
|
+
* every leaf, unlike `path`/`kind`/`ordinal`, which narrow to one leaf. Never
|
|
46
|
+
* `undefined`: clock is always a concrete instant before any leaf dispatches
|
|
48
47
|
* (`RandomSource`'s `Options.clock`).
|
|
49
|
-
* - `
|
|
50
|
-
*
|
|
51
|
-
* Distinguishes the
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* `
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* (`Constructor.ts`'s `make`). Distinguishes two leaves of the same
|
|
63
|
-
* kind in one construction. Stable under insert/remove/reorder of
|
|
64
|
-
* unrelated siblings — only a leaf's own position identifies it.
|
|
65
|
-
* - `kind` — schema kind the node was constructed as. Redundant given a
|
|
66
|
-
* unique `path`, but changing a field's kind must change its data.
|
|
67
|
-
* - `ordinal` — which construction among those sharing this `file` this
|
|
68
|
-
* node belongs to. `undefined` for `"unattributed"`, already unique
|
|
69
|
-
* by its forked seed.
|
|
48
|
+
* - `path` — structural position within the construction: field name, tuple slot,
|
|
49
|
+
* choice option, one segment per nesting level (`Constructor.ts`'s `make`).
|
|
50
|
+
* Distinguishes two leaves of the same kind in one construction. Stable under
|
|
51
|
+
* insert/remove/reorder of unrelated siblings — only a leaf's own position
|
|
52
|
+
* identifies it.
|
|
53
|
+
* - `kind` — schema kind the node was constructed as. Redundant given a unique
|
|
54
|
+
* `path`, but changing a field's kind must change its data.
|
|
55
|
+
* - `ordinal` — which construction on this source this node belongs to: the next
|
|
56
|
+
* value of the source's one counter, or a pinned value
|
|
57
|
+
* (`ConstructorOptions.ordinal`), which is how a replay avoids advancing that
|
|
58
|
+
* counter. `null` for a build that deliberately takes no ordinal — a
|
|
59
|
+
* `combinatorial`/`coverage` rebuild. `null`, not absent: a pin counts when
|
|
60
|
+
* it is defined, so only a defined "no ordinal" survives being replayed.
|
|
70
61
|
*/
|
|
71
62
|
export type Trace = {
|
|
72
|
-
readonly
|
|
63
|
+
readonly salt: ReadonlyArray<string>;
|
|
73
64
|
readonly clock: number;
|
|
74
|
-
readonly root: RootKind;
|
|
75
|
-
readonly file: string | undefined;
|
|
76
65
|
readonly path: ReadonlyArray<string>;
|
|
77
66
|
readonly kind: string;
|
|
78
|
-
readonly ordinal: number |
|
|
67
|
+
readonly ordinal: number | null;
|
|
79
68
|
};
|
|
80
69
|
/**
|
|
81
|
-
* Caller-supplied overrides for the construction-owned {@link Trace}
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* {@link ConstructorOptions.seed} when replaying), not something
|
|
85
|
-
* `toRoot` substitutes. `path`/`kind` are per-node and applied in
|
|
86
|
-
* `construct()`, not here.
|
|
70
|
+
* Caller-supplied overrides for the construction-owned {@link Trace} slots
|
|
71
|
+
* {@link RandomSource.toRoot} resolves. `path`/`kind` are the only slots absent:
|
|
72
|
+
* they are per-node and applied in `construct()`, not here.
|
|
87
73
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
74
|
+
* `salt` is a pin like the rest — it substitutes into that trace slot and does
|
|
75
|
+
* nothing else. It does not fork, so it neither resets nor sidesteps this
|
|
76
|
+
* source's construction counter: a salted build takes the next ordinal exactly
|
|
77
|
+
* as an unsalted one does. A caller who wants an isolated source, with its own
|
|
78
|
+
* counters, forks — that is what `fork`/`wrap` are for. Already normalized to
|
|
79
|
+
* parts here, since {@link Trace} carries the array form.
|
|
80
|
+
*
|
|
81
|
+
* Definedness, not `in`: a defined `ordinal` — a number, or `null` for "no
|
|
82
|
+
* ordinal" — is taken verbatim and does not advance the counter. That is all a
|
|
83
|
+
* replay needs, since every real {@link Trace} carries a defined ordinal.
|
|
90
84
|
*/
|
|
91
85
|
export type RootPins = {
|
|
86
|
+
salt?: ReadonlyArray<string> | undefined;
|
|
92
87
|
clock?: number | undefined;
|
|
93
|
-
|
|
94
|
-
file?: string | undefined;
|
|
95
|
-
ordinal?: number | undefined;
|
|
88
|
+
ordinal?: number | null | undefined;
|
|
96
89
|
};
|
|
97
90
|
/**
|
|
98
|
-
* A construction's root: every {@link Trace} slot a
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* bump — reused across every leaf that construction dispatches.
|
|
91
|
+
* A construction's root: every {@link Trace} slot a construction fixes, before a
|
|
92
|
+
* leaf supplies its own `path`/`kind`. `RandomSource.toRoot` resolves this once
|
|
93
|
+
* per construction; callers spread it into a full {@link Trace} per leaf and
|
|
94
|
+
* hand that to `toStreamFromTrace`. One construction-ordinal bump is reused
|
|
95
|
+
* across every leaf that construction dispatches.
|
|
104
96
|
*/
|
|
105
97
|
export type ConstructionTrace = Omit<Trace, "path" | "kind">;
|
|
106
98
|
/**
|
|
107
|
-
* What a producer is told about the fabrication it is running inside.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* this leaf's own derived seed.
|
|
99
|
+
* What a producer is told about the fabrication it is running inside. One
|
|
100
|
+
* object, not a positional list: this is the only channel a producer has, and a
|
|
101
|
+
* future addition must not change every kind's `.as(...)` arity. Curated, not
|
|
102
|
+
* the whole instance `Config` — a producer has no business reading `salt`, and
|
|
103
|
+
* `random` already carries this leaf's own derived seed.
|
|
113
104
|
*/
|
|
114
105
|
export type ProduceContext = {
|
|
115
106
|
/** This leaf's own seeded stream, keyed by its structural path. */
|
|
116
107
|
random: Stream;
|
|
117
108
|
/**
|
|
118
|
-
* This construction's resolved "now," epoch milliseconds — the active
|
|
119
|
-
*
|
|
120
|
-
* (`Instance/Types.ts`'s `Config.clock`). Defaults to an instant derived
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
109
|
+
* This construction's resolved "now," epoch milliseconds — the active `wrap`
|
|
110
|
+
* frame's `Config.clock` if one is active, else the instance's
|
|
111
|
+
* (`Instance/Types.ts`'s `Config.clock`). Defaults to an instant derived from
|
|
112
|
+
* the instance's salt, not `Date.now()`, so a producer that reads it replays
|
|
113
|
+
* like one that only reads `random`. A number, not a `Date`: the instant is
|
|
114
|
+
* fixed once resolved, and a `Date` handed to every producer would be a
|
|
115
|
+
* shared mutable — the same footgun `T.always([])` sharing one array
|
|
116
|
+
* reference already warns against.
|
|
126
117
|
*/
|
|
127
118
|
clock: number;
|
|
128
119
|
};
|
|
129
120
|
/**
|
|
130
121
|
* A kind's opaque custom producer — `.as(produce)` — given a
|
|
131
|
-
* {@link ProduceContext} so its output replays under a
|
|
132
|
-
*
|
|
133
|
-
*
|
|
122
|
+
* {@link ProduceContext} so its output replays under a salt like every other
|
|
123
|
+
* primitive's draws. A zero-argument function (`() => $T`) is still assignable,
|
|
124
|
+
* so every existing `.as(() => ...)` call compiles.
|
|
134
125
|
*/
|
|
135
126
|
export type Produce<$T> = (context: ProduceContext) => $T;
|
|
136
127
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* Each variant is a decision about the two {@link Trace} slots a
|
|
144
|
-
* construction owns — `file` and `ordinal` — and nothing else:
|
|
145
|
-
*
|
|
146
|
-
* | variant | `file` | `ordinal` |
|
|
147
|
-
* | ----------------- | -------------------------- | ---------------------- |
|
|
148
|
-
* | `"attributed"` | resolved from the stack | next ordinal for it |
|
|
149
|
-
* | `"counted"` | `undefined` | next file-less ordinal |
|
|
150
|
-
* | `"unattributed"` | `undefined` | `undefined` |
|
|
151
|
-
*
|
|
152
|
-
* `"attributed"` walks the live call stack once (`resolveCallerFile()`)
|
|
153
|
-
* and relativizes it under the instance's `Attribution` root — or, under
|
|
154
|
-
* `{ kind: "none" }`, resolves no file — then assigns the next ordinal
|
|
155
|
-
* for that file. Two constructions in the same file diverge by default;
|
|
156
|
-
* a lone construction stays reproducible across however many times its
|
|
157
|
-
* file is imported or re-run. `ConstructorOptions.file` without `root`
|
|
158
|
-
* pins that file and draws the next ordinal for it (the wrapping-
|
|
159
|
-
* integration case). A replay (`pins.root` given) takes `file` and
|
|
160
|
-
* `ordinal` verbatim, including `undefined`.
|
|
161
|
-
* `test/fixtures/checkout-a`/`checkout-b` stand in for two checkouts
|
|
162
|
-
* using real frames instead.
|
|
163
|
-
*
|
|
164
|
-
* `"counted"` indexes without attributing. Under `{ kind: "none" }` an
|
|
165
|
-
* `"attributed"` scope resolves to precisely this, and the two share
|
|
166
|
-
* the one file-less bucket — reachable only if both kinds of scope were
|
|
167
|
-
* opened on a single source, which nothing does. `"counted"` is not a
|
|
168
|
-
* variant you choose when building — it is recorded on a node's
|
|
169
|
-
* {@link Trace} (and replayed) for expansions inside `T.recursive`,
|
|
170
|
-
* whose private fork uses it so each lazy expansion gets an ordinal
|
|
171
|
-
* (see `recursive/Fabricator.ts`). An identity that isn't a stack frame
|
|
172
|
-
* but should still vary with the instance's seed is
|
|
173
|
-
* `ConstructorOptions.seed`'s layered form (`{@link Layered}`, via
|
|
174
|
-
* `layer(...)`) — see `ConstructorOptions`.
|
|
175
|
-
*
|
|
176
|
-
* `"unattributed"` fixes neither slot — used only by a source already
|
|
177
|
-
* isolated for one construction (an explicitly seeded
|
|
178
|
-
* `new Fabricator(schema, { seed })`, which forks a new `RandomSource`
|
|
179
|
-
* for that one build). A layered seed (`{@link Layered}`, via
|
|
180
|
-
* `layer(...)`) still opens this same scope: composing onto a base seed
|
|
181
|
-
* is a statement about *what* the fork's seed is, not how the fork
|
|
182
|
-
* itself should be rooted.
|
|
183
|
-
*
|
|
184
|
-
* A plain string union, not a discriminant object: none of the three
|
|
185
|
-
* variants carries data of its own. Caller-supplied overrides go
|
|
186
|
-
* through {@link RootPins} on {@link RandomSource.toRoot}, not through
|
|
187
|
-
* this union. A `{ kind: RootKind }` wrapper would invite a field an
|
|
188
|
-
* object shape can carry and a switch can silently ignore.
|
|
189
|
-
*/
|
|
190
|
-
export type RootKind = "attributed" | "counted" | "unattributed";
|
|
191
|
-
/**
|
|
192
|
-
* A seed as a caller supplies it: one string, or several — several lets
|
|
193
|
-
* a caller compose independent parts (user id, session id, scenario
|
|
194
|
-
* label) without joining them first. Always normalized internally to
|
|
195
|
-
* `ReadonlyArray<string>` (`toRandomSource`'s `normalizeSeed`); a single
|
|
196
|
-
* string is the one-element case.
|
|
128
|
+
* A salt as a caller supplies it: one string, or several — several lets a
|
|
129
|
+
* caller compose independent parts (user id, session id, scenario label)
|
|
130
|
+
* without joining them first. Always normalized internally to
|
|
131
|
+
* `ReadonlyArray<string>` (`toRandomSource`'s `normalizeSalt`); a single string
|
|
132
|
+
* is the one-element case.
|
|
197
133
|
*/
|
|
198
|
-
export type
|
|
134
|
+
export type Salt = string | ReadonlyArray<string>;
|
|
199
135
|
/**
|
|
200
|
-
* A {@link
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* array — can be mistaken for one.
|
|
136
|
+
* A {@link Salt} tagged as composing onto whatever base is in effect, rather
|
|
137
|
+
* than replacing it — what `layer(salt)` (`Random/index.ts`) produces. Tagged
|
|
138
|
+
* with `[Layer]` exactly as `replace()` (`Utility/Core.ts`) tags a merge
|
|
139
|
+
* operand with `[Replace]`, so a caller never names the symbol and no ordinary
|
|
140
|
+
* `Salt` — string or array — can be mistaken for one.
|
|
206
141
|
*/
|
|
207
142
|
export type Layered = {
|
|
208
|
-
readonly [Layer]:
|
|
143
|
+
readonly [Layer]: Salt;
|
|
209
144
|
};
|
|
210
145
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* `
|
|
214
|
-
*
|
|
215
|
-
* `
|
|
216
|
-
* a different absolute path on a different machine derives the same
|
|
217
|
-
* seeds. `root` accepts an absolute path or a `file://` URL (e.g.
|
|
218
|
-
* `new URL("..", import.meta.url).href`, which needs no `node:path`),
|
|
219
|
-
* and is a normalization parameter only — it never enters the hashed
|
|
220
|
-
* material, so moving the root without moving the files under it
|
|
221
|
-
* changes nothing. A file outside `root` is expressed with a leading
|
|
222
|
-
* `..` run rather than left absolute, so it stays stable too, as long
|
|
223
|
-
* as both locations move together under the same checkout (see
|
|
224
|
-
* `relativize`, `Random/CallSite.ts`).
|
|
225
|
-
*
|
|
226
|
-
* `"call site"`, the default, is `"rooted"` at the directory of
|
|
227
|
-
* whichever file called `initialize()` — resolved once, at that call,
|
|
228
|
-
* from the live stack. Two `initialize()` calls in different files that
|
|
229
|
-
* share a seed and happen to produce the same file's-worth of relative
|
|
230
|
-
* paths (a symmetric monorepo layout — this repo's `pkg/fabricator` and
|
|
231
|
-
* `pkg/fabricator-adapter-typebox-v0` test suites) will collide; use
|
|
232
|
-
* `"rooted"` at a shared repository root instead.
|
|
233
|
-
*
|
|
234
|
-
* `"none"` attributes nothing: every construction in the instance draws
|
|
235
|
-
* its root from one shared, file-less counter. Maximally portable — no
|
|
236
|
-
* path can influence a seed — at the cost of every construction sharing
|
|
237
|
-
* one counter, so adding, removing, or reordering a *construction*
|
|
238
|
-
* anywhere in the instance shifts every later one. Individual fields
|
|
239
|
-
* within one construction are unaffected: they're keyed by structural
|
|
240
|
-
* path, not dispatch order.
|
|
241
|
-
*/
|
|
242
|
-
export type Attribution = {
|
|
243
|
-
kind: "rooted";
|
|
244
|
-
root: string;
|
|
245
|
-
} | {
|
|
246
|
-
kind: "call site";
|
|
247
|
-
} | {
|
|
248
|
-
kind: "none";
|
|
249
|
-
};
|
|
250
|
-
/**
|
|
251
|
-
* {@link Attribution} after `"call site"` has been resolved against the
|
|
252
|
-
* live stack and `root` normalized — what actually drives
|
|
253
|
-
* construction-root resolution. This, not the caller-facing
|
|
254
|
-
* `Attribution`, is what `RandomSource.fork` threads through:
|
|
255
|
-
* re-resolving `"call site"` inside a fork would read the stack at
|
|
256
|
-
* whatever moment the fork happens to run (an explicitly seeded build,
|
|
257
|
-
* an enumeration's rebuild) and root the child somewhere unrelated to
|
|
258
|
-
* the instance that spawned it.
|
|
259
|
-
*/
|
|
260
|
-
export type ResolvedAttribution = {
|
|
261
|
-
kind: "rooted";
|
|
262
|
-
root: string;
|
|
263
|
-
} | {
|
|
264
|
-
kind: "none";
|
|
265
|
-
};
|
|
266
|
-
/**
|
|
267
|
-
* `clock` is required, unlike `seed`/`algorithm`/`attribution` — by the
|
|
268
|
-
* time a `RandomSource` is built, both the wall-clock default and the
|
|
269
|
-
* `"seeded"` policy (`Instance/Types.ts`'s `Config.clock`) have already
|
|
270
|
-
* been resolved to a concrete epoch-millisecond instant
|
|
271
|
-
* (`Instance/Core.ts`'s `resolveClock`), so `toRandomSource` has no
|
|
272
|
-
* default left to supply.
|
|
146
|
+
* `clock` is required, unlike `salt`/`algorithm` — by the time a `RandomSource`
|
|
147
|
+
* is built, both the wall-clock default and the `"derived"` policy
|
|
148
|
+
* (`Instance/Types.ts`'s `Config.clock`) have already been resolved to a
|
|
149
|
+
* concrete epoch-millisecond instant (`Instance/Core.ts`'s `resolveClock`), so
|
|
150
|
+
* `toRandomSource` has no default left to supply.
|
|
273
151
|
*/
|
|
274
152
|
export type Options = {
|
|
275
|
-
|
|
153
|
+
salt?: Salt | undefined;
|
|
276
154
|
algorithm?: Algorithm | undefined;
|
|
277
|
-
attribution?: Attribution | undefined;
|
|
278
155
|
clock: number;
|
|
279
156
|
};
|
|
280
157
|
/**
|
|
281
|
-
* `new Fabricator(schema, options)`'s own option shape — not
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* `
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* is.
|
|
315
|
-
*
|
|
316
|
-
* `path` is the base structural path `make` extends for descendants,
|
|
317
|
-
* so replaying a nested node's trace reproduces its subtree at the
|
|
318
|
-
* positions it originally occupied. `kind`, when a string, must match
|
|
319
|
-
* `schema`'s own `[Kind]` or `construct()` throws
|
|
320
|
-
* `TraceKindMismatchError`.
|
|
321
|
-
*
|
|
322
|
-
* No per-build algorithm override: it complicates root resolution for
|
|
323
|
-
* a capability nobody asked for, unlike `seed`, which is a real
|
|
324
|
-
* statement about attribution. Only `initialize({ seed, algorithm })`
|
|
325
|
-
* — instance-wide, via `Options` — sets the algorithm.
|
|
158
|
+
* `new Fabricator(schema, options)`'s own option shape — not `Options`.
|
|
159
|
+
* {@link Trace} is assignable to this (every slot optional here, required
|
|
160
|
+
* there), which is what makes `new Fabricator(schema, trace)` a legal replay.
|
|
161
|
+
*
|
|
162
|
+
* Every field here pins one {@link Trace} slot, `salt` included — it is not a
|
|
163
|
+
* special case, and it does not fork. A bare `salt` replaces the instance's own
|
|
164
|
+
* for this build; `salt: layer(...)` (via `layer()`, `Random/index.ts`)
|
|
165
|
+
* composes onto whichever base is in effect (`[...instance.salt, ...salt]`), so
|
|
166
|
+
* the construction still varies when the instance is re-salted, which the bare
|
|
167
|
+
* form does not. That is `fork`/`wrap`'s own `Overlay.salt` parity, one level
|
|
168
|
+
* down.
|
|
169
|
+
*
|
|
170
|
+
* Pinning a salt says nothing about anything else. The build takes the next
|
|
171
|
+
* ordinal on its source and inherits the instance's clock, exactly as an
|
|
172
|
+
* unsalted build does — so it shifts, and is shifted by, neighbouring
|
|
173
|
+
* constructions like any other. Two same-salt builds therefore diverge.
|
|
174
|
+
*
|
|
175
|
+
* `clock` / `ordinal` pin the construction-owned {@link Trace} slots
|
|
176
|
+
* {@link RandomSource.toRoot} would otherwise resolve. Definedness, not `in`: a
|
|
177
|
+
* given `ordinal` — a number, or `null` for "no ordinal" — is taken verbatim
|
|
178
|
+
* with no counter bump, which is all a replay needs; without it, the
|
|
179
|
+
* construction takes the source counter's next value. A salted construction is
|
|
180
|
+
* not, by default, asking for a different "now"; a replayed trace whose `clock`
|
|
181
|
+
* is present explicitly is.
|
|
182
|
+
*
|
|
183
|
+
* `path` is the base structural path `make` extends for descendants, so
|
|
184
|
+
* replaying a nested node's trace reproduces its subtree at the positions it
|
|
185
|
+
* originally occupied. `kind`, when a string, must match `schema`'s own
|
|
186
|
+
* `[Kind]` or `construct()` throws `TraceKindMismatchError`.
|
|
187
|
+
*
|
|
188
|
+
* No per-build algorithm override: unlike `salt`, the algorithm is not a trace
|
|
189
|
+
* slot. Only `initialize({ salt, algorithm })` — instance-wide, via `Options` —
|
|
190
|
+
* sets it.
|
|
326
191
|
*/
|
|
327
192
|
export type ConstructorOptions = {
|
|
328
|
-
|
|
193
|
+
salt?: Salt | Layered | undefined;
|
|
329
194
|
clock?: number | undefined;
|
|
330
|
-
root?: RootKind | undefined;
|
|
331
|
-
file?: string | undefined;
|
|
332
195
|
path?: ReadonlyArray<string> | undefined;
|
|
333
196
|
kind?: string | undefined;
|
|
334
|
-
ordinal?: number | undefined;
|
|
197
|
+
ordinal?: number | null | undefined;
|
|
335
198
|
};
|
|
336
199
|
/**
|
|
337
|
-
* An isolated source of randomness: everything a single `initialize()`
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* can never perturb each other.
|
|
200
|
+
* An isolated source of randomness: everything a single `initialize()` instance
|
|
201
|
+
* needs to derive private, reproducible seeds for the fabricators it builds.
|
|
202
|
+
* Each instance owns its own salt, builder, and per-construction counters —
|
|
203
|
+
* nothing here is shared module-level state, so independently initialized
|
|
204
|
+
* instances (e.g. parallel tests) can never perturb each other.
|
|
343
205
|
*/
|
|
344
206
|
export type RandomSource = {
|
|
345
207
|
/**
|
|
346
|
-
* Resolve one construction's root
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* hands to `toStreamFromTrace`. One
|
|
208
|
+
* Resolve one construction's root: this source's own `salt`/`clock` and the
|
|
209
|
+
* next construction ordinal, each overridable by {@link RootPins}. Called once
|
|
210
|
+
* per `new Fabricator(...)` (or per lazy expansion of a `T.recursive` schema,
|
|
211
|
+
* each of which resolves its own root on a private forked source), never per
|
|
212
|
+
* leaf: the returned {@link ConstructionTrace} is what every leaf beneath it
|
|
213
|
+
* completes into a full {@link Trace} and hands to `toStreamFromTrace`. One
|
|
352
214
|
* construction-ordinal bump serves the whole construction.
|
|
353
215
|
*/
|
|
354
|
-
toRoot(
|
|
216
|
+
toRoot(pins?: RootPins): ConstructionTrace;
|
|
355
217
|
/**
|
|
356
|
-
* The algorithm this source (and every fork of it) hashes with.
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
* counters).
|
|
218
|
+
* The algorithm this source (and every fork of it) hashes with. Stream
|
|
219
|
+
* derivation is _not_ a member: it depends on no per-source state, so it is
|
|
220
|
+
* the free function `toStreamFromTrace(algorithm, trace)`. `toRoot` is the
|
|
221
|
+
* only stateful member (the construction counter).
|
|
361
222
|
*/
|
|
362
223
|
readonly algorithm: Algorithm;
|
|
363
224
|
/**
|
|
364
|
-
* The
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* `Seed`-accepting surface it came from.
|
|
225
|
+
* The salt this instance currently derives every stream from, normalized to
|
|
226
|
+
* its parts — a single string becomes a one-element array. Always an array so
|
|
227
|
+
* a caller reading it back (e.g. `initialize({ salt: instance.salt })`)
|
|
228
|
+
* round-trips through the same `Salt`-accepting surface it came from.
|
|
369
229
|
*/
|
|
370
|
-
readonly
|
|
230
|
+
readonly salt: ReadonlyArray<string>;
|
|
371
231
|
/**
|
|
372
|
-
* Create a new, fully isolated `RandomSource` — its own private
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* same `initialize()` instance, no matter how many times or how deeply
|
|
381
|
-
* it expands.
|
|
232
|
+
* Create a new, fully isolated `RandomSource` — its own private construction
|
|
233
|
+
* counters, sharing only the algorithm — salted independently from this one.
|
|
234
|
+
* For a build whose randomness must stay entirely self-contained
|
|
235
|
+
* (`T.recursive`, whose expansion count is data-dependent, unlike every other
|
|
236
|
+
* kind's fixed, schema-determined dispatch count; and `coverage`'s
|
|
237
|
+
* permutation stream): forking means its internal draws can never perturb, or
|
|
238
|
+
* be perturbed by, anything else built from the same `initialize()` instance,
|
|
239
|
+
* no matter how many times or how deeply it expands.
|
|
382
240
|
*/
|
|
383
|
-
fork(
|
|
241
|
+
fork(salt: Salt): RandomSource;
|
|
384
242
|
};
|