@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.
- package/README.md +18 -6
- package/dist/esm/Adapter/Core.js +3 -3
- package/dist/esm/Enumeration/Enumerate.js +22 -18
- package/dist/esm/Error/index.js +8 -8
- package/dist/esm/Fabricator/Constructor.js +22 -23
- package/dist/esm/Harnessing/Core.js +30 -0
- package/dist/esm/Harnessing/Salt.js +12 -0
- package/dist/esm/Harnessing/Types.js +1 -0
- package/dist/esm/Instance/Core.js +65 -38
- package/dist/esm/Instance/Stack/Async.js +10 -2
- package/dist/esm/Instance/Stack/Sync.js +2 -1
- package/dist/esm/Instance/Stack/Visible.js +13 -0
- package/dist/esm/Primitive/recursive/Fabricator.js +1 -1
- package/dist/esm/Random/index.js +25 -80
- package/dist/esm/adapting.js +2 -0
- package/dist/esm/harnessing.js +1 -0
- package/dist/esm/index.js +1 -2
- package/dist/esm/internal.js +1 -2
- package/dist/types/Adapter/Core.d.ts +1 -1
- package/dist/types/Adapter/Types.d.ts +1 -1
- package/dist/types/Enumeration/Enumerate.d.ts +23 -21
- package/dist/types/Error/index.d.ts +15 -19
- package/dist/types/Fabricator/Constructor.d.ts +14 -11
- package/dist/types/Fabricator/Types.d.ts +15 -14
- package/dist/types/Harnessing/Core.d.ts +43 -0
- package/dist/types/Harnessing/Salt.d.ts +31 -0
- package/dist/types/Harnessing/Types.d.ts +79 -0
- package/dist/types/Instance/Core.d.ts +31 -37
- package/dist/types/Instance/Stack/Async.d.ts +7 -0
- package/dist/types/Instance/Stack/Sync.d.ts +5 -3
- package/dist/types/Instance/Stack/Visible.d.ts +22 -0
- package/dist/types/Instance/Types.d.ts +202 -58
- package/dist/types/Primitive/opaque/Registry.d.ts +1 -1
- package/dist/types/Primitive/recursive/Fabricator.d.ts +8 -9
- package/dist/types/Random/Types.d.ts +103 -221
- package/dist/types/Random/index.d.ts +33 -51
- package/dist/types/Types.d.ts +3 -3
- package/dist/types/adapting.d.ts +32 -0
- package/dist/types/harnessing.d.ts +30 -0
- package/dist/types/index.d.ts +51 -77
- package/dist/types/internal.d.ts +5 -23
- package/package.json +23 -3
- package/dist/esm/Random/CallSite.js +0 -70
- 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
|
|
15
|
-
* built-in algorithm,
|
|
16
|
-
*
|
|
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
|
-
* `
|
|
23
|
-
* `layer(...)` (`{@link isLayered}`) — a bare `
|
|
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
|
-
* `
|
|
26
|
-
* `
|
|
27
|
-
* `clock` is the default entropy; `
|
|
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
|
-
* `"
|
|
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 `"
|
|
55
|
-
* to a number, so `resolveClock` can re-derive it from whichever
|
|
56
|
-
* actually in effect at read time. An omitted `clock` on a
|
|
57
|
-
*
|
|
58
|
-
* itself `"
|
|
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`
|
|
72
|
-
* function never reads
|
|
73
|
-
* `Fabricator`/`combinatorial`/`coverage`
|
|
74
|
-
*
|
|
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
|
|
10
|
-
* shared
|
|
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,
|
|
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
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
|
|
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 | "
|
|
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 `
|
|
36
|
-
* {@link Layered}: a bare `
|
|
37
|
-
* means everywhere else in this library), `layer(
|
|
38
|
-
* (`[...base.
|
|
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 `
|
|
41
|
-
* caller-facing `Date | "
|
|
42
|
-
* "
|
|
43
|
-
* epoch milliseconds themselves. `"
|
|
44
|
-
* "now" from the instance
|
|
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 `
|
|
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>, "
|
|
52
|
-
readonly
|
|
53
|
-
readonly clock?: Date | "
|
|
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
|
|
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
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
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
|
-
*
|
|
91
|
-
*
|
|
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
|
-
*
|
|
97
|
-
* or
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
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
|
|
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
|
|
111
|
-
* and
|
|
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, "
|
|
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
|
|
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 `
|
|
142
|
-
* instance's
|
|
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
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
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
|
|
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).
|
|
32
|
-
*
|
|
33
|
-
* `
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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: (
|
|
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>>;
|