@ghostry/fabricator 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +18 -6
  2. package/dist/esm/Adapter/Core.js +3 -3
  3. package/dist/esm/Enumeration/Enumerate.js +22 -18
  4. package/dist/esm/Error/index.js +8 -8
  5. package/dist/esm/Fabricator/Constructor.js +22 -23
  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 +65 -38
  10. package/dist/esm/Instance/Stack/Async.js +10 -2
  11. package/dist/esm/Instance/Stack/Sync.js +2 -1
  12. package/dist/esm/Instance/Stack/Visible.js +13 -0
  13. package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
  14. package/dist/esm/Random/index.js +25 -80
  15. package/dist/esm/adapting.js +2 -0
  16. package/dist/esm/harnessing.js +1 -0
  17. package/dist/esm/index.js +1 -2
  18. package/dist/esm/internal.js +1 -2
  19. package/dist/types/Adapter/Core.d.ts +1 -1
  20. package/dist/types/Adapter/Types.d.ts +1 -1
  21. package/dist/types/Enumeration/Enumerate.d.ts +23 -21
  22. package/dist/types/Error/index.d.ts +15 -19
  23. package/dist/types/Fabricator/Constructor.d.ts +14 -11
  24. package/dist/types/Fabricator/Types.d.ts +15 -14
  25. package/dist/types/Harnessing/Core.d.ts +43 -0
  26. package/dist/types/Harnessing/Salt.d.ts +31 -0
  27. package/dist/types/Harnessing/Types.d.ts +79 -0
  28. package/dist/types/Instance/Core.d.ts +31 -37
  29. package/dist/types/Instance/Stack/Async.d.ts +7 -0
  30. package/dist/types/Instance/Stack/Sync.d.ts +5 -3
  31. package/dist/types/Instance/Stack/Visible.d.ts +22 -0
  32. package/dist/types/Instance/Types.d.ts +202 -58
  33. package/dist/types/Primitive/opaque/Registry.d.ts +1 -1
  34. package/dist/types/Primitive/recursive/Fabricator.d.ts +8 -9
  35. package/dist/types/Random/Types.d.ts +103 -221
  36. package/dist/types/Random/index.d.ts +33 -51
  37. package/dist/types/Types.d.ts +3 -3
  38. package/dist/types/adapting.d.ts +32 -0
  39. package/dist/types/harnessing.d.ts +30 -0
  40. package/dist/types/index.d.ts +51 -77
  41. package/dist/types/internal.d.ts +5 -23
  42. package/package.json +23 -3
  43. package/dist/esm/Random/CallSite.js +0 -70
  44. package/dist/types/Random/CallSite.d.ts +0 -78
@@ -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
@@ -11,36 +11,19 @@ export declare const DEFAULT_COMBINATORIAL_LIMIT = 1024;
11
11
  /**
12
12
  * The single place a `Config` inherits from a base — `initialize` lays its own
13
13
  * config over an empty `{}` base (nothing to inherit, so every field falls
14
- * through to a hardcoded default: an empty seed, wall-clock `clock`, the
15
- * built-in algorithm, `resolveAttribution(undefined)`'s `"call site"`
16
- * resolution, the default registry, the default combinatorial limit), and
17
- * `fork` lays its overlay over the instance it was called on (a full,
14
+ * through to a hardcoded default: an empty salt, wall-clock `clock`, the
15
+ * built-in algorithm, the default registry, the default combinatorial limit),
16
+ * and `fork` lays its overlay over the instance it was called on (a full,
18
17
  * already-resolved `Config`, so every field has a real value to fall back to).
19
18
  * `base` is typed `Partial<Config<PlainObject>>` rather than `Config`
20
19
  * specifically so both calls go through the same function.
21
20
  *
22
- * `seed` composes onto the base rather than replacing it only when tagged with
23
- * `layer(...)` (`{@link isLayered}`) — a bare `seed` (the ordinary meaning
21
+ * `salt` composes onto the base rather than replacing it only when tagged with
22
+ * `layer(...)` (`{@link isLayered}`) — a bare `salt` (the ordinary meaning
24
23
  * everywhere else in this library) replaces the base's outright, and an omitted
25
- * `seed` inherits the base's unchanged (or, at the root, an empty mixer via
26
- * `normalizeSeed(undefined)`, unless an env var supplies one). Wall-clock
27
- * `clock` is the default entropy; `seed` is an optional mixer.
28
- *
29
- * `attribution` resolves through `resolveAttribution` at most once per call,
30
- * and only when it's actually needed:
31
- *
32
- * - an explicit `over.attribution` always wins (resolved fresh, so `fork({
33
- * attribution: { kind: "call site" } })` roots at _that_ call);
34
- * - otherwise an already-resolved `base.attribution` is reused as-is — never
35
- * re-resolved, which keeps a fork from silently re-rooting `"call site"` at
36
- * wherever `fork()` itself happens to be called (`resolveCallerFile()` skips
37
- * this library's own frames, so calling it from here still lands on the
38
- * user's call site either way);
39
- * - only when neither is available (the root case, `base.attribution` absent)
40
- * does this fall back to resolving the `"call site"` default. That also keeps
41
- * `initialize({ attribution: { kind: "none" } })` — or any other explicit
42
- * override — from paying for a stack walk whose result would be immediately
43
- * discarded.
24
+ * `salt` inherits the base's unchanged (or, at the root, an empty mixer via
25
+ * `normalizeSalt(undefined)`, unless an env var supplies one). Wall-clock
26
+ * `clock` is the default entropy; `salt` is an optional mixer.
44
27
  *
45
28
  * `algorithm`/`types`: wholesale replacement when given, matching how
46
29
  * `initialize({ types })` already behaves — no deep merge;
@@ -49,13 +32,13 @@ export declare const DEFAULT_COMBINATORIAL_LIMIT = 1024;
49
32
  * inherited, or defaulted), so a bad limit fails at `fork()`/`initialize()`
50
33
  * time rather than at first use. `clock` follows the given → inherited →
51
34
  * default shape `algorithm` does, but stays _unresolved_ only for the explicit
52
- * `"seeded"` sentinel: an explicit `Date` and the unconfigured wall-clock
35
+ * `"derived"` sentinel: an explicit `Date` and the unconfigured wall-clock
53
36
  * default are stored as epoch milliseconds (a stated instant, inherited as-is
54
- * from then on), while `"seeded"` is left as the sentinel rather than collapsed
55
- * to a number, so `resolveClock` can re-derive it from whichever `seed` is
56
- * actually in effect at read time. An omitted `clock` on a `fork`/`wrap` whose
57
- * seed changed therefore keeps the parent's instant unless that parent was
58
- * itself `"seeded"`.
37
+ * from then on), while `"derived"` is left as the sentinel rather than
38
+ * collapsed to a number, so `resolveClock` can re-derive it from whichever
39
+ * `salt` is actually in effect at read time. An omitted `clock` on a
40
+ * `fork`/`wrap` whose salt changed therefore keeps the parent's instant unless
41
+ * that parent was itself `"derived"`.
59
42
  */
60
43
  export declare function overlay<$Registry extends PlainObject>(base: Partial<Config<PlainObject>>, over: Overlay<$Registry>): Config<$Registry>;
61
44
  /**
@@ -68,12 +51,23 @@ export declare function overlay<$Registry extends PlainObject>(base: Partial<Con
68
51
  * than each independently re-deriving one from the same config (and so silently
69
52
  * diverging/duplicating construction ordinals).
70
53
  *
71
- * `stack` is threaded straight through to `Constructor`/`enumerables` — this
72
- * function never reads or writes it itself, only passes it along so every built
73
- * `Fabricator`/`combinatorial`/`coverage` can consult whichever frame is active
74
- * _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.
75
66
  */
76
- 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
+ }): {
77
71
  instance: Instance<$Registry>;
78
72
  source: RandomSource;
79
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;
@@ -1,78 +1,123 @@
1
1
  import type { Enumerable, Limits } from "../Enumeration/Types";
2
2
  import type { Constructor } from "../Fabricator/Constructor";
3
- import type { Algorithm, Attribution, Layered, RandomSource, Seed } from "../Random/Types";
3
+ import type { Algorithm, Layered, RandomSource, Salt } from "../Random/Types";
4
4
  import type { PlainObject } from "../Utility/Types";
5
5
  /**
6
6
  * A fully resolved instance configuration — every field present, nothing left
7
7
  * to default. `Overlay` is what `fork` accepts; `overlay()`
8
8
  * (`Instance/Core.ts`) is the only thing producing a complete `Config`.
9
9
  *
10
- * Two fields are declared at their caller-facing type but always hold their
11
- * resolved form once `overlay()` has run: `seed` is `Seed` because that is what
12
- * a caller may supply, but always holds the normalized array (`Instance.seed`
13
- * is the authoritative read); `attribution` is `Attribution` for the same
14
- * reason but always holds a `ResolvedAttribution`, a subtype. `clock` is
15
- * different from both: unlike `seed`/`attribution`, it holds _either_ a
16
- * resolved instant (epoch milliseconds) or the unresolved `"seeded"` policy
17
- * never collapsed to a number by `overlay()` when `"seeded"`, because that
18
- * policy must re-derive whenever the seed it composes changes (a `fork({ seed:
19
- * layer(...) })`). The unconfigured default is a wall- clock number, inherited
20
- * as-is like an explicit `Date`. `resolveClock` (`Instance/Core.ts`) is the one
21
- * place that resolves `"seeded"` to a number, called fresh wherever the clock
22
- * actually matters (`instantiate`, the `context.clock` getter) rather than once
23
- * here.
10
+ * `salt` is declared at its caller-facing `Salt` type but always holds the
11
+ * normalized array once `overlay()` has run (`Instance.salt` is the
12
+ * authoritative read). `clock` holds _either_ a resolved instant (epoch
13
+ * milliseconds) or the unresolved `"derived"` policy never collapsed to a
14
+ * number by `overlay()` when `"derived"`, because that policy must re-derive
15
+ * whenever the salt it composes changes (a `fork({ salt: layer(...) })`). The
16
+ * unconfigured default is a wall- clock number, inherited as-is like an
17
+ * explicit `Date`. `resolveClock` (`Instance/Core.ts`) is the one place that
18
+ * resolves `"derived"` to a number, called fresh wherever the clock actually
19
+ * matters (`instantiate`, the `context.clock` getter) rather than once here.
24
20
  */
25
21
  export type Config<$Registry extends PlainObject> = {
26
- readonly seed: Seed;
22
+ readonly salt: Salt;
27
23
  readonly algorithm: Algorithm;
28
- readonly attribution: Attribution;
29
24
  readonly types: $Registry;
30
25
  readonly limits: Limits;
31
- readonly clock: number | "seeded";
26
+ readonly clock: number | "derived";
32
27
  };
33
28
  /**
34
29
  * What `fork` accepts — a `Config` to lay over a base, every field optional.
35
- * Identical to `Partial<Config>` but for `seed`, which additionally accepts a
36
- * {@link Layered}: a bare `Seed` replaces the base's seed outright (what `seed`
37
- * means everywhere else in this library), `layer(seed)` appends onto it
38
- * (`[...base.seed, ...seed]`) — the shape a wrapping integration wants, and
30
+ * Identical to `Partial<Config>` but for `salt`, which additionally accepts a
31
+ * {@link Layered}: a bare `Salt` replaces the base's salt outright (what `salt`
32
+ * means everywhere else in this library), `layer(salt)` appends onto it
33
+ * (`[...base.salt, ...salt]`) — the shape a wrapping integration wants, and
39
34
  * what `ConstructorOptions`' layered form is for a single construction.
40
- * Omitting `seed` inherits the base's unchanged. `clock` similarly accepts the
41
- * caller-facing `Date | "seeded"` rather than `Config`'s own resolved `number |
42
- * "seeded"`, so a caller can hand in a literal instant without converting it to
43
- * epoch milliseconds themselves. `"seeded"` is the explicit opt-in that derives
44
- * "now" from the instance seed; omitting `clock` at the root captures
35
+ * Omitting `salt` inherits the base's unchanged. `clock` similarly accepts the
36
+ * caller-facing `Date | "derived"` rather than `Config`'s own resolved `number
37
+ * | "derived"`, so a caller can hand in a literal instant without converting it
38
+ * to epoch milliseconds themselves. `"derived"` is the explicit opt-in that
39
+ * derives "now" from the instance salt; omitting `clock` at the root captures
45
40
  * wall-clock time instead.
46
41
  *
47
- * `initialize`'s own parameter keeps a plain `seed?: Seed`, so passing
42
+ * `initialize`'s own parameter keeps a plain `salt?: Salt`, so passing
48
43
  * `layer(...)` there is a compile error: there is no base to layer onto at the
49
44
  * root.
50
45
  */
51
- export type Overlay<$Registry extends PlainObject> = Partial<Omit<Config<$Registry>, "seed" | "clock">> & {
52
- readonly seed?: Seed | Layered;
53
- readonly clock?: Date | "seeded" | undefined;
46
+ export type Overlay<$Registry extends PlainObject> = Partial<Omit<Config<$Registry>, "salt" | "clock">> & {
47
+ readonly salt?: Salt | Layered;
48
+ readonly clock?: Date | "derived" | undefined;
54
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>];
55
81
  /**
56
82
  * One active `wrap` — its resolved config plus the single `RandomSource` every
57
83
  * build reached inside that `wrap` shares, whether reached implicitly (any
58
- * instance in the lineage consulting the active frame) or explicitly
84
+ * instance on the origin's ancestral line consulting the frame) or explicitly
59
85
  * (`scope.Fabricator`, the `Instance` passed to the block). Storing the scope's
60
86
  * own already-built `source` here, rather than each consumer re-deriving one
61
87
  * from `config`, keeps the two routes resolving against the _same_ source —
62
88
  * sharing one set of construction-ordinal counters — instead of each silently
63
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.
64
101
  */
65
102
  export type Frame = {
66
103
  readonly config: Config<PlainObject>;
67
104
  readonly source: RandomSource;
105
+ readonly instance: Instance<PlainObject>;
106
+ readonly ancestry: Ancestry;
68
107
  };
69
108
  /**
70
- * The per-lineage ambient stack. Created once at a root `initialize()` and
71
- * threaded — never re-created — through every `fork`/`wrap` descended from it
72
- * (see `instantiate`, `Instance/Core.ts`). Two unrelated `initialize()` calls
73
- * stay fully isolated; one lineage's `wrap` reaches every instance in that
74
- * lineage its `Fabricator`, `combinatorial`, and `coverage` alike
75
- * 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.
76
121
  */
77
122
  export type Stack = {
78
123
  /**
@@ -85,39 +130,128 @@ export type Stack = {
85
130
  * instance. See `Instance/Stack/Sync.ts` and `Instance/Stack/Async.ts`.
86
131
  */
87
132
  readonly asynchronous: boolean;
88
- current(): Frame | undefined;
89
133
  /**
90
- * Push `frame`, run `block`, pop in a `finally`, so a frame unwinds
91
- * 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.
92
152
  */
93
153
  enter<$Return>(frame: Frame, block: () => $Return): $Return;
94
154
  };
95
155
  /**
96
- * The configuration in effect _right now_ — the innermost active `wrap` frame,
97
- * or this instance's own when there is none. Distinct from
98
- * `ConstructionContext` (`Fabricator/Types.ts`), which is one construction's
99
- * internal dispatch plumbing; this is the caller-facing "what configuration is
100
- * 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."
101
170
  */
102
171
  export type Context = {
103
- readonly seed: ReadonlyArray<string>;
172
+ readonly salt: ReadonlyArray<string>;
104
173
  readonly algorithm: Algorithm;
105
- readonly attribution: Attribution;
106
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;
107
198
  };
108
199
  /**
109
200
  * A single initialized library instance: the registry it was given, and a
110
- * `construct()` bound to its own isolated randomness — its own seed, builder,
111
- * and per-file overrides/streams, held internally and never shared with any
201
+ * `construct()` bound to its own isolated randomness — its own salt, builder,
202
+ * and construction counter/streams, held internally and never shared with any
112
203
  * other `initialize()` call. Independently initialized instances (e.g. parallel
113
204
  * tests) can never perturb each other.
114
205
  */
115
- export interface Instance<$Registry extends PlainObject> extends Pick<RandomSource, "seed"> {
206
+ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSource, "salt"> {
116
207
  /** The registry of type definers this instance was initialized with. */
117
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>;
118
252
  /**
119
253
  * Turn a Schema built from `T` into a live Fabricator, deriving fresh
120
- * randomness from this instance's own seed for whichever leaves actually need
254
+ * randomness from this instance's own salt for whichever leaves actually need
121
255
  * it.
122
256
  */
123
257
  Fabricator: Constructor;
@@ -138,8 +272,8 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
138
272
  coverage: Enumerable;
139
273
  /**
140
274
  * Derive a new instance laid over this one: anything the overlay names
141
- * overrides, anything it omits inherits. A bare `seed` replaces this
142
- * instance's seed; `seed: layer(...)` appends onto it. A peer of an
275
+ * overrides, anything it omits inherits. A bare `salt` replaces this
276
+ * instance's salt; `salt: layer(...)` appends onto it. A peer of an
143
277
  * `initialize()` return value in every respect, including its own `fork`.
144
278
  */
145
279
  fork<const $ForkRegistry extends PlainObject = $Registry>(overlay?: Overlay<$ForkRegistry>): Instance<$ForkRegistry>;
@@ -153,10 +287,19 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
153
287
  * — a build reached after an `await` inside `block` sees this instance's own
154
288
  * configuration again, not the wrap's.
155
289
  *
156
- * A nested `wrap` lays over whichever frame is currently active, not over the
157
- * instance it was called on so `overlay.seed: layer(...)` accumulates with
158
- * nesting depth while a bare `seed` still replaces outright, discarding every
159
- * 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}.
160
303
  */
161
304
  wrap<$Return, const $WrapRegistry extends PlainObject = $Registry>(overlay: Overlay<$WrapRegistry>, block: (scope: Instance<$WrapRegistry>) => $Return): $Return;
162
305
  /**
@@ -167,3 +310,4 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
167
310
  */
168
311
  readonly context: Context;
169
312
  }
313
+ export {};
@@ -6,7 +6,7 @@ import { Schema } from "./Schema";
6
6
  * `T.opaque((random) => new Map<string, number>())` needs no annotation.
7
7
  *
8
8
  * `produce` receives this schema's own seeded stream, so an opaque value still
9
- * replays from a seed; see `Types.ts`'s `Produce`. No bare form and no `.as()`
9
+ * replays from a salt; see `Types.ts`'s `Produce`. No bare form and no `.as()`
10
10
  * — `produce` is the whole schema.
11
11
  */
12
12
  export default function <$T>(produce: Produce<$T>): Schema<$T>;
@@ -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` seeded from this node's draw, and each
33
- * `fabricateAt` opens a `"counted"` scope on it (`Random/Types.ts`'s `RootKind`
34
- * — recorded on each expansion's `trace`, not chosen at `ConstructorOptions`).
35
- * The private source's construction counter orders expansions; nothing to
36
- * increment here. Isolation also keeps this node's data-dependent draws from
37
- * perturbing (or being perturbed by) an unrelated Fabricator from the same
38
- * `initialize()` 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
@@ -43,4 +42,4 @@ export type Fabricator<$Schema extends {
43
42
  * shared-element one. Sibling tree branches therefore draw independently, not a
44
43
  * correlated shared sequence.
45
44
  */
46
- export declare function Fabricator<$Body>(context: FabricatorContext<Schema<$Body>>, forkSource: (seed: string) => RandomSource, make: (schema: unknown, path: ReadonlyArray<string>, context: ConstructionContext) => NaiveFabricator<unknown>): Fabricator<Schema<$Body>>;
45
+ export declare function Fabricator<$Body>(context: FabricatorContext<Schema<$Body>>, forkSource: (salt: string) => RandomSource, make: (schema: unknown, path: ReadonlyArray<string>, context: ConstructionContext) => NaiveFabricator<unknown>): Fabricator<Schema<$Body>>;