@ghostry/fabricator 0.0.3 → 0.0.5

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 (36) hide show
  1. package/dist/esm/Enumeration/Enumerate.js +7 -6
  2. package/dist/esm/Error/index.js +1 -1
  3. package/dist/esm/Fabricator/Constructor.js +11 -10
  4. package/dist/esm/Harnessing/Core.js +7 -16
  5. package/dist/esm/Instance/Core.js +51 -17
  6. package/dist/esm/Instance/Stack/Async.js +10 -2
  7. package/dist/esm/Instance/Stack/Sync.js +2 -1
  8. package/dist/esm/Instance/Stack/Visible.js +13 -0
  9. package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
  10. package/dist/esm/Random/index.js +2 -2
  11. package/dist/types/Enumeration/Enumerate.d.ts +15 -15
  12. package/dist/types/Enumeration/Types.d.ts +2 -2
  13. package/dist/types/Error/index.d.ts +8 -7
  14. package/dist/types/Fabricator/Constructor.d.ts +10 -7
  15. package/dist/types/Fabricator/Types.d.ts +15 -14
  16. package/dist/types/Harnessing/Core.d.ts +25 -25
  17. package/dist/types/Harnessing/Types.d.ts +56 -13
  18. package/dist/types/Instance/Core.d.ts +17 -6
  19. package/dist/types/Instance/Stack/Async.d.ts +7 -0
  20. package/dist/types/Instance/Stack/Sync.d.ts +5 -3
  21. package/dist/types/Instance/Stack/Visible.d.ts +22 -0
  22. package/dist/types/Instance/Types.d.ts +169 -19
  23. package/dist/types/Primitive/choice/Fabricator.d.ts +1 -1
  24. package/dist/types/Primitive/nullable/Fabricator.d.ts +1 -1
  25. package/dist/types/Primitive/nullish/Fabricator.d.ts +2 -2
  26. package/dist/types/Primitive/object/optional/Fabricator.d.ts +2 -2
  27. package/dist/types/Primitive/recursive/Fabricator.d.ts +7 -8
  28. package/dist/types/Primitive/recursive/Types.d.ts +1 -1
  29. package/dist/types/Primitive/recursive/self/Types.d.ts +1 -1
  30. package/dist/types/Primitive/undefinable/Fabricator.d.ts +1 -1
  31. package/dist/types/Random/Types.d.ts +23 -21
  32. package/dist/types/Random/index.d.ts +3 -3
  33. package/dist/types/harnessing.d.ts +18 -6
  34. package/dist/types/index.d.ts +11 -2
  35. package/dist/types/internal.d.ts +1 -1
  36. package/package.json +4 -1
@@ -38,34 +38,77 @@ export type Identity = {
38
38
  /**
39
39
  * One context key's value, as a function of the test's `Identity` rather than a
40
40
  * fixed value.
41
+ *
42
+ * `established` is whatever this integration's own wrapper handed forward. For
43
+ * this integration that is the scoped `Instance` `wrap` opened, which is the
44
+ * whole of what `provides.fabricator` returns — so the value travels from the
45
+ * wrapper to the provider directly, with no mutable slot written on the way in
46
+ * and read on the way out.
47
+ */
48
+ export type Provider<$Value, $Established = void> = (args: ProviderArgs<$Established>) => $Value;
49
+ /**
50
+ * What `@ghostry/harness` hands {@link Integration.frame}: one object, never
51
+ * positional arguments, so a field added to the contract later is a key an
52
+ * existing hook ignores rather than a parameter it has to thread past.
41
53
  */
42
- export type Provider<$Value> = (identity: Identity) => $Value;
54
+ export type FrameArgs = {
55
+ readonly identity: Identity;
56
+ };
57
+ /**
58
+ * What it hands each provider: everything {@link FrameArgs} carries, plus what
59
+ * this integration's own wrapper established — for this integration, the scoped
60
+ * `Instance` that `wrap` opened.
61
+ */
62
+ export type ProviderArgs<$Established = void> = FrameArgs & {
63
+ readonly established: $Established;
64
+ };
43
65
  /**
44
66
  * The keys an integration contributes, and how each is produced. Homomorphic
45
67
  * over `$Context`, so the context an integration contributes is read back out
46
68
  * of this object's shape with no separate key declaration to keep in sync.
47
69
  */
48
- export type Provides<$Context extends object> = {
49
- readonly [$Key in keyof $Context]: Provider<$Context[$Key]>;
70
+ export type Provides<$Context extends object, $Established = void> = {
71
+ readonly [$Key in keyof $Context]: Provider<$Context[$Key], $Established>;
50
72
  };
73
+ /**
74
+ * How an integration runs the body when the body must run _inside_ something.
75
+ * Here that is fabricator's own `wrap`, whose block parameter is already this
76
+ * shape — the scoped `Instance` it opens is what reaches `body`, and therefore
77
+ * what reaches the providers.
78
+ *
79
+ * Generic in its return and must hand the body's value back unchanged: that is
80
+ * what keeps a synchronous test synchronous and what lets frames nest.
81
+ */
82
+ export type Wrapper<$Established = void> = <$Return>(body: (established: $Established) => $Return) => $Return;
83
+ /**
84
+ * What {@link Integration.frame} returns: a generator with **one** suspension
85
+ * point. Everything before the `yield` is setup, the body runs at the `yield`,
86
+ * and everything after it is teardown, resumed when the body _settles_.
87
+ *
88
+ * `@ghostry/harness` accepts an `AsyncGenerator` here too. This declares only
89
+ * the synchronous half, because that is the half this integration uses and the
90
+ * narrower type still satisfies the wider one — the same reason `frame` is
91
+ * required below though it is optional there. Opening a fabricator scope is
92
+ * synchronous, and declaring the async arm would invite an integration that
93
+ * promotes every test in the suite to a promise for no reason.
94
+ */
95
+ export type Frame<$Established = void> = Generator<Wrapper<$Established> | void, void, unknown>;
51
96
  /**
52
97
  * What `integration(instance)` is, as `@ghostry/harness`'s `initialize` sees
53
98
  * it. The subset of that package's contract this integration actually uses, not
54
- * a copy of all of it: `@ghostry/harness` also accepts an optional `setup`,
55
- * which fabricator has no teardown to put in, and an object lacking an optional
56
- * member still satisfies the contract structurally. `around` is required here,
57
- * though optional there, because this integration always declares it.
99
+ * a copy of all of it: an object lacking an optional member still satisfies the
100
+ * contract structurally, so `frame` is required here though optional there,
101
+ * because this integration always declares it.
58
102
  *
59
103
  * `provides` is the _only_ source of context keys; `initialize` rejects a
60
104
  * collision across integrations by reading `Object.keys(provides)`. Each
61
- * provider runs inside its integration's `around`. `around` is generic in its
62
- * return and must return the body's value unchanged: that is what makes async
63
- * work and what lets frames nest.
105
+ * provider runs _inside_ that frame and receives the same `$Established` the
106
+ * wrapper handed to the body.
64
107
  */
65
- export type Integration<$Context extends object> = {
108
+ export type Integration<$Context extends object, $Established = void> = {
66
109
  readonly name: string;
67
- readonly provides: Provides<$Context>;
68
- around<$Return>(identity: Identity, body: () => $Return): $Return;
110
+ readonly provides: Provides<$Context, $Established>;
111
+ frame(args: FrameArgs): Frame<$Established>;
69
112
  };
70
113
  /**
71
114
  * The slice of the test context `integration(instance)` contributes — one key,
@@ -1,6 +1,6 @@
1
1
  import type { RandomSource } from "../Random/Types";
2
2
  import type { PlainObject } from "../Utility/Types";
3
- import type { Config, Instance, Overlay, Stack } from "./Types";
3
+ import type { Ancestry, Config, Instance, Overlay, Stack } from "./Types";
4
4
  /**
5
5
  * `combinatorial`'s default limit — `2**10`, so it admits ten independent
6
6
  * binary axes before requiring the caller to raise it explicitly. Each
@@ -51,12 +51,23 @@ export declare function overlay<$Registry extends PlainObject>(base: Partial<Con
51
51
  * than each independently re-deriving one from the same config (and so silently
52
52
  * diverging/duplicating construction ordinals).
53
53
  *
54
- * `stack` is threaded straight through to `Constructor`/`enumerables` — this
55
- * function never reads or writes it itself, only passes it along so every built
56
- * `Fabricator`/`combinatorial`/`coverage` can consult whichever frame is active
57
- * _at the moment each is called_, not at this moment.
54
+ * `stack` and `ancestry` are threaded straight through to
55
+ * `Constructor`/`enumerables` — this function never reads the stack itself,
56
+ * only passes both along so every built `Fabricator`/`combinatorial`/`coverage`
57
+ * can consult whichever frame is visible to _this_ instance _at the moment each
58
+ * is called_, not at this moment.
59
+ *
60
+ * `parent` describes the instance this one is derived from, and its absence is
61
+ * the single marker of a root: `initialize` passes none, every `fork`/`wrap`
62
+ * passes the receiver's.
63
+ *
64
+ * A fresh token is minted either way, so no two instances share an identity,
65
+ * and the `ancestry` built here is _this_ instance's: the parent's plus one.
58
66
  */
59
- export declare function instantiate<$Registry extends PlainObject>(config: Config<$Registry>, stack: Stack): {
67
+ export declare function instantiate<$Registry extends PlainObject>(config: Config<$Registry>, stack: Stack, parent?: {
68
+ ancestry: Ancestry;
69
+ root: Instance<PlainObject>;
70
+ }): {
60
71
  instance: Instance<$Registry>;
61
72
  source: RandomSource;
62
73
  };
@@ -7,6 +7,13 @@ import type { Stack } from "../Types";
7
7
  * package importable on a runtime with no `node:async_hooks` while every
8
8
  * runtime that has one gets async-safe `wrap` with nothing to configure.
9
9
  *
10
+ * The store holds the whole open chain, not one `Frame`: `run` _replaces_ the
11
+ * store for the duration of `block`, so `enter` rebuilds the chain with the new
12
+ * frame appended. A fresh array per `enter` is also what isolates concurrent
13
+ * `wrap`s — each async context keeps the chain it entered with, and an inner
14
+ * `enter` cannot mutate an outer one's view. Chains are at nesting depth, so
15
+ * copying one costs nothing worth avoiding.
16
+ *
10
17
  * `AsyncLocalStorage.run` returns whatever `block` returns, so this satisfies
11
18
  * `enter`'s sync-preserving `<$Return>` signature exactly as the sync carrier
12
19
  * does — a synchronous `wrap` is unaffected by which carrier is in play.
@@ -1,13 +1,15 @@
1
1
  import type { Stack } from "../Types";
2
2
  /**
3
3
  * The synchronous carrier: a private `Frame[]`, pushed on `enter` and popped in
4
- * a `finally` — correct even around a `throw` from `block`.
4
+ * a `finally` — correct even around a `throw` from `block`. Already in
5
+ * outermost-first order, which is the order `visible` reports, so it hands the
6
+ * array straight to `toVisible` and does no filtering of its own.
5
7
  *
6
8
  * Selected by the `#stack` `default` condition (`package.json`), i.e. on any
7
9
  * runtime without `node:async_hooks` — in practice a browser bundle. Its frame
8
10
  * cannot survive an `await`: `enter` returns `block()` without awaiting, so an
9
- * async block's frame unwinds at the block's first suspension point, and a
10
- * shared LIFO could not represent two overlapping scopes even if it did await.
11
+ * async block's frame unwinds at the block's first suspension point, and one
12
+ * shared array could not represent two overlapping scopes even if it did await.
11
13
  * Both are why `asynchronous` is `false` and `wrap` refuses an async block here
12
14
  * rather than resolving it against the base instance with no signal.
13
15
  */
@@ -0,0 +1,22 @@
1
+ import type { Ancestry, Frame, Stack } from "../Types";
2
+ /**
3
+ * The frames in `frames` that `ancestry` may resolve against, outermost first —
4
+ * the single definition of {@link Stack.visible}'s rule, which both carriers
5
+ * delegate to so neither can drift from the other. A carrier's own job is
6
+ * reduced to holding the chain in whatever way its runtime allows.
7
+ *
8
+ * Order is preserved rather than reduced to the innermost match, because the
9
+ * count is `context.depth` and the innermost is just the last element.
10
+ * Skipping, rather than stopping at, the first invisible frame is the outward
11
+ * walk: with a parent's `wrap` open and a child's nested inside it, that
12
+ * child's sibling must pass over the inner frame and still resolve against the
13
+ * outer one.
14
+ */
15
+ export declare function toVisible(frames: ReadonlyArray<Frame>, ancestry: Ancestry): ReadonlyArray<Frame>;
16
+ /**
17
+ * The one frame a read resolves against: the innermost frame visible to
18
+ * `ancestry`, or `undefined` outside any. Every consumer — `resolveScope`,
19
+ * `effectiveSource`, the `context` getters — goes through here rather than
20
+ * indexing `visible()` itself, so "innermost visible" has one definition.
21
+ */
22
+ export declare function toInnermostFrame(stack: Stack, ancestry: Ancestry): Frame | undefined;
@@ -47,27 +47,77 @@ export type Overlay<$Registry extends PlainObject> = Partial<Omit<Config<$Regist
47
47
  readonly salt?: Salt | Layered;
48
48
  readonly clock?: Date | "derived" | undefined;
49
49
  };
50
+ declare const Brand: unique symbol;
51
+ /**
52
+ * One instance's identity — minted fresh by every `instantiate`
53
+ * (`Instance/Core.ts`) and never equal to any other. Branded, and the brand key
54
+ * is module-private, so a token cannot be forged from outside this package: the
55
+ * only way to hold one is to have been handed an `Instance`.
56
+ */
57
+ export type Token = symbol & {
58
+ readonly [Brand]: "Instance";
59
+ };
60
+ /**
61
+ * Where an instance sits in its lineage: every token from the root down to and
62
+ * including its own, so `ancestry[0]` is the lineage identity (`a.ancestry[0]
63
+ * === b.ancestry[0]` answers "same root?") and the last element is the instance
64
+ * itself. Non-empty by construction — a root `initialize()` mints one token
65
+ * before there is anything to inherit.
66
+ *
67
+ * Derived from the _receiver_: `fork` and `wrap` alike append to the ancestry
68
+ * of the instance they were called on, exactly as they both lay their overlay
69
+ * over that instance's `config`. One rule, so an instance's position and its
70
+ * configuration always agree about who its parent is.
71
+ *
72
+ * Two chains describe instances on the same ancestral line when either is a
73
+ * prefix of the other, which is what {@link Stack.visible} tests. That relation
74
+ * decides whose calls resolve against whose frames: a parent's calls resolve
75
+ * against a child's frame and a child's against a parent's, while two siblings
76
+ * resolve against neither's. Note this never crosses lineages, and not for want
77
+ * of identity — two roots hold two separate carriers, so a `wrap` on one pushes
78
+ * where the other's reads never look.
79
+ */
80
+ export type Ancestry = readonly [Token, ...ReadonlyArray<Token>];
50
81
  /**
51
82
  * One active `wrap` — its resolved config plus the single `RandomSource` every
52
83
  * build reached inside that `wrap` shares, whether reached implicitly (any
53
- * instance in the lineage consulting the active frame) or explicitly
84
+ * instance on the origin's ancestral line consulting the frame) or explicitly
54
85
  * (`scope.Fabricator`, the `Instance` passed to the block). Storing the scope's
55
86
  * own already-built `source` here, rather than each consumer re-deriving one
56
87
  * from `config`, keeps the two routes resolving against the _same_ source —
57
88
  * sharing one set of construction-ordinal counters — instead of each silently
58
89
  * starting its own.
90
+ *
91
+ * `instance` is that same scope, kept so `context.scope()` can hand back the
92
+ * configuration in effect as a usable `Instance` rather than only as its
93
+ * separate `salt`/`algorithm`/`clock` fields.
94
+ *
95
+ * `ancestry` is the **origin's** — the instance `wrap` was called on — not the
96
+ * scope's. The scope is a fresh child of the origin, so keying on it would make
97
+ * every `fork` taken off that origin a _sibling_ of the scope, and calls on
98
+ * those forks would stop resolving against the frame. Keying on the origin
99
+ * keeps everything on the origin's own line resolving against it, which is the
100
+ * whole point of entering one.
59
101
  */
60
102
  export type Frame = {
61
103
  readonly config: Config<PlainObject>;
62
104
  readonly source: RandomSource;
105
+ readonly instance: Instance<PlainObject>;
106
+ readonly ancestry: Ancestry;
63
107
  };
64
108
  /**
65
- * The per-lineage ambient stack. Created once at a root `initialize()` and
66
- * threaded — never re-created — through every `fork`/`wrap` descended from it
67
- * (see `instantiate`, `Instance/Core.ts`). Two unrelated `initialize()` calls
68
- * stay fully isolated; one lineage's `wrap` reaches every instance in that
69
- * lineage its `Fabricator`, `combinatorial`, and `coverage` alike
70
- * regardless of where in the lineage that instance was itself created.
109
+ * The ambient frame carrier. Created once at a root `initialize()` and threaded
110
+ * — never re-created — through every `fork`/`wrap` descended from it (see
111
+ * `instantiate`, `Instance/Core.ts`).
112
+ *
113
+ * A carrier holds a chain of open frames and nothing else; which of them any
114
+ * given reader may see is {@link Ancestry}'s business, resolved by
115
+ * {@link visible}. Two unrelated `initialize()` calls hold separate carriers
116
+ * and so stay fully isolated — and because a reader is now gated on ancestry
117
+ * rather than on carrier identity, handing the _same_ carrier to two
118
+ * `initialize()` calls does not join them either: their roots mint unrelated
119
+ * tokens, so neither one's reads ever resolve the other's frames. `initialize({
120
+ * stack })` is therefore purely a choice of carrier.
71
121
  */
72
122
  export type Stack = {
73
123
  /**
@@ -80,24 +130,71 @@ export type Stack = {
80
130
  * instance. See `Instance/Stack/Sync.ts` and `Instance/Stack/Async.ts`.
81
131
  */
82
132
  readonly asynchronous: boolean;
83
- current(): Frame | undefined;
84
133
  /**
85
- * Push `frame`, run `block`, pop in a `finally`, so a frame unwinds
86
- * correctly even if `block` throws.
134
+ * Every open frame `ancestry` may resolve against, outermost first: those
135
+ * whose own `ancestry` is a prefix of this one or has this one as a prefix.
136
+ * The innermost visible frame — what a build or a `context` read actually
137
+ * resolves against — is the last element, and the count is `context.depth`.
138
+ *
139
+ * Filtering, rather than simply reporting the innermost frame, is what makes
140
+ * the outward walk possible: with a parent's `wrap` open and a child's nested
141
+ * inside it, the child's _sibling_ must skip the inner frame and still find
142
+ * the outer one. Callers never filter themselves — `toVisible`
143
+ * (`Instance/Stack/Visible.ts`) is the single definition both carriers
144
+ * delegate to, so the rule cannot drift between them.
145
+ */
146
+ visible(ancestry: Ancestry): ReadonlyArray<Frame>;
147
+ /**
148
+ * Append `frame`, run `block`, remove it in a `finally`, so a frame unwinds
149
+ * correctly even if `block` throws. Appends rather than replaces: the chain
150
+ * has to stay intact for {@link visible} to walk outward past a frame this
151
+ * reader cannot see.
87
152
  */
88
153
  enter<$Return>(frame: Frame, block: () => $Return): $Return;
89
154
  };
90
155
  /**
91
- * The configuration in effect _right now_ — the innermost active `wrap` frame,
92
- * or this instance's own when there is none. Distinct from
93
- * `ConstructionContext` (`Fabricator/Types.ts`), which is one construction's
94
- * internal dispatch plumbing; this is the caller-facing "what configuration is
95
- * in effect."
156
+ * What is in effect _right now_ — the innermost `wrap` frame this instance's
157
+ * calls can resolve against, or the instance itself when there is none.
158
+ *
159
+ * The four value properties are getters, so they are a live view only while
160
+ * this stays an object: destructuring one, or spreading the object, calls that
161
+ * getter once and freezes the result.
162
+ *
163
+ * {@link Context.scope} is deliberately a **function** rather than another
164
+ * getter: it is the one member a caller _acts through_ rather than reads, so
165
+ * freezing it would yield correct-looking code deriving from the wrong base. As
166
+ * a function it survives destructuring — `const { scope } = instance.context`,
167
+ * then `scope()`, still resolves live. Distinct from `ConstructionContext`
168
+ * (`Fabricator/Types.ts`), which is one construction's internal dispatch
169
+ * plumbing; this is the caller-facing "what is in effect."
96
170
  */
97
171
  export type Context = {
98
172
  readonly salt: ReadonlyArray<string>;
99
173
  readonly algorithm: Algorithm;
100
174
  readonly clock: number;
175
+ /**
176
+ * The configuration in effect as an `Instance` — the visible frame's own
177
+ * scope, or this instance outside any. This is how to compose deliberately
178
+ * against whatever is active: `context.scope().wrap({ salt: layer("x") },
179
+ * ...)` lays over the frame in effect, where a plain `wrap` lays over the
180
+ * instance it was called on. Unlike rebuilding an overlay out of `salt` by
181
+ * hand, it carries `types`, `limits`, `algorithm` and `clock` across too.
182
+ *
183
+ * A function, not a getter, so capturing it captures the _lookup_ rather than
184
+ * one answer (see this type's own note above). What it returns is an ordinary
185
+ * `Instance`, fixed like any other — so holding the **result** across a frame
186
+ * change is a caller stating they wanted that one, while holding `scope`
187
+ * itself stays live.
188
+ */
189
+ scope(): Instance<PlainObject>;
190
+ /**
191
+ * How many frames are currently visible to this instance — 0 outside any.
192
+ * Genuine dynamic nesting depth, counted off the carrier rather than inferred
193
+ * from {@link Ancestry}: a frame a sibling cannot see is not counted for that
194
+ * sibling, and entering two `wrap`s on one instance reads as 2 even though
195
+ * neither deepened anyone's ancestry.
196
+ */
197
+ readonly depth: number;
101
198
  };
102
199
  /**
103
200
  * A single initialized library instance: the registry it was given, and a
@@ -109,6 +206,49 @@ export type Context = {
109
206
  export interface Instance<$Registry extends PlainObject> extends Pick<RandomSource, "salt"> {
110
207
  /** The registry of type definers this instance was initialized with. */
111
208
  readonly T: $Registry;
209
+ /**
210
+ * This instance's position in its lineage — see {@link Ancestry}. Exposed so a
211
+ * caller can reason about which frames a given instance's calls resolve
212
+ * against; the tokens themselves are opaque and comparable only by identity.
213
+ * For the ordinary "same lineage?" question, compare {@link root} instead.
214
+ */
215
+ readonly ancestry: Ancestry;
216
+ /**
217
+ * The instance at the head of this lineage — the one `initialize()` returned.
218
+ * A root's own `root` is itself, so this is never `undefined` and no caller
219
+ * handles absence.
220
+ *
221
+ * Its job is identity: `a.root === b.root` answers "same lineage?", which is
222
+ * what `fork`/`wrap` descent preserves and what two separate `initialize()`
223
+ * calls never share — even when handed the same `stack`.
224
+ *
225
+ * It is **not** a way to reach "the ambient instance": every instance in a
226
+ * lineage resolves against the frames on its own line, so there is nothing to
227
+ * reach for. Nor is it a configuration to build against in preference to this
228
+ * one — `root`'s config is the lineage's starting point, not whatever is
229
+ * currently in effect. For that, see {@link Context.scope}.
230
+ *
231
+ * Typed at `PlainObject`, which is a deliberate shortcut rather than a
232
+ * necessity — unlike {@link Context.scope}, whose registry depends on which
233
+ * instance entered the innermost visible frame and so cannot be known
234
+ * statically at all.
235
+ *
236
+ * A root's registry is fixed at `initialize` and nothing later disturbs it: a
237
+ * `fork({ types })` mints a _new_ instance with a different registry and
238
+ * leaves the root exactly as it was. What is lost is the descendant's ability
239
+ * to name it — after such a fork this instance's `$Registry` is the fork's,
240
+ * so the root's is no longer recoverable from it. Typing this
241
+ * `Instance<$Registry>` would therefore be wrong if someone overrode
242
+ * `types`.
243
+ *
244
+ * Recovering it would mean threading a second parameter (`Instance<$Registry,
245
+ * $Root>`) through `fork` and `wrap`, which is a poor trade for an accessor
246
+ * whose job is identity: if you mean to _build_, you want the registry of the
247
+ * instance you are holding, not the one the lineage started from. So `root.T`
248
+ * is untyped, while `root.Fabricator` is unaffected since it carries no
249
+ * registry parameter.
250
+ */
251
+ readonly root: Instance<PlainObject>;
112
252
  /**
113
253
  * Turn a Schema built from `T` into a live Fabricator, deriving fresh
114
254
  * randomness from this instance's own salt for whichever leaves actually need
@@ -147,10 +287,19 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
147
287
  * — a build reached after an `await` inside `block` sees this instance's own
148
288
  * configuration again, not the wrap's.
149
289
  *
150
- * A nested `wrap` lays over whichever frame is currently active, not over the
151
- * instance it was called on so `overlay.salt: layer(...)` accumulates with
152
- * nesting depth while a bare `salt` still replaces outright, discarding every
153
- * enclosing layer.
290
+ * The overlay lays over _this instance's_ config, exactly as `fork`'s does,
291
+ * whether or not a frame is already open. So a nested `wrap` accumulates when
292
+ * it is called on the enclosing scope `wrap(a, (scope) => scope.wrap(b,
293
+ * ...))` — and restates from this instance when it is called on a receiver
294
+ * bound outside, as a destructured `wrap` is. To compose onto whatever is
295
+ * active regardless of receiver, go through `context.scope().wrap(...)`.
296
+ *
297
+ * While the block runs, calls made on this instance's ancestral line resolve
298
+ * against the scope — its forks, their forks, and its own ancestors up to the
299
+ * root. Calls on a _sibling_ do not: a frame entered on one `fork` is not one
300
+ * that another `fork` of the same parent can resolve against, which keeps two
301
+ * unrelated derivations from drawing each other's data. See
302
+ * {@link Ancestry}.
154
303
  */
155
304
  wrap<$Return, const $WrapRegistry extends PlainObject = $Registry>(overlay: Overlay<$WrapRegistry>, block: (scope: Instance<$WrapRegistry>) => $Return): $Return;
156
305
  /**
@@ -161,3 +310,4 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
161
310
  */
162
311
  readonly context: Context;
163
312
  }
313
+ export {};
@@ -26,6 +26,6 @@ export type Fabricator<$Schema extends {
26
26
  * shared across every element. The weighted pick happens on this field's
27
27
  * private stream; only the chosen option's `fabricate()` is called, so an
28
28
  * unpicked option never advances its stream (safe for the same reason skipping
29
- * `object.omittable`'s inner draw is — see CLAUDE.md's "Randomness").
29
+ * `object.omittable`'s inner draw is — see AGENTS.md's "Randomness").
30
30
  */
31
31
  export declare function Fabricator<$Items extends Items>(context: FabricatorContext<Schema<$Items>>, weightings: ReadonlyArray<readonly [number, NaiveFabricator<any>]>): Fabricator<Schema<$Items>>;
@@ -24,7 +24,7 @@ export type Fabricator<$Schema extends {
24
24
  * `source` is already dispatched into its own independent stream by
25
25
  * `Constructor.ts` regardless of this roll, skipping its draw here can never
26
26
  * perturb any other field's reproducibility (see
27
- * `object/omittable/Fabricator.ts`, and CLAUDE.md's "Randomness"). 50/50 by
27
+ * `object/omittable/Fabricator.ts`, and AGENTS.md's "Randomness"). 50/50 by
28
28
  * default; `.weighted(...)` (`Schema.ts`) reweights either outcome relative to
29
29
  * that same default of `1`.
30
30
  */
@@ -24,12 +24,12 @@ export type Fabricator<$Schema extends {
24
24
  * by default; not achievable by composing `nullable(T.undefinable(inner))`'s
25
25
  * two independent 50/50 rolls (that is 50/25/25, not 33/33/33), hence the
26
26
  * dedicated three-way `weighted()` here — see `object/optional/Fabricator.ts`
27
- * and CLAUDE.md's "Why `T.optional` isn't `omittable(undefinable(inner))`".
27
+ * and AGENTS.md's "Why `T.optional` isn't `omittable(undefinable(inner))`".
28
28
  * `.weighted(...)` (`Schema.ts`) reweights individual outcomes relative to that
29
29
  * same default of `1`; an unspecified outcome keeps it. Skipping `source`'s
30
30
  * draw on the two non-"value" outcomes is safe for the same reason as
31
31
  * `object/omittable/Fabricator.ts`: `source` already has its own independent
32
- * stream, minted at build time regardless of this roll (see CLAUDE.md's
32
+ * stream, minted at build time regardless of this roll (see AGENTS.md's
33
33
  * "Randomness").
34
34
  */
35
35
  export declare function Fabricator<$Definition extends Definition>(context: FabricatorContext<Schema<$Definition>>, source: BaseFabricator<any>): Fabricator<Schema<$Definition>>;
@@ -32,12 +32,12 @@ export declare function isObjectOptionalFabricator(candidate: BaseFabricator<unk
32
32
  * Uniform (1/3 each) by default; not achievable by composing
33
33
  * `object.omittable(T.undefinable(inner))`'s two independent 50/50 rolls (that
34
34
  * is 50/25/25, not 33/33/33), hence the dedicated three-way `weighted()` here —
35
- * see CLAUDE.md's "Why `T.optional` isn't `omittable(undefinable(inner))`"
35
+ * see AGENTS.md's "Why `T.optional` isn't `omittable(undefinable(inner))`"
36
36
  * under "Compound / field-only kinds". `.weighted(...)` (`Schema.ts`) reweights
37
37
  * individual outcomes relative to that same default of `1`; an unspecified
38
38
  * outcome keeps it. Skipping `source`'s draw on the two non-"value" outcomes is
39
39
  * safe for the same reason as `object/omittable/Fabricator.ts`: `source`
40
40
  * already has its own independent stream, minted at build time regardless of
41
- * this roll (see CLAUDE.md's "Randomness").
41
+ * this roll (see AGENTS.md's "Randomness").
42
42
  */
43
43
  export declare function Fabricator<$Definition extends Definition>(context: FabricatorContext<Schema<$Definition>>, source: BaseFabricator<any>): Fabricator<Schema<$Definition>>;
@@ -28,14 +28,13 @@ export type Fabricator<$Schema extends {
28
28
  * how deep this `fabricate()` goes — so no structural path distinguishes
29
29
  * sibling expansions at the same depth (an `array` of three `self` children
30
30
  * calls `fabricateAt` three times on one shared element Fabricator; the schema
31
- * does not tell them apart). Each expansion gets its own _root_: `forkSource`
32
- * mints an isolated `RandomSource` salted from this node's draw, and each
33
- * `fabricateAt` resolves an ordinary construction root on it
34
- * (`RandomSource.toRoot`), recorded on each expansion's `trace`. The private
35
- * source's construction counter orders expansions; nothing to increment here.
36
- * Isolation also keeps this node's data-dependent draws from perturbing (or
37
- * being perturbed by) an unrelated Fabricator from the same `initialize()`
38
- * instance.
31
+ * does not tell them apart). `forkSource` mints an isolated `RandomSource`
32
+ * salted from this node's draw, and each `fabricateAt` resolves a
33
+ * `ConstructionTrace` on it (`RandomSource.toConstructionTrace`), recorded on
34
+ * each expansion's `trace`. The private source's construction counter orders
35
+ * expansions; nothing to increment here. Isolation also keeps this node's
36
+ * data-dependent draws from perturbing (or being perturbed by) an unrelated
37
+ * Fabricator from the same `initialize()` instance.
39
38
  *
40
39
  * Each `self` gets its own independently-dispatched expansion — calling
41
40
  * `context.self` twice (two array slots) is two `fabricateAt` calls, each with
@@ -6,7 +6,7 @@ import { Produces, type Adaptation, type Kind, type Meta } from "../../Types";
6
6
  * `TS2456` ("`RecursiveValue` circularly references itself") — legal _only_
7
7
  * because `ValueOf`'s second argument is read through an interface member
8
8
  * (`this["bindings"]` on every composite `Core`), which TypeScript defers. Do
9
- * not rewrite as a conditional; that reintroduces the error. See `CLAUDE.md`'s
9
+ * not rewrite as a conditional; that reintroduces the error. See `AGENTS.md`'s
10
10
  * "`ValueOf`'s `$Bindings`".
11
11
  *
12
12
  * Wherever `self` sits in `$Body` (nested through `array`/`object`/`tuple`/
@@ -15,7 +15,7 @@ export type Fabricated<$Bindings extends unknown[] = []> = $Bindings[0];
15
15
  export type Meta = Record<string, never>;
16
16
  /**
17
17
  * An `interface`, not a `type` alias, so `this["bindings"]` resolves — the
18
- * entire mechanism this kind exists for. See `CLAUDE.md`'s "`ValueOf`'s
18
+ * entire mechanism this kind exists for. See `AGENTS.md`'s "`ValueOf`'s
19
19
  * `$Bindings`" section for why threading requires this.
20
20
  */
21
21
  export interface Core<$Adaptations extends Adaptations = {}> {
@@ -24,7 +24,7 @@ export type Fabricator<$Schema extends {
24
24
  * since `source` is already dispatched into its own independent stream by
25
25
  * `Constructor.ts` regardless of this roll, skipping its draw here can never
26
26
  * perturb any other field's reproducibility (see
27
- * `object/omittable/Fabricator.ts`, and CLAUDE.md's "Randomness"). 50/50 by
27
+ * `object/omittable/Fabricator.ts`, and AGENTS.md's "Randomness"). 50/50 by
28
28
  * default; `.weighted(...)` (`Schema.ts`) reweights either outcome relative to
29
29
  * that same default of `1`.
30
30
  */
@@ -68,8 +68,8 @@ export type Trace = {
68
68
  };
69
69
  /**
70
70
  * Caller-supplied overrides for the construction-owned {@link Trace} slots
71
- * {@link RandomSource.toRoot} resolves. `path`/`kind` are the only slots absent:
72
- * they are per-node and applied in `construct()`, not here.
71
+ * {@link RandomSource.toConstructionTrace} resolves. `path`/`kind` are the only
72
+ * slots absent: they are per-node and applied in `construct()`, not here.
73
73
  *
74
74
  * `salt` is a pin like the rest — it substitutes into that trace slot and does
75
75
  * nothing else. It does not fork, so it neither resets nor sidesteps this
@@ -82,17 +82,17 @@ export type Trace = {
82
82
  * ordinal" — is taken verbatim and does not advance the counter. That is all a
83
83
  * replay needs, since every real {@link Trace} carries a defined ordinal.
84
84
  */
85
- export type RootPins = {
85
+ export type ConstructionPins = {
86
86
  salt?: ReadonlyArray<string> | undefined;
87
87
  clock?: number | undefined;
88
88
  ordinal?: number | null | undefined;
89
89
  };
90
90
  /**
91
- * 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.
91
+ * Every {@link Trace} slot fixed once per construction, before a leaf supplies
92
+ * its own `path`/`kind`. `RandomSource.toConstructionTrace` resolves this once;
93
+ * callers spread it into a full {@link Trace} per leaf and hand that to
94
+ * `toStreamFromTrace`. One construction-ordinal bump is reused across every
95
+ * leaf that construction dispatches.
96
96
  */
97
97
  export type ConstructionTrace = Omit<Trace, "path" | "kind">;
98
98
  /**
@@ -173,9 +173,9 @@ export type Options = {
173
173
  * constructions like any other. Two same-salt builds therefore diverge.
174
174
  *
175
175
  * `clock` / `ordinal` pin the construction-owned {@link Trace} slots
176
- * {@link RandomSource.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
176
+ * {@link RandomSource.toConstructionTrace} would otherwise resolve. Definedness,
177
+ * not `in`: a given `ordinal` — a number, or `null` for "no ordinal" — is taken
178
+ * verbatim with no counter bump, which is all a replay needs; without it, the
179
179
  * construction takes the source counter's next value. A salted construction is
180
180
  * not, by default, asking for a different "now"; a replayed trace whose `clock`
181
181
  * is present explicitly is.
@@ -205,20 +205,22 @@ export type ConstructorOptions = {
205
205
  */
206
206
  export type RandomSource = {
207
207
  /**
208
- * Resolve 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
214
- * construction-ordinal bump serves the whole construction.
208
+ * Resolve this source's `salt`/`clock` and the next construction ordinal,
209
+ * each overridable by {@link ConstructionPins}. Called once per `new
210
+ * Fabricator(...)` (or per lazy expansion of a `T.recursive` schema, each of
211
+ * which resolves its own construction trace on a private forked source),
212
+ * never per leaf: the returned {@link ConstructionTrace} is what every leaf
213
+ * beneath it completes into a full {@link Trace} and hands to
214
+ * `toStreamFromTrace`. One construction-ordinal bump serves the whole
215
+ * construction.
215
216
  */
216
- toRoot(pins?: RootPins): ConstructionTrace;
217
+ toConstructionTrace(pins?: ConstructionPins): ConstructionTrace;
217
218
  /**
218
219
  * The algorithm this source (and every fork of it) hashes with. Stream
219
220
  * derivation is _not_ a member: it depends on no per-source state, so it is
220
- * the free function `toStreamFromTrace(algorithm, trace)`. `toRoot` is the
221
- * only stateful member (the construction counter).
221
+ * the free function `toStreamFromTrace(algorithm, trace)`.
222
+ * `toConstructionTrace` is the only stateful member (the construction
223
+ * counter).
222
224
  */
223
225
  readonly algorithm: Algorithm;
224
226
  /**