@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
@@ -1,14 +1,15 @@
1
1
  import { shuffle } from "../Distribution/index.js";
2
2
  import { FabricatorError } from "../Error/index.js";
3
3
  import { Constructor } from "../Fabricator/Constructor.js";
4
+ import { toInnermostFrame } from "../Instance/Stack/Visible.js";
4
5
  import { toStreamFromTrace } from "../Random/index.js";
5
6
  import { plan, resolve } from "./Plan.js";
6
- function enumerables(source, limits, stack) {
7
- const Fabricator = Constructor(source, stack);
7
+ function enumerables(source, limits, stack, ancestry) {
8
+ const Fabricator = Constructor(source, stack, ancestry);
8
9
  function effectiveSource() {
9
10
  var _ref;
10
- var _stack_current;
11
- return null != (_ref = null == (_stack_current = stack.current()) ? void 0 : _stack_current.source) ? _ref : source;
11
+ var _toInnermostFrame;
12
+ return null != (_ref = null == (_toInnermostFrame = toInnermostFrame(stack, ancestry)) ? void 0 : _toInnermostFrame.source) ? _ref : source;
12
13
  }
13
14
  function iterable(rebuild) {
14
15
  return {
@@ -90,9 +91,9 @@ function enumerables(source, limits, stack) {
90
91
  }
91
92
  function orderer(source, salt) {
92
93
  const forked = source.fork(salt);
93
- const root = forked.toRoot();
94
+ const trace = forked.toConstructionTrace();
94
95
  const stream = toStreamFromTrace(forked.algorithm, {
95
- ...root,
96
+ ...trace,
96
97
  path: [],
97
98
  kind: "order"
98
99
  });
@@ -201,7 +201,7 @@ class Error_FabricatorError extends Error {
201
201
  constructor(){
202
202
  super();
203
203
  this.name = "HarnessingProviderError";
204
- this.message = "The `fabricator` provider was called outside its integration's `around`, so there is no per-test scope to provide. A composer must run each integration's providers inside that integration's `around` frame, as `@ghostry/harness` does.";
204
+ this.message = "The `fabricator` provider was called outside its integration's frame, so there is no per-test scope to provide. A composer must run each integration's providers inside the frame it opened, passing forward what that frame's wrapper established, as `@ghostry/harness` does.";
205
205
  }
206
206
  }
207
207
  FabricatorError.HarnessingProviderError = HarnessingProviderError;
@@ -1,10 +1,11 @@
1
1
  import { FabricatorError } from "../Error/index.js";
2
+ import { toInnermostFrame } from "../Instance/Stack/Visible.js";
2
3
  import { Primitive } from "../Primitive/index.js";
3
4
  import { isLayered, normalizeSalt } from "../Random/index.js";
4
5
  import { toSchema } from "../Schema/Core.js";
5
6
  import { Adaptation, Fixed, Kind, Layer, Meta } from "../Types.js";
6
7
  import { inline } from "../Utility/Core.js";
7
- function Constructor(source, stack) {
8
+ function Constructor(source, stack, ancestry) {
8
9
  function make(schema, path, context) {
9
10
  schema = toSchema(schema);
10
11
  const kind = schema[Kind];
@@ -280,7 +281,7 @@ function Constructor(source, stack) {
280
281
  function construct(schema, options = {}) {
281
282
  var _options_path;
282
283
  if ("string" == typeof options.kind && options.kind !== schema[Kind]) throw new FabricatorError.TraceKindMismatchError(schema[Kind], options.kind);
283
- const context = toConstructionContext(source, options, stack);
284
+ const context = toConstructionContext(source, options, stack, ancestry);
284
285
  const made = make(schema, null != (_options_path = options.path) ? _options_path : [], context);
285
286
  const adaptations = schema[Adaptation];
286
287
  return adaptations ? {
@@ -290,21 +291,21 @@ function Constructor(source, stack) {
290
291
  }
291
292
  return construct;
292
293
  }
293
- function toConstructionContext(source, options, stack) {
294
- const { source: rooted, root: construction } = resolveScope(source, options, stack);
294
+ function toConstructionContext(source, options, stack, ancestry) {
295
+ const { source: resolved, trace } = resolveScope(source, options, stack, ancestry);
295
296
  return {
296
297
  toTrace: (path, kind)=>({
297
- ...construction,
298
+ ...trace,
298
299
  path,
299
300
  kind
300
301
  }),
301
- algorithm: rooted.algorithm
302
+ algorithm: resolved.algorithm
302
303
  };
303
304
  }
304
- function resolveScope(source, options, stack) {
305
+ function resolveScope(source, options, stack, ancestry) {
305
306
  var _ref;
306
- const frame = stack.current();
307
- const base = null != (_ref = null == frame ? void 0 : frame.source) ? _ref : source;
307
+ var _toInnermostFrame;
308
+ const base = null != (_ref = null == (_toInnermostFrame = toInnermostFrame(stack, ancestry)) ? void 0 : _toInnermostFrame.source) ? _ref : source;
308
309
  const salt = inline(()=>{
309
310
  if (!options.salt) return;
310
311
  if (isLayered(options.salt)) return [
@@ -320,7 +321,7 @@ function resolveScope(source, options, stack) {
320
321
  };
321
322
  return {
322
323
  source: base,
323
- root: base.toRoot(pins)
324
+ trace: base.toConstructionTrace(pins)
324
325
  };
325
326
  }
326
327
  export { Constructor };
@@ -2,28 +2,19 @@ import { FabricatorError } from "../Error/index.js";
2
2
  import { layer as index_js_layer } from "../Random/index.js";
3
3
  import { saltFor } from "./Salt.js";
4
4
  function integration(instance) {
5
- let scope;
6
5
  return {
7
6
  name: "@ghostry/fabricator",
8
7
  provides: {
9
- fabricator: ()=>{
10
- if (void 0 === scope) throw new FabricatorError.HarnessingProviderError();
11
- return scope;
8
+ fabricator: ({ established })=>{
9
+ if (void 0 === established) throw new FabricatorError.HarnessingProviderError();
10
+ return established;
12
11
  }
13
12
  },
14
- around (identity, body) {
13
+ *frame ({ identity }) {
15
14
  const salt = index_js_layer(saltFor(identity));
16
- return instance.wrap({
17
- salt
18
- }, (entered)=>{
19
- const previous = scope;
20
- scope = entered;
21
- try {
22
- return body();
23
- } finally{
24
- scope = previous;
25
- }
26
- });
15
+ yield (body)=>instance.wrap({
16
+ salt
17
+ }, body);
27
18
  }
28
19
  };
29
20
  }
@@ -5,6 +5,7 @@ import { defaultAlgorithm, deriveClock, isLayered, normalizeSalt, toRandomSource
5
5
  import { registry } from "../Schema/Registry.js";
6
6
  import { Layer } from "../Types.js";
7
7
  import { inline, isThenable, noop } from "../Utility/Core.js";
8
+ import { toInnermostFrame } from "./Stack/Visible.js";
8
9
  const DEFAULT_COMBINATORIAL_LIMIT = 1024;
9
10
  function resolveCombinatorialLimit(limit) {
10
11
  if (void 0 === limit) return DEFAULT_COMBINATORIAL_LIMIT;
@@ -14,6 +15,9 @@ function resolveCombinatorialLimit(limit) {
14
15
  function resolveClock(config) {
15
16
  return "number" == typeof config.clock ? config.clock : deriveClock(config.algorithm, normalizeSalt(config.salt));
16
17
  }
18
+ function mint() {
19
+ return Symbol("fabricator.instance");
20
+ }
17
21
  function overlay(base, over) {
18
22
  var _ref, _over_algorithm, _ref1, _over_types, _over_limits;
19
23
  var _this;
@@ -41,26 +45,40 @@ function overlay(base, over) {
41
45
  }
42
46
  };
43
47
  }
44
- function instantiate(config, stack) {
48
+ function instantiate(config, stack, parent) {
49
+ const ancestry = void 0 === parent ? [
50
+ mint()
51
+ ] : [
52
+ ...parent.ancestry,
53
+ mint()
54
+ ];
45
55
  const source = toRandomSource({
46
56
  salt: config.salt,
47
57
  algorithm: config.algorithm,
48
58
  clock: resolveClock(config)
49
59
  });
50
- const Fabricator = Constructor(source, stack);
51
- const { combinatorial, coverage } = enumerables(source, config.limits, stack);
60
+ const Fabricator = Constructor(source, stack, ancestry);
61
+ const { combinatorial, coverage } = enumerables(source, config.limits, stack, ancestry);
62
+ function derive(over) {
63
+ const derived = overlay(config, over);
64
+ return {
65
+ ...instantiate(derived, stack, {
66
+ ancestry,
67
+ root: instance.root
68
+ }),
69
+ config: derived
70
+ };
71
+ }
52
72
  function fork(forkOverlay = {}) {
53
- return instantiate(overlay(config, forkOverlay), stack).instance;
73
+ return derive(forkOverlay).instance;
54
74
  }
55
75
  function wrap(wrapOverlay, block) {
56
- var _ref;
57
- var _stack_current;
58
- const base = null != (_ref = null == (_stack_current = stack.current()) ? void 0 : _stack_current.config) ? _ref : config;
59
- const scopedConfig = overlay(base, wrapOverlay);
60
- const scoped = instantiate(scopedConfig, stack);
76
+ const scoped = derive(wrapOverlay);
61
77
  const result = stack.enter({
62
- config: scopedConfig,
63
- source: scoped.source
78
+ config: scoped.config,
79
+ source: scoped.source,
80
+ instance: scoped.instance,
81
+ ancestry
64
82
  }, ()=>block(scoped.instance));
65
83
  if (!stack.asynchronous && isThenable(result)) {
66
84
  result.then(noop, noop);
@@ -68,25 +86,41 @@ function instantiate(config, stack) {
68
86
  }
69
87
  return result;
70
88
  }
89
+ function visibleFrame() {
90
+ return toInnermostFrame(stack, ancestry);
91
+ }
71
92
  const context = {
72
93
  get salt () {
73
94
  var _ref;
74
- var _stack_current;
75
- return normalizeSalt((null != (_ref = null == (_stack_current = stack.current()) ? void 0 : _stack_current.config) ? _ref : config).salt);
95
+ var _visibleFrame;
96
+ return normalizeSalt((null != (_ref = null == (_visibleFrame = visibleFrame()) ? void 0 : _visibleFrame.config) ? _ref : config).salt);
76
97
  },
77
98
  get algorithm () {
78
99
  var _ref1;
79
- var _stack_current1;
80
- return (null != (_ref1 = null == (_stack_current1 = stack.current()) ? void 0 : _stack_current1.config) ? _ref1 : config).algorithm;
100
+ var _visibleFrame1;
101
+ return (null != (_ref1 = null == (_visibleFrame1 = visibleFrame()) ? void 0 : _visibleFrame1.config) ? _ref1 : config).algorithm;
81
102
  },
82
103
  get clock () {
83
104
  var _ref2;
84
- var _stack_current2;
85
- return resolveClock(null != (_ref2 = null == (_stack_current2 = stack.current()) ? void 0 : _stack_current2.config) ? _ref2 : config);
105
+ var _visibleFrame2;
106
+ return resolveClock(null != (_ref2 = null == (_visibleFrame2 = visibleFrame()) ? void 0 : _visibleFrame2.config) ? _ref2 : config);
107
+ },
108
+ scope: ()=>{
109
+ var _ref;
110
+ var _visibleFrame;
111
+ return null != (_ref = null == (_visibleFrame = visibleFrame()) ? void 0 : _visibleFrame.instance) ? _ref : instance;
112
+ },
113
+ get depth () {
114
+ return stack.visible(ancestry).length;
86
115
  }
87
116
  };
88
117
  const instance = {
89
118
  T: config.types,
119
+ ancestry,
120
+ get root () {
121
+ var _ref3;
122
+ return null != (_ref3 = null == parent ? void 0 : parent.root) ? _ref3 : instance;
123
+ },
90
124
  Fabricator,
91
125
  combinatorial,
92
126
  coverage,
@@ -1,10 +1,18 @@
1
1
  import { AsyncLocalStorage } from "async_hooks";
2
+ import { toVisible } from "./Visible.js";
2
3
  function toStack() {
3
4
  const store = new AsyncLocalStorage();
5
+ const getFrames = ()=>{
6
+ var _store_getStore;
7
+ return null != (_store_getStore = store.getStore()) ? _store_getStore : [];
8
+ };
4
9
  return {
5
10
  asynchronous: true,
6
- current: ()=>store.getStore(),
7
- enter: (frame, block)=>store.run(frame, block)
11
+ visible: (ancestry)=>toVisible(getFrames(), ancestry),
12
+ enter: (frame, block)=>store.run([
13
+ ...getFrames(),
14
+ frame
15
+ ], block)
8
16
  };
9
17
  }
10
18
  export { toStack };
@@ -1,8 +1,9 @@
1
+ import { toVisible } from "./Visible.js";
1
2
  function toStack() {
2
3
  const frames = [];
3
4
  return {
4
5
  asynchronous: false,
5
- current: ()=>frames[frames.length - 1],
6
+ visible: (ancestry)=>toVisible(frames, ancestry),
6
7
  enter: (frame, block)=>{
7
8
  frames.push(frame);
8
9
  try {
@@ -0,0 +1,13 @@
1
+ function onDirectLine(a, b) {
2
+ const shared = Math.min(a.length, b.length);
3
+ for(let index = 0; index < shared; index++)if (a[index] !== b[index]) return false;
4
+ return true;
5
+ }
6
+ function toVisible(frames, ancestry) {
7
+ return frames.filter((frame)=>onDirectLine(frame.ancestry, ancestry));
8
+ }
9
+ function toInnermostFrame(stack, ancestry) {
10
+ const frames = stack.visible(ancestry);
11
+ return frames[frames.length - 1];
12
+ }
13
+ export { toInnermostFrame, toVisible };
@@ -10,7 +10,7 @@ function Fabricator(context, forkSource, make) {
10
10
  function fabricateAt(depth) {
11
11
  const atMax = depth >= meta.depth.max;
12
12
  const target = atMax ? meta.terminal : meta.body;
13
- const construction = privateSource.toRoot();
13
+ const construction = privateSource.toConstructionTrace();
14
14
  const context = {
15
15
  toTrace: (path, kind)=>({
16
16
  ...construction,
@@ -71,7 +71,7 @@ function toRandomSource(options) {
71
71
  let algorithm = null != (_options_algorithm = options.algorithm) ? _options_algorithm : defaultAlgorithm;
72
72
  const clock = options.clock;
73
73
  let constructionOrdinal = 0;
74
- function toRoot(pins = {}) {
74
+ function toConstructionTrace(pins = {}) {
75
75
  var _pins_salt, _pins_clock;
76
76
  return {
77
77
  salt: null != (_pins_salt = pins.salt) ? _pins_salt : salt,
@@ -87,7 +87,7 @@ function toRandomSource(options) {
87
87
  });
88
88
  }
89
89
  return {
90
- toRoot,
90
+ toConstructionTrace,
91
91
  algorithm,
92
92
  salt,
93
93
  fork
@@ -1,25 +1,25 @@
1
- import type { Stack } from "../Instance/Types";
1
+ import type { Ancestry, Stack } from "../Instance/Types";
2
2
  import type { RandomSource } from "../Random/Types";
3
3
  import type { Enumerable, Limits } from "./Types";
4
4
  /**
5
5
  * Typed `combinatorial`/`coverage` boundary, closing over one instance's
6
6
  * `source` and its already-validated `limits` — same shape as
7
- * `Constructor(source, stack)`. No separate `clock`: `source` already carries
7
+ * `Constructor(source, stack, ancestry)`. No separate `clock`: `source` carries
8
8
  * its resolved clock (`Random/Types.ts`'s `Options.clock`), so `Constructor`'s
9
- * `toConstructionContext` reads it off whichever root a construction resolves
10
- * against. `plan`/ `resolve` (`./Plan.ts`) do the untyped recursive work; this
11
- * is the one precisely-typed layer, mirroring `Constructor.ts`'s `make`/
12
- * `construct` split.
9
+ * `toConstructionContext` reads it off the resolved `ConstructionTrace`.
10
+ * `plan`/ `resolve` (`./Plan.ts`) do the untyped recursive work; this is the
11
+ * one precisely-typed layer, mirroring `Constructor.ts`'s `make`/`construct`
12
+ * split.
13
13
  *
14
14
  * Two derived salts — one per API — each composed from the _effective_ source's
15
- * salt (`effectiveSource()` below — the active `wrap` frame's, or this
16
- * instance's `source`; read fresh on every `combinatorial(...)`/`coverage(...)`
17
- * call, not once when `enumerables()` was built, so the same `combinatorial`
18
- * reference behaves differently inside an active `wrap`). Each build pins that
19
- * salt via `new Fabricator(schema, { salt })` (see `Constructor.ts`'s
20
- * `construct()`) — a pin, not a fork — so every rebuild of one schema draws
21
- * from the same universe, distinct from anything built under the instance's own
22
- * salt.
15
+ * salt (`effectiveSource()` below — the innermost visible `wrap` frame's, or
16
+ * this instance's `source`; read fresh on every
17
+ * `combinatorial(...)`/`coverage(...)` call, not once when `enumerables()` was
18
+ * built, so the same `combinatorial` reference behaves differently inside a
19
+ * `wrap` this instance can see). Each build pins that salt via `new
20
+ * Fabricator(schema, { salt })` (see `Constructor.ts`'s `construct()`) — a pin,
21
+ * not a fork so every rebuild of one schema draws from the same universe,
22
+ * distinct from anything built under the instance's own salt.
23
23
  *
24
24
  * `ordinal: null` is pinned alongside it, and is not incidental: a salt says
25
25
  * nothing about ordering, so without this pin each lazy rebuild would take the
@@ -29,7 +29,7 @@ import type { Enumerable, Limits } from "./Types";
29
29
  * every iteration rebuilds from the same explicit identity, and the `null` can
30
30
  * never coincide with a counted construction.
31
31
  */
32
- export declare function enumerables(source: RandomSource, limits: Limits, stack: Stack): {
32
+ export declare function enumerables(source: RandomSource, limits: Limits, stack: Stack, ancestry: Ancestry): {
33
33
  combinatorial: Enumerable;
34
34
  coverage: Enumerable;
35
35
  };
@@ -7,7 +7,7 @@ import type { PlainObject } from "../Utility/Types";
7
7
  * is the cartesian product — width is the product of every child's width;
8
8
  * `at(index)` mixed-radix decodes to a full combination (`combinatorial`).
9
9
  * `"cycle"` (`coverage`) takes the widest child as the composite's width,
10
- * cycling narrower children to fill it — see CLAUDE.md's "sum vs product" note
10
+ * cycling narrower children to fill it — see AGENTS.md's "sum vs product" note
11
11
  * for why cycling only ever applies to `object`/`tuple` and never to a sum node
12
12
  * (`choice`, the presence wrappers), which always total their children's widths
13
13
  * regardless of strategy.
@@ -17,7 +17,7 @@ export type Strategy = "product" | "cycle";
17
17
  * A fresh, reproducible permutation of `0..width-1` each call — one per
18
18
  * width-`>1` node `plan()` visits, in walk order, so two nodes of the same
19
19
  * width never receive the same permutation (which would otherwise iterate them
20
- * in lockstep — see CLAUDE.md's note on why a constant phase offset isn't
20
+ * in lockstep — see AGENTS.md's note on why a constant phase offset isn't
21
21
  * enough). Only consulted under `"cycle"`; `"product"`'s mixed-radix decode
22
22
  * already visits every combination, so permuting there would only reorder
23
23
  * identical output. Built in `Enumerate.ts` (which can reach `Random/`), then
@@ -540,14 +540,15 @@ export declare namespace FabricatorError {
540
540
  /**
541
541
  * The `fabricator` provider of `integration(instance)`
542
542
  * (`@ghostry/fabricator/harnessing`) was called outside that integration's
543
- * own `around`.
543
+ * own frame.
544
544
  *
545
- * The provider hands back the per-test scope `around` just entered, so there
546
- * is no scope for it to return anywhere else. `@ghostry/harness` never does
547
- * this it runs each provider inside its integration's `around`so this
548
- * means a composer that breaks the contract. Raised rather than returning the
549
- * base instance, which would draw plausible data from the wrong configuration
550
- * with no signal.
545
+ * The provider hands back the per-test scope the frame's wrapper established,
546
+ * which reaches it as the provider's second argument, so there is no scope
547
+ * for it to return anywhere else. `@ghostry/harness` never does this it
548
+ * runs each provider inside the frame it opened and passes that scope forward
549
+ * so this means a composer that breaks the contract. Raised rather than
550
+ * returning the base instance, which would draw plausible data from the wrong
551
+ * configuration with no signal.
551
552
  */
552
553
  class HarnessingProviderError extends FabricatorError {
553
554
  constructor();
@@ -1,4 +1,4 @@
1
- import type { Stack } from "../Instance/Types";
1
+ import type { Ancestry, Stack } from "../Instance/Types";
2
2
  import type { ConstructorOptions, RandomSource } from "../Random/Types";
3
3
  import { type Buildable } from "../Types";
4
4
  import { type AsFabricator } from "./Types";
@@ -25,17 +25,20 @@ export type Constructor = {
25
25
  * fabricator this `construct()` produces draws from that instance's own
26
26
  * salt/streams and never another instance's.
27
27
  *
28
- * `stack` is the instance's own lineage-wide ambient stack
29
- * (`Instance/Core.ts`'s `toStack()`) — passed straight through to
28
+ * `stack` is the ambient carrier and `ancestry` is this instance's position in
29
+ * its lineage (`Instance/Core.ts`) — both passed straight through to
30
30
  * `resolveScope` on every `construct()` call, never read here directly, so a
31
- * build reached inside an active `wrap` resolves against that frame
32
- * automatically, with nothing threaded through by the caller.
31
+ * build reached inside a `wrap` this instance can see resolves against that
32
+ * frame automatically, with nothing threaded through by the caller. `ancestry`
33
+ * is what decides "can see": a frame entered on a sibling instance is not one
34
+ * this `construct()` will ever resolve against.
33
35
  *
34
36
  * No separate `clock` parameter: `source` already carries its own resolved
35
37
  * clock intrinsically (`Random/Types.ts`'s `Options.clock`, baked in when the
36
38
  * source was built), and `resolveScope`'s chosen source — the active `wrap`
37
39
  * frame's, or this one — is exactly the source whose clock a construction
38
40
  * should resolve "now" against. `toConstructionContext` reads it straight off
39
- * the resolved root rather than threading a second value alongside `source`.
41
+ * the resolved construction trace rather than threading a second value
42
+ * alongside `source`.
40
43
  */
41
- export declare function Constructor(source: RandomSource, stack: Stack): Constructor;
44
+ export declare function Constructor(source: RandomSource, stack: Stack, ancestry: Ancestry): Constructor;
@@ -14,19 +14,20 @@ import type { PlainObject } from "../Utility/Types";
14
14
  * `toTrace` records this node's {@link Trace} — a plain object literal, no
15
15
  * hashing. Hashing is paid only where a kind actually calls
16
16
  * `toStreamFromTrace(algorithm, trace)`. Bound once in `construct()` to this
17
- * one construction's already-resolved `RandomSource`/ `ConstructionTrace` pair
17
+ * one construction's already-resolved `RandomSource`/`ConstructionTrace` pair
18
18
  * (see `Constructor.ts`'s `resolveScope`) — every leaf calls `toTrace` with
19
19
  * only its own structural `path` and kind, never re-resolving the
20
- * construction's root itself. `T.recursive` is the one kind that rebinds
21
- * `toTrace`: each lazy expansion opens its own scope on the node's own private
22
- * forked `RandomSource` (see `recursive/Fabricator.ts`), so a data-dependent
23
- * expansion count can never perturb, or be perturbed by, anything else built
24
- * from the same `initialize()` instance — `RandomSource.fork`
25
- * (`Random/Types.ts`) is the isolation primitive.
20
+ * construction-owned slots itself. `T.recursive` is the one kind that rebinds
21
+ * `toTrace`: each lazy expansion resolves its construction trace on the node's
22
+ * own private forked `RandomSource` (see `recursive/Fabricator.ts`), so a
23
+ * data-dependent expansion count can never perturb, or be perturbed by,
24
+ * anything else built from the same `initialize()` instance —
25
+ * `RandomSource.fork` (`Random/Types.ts`) is the isolation primitive.
26
26
  *
27
27
  * `algorithm` rather than the `RandomSource` itself: stream derivation depends
28
- * on no per-source state, and a leaf has no business with `toRoot`/`fork`.
29
- * `clock` is not a field of its own — it is always `trace.clock`.
28
+ * on no per-source state, and a leaf has no business with
29
+ * `toConstructionTrace`/`fork`. `clock` is not a field of its own — it is
30
+ * always `trace.clock`.
30
31
  *
31
32
  * `self` is what makes `case "recursive.self"` resolve to "recurse one level
32
33
  * deeper, right now" — absent outside any active recursion, which is how `case
@@ -47,11 +48,11 @@ export type ConstructionContext = {
47
48
  * `toStreamFromTrace(algorithm, trace)`. The guard is the call site's own `if
48
49
  * (meta.produce)` branch (or the equivalent drawing path), not an unevaluated
49
50
  * closure. `algorithm` rather than the `RandomSource`: derivation depends on no
50
- * per-source state, and a leaf has no business with `toRoot`/`fork`. No
51
- * `clock`: it is `trace.clock`, always. A kind-specific extra — an array's
52
- * `element`, an object's `fields`, a choice's `weightings` — still follows as
53
- * its own trailing parameter: those vary per kind and were never part of the
54
- * shared prefix this replaces.
51
+ * per-source state, and a leaf has no business with
52
+ * `toConstructionTrace`/`fork`. No `clock`: it is `trace.clock`, always. A
53
+ * kind-specific extra — an array's `element`, an object's `fields`, a choice's
54
+ * `weightings` — still follows as its own trailing parameter: those vary per
55
+ * kind and were never part of the shared prefix this replaces.
55
56
  */
56
57
  export type FabricatorContext<$Schema> = {
57
58
  schema: $Schema;
@@ -2,33 +2,33 @@ import type { Instance } from "../Instance/Types";
2
2
  import type { PlainObject } from "../Utility/Types";
3
3
  import type { FabricatorTestContext, Integration } from "./Types";
4
4
  /**
5
- * Decorate an existing `Instance` as a `@ghostry/harness` integration.
6
- * `around(identity, body)` is `instance.wrap({ salt: layer(saltFor(identity))
7
- * })` — one line of real work. Construction ordinals therefore restart per test
8
- * (each `wrap` re-instantiates), which is what makes `.only`, filters, shards,
9
- * and `.concurrent` unable to shift a neighbor's data. That per-test
10
- * partitioning is also why the salt needs no file in it; see `saltFor`
5
+ * Decorate an existing `Instance` as a `@ghostry/harness` integration. The
6
+ * whole of it is `instance.wrap({ salt: layer(saltFor(identity)) })` — one line
7
+ * of real work. Construction ordinals therefore restart per test (each `wrap`
8
+ * re-instantiates), which is what makes `.only`, filters, shards, and
9
+ * `.concurrent` unable to shift a neighbor's data. That per-test partitioning
10
+ * is also why the salt needs no file in it; see `saltFor`
11
11
  * (`Harnessing/Salt.ts`).
12
12
  *
13
- * `around`, not `setup`: the ambient frame has to enclose the body, and only
14
- * `around` does. Its `finally` running at the call boundary rather than at test
15
- * settlement costs nothing here there is no teardown, and the
16
- * `AsyncLocalStorage` carrier keeps the frame alive across the body's `await`s
17
- * regardless of when `wrap` returns. On the synchronous carrier an async body
18
- * still raises `SynchronousStackError` from `wrap`. Declaring no `setup` also
19
- * keeps `@ghostry/harness` on its uninstrumented path, where a synchronous
20
- * assertion failure is reported at the user's own line.
13
+ * `frame` is a generator so that `yield` can be both where the body runs and
14
+ * where this hook waits. There is nothing after the `yield` here: fabricator
15
+ * has no teardown, and the `AsyncLocalStorage` carrier keeps the ambient frame
16
+ * alive across the body's `await`s on its own. What the `yield` carries is the
17
+ * wrapper, because the ambient frame has to _enclose_ the body rather than
18
+ * merely precede it. On the synchronous carrier an async body still raises
19
+ * `SynchronousStackError` from `wrap`.
21
20
  *
22
- * `provides.fabricator` hands back the scope `wrap` gave its block, not the
23
- * base instance, so `context.fabricator.salt` is the per-test salt and
24
- * `.fork()` forks from the test's configuration. The two calls meet through
25
- * `scope`: set just before `body()`, read by the provider, restored in a
26
- * `finally`. That is safe under `.concurrent` because `@ghostry/harness` runs a
27
- * provider synchronously inside its own integration's `around` no other test
28
- * can enter between the write and the read, and the context object already
29
- * holds the scope by the body's first `await`. Restoring rather than clearing
30
- * keeps a nested `around` from dropping its parent's scope. A provider reached
31
- * with no scope is a composer breaking that contract, and raises
21
+ * `instance.wrap` is handed to the wrapper directly rather than called inside a
22
+ * closure. `wrap(overlay, block)` already takes `(scope) => $Return`, which is
23
+ * exactly the wrapper's own `(established) => $Return`, so the scope `wrap`
24
+ * opens _is_ the established value with nothing in between to adapt it.
25
+ *
26
+ * `provides.fabricator` is then that scope the instance `wrap` gave its
27
+ * block, not the base instance so `context.fabricator.salt` is the per-test
28
+ * salt and `.fork()` forks from the test's configuration. Nothing mediates
29
+ * between the two: the wrapper hands the scope forward and the provider
30
+ * receives it. A provider reached with no established scope is a composer
31
+ * running providers outside the frame it opened, and raises
32
32
  * `HarnessingProviderError` rather than quietly providing the base instance.
33
33
  *
34
34
  * Nothing here reads `instance.context`. The integration is a pure function of
@@ -40,4 +40,4 @@ import type { FabricatorTestContext, Integration } from "./Types";
40
40
  * meaning is that the salt _is_ the reproducibility unit, so per-test clocks
41
41
  * are the request honored, not a bug to override.
42
42
  */
43
- export declare function integration<$Registry extends PlainObject>(instance: Instance<$Registry>): Integration<FabricatorTestContext<$Registry>>;
43
+ export declare function integration<$Registry extends PlainObject>(instance: Instance<$Registry>): Integration<FabricatorTestContext<$Registry>, Instance<$Registry>>;