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