@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.
Files changed (139) hide show
  1. package/README.md +18 -12
  2. package/dist/esm/Adapter/Core.js +3 -3
  3. package/dist/esm/Enumeration/Enumerate.js +16 -13
  4. package/dist/esm/Error/index.js +16 -8
  5. package/dist/esm/Fabricator/Constructor.js +12 -14
  6. package/dist/esm/Harnessing/Core.js +30 -0
  7. package/dist/esm/Harnessing/Salt.js +12 -0
  8. package/dist/esm/Harnessing/Types.js +1 -0
  9. package/dist/esm/Instance/Core.js +25 -41
  10. package/dist/esm/Instance/Stack/Async.js +10 -0
  11. package/dist/esm/Instance/Stack/Sync.js +16 -0
  12. package/dist/esm/Primitive/bigint/Registry.js +12 -12
  13. package/dist/esm/Primitive/boolean/Registry.js +2 -1
  14. package/dist/esm/Primitive/date/Registry.js +15 -14
  15. package/dist/esm/Primitive/null/Registry.js +2 -1
  16. package/dist/esm/Primitive/number/Registry.js +18 -17
  17. package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
  18. package/dist/esm/Primitive/symbol/Registry.js +2 -1
  19. package/dist/esm/Primitive/undefined/Registry.js +2 -1
  20. package/dist/esm/Random/index.js +24 -79
  21. package/dist/esm/Utility/Core.js +6 -1
  22. package/dist/esm/adapting.js +2 -0
  23. package/dist/esm/harnessing.js +1 -0
  24. package/dist/esm/index.js +5 -4
  25. package/dist/esm/internal.js +2 -2
  26. package/dist/types/Adapter/Core.d.ts +30 -33
  27. package/dist/types/Adapter/Types.d.ts +78 -88
  28. package/dist/types/Bound.d.ts +15 -15
  29. package/dist/types/Distribution/index.d.ts +54 -61
  30. package/dist/types/Enumeration/Enumerate.d.ts +24 -24
  31. package/dist/types/Enumeration/Plan.d.ts +22 -26
  32. package/dist/types/Enumeration/Types.d.ts +38 -43
  33. package/dist/types/Error/index.d.ts +103 -89
  34. package/dist/types/Fabricator/Constructor.d.ts +24 -26
  35. package/dist/types/Fabricator/Types.d.ts +73 -81
  36. package/dist/types/Harnessing/Core.d.ts +43 -0
  37. package/dist/types/Harnessing/Salt.d.ts +31 -0
  38. package/dist/types/Harnessing/Types.d.ts +79 -0
  39. package/dist/types/Instance/Core.d.ts +38 -74
  40. package/dist/types/Instance/Stack/Async.d.ts +14 -0
  41. package/dist/types/Instance/Stack/Sync.d.ts +14 -0
  42. package/dist/types/Instance/Types.d.ts +98 -102
  43. package/dist/types/Primitive/always/Schema.d.ts +8 -8
  44. package/dist/types/Primitive/always/Types.d.ts +7 -7
  45. package/dist/types/Primitive/array/Registry.d.ts +10 -8
  46. package/dist/types/Primitive/array/Schema.d.ts +4 -5
  47. package/dist/types/Primitive/array/Types.d.ts +6 -6
  48. package/dist/types/Primitive/bigint/Registry.d.ts +6 -6
  49. package/dist/types/Primitive/bigint/Schema.d.ts +8 -8
  50. package/dist/types/Primitive/bigint/Types.d.ts +5 -5
  51. package/dist/types/Primitive/boolean/Outcomes.d.ts +8 -8
  52. package/dist/types/Primitive/boolean/Registry.d.ts +2 -12
  53. package/dist/types/Primitive/boolean/Schema.d.ts +8 -8
  54. package/dist/types/Primitive/boolean/Types.d.ts +3 -3
  55. package/dist/types/Primitive/choice/Fabricator.d.ts +6 -7
  56. package/dist/types/Primitive/choice/Registry.d.ts +13 -13
  57. package/dist/types/Primitive/choice/Schema.d.ts +6 -6
  58. package/dist/types/Primitive/choice/Types.d.ts +11 -11
  59. package/dist/types/Primitive/date/Registry.d.ts +25 -48
  60. package/dist/types/Primitive/date/Schema.d.ts +9 -10
  61. package/dist/types/Primitive/date/Types.d.ts +4 -4
  62. package/dist/types/Primitive/enum/Registry.d.ts +13 -13
  63. package/dist/types/Primitive/enum/Schema.d.ts +5 -5
  64. package/dist/types/Primitive/enum/Types.d.ts +17 -19
  65. package/dist/types/Primitive/namespace.d.ts +10 -10
  66. package/dist/types/Primitive/null/Registry.d.ts +2 -2
  67. package/dist/types/Primitive/null/Schema.d.ts +2 -2
  68. package/dist/types/Primitive/null/Types.d.ts +3 -3
  69. package/dist/types/Primitive/nullable/Fabricator.d.ts +7 -7
  70. package/dist/types/Primitive/nullable/Schema.d.ts +13 -13
  71. package/dist/types/Primitive/nullable/Types.d.ts +9 -10
  72. package/dist/types/Primitive/nullish/Fabricator.d.ts +12 -13
  73. package/dist/types/Primitive/nullish/Schema.d.ts +8 -8
  74. package/dist/types/Primitive/nullish/Types.d.ts +12 -13
  75. package/dist/types/Primitive/number/Registry.d.ts +20 -38
  76. package/dist/types/Primitive/number/Schema.d.ts +11 -12
  77. package/dist/types/Primitive/number/Types.d.ts +13 -13
  78. package/dist/types/Primitive/number/defaults.d.ts +3 -3
  79. package/dist/types/Primitive/object/Fabricator.d.ts +15 -15
  80. package/dist/types/Primitive/object/Registry.d.ts +8 -8
  81. package/dist/types/Primitive/object/Schema.d.ts +10 -10
  82. package/dist/types/Primitive/object/Types.d.ts +20 -22
  83. package/dist/types/Primitive/object/compute/Fabricator.d.ts +4 -5
  84. package/dist/types/Primitive/object/compute/Schema.d.ts +4 -4
  85. package/dist/types/Primitive/object/compute/Types.d.ts +19 -20
  86. package/dist/types/Primitive/object/omittable/Fabricator.d.ts +10 -10
  87. package/dist/types/Primitive/object/omittable/Outcomes.d.ts +2 -2
  88. package/dist/types/Primitive/object/omittable/Schema.d.ts +11 -11
  89. package/dist/types/Primitive/object/omittable/Types.d.ts +14 -15
  90. package/dist/types/Primitive/object/optional/Fabricator.d.ts +16 -18
  91. package/dist/types/Primitive/object/optional/Outcomes.d.ts +2 -2
  92. package/dist/types/Primitive/object/optional/Schema.d.ts +4 -4
  93. package/dist/types/Primitive/object/optional/Types.d.ts +17 -17
  94. package/dist/types/Primitive/opaque/Registry.d.ts +3 -3
  95. package/dist/types/Primitive/opaque/Schema.d.ts +8 -9
  96. package/dist/types/Primitive/record/Registry.d.ts +8 -6
  97. package/dist/types/Primitive/record/Schema.d.ts +9 -11
  98. package/dist/types/Primitive/record/Types.d.ts +25 -25
  99. package/dist/types/Primitive/recursive/Fabricator.d.ts +20 -21
  100. package/dist/types/Primitive/recursive/Registry.d.ts +6 -7
  101. package/dist/types/Primitive/recursive/Schema.d.ts +8 -8
  102. package/dist/types/Primitive/recursive/Terminate.d.ts +13 -14
  103. package/dist/types/Primitive/recursive/Types.d.ts +31 -31
  104. package/dist/types/Primitive/recursive/self/Fabricator.d.ts +11 -12
  105. package/dist/types/Primitive/recursive/self/Schema.d.ts +10 -10
  106. package/dist/types/Primitive/recursive/self/Types.d.ts +7 -8
  107. package/dist/types/Primitive/string/Constants.d.ts +12 -13
  108. package/dist/types/Primitive/string/Fabricator.d.ts +2 -2
  109. package/dist/types/Primitive/string/Registry.d.ts +10 -16
  110. package/dist/types/Primitive/string/Schema.d.ts +5 -5
  111. package/dist/types/Primitive/string/Types.d.ts +17 -17
  112. package/dist/types/Primitive/symbol/Fabricator.d.ts +2 -2
  113. package/dist/types/Primitive/symbol/Registry.d.ts +4 -11
  114. package/dist/types/Primitive/symbol/Schema.d.ts +6 -6
  115. package/dist/types/Primitive/symbol/Types.d.ts +2 -2
  116. package/dist/types/Primitive/tuple/Fabricator.d.ts +7 -8
  117. package/dist/types/Primitive/tuple/Schema.d.ts +5 -5
  118. package/dist/types/Primitive/tuple/Types.d.ts +24 -25
  119. package/dist/types/Primitive/undefinable/Fabricator.d.ts +7 -7
  120. package/dist/types/Primitive/undefinable/Schema.d.ts +13 -13
  121. package/dist/types/Primitive/undefinable/Types.d.ts +10 -10
  122. package/dist/types/Primitive/undefined/Registry.d.ts +2 -2
  123. package/dist/types/Primitive/undefined/Schema.d.ts +4 -4
  124. package/dist/types/Primitive/undefined/Types.d.ts +3 -3
  125. package/dist/types/Random/Generator/sfc32.d.ts +4 -4
  126. package/dist/types/Random/Types.d.ts +151 -293
  127. package/dist/types/Random/index.d.ts +56 -81
  128. package/dist/types/Schema/Core.d.ts +19 -21
  129. package/dist/types/Schema/Registry.d.ts +3 -3
  130. package/dist/types/Schema/Types.d.ts +41 -48
  131. package/dist/types/Types.d.ts +39 -45
  132. package/dist/types/Utility/Core.d.ts +18 -9
  133. package/dist/types/adapting.d.ts +32 -0
  134. package/dist/types/harnessing.d.ts +30 -0
  135. package/dist/types/index.d.ts +95 -128
  136. package/dist/types/internal.d.ts +53 -40
  137. package/package.json +39 -7
  138. package/dist/esm/Random/CallSite.js +0 -56
  139. 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
- * like `Math.random()` does.
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 has been requested from this stream.
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
- * fixed tuple (`Random/index.ts`'s `encode`), this *is* the stream seed:
30
- * `toStream(algorithm, encode(trace)).seed === stream.seed` for every
31
- * traced leaf, so the derived seed is not a field of its own.
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
- * unconditional, even for a node that never draws (a bare `object`,
35
- * `always`, `object.compute`). Minting a stream from it
36
- * (`toStreamFromTrace`) is still paid only by nodes that draw.
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 *instance's* seed, normalized to its parts
41
- * ({@link RandomSource.seed}), not anything derived per leaf.
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
- * per-leaf slots: blast radius matches `seed` — both are instance-level
45
- * and perturb every leaf, unlike `file`/`path`/`kind`/`ordinal`, which
46
- * narrow to one leaf. Never `undefined` (unlike `file`/`ordinal`):
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
- * - `root` — how `file` and `ordinal` were resolved ({@link RootKind}).
50
- * Sits immediately before `file` because that is what it explains.
51
- * Distinguishes the three situations that all produce `file:
52
- * undefined` (`{ kind: "none" }` attributed, an unattributed seeded
53
- * construction, a counted recursive expansion), so a captured trace
54
- * can be replayed faithfully via `new Fabricator(schema, trace)`.
55
- * - `file` — the file this node's *construction* (not the node) was
56
- * attributed to, relative to the instance's {@link Attribution} root
57
- * (absolute only if outside that root). `undefined` under
58
- * `"counted"` / `"unattributed"`, or `"attributed"` with
59
- * `{ kind: "none" }` — {@link RootKind} says which.
60
- * - `path` — structural position within the construction: field name,
61
- * tuple slot, choice option, one segment per nesting level
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 seed: ReadonlyArray<string>;
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 | undefined;
67
+ readonly ordinal: number | null;
79
68
  };
80
69
  /**
81
- * Caller-supplied overrides for the construction-owned {@link Trace}
82
- * slots {@link RandomSource.toRoot} resolves. `seed` is not among
83
- * them: it is the {@link RandomSource}'s own identity (forked via
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
- * Definedness, not `in`: `root` is present exactly when a trace is
89
- * being replayed and is never `undefined` on a real {@link Trace}.
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
- root?: RootKind | undefined;
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 {@link RootKind}
99
- * resolution fixes, before a leaf supplies its own `path`/`kind`.
100
- * `RandomSource.toRoot` resolves this once per construction; callers
101
- * spread it into a full {@link Trace} per leaf and hand that to
102
- * `toStreamFromTrace`. One stack walk, one construction-ordinal
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
- * One object, not a positional list: this is the only channel a producer
109
- * has, and a future addition must not change every kind's `.as(...)`
110
- * arity. Curated, not the whole instance `Config` — a producer has no
111
- * business reading `seed` or `attribution`, and `random` already carries
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
- * `wrap` frame's `Config.clock` if one is active, else the instance's
120
- * (`Instance/Types.ts`'s `Config.clock`). Defaults to an instant derived
121
- * from the instance's seed, not `Date.now()`, so a producer that reads
122
- * it replays like one that only reads `random`. A number, not a `Date`:
123
- * the instant is fixed once resolved, and a `Date` handed to every
124
- * producer would be a shared mutable — the same footgun `T.always([])`
125
- * sharing one array reference already warns against.
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 seed like every
132
- * other primitive's draws. A zero-argument function (`() => $T`) is
133
- * still assignable, so every existing `.as(() => ...)` call compiles.
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
- * How one *construction* (a single `new Fabricator(...)` call) is rooted,
138
- * before any of its leaves are dispatched — resolved once, not per leaf,
139
- * at `Constructor.ts`'s `construct()`. Every leaf then derives its stream
140
- * from this root plus its own structural path, so inserting or reordering
141
- * a sibling field can never shift another field onto a different stream.
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 Seed = string | ReadonlyArray<string>;
134
+ export type Salt = string | ReadonlyArray<string>;
199
135
  /**
200
- * A {@link Seed} tagged as composing onto whatever base is in effect,
201
- * rather than replacing it — what `layer(seed)` (`Random/index.ts`)
202
- * produces. Tagged with `[Layer]` exactly as `replace()`
203
- * (`Utility/Core.ts`) tags a merge operand with `[Replace]`, so a
204
- * caller never names the symbol and no ordinary `Seed` — string or
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]: Seed;
143
+ readonly [Layer]: Salt;
209
144
  };
210
145
  /**
211
- * How an instance normalizes a construction's resolved file before it
212
- * becomes part of that construction's root — set once via
213
- * `initialize({ attribution })`.
214
- *
215
- * `"rooted"` expresses every file relative to `root`, so a checkout at
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
- seed?: Seed | undefined;
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
- * `Options`. {@link Trace} is assignable to this (every slot optional
283
- * here, required there, and `file`/`ordinal` are `T | undefined` on
284
- * both so `{ ...trace }` typechecks under
285
- * `exactOptionalPropertyTypes`), which is what makes
286
- * `new Fabricator(schema, trace)` a legal replay.
287
- *
288
- * A bare `seed` is a statement about *attribution*: it forks an
289
- * isolated `RandomSource` from exactly that value, sidestepping both
290
- * the default call-site logic and the instance's own seed. The same
291
- * seed reproduces the same result regardless of which file it's
292
- * constructed from, which instance built it, or how that instance was
293
- * itself seeded — useful for a fixture that should never change no
294
- * matter how the surrounding run is reseeded.
295
- *
296
- * `seed: layer(...)` (via `layer()`, `Random/index.ts`) is the same
297
- * fork, but composed onto the instance's own seed
298
- * (`[...instance.seed, ...seed]`) instead of replacing it — the
299
- * construction still varies when the instance is reseeded, which the
300
- * bare form does not (see {@link RootKind}'s `"counted"` paragraph).
301
- * This is `fork`/`wrap`'s own `Overlay.seed` mechanism one level down:
302
- * the instance itself is the base, so no separate instance is needed
303
- * just to pin an identity that should still track the instance's seed.
304
- *
305
- * `clock` / `root` / `file` / `ordinal` pin the construction-owned
306
- * {@link Trace} slots {@link RandomSource.toRoot} would otherwise
307
- * resolve. Definedness, not `in`: `root` given means this is a replay
308
- * (`file` and `ordinal` taken verbatim, `undefined` included, no
309
- * stack walk and no counter bump). `root` absent but `file` given pins
310
- * that file and draws `ordinal` from *that file's* counter — the
311
- * wrapping-integration case. Neither given resolves as an ordinary
312
- * construction. A seeded construction is not, by default, asking for a
313
- * different "now"; a replayed trace whose `clock` is present explicitly
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
- seed?: Seed | Layered | undefined;
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
- * instance needs to derive private, reproducible seeds for the
339
- * fabricators it builds. Each instance owns its own seed, builder, and
340
- * per-construction counters — nothing here is shared module-level
341
- * state, so independently initialized instances (e.g. parallel tests)
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 — see {@link RootKind}. Called once
347
- * per `new Fabricator(...)` (or per lazy expansion of a `T.recursive`
348
- * schema, each of which resolves its own root on a private forked
349
- * source), never per leaf: the returned {@link ConstructionTrace} is
350
- * what every leaf beneath it completes into a full {@link Trace} and
351
- * hands to `toStreamFromTrace`. One stack walk and
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(kind: RootKind, pins?: RootPins): ConstructionTrace;
216
+ toRoot(pins?: RootPins): ConstructionTrace;
355
217
  /**
356
- * The algorithm this source (and every fork of it) hashes with.
357
- * Stream derivation is *not* a member: it depends on no per-source
358
- * state, so it is the free function `toStreamFromTrace(algorithm,
359
- * trace)`. `toRoot` is the only stateful member (per-file ordinal
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 seed this instance currently derives every stream from,
365
- * normalized to its parts — a single string becomes a one-element
366
- * array. Always an array so a caller reading it back (e.g.
367
- * `initialize({ seed: instance.seed })`) round-trips through the same
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 seed: ReadonlyArray<string>;
230
+ readonly salt: ReadonlyArray<string>;
371
231
  /**
372
- * Create a new, fully isolated `RandomSource` — its own private
373
- * per-file construction counters, sharing only the algorithm — seeded
374
- * independently from this one. For a build whose randomness must stay
375
- * entirely self-contained (an explicitly seeded
376
- * `new Fabricator(schema, { seed })`, or `T.recursive`, whose expansion
377
- * count is data-dependent, unlike every other kind's fixed,
378
- * schema-determined dispatch count): forking means its internal draws
379
- * can never perturb, or be perturbed by, anything else built from the
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(seed: Seed): RandomSource;
241
+ fork(salt: Salt): RandomSource;
384
242
  };